Index: stable/8/contrib/bind9/CHANGES =================================================================== --- stable/8/contrib/bind9/CHANGES (revision 275668) +++ stable/8/contrib/bind9/CHANGES (revision 275669) @@ -1,11251 +1,11263 @@ +4006. [security] A flaw in delegation handling could be exploited + to put named into an infinite loop. This has + been addressed by placing limits on the number + of levels of recursion named will allow (default 7), + and the number of iterative queries that it will + send (default 50) before terminating a recursive + query (CVE-2014-8500). + + The recursion depth limit is configured via the + "max-recursion-depth" option, and the query limit + via the "max-recursion-queries" option. [RT #37580] + --- 9.8.7 released --- --- 9.8.7rc2 released --- 3710. [bug] Address double dns_zone_detach when switching to using automatic empty zones from regular zones. [RT #35177] 3707. [bug] irs_resconf_load now returns ISC_R_FILENOTFOUND on a missing resolv.conf file and initializes the structure as if it had been configured with: nameserver ::1 nameserver 127.0.0.1 Note: Callers will need to be updated to treat ISC_R_FILENOTFOUND as a qualified success or else they will leak memory. The following code fragment will work with both old and new versions without changing the behaviour of the existing code. resconf = NULL; result = irs_resconf_load(mctx, "/etc/resolv.conf", &resconf); if (result != ISC_SUCCESS) { if (resconf != NULL) irs_resconf_destroy(&resconf); .... } [RT #35194] 3706. [contrib] queryperf: Fixed a possible integer overflow when printing results. [RT #35182] 3704. [protocol] Accept integer timestamps in RRSIG records. [RT #35185] --- 9.8.7rc1 released --- 3701. [func] named-checkconf can now suppress the printing of shared secrets by specifying '-x'. [RT #34465] 3698. [cleanup] Replaced all uses of memcpy() with memmove(). [RT #35120] 3697. [bug] Handle "." as a search list element when IDN support is enabled. [RT #35133] 3696. [bug] dig failed to handle AXFR style IXFR responses which span multiple messages. [RT #35137] 3695. [bug] Address a possible race in dispatch.c. [RT #35107] 3694. [bug] Warn when a key-directory is configured for a zone, but does not exist or is not a directory. [RT #35108] 3693. [security] memcpy was incorrectly called with overlapping ranges resulting in malformed names being generated on some platforms. This could cause INSIST failures when serving NSEC3 signed zones (CVE-2014-0591). [RT #35120] 3692. [bug] Two calls to dns_db_getoriginnode were fatal if there was no data at the node. [RT #35080] 3689. [bug] Fixed a bug causing an insecure delegation from one static-stub zone to another to fail with a broken trust chain. [RT #35081] --- 9.8.7b1 released --- 3688. [bug] loadnode could return a freed node on out of memory. [RT #35106] 3683. [cleanup] Add a more detailed "not found" message to rndc commands which specify a zone name. [RT #35059] 3681. [port] Update the Windows build system to support feature selection and WIN64 builds. This is a work in progress. [RT #34160] 3679. [bug] dig could fail to clean up TCP sockets still waiting on connect(). [RT #35074] 3678. [port] Update config.guess and config.sub. [RT #35060] 3677. [bug] 'nsupdate' leaked memory if 'realm' was used multiple times. [RT #35073] 3676. [bug] "named-checkconf -z" now checks zones of type hint as well as master. [RT #35046] 3675. [misc] Provide a place for third parties to add version information for their extensions in the version file by setting the EXTENSIONS variable. 3670. [bug] Address read after free in server side of lwres_getrrsetbyname. [RT #29075] 3669. [port] freebsd: --with-gssapi needs -lhx509. [RT #35001] 3668. [bug] Fix cast in lex.c which could see 0xff treated as eof. [RT #34993] 3667. [test] dig: add support to keep the TCP socket open between successive queries (+[no]keepopen). [RT #34918] 3664. [bug] Updated OpenSSL PKCS#11 patches to fix active list locking and other bugs. [RT #34855] 3663. [bug] Address bugs in dns_rdata_fromstruct and dns_rdata_tostruct for WKS and ISDN types. [RT #34910] 3662. [bug] 'host' could die if a UDP query timed out. [RT #34870] 3660. [cleanup] Changed the name of "isc-config.sh" to "bind9-config". [RT #23825] 3658. [port] linux: Address platform specific compilation issue when libcap-devel is installed. [RT #34838] 3656. [security] Treat an all zero netmask as invalid when generating the localnets acl. (The prior behavior could allow unexpected matches when using some versions of Winsock: CVE-2013-6320.) [RT #34687] 3655. [cleanup] Simplify TCP message processing when requesting a zone transfer. [RT #34825] 3654. [bug] Address race condition with manual notify requests. [RT #34806] 3653. [func] Create delegations for all "children" of empty zones except "forward first". [RT #34826] 3651. [tuning] Adjust when a master server is deemed unreachable. [RT #27075] 3650. [tuning] Use separate rate limiting queues for refresh and notify requests. [RT #30589] 3649. [cleanup] Include a comment in .nzf files, giving the name of the associated view. [RT #34765] 3648. [test] Updated the ATF test framework to version 0.17. [RT #25627] 3646. [bug] Journal filename string could be set incorrectly, causing garbage in log messages. [RT #34738] 3645. [protocol] Use case sensitive compression when responding to queries. [RT #34737] 3644. [protocol] Check that EDNS subnet client options are well formed. [RT #34718] 3641. [bug] Handle changes to sig-validity-interval settings better. [RT #34625] 3640. [bug] ndots was not being checked when searching. Only continue searching on NXDOMAIN responses. Add the ability to specify ndots to nslookup. [RT #34711] 3639. [bug] Treat type 65533 (KEYDATA) as opaque except when used in a key zone. [RT #34238] --- 9.8.6 released --- 3638. [cleanup] Add the ability to handle ENOPROTOOPT in case it is encountered. [RT #34668] --- 9.8.6rc2 released --- 3637. [bug] 'allow-query-on' was checking the source address rather than the destination address. [RT #34590] 3636. [bug] Automatic empty zones now behave better with forward only "zones" beneath them. [RT #34583] 3635. [bug] Signatures were not being removed from a zone with only KSK keys for a algorithm. [RT #34439] 3634. [func] Report build-id in rndc status. Report build-id when building from a git repository. [RT #20422] 3633. [cleanup] Refactor OPT processing in named to make it easier to support new EDNS options. [RT #34414] 3632. [bug] Signature from newly inactive keys were not being removed. [RT #32178] 3631. [bug] Remove spurious warning about missing signatures when qtype is SIG. [RT #34600] 3630. [bug] Ensure correct ID computation for MD5 keys. [RT #33033] 3627. [bug] RPZ changes were not effective on slaves. [RT #34450] 3625. [bug] Don't send notify messages to machines outside of the test setup. --- 9.8.6rc1 released --- 3621. [security] Incorrect bounds checking on private type 'keydata' can lead to a remotely triggerable REQUIRE failure (CVE-2013-4854). [RT #34238] 3615. [cleanup] "configure" now finishes by printing a summary of optional BIND features and whether they are active or inactive. ("configure --enable-full-report" increases the verbosity of the summary.) [RT #31777] 3614. [port] Check for . [RT #34162] 3611. [bug] Improved resistance to a theoretical authentication attack based on differential timing. [RT #33939] 3610. [cleanup] win32: Some executables had been omitted from the installer. [RT #34116] 3608. [port] win32: added todos.pl script to ensure all text files the win32 build depends on are converted to DOS newline format. [RT #22067] 3607. [bug] dnssec-keygen had broken 'Invalid keyfile' error message. [RT #34045] --- 9.8.6b1 released --- 3605. [port] win32: Addressed several compatibility issues with newer versions of Visual Studio. [RT #33916] 3603. [bug] Install . [RT #33956] 3601. [bug] Added to PKCS#11 openssl patches a value len attribute in DH derive key. [RT #33928] 3600. [cleanup] dig: Fixed a typo in the warning output when receiving an oversized response. [RT #33910] 3599. [tuning] Check for pointer equivalence in name comparisons. [RT #18125] 3594. [maint] Update config.guess and config.sub. [RT #33816] 3592. [doc] Moved documentation of rndc command options to the rndc man page. [RT #33506] 3588. [bug] dig: addressed a memory leak in the sigchase code that could cause a shutdown crash. [RT #33733] 3587. [func] 'named -g' now checks the logging configuration but does not use it. [RT #33473] 3586. [bug] Handle errors in xmlDocDumpFormatMemoryEnc. [RT #33706] 3584. [security] Caching data from an incompletely signed zone could trigger an assertion failure in resolver.c (CVE-2013-3919). [RT #33690] 3583. [bug] Address memory leak in GSS-API processing [RT #33574] 3581. [bug] Changed the tcp-listen-queue default to 10. [RT #33029] 3580. [bug] Addressed a possible race in acache.c [RT #33602] 3579. [maint] Updates to PKCS#11 openssl patches, supporting versions 0.9.8y, 1.0.0k, 1.0.1e [RT #33463] 3578. [bug] 'rndc -c file' now fails if 'file' does not exist. [RT #33571] 3577. [bug] Handle zero TTL values better. [RT #33411] 3576. [bug] Address a shutdown race when validating. [RT #33573] 3574. [doc] The 'hostname' keyword was missing from server-id description in the named.conf man page. [RT #33476] 3573. [bug] "rndc addzone" and "rndc delzone" incorrectly handled zone names containing punctuation marks and other nonstandard characters. [RT #33419] 3571. [bug] Address race condition in dns_client_startresolve(). [RT #33234] 3566. [func] Log when forwarding updates to master. [RT #33240] --- 9.8.5 released --- 3568. [cleanup] Add a product description line to the version file, to be reported by named -v/-V. [RT #33366] 3567. [bug] Silence clang static analyzer warnings. [RT #33365] 3563. [contrib] zone2sqlite failed with some table names. [RT #33375] 3561. [bug] dig: issue a warning if an EDNS query returns FORMERR or NOTIMP. Adjust usage message. [RT #33363] --- 9.8.5rc1 released --- 3560. [bug] isc-config.sh did not honor includedir and libdir when set via configure. [RT #33345] 3559. [func] Check that both forms of Sender Policy Framework records exist or do not exist. [RT #33355] 3558. [bug] IXFR of a DLZ stored zone was broken. [RT #33331] 3556. [maint] Added AAAA for D.ROOT-SERVERS.NET. 3555. [bug] Address theoretical race conditions in acache.c (change #3553 was incomplete). [RT #33252] 3553. [bug] Address suspected double free in acache. [RT #33252] 3552. [bug] Wrong getopt option string for 'nsupdate -r'. [RT #33280] 3549. [doc] Documentation for "request-nsid" was missing. [RT #33153] 3548. [bug] The NSID request code in resolver.c was broken resulting in invalid EDNS options being sent. [RT #33153] 3547. [bug] Some malformed unknown rdata records were not properly detected and rejected. [RT #33129] 3056. [func] Added support for URI resource record. [RT #23386] --- 9.8.5rc1 released --- 3546. [func] Add EUI48 and EUI64 types. [RT #33082] 3544. [contrib] check5011.pl: Script to report the status of managed keys as recorded in managed-keys.bind. Contributed by Tony Finch 3543. [bug] Update socket structure before attaching to socket manager after accept. [RT #33084] 3542. [bug] masterformat system test was broken. [RT #33086] 3541. [bug] Parts of libdns were not properly initialized when built in libexport mode. [RT #33028] 3540. [test] libt_api: t_info and t_assert were not thread safe. 3539. [port] win32: timestamp format didn't match other platforms. 3538. [test] Running "make test" now requires loopback interfaces to be set up. [RT #32452] 3537. [tuning] Slave zones, when updated, now send NOTIFY messages to peers before being dumped to disk rather than after. [RT #27242] 3535. [bug] Minor win32 cleanups. [RT #32962] 3534. [bug] Extra text after an embedded NULL was ignored when parsing zone files. [RT #32699] 3533. [contrib] query-loc-0.4.0: memory leaks. [RT #32960] 3532. [contrib] zkt: fixed buffer overrun, resource leaks. [RT #32960] 3531. [bug] win32: A uninitialized value could be returned on out of memory. [RT #32960] 3530. [contrib] Better RTT tracking in queryperf. [RT #30128] 3526. [cleanup] Set up dependencies for unit tests correctly during build. [RT #32803] 3521. [bug] Address memory leak in opensslecdsa_link.c. [RT #32249] 3520. [bug] 'mctx' was not being referenced counted in some places where it should have been. [RT #32794] --- 9.8.5b2 released --- 3517. [bug] Reorder destruction to avoid shutdown race. [RT #32777] 3515. [port] '%T' is not portable in strftime(). [RT #32763] 3514. [bug] The ranges for valid key sizes in ddns-confgen and rndc-confgen were too constrained. Keys up to 512 bits are now allowed for most algorithms, and up to 1024 bits for hmac-sha384 and hmac-sha512. [RT #32753] 3509. [cleanup] Added a product line to version file to allow for easy naming of different products (BIND vs BIND ESV, for example). [RT #32755] 3508. [contrib] queryperf was incorrectly rejecting the -T option. [RT #32338] 3503. [doc] Clarify size_spec syntax. [RT #32449] 3500. [security] Support NAPTR regular expression validation on all platforms without using libregex, which can be vulnerable to memory exhaustion attack (CVE-2013-2266). [RT #32688] 3499. [doc] Corrected ARM documentation of built-in zones. [RT #32694] 3498. [bug] zone statistics for zones which matched a potential empty zone could have their zone-statistics setting overridden. 3496. [func] Improvements to RPZ performance. The "response-policy" syntax now includes a "min-ns-dots" clause, with default 1, to exclude top-level domains from NSIP and NSDNAME checking. --enable-rpz-nsip and --enable-rpz-nsdname are now the default. [RT #32251] 3489. [bug] --enable-developer now turns on ISC_LIST_CHECKINIT. When cloning a rdataset do not copy the link contents. [RT #32651] 3488. [bug] Use after free error with DH generated keys. [RT #32649] 3487. [bug] Change 3444 was not complete. There was a additional place where the NOQNAME proof needed to be saved. [RT #32629] 3486. [bug] named could crash when using TKEY-negotiated keys that had been deleted and then recreated. [RT #32506] 3485. [cleanup] Only compile openssl_gostlink.c if we support GOST. 3481. [cleanup] Removed use of const const in atf. 3479. [bug] Address potential memory leaks in gssapi support code. [RT #32405] 3478. [port] Fix a build failure in strict C99 environments [RT #32475] 3474. [bug] nsupdate could assert when the local and remote address families didn't match. [RT #22897] 3470. [bug] Slave zones could fail to dump when successfully refreshing after an initial failure. [RT #31276] --- 9.8.5b1 released --- 3468. [security] RPZ rules to generate A records (but not AAAA records) could trigger an assertion failure when used in conjunction with DNS64 (CVE-2012-5689). [RT #32141] 3467. [bug] Added checks in dnssec-keygen and dnssec-settime to check for delete date < inactive date. [RT #31719] 3465. [bug] Handle isolated reserved ports. [RT #31778] 3464. [maint] Updates to PKCS#11 openssl patches, supporting versions 0.9.8x, 1.0.0j, 1.0.1c [RT #29749] 3463. [doc] Clarify managed-keys syntax in ARM. [RT #32232] 3462. [doc] Clarify server selection behavior of dig when using -4 or -6 options. [RT #32181] 3461. [bug] Negative responses could incorrectly have AD=1 set. [RT #32237] 3458. [bug] Return FORMERR when presented with a overly long domain named in a request. [RT #29682] 3457. [protocol] Add ILNP records (NID, LP, L32, L64). [RT #31836] 3456. [port] g++47: ATF failed to compile. [RT #32012] 3455. [contrib] queryperf: fix getopt option list. [RT #32338] 3454. [port] sparc64: improve atomic support. [RT #25182] 3452. [bug] Accept duplicate singleton records. [RT #32329] 3451. [port] Increase per thread stack size from 64K to 1M. [RT #32230] 3450. [bug] Stop logfileconfig system test spam system logs. [RT #32315] 3449. [bug] gen.c: use the pre-processor to construct format strings so that compiler can perform sanity checks; check the snprintf results. [RT #17576] 3448. [bug] The allow-query-on ACL was not processed correctly. [RT #29486] 3447. [port] Add support for libxml2-2.9.x [RT #32231] 3446. [port] win32: Add source ID (see change #3400) to build. [RT #31683] 3445. [bug] Warn about zone files with blank owner names immediately after $ORIGIN directives. [RT #31848] 3444. [bug] The NOQNAME proof was not being returned from cached insecure responses. [RT #21409] 3443. [bug] ddns-confgen: Some TSIG algorithms were incorrectly rejected when generating keys. [RT #31927] 3442. [port] Net::DNS 0.69 introduced a non backwards compatible change. [RT #32216] 3441. [maint] D.ROOT-SERVERS.NET is now 199.7.91.13. 3440. [bug] Reorder get_key_struct to not trigger a assertion when cleaning up due to out of memory error. [RT #32131] 3439. [bug] contrib/dlz error checking fixes. [RT #32102] 3438. [bug] Don't accept unknown data escape in quotes. [RT #32031] 3437. [bug] isc_buffer_init -> isc_buffer_constinit to initialize buffers with constant data. [RT #32064] 3436. [bug] Check malloc/calloc return values. [RT #32088] 3435. [bug] Cross compilation support in configure was broken. [RT #32078] 3431. [bug] ddns-confgen: Some valid key algorithms were not accepted. [RT #31927] 3430. [bug] win32: isc_time_formatISO8601 was missing the 'T' between the date and time. [RT #32044] 3429. [bug] dns_zone_getserial2 could a return success without returning a valid serial. [RT #32007] 3428. [cleanup] dig: Add timezone to date output. [RT #2269] 3427. [bug] dig +trace incorrectly displayed name server addresses instead of names. [RT #31641] 3425. [bug] "acacheentry" reference counting was broken resulting in use after free. [RT #31908] 3422. [bug] Added a clear error message for when the SOA does not match the referral. [RT #31281] 3421. [bug] Named loops when re-signing if all keys are offline. [RT #31916] 3420. [bug] Address VPATH compilation issues. [RT #31879] 3419. [bug] Memory leak on validation cancel. [RT #31869] 3415. [bug] named could die with a REQUIRE failure if a validation was canceled. [RT #31804] 3412. [bug] Copy timeval structure from control message data. [RT #31548] 3411. [tuning] Use IPV6_USE_MIN_MTU or equivalent with TCP in addition to UDP. [RT #31690] 3410. [bug] Addressed Coverity warnings. [RT #31626] 3409. [contrib] contrib/dane/mkdane.sh: Tool to generate TLSA RR's from X.509 certificates, for use with DANE (DNS-based Authentication of Named Entities). [RT #30513] 3406. [bug] mem.c: Fix compilation errors when building with ISC_MEM_TRACKLINES or ISC_MEMPOOL_NAMES disabled. Also, ISC_MEM_DEBUG is no longer optional. [RT #31559] 3405. [bug] Handle time going backwards in acache. [RT #31253] 3404. [bug] dnssec-signzone: When re-signing a zone, remove RRSIG and NSEC records from nodes that used to be in-zone but are now below a zone cut. [RT #31556] 3403. [bug] Silence noisy OpenSSL logging. [RT #31497] 3402. [test] The IPv6 interface numbers used for system tests were incorrect on some platforms. [RT #25085] 3401. [bug] Addressed Coverity warnings. [RT #31484] 3400. [cleanup] "named -V" can now report a source ID string, defined in the "srcid" file in the build tree and normally set to the most recent git hash. [RT #31494] 3397. [bug] dig crashed when using +nssearch with +tcp. [RT #25298] 3396. [bug] OPT records were incorrectly removed from signed, truncated responses. [RT #31439] 3395. [protocol] Add RFC 6598 reverse zones to built in empty zones list, 64.100.IN-ADDR.ARPA ... 127.100.IN-ADDR.ARPA. [RT #31336] 3394. [bug] Adjust 'successfully validated after lower casing signer' log level and category. [RT #31414] 3393. [bug] 'host -C' could core dump if REFUSED was received. [RT #31381] 3391. [bug] A DNSKEY lookup that encountered a CNAME failed. [RT #31262] 3390. [bug] Silence clang compiler warnings. [RT #30417] 3389. [bug] Always return NOERROR (not 0) in TSIG. [RT #31275] 3388. [bug] Fixed several Coverity warnings. Note: This change includes a fix for a bug that was subsequently determined to be an exploitable security vulnerability, CVE-2012-5688: named could die on specific queries with dns64 enabled. [RT #30996] 3386. [bug] Address locking violation when generating new NSEC / NSEC3 chains. [RT #31224] 3384. [bug] Improved logging of crypto errors. [RT #30963] 3383. [security] A certain combination of records in the RBT could cause named to hang while populating the additional section of a response. [RT #31090] 3382. [bug] SOA query from slave used use-v6-udp-ports range, if set, regardless of the address family in use. [RT #24173] 3381. [contrib] Update queryperf to support more RR types. [RT #30762] 3380. [bug] named could die if a nonexistent master list was referenced in a also-notify. [RT #31004] 3379. [bug] isc_interval_zero and isc_time_epoch should be "const (type)* const". [RT #31069] 3378. [bug] Handle missing 'managed-keys-directory' better. [RT #30625] 3376. [bug] Lack of EDNS support was being recorded without a successful response. [RT #30811] 3375. [func] Check that 'rndc dumpdb' works on a empty cache. [RT #30808] 3374. [bug] isc_parse_uint32 failed to return a range error on systems with 64 bit longs. [RT #30232] 3372. [bug] Silence spurious "deleted from unreachable cache" messages. [RT #30501] 3371. [bug] AD=1 should behave like DO=1 when deciding whether to add NS RRsets to the additional section or not. [RT #30479] --- 9.8.4 released --- 3373. [bug] win32: open raw files in binary mode. [RT #30944] 3364. [security] Named could die on specially crafted record. [RT #30416] --- 9.8.4rc1 released --- 3369. [bug] nsupdate terminated unexpectedly in interactive mode if built with readline support. [RT #29550] 3368. [bug] and were not C++ safe. 3367. [bug] dns_dnsseckey_create() result was not being checked. [RT #30685] 3366. [bug] Fixed Read-After-Write dependency violation for IA64 atomic operations. [RT #25181] 3365. [bug] Removed spurious newlines from log messages in zone.c [RT #30675] 3363. [bug] Need to allow "forward" and "fowarders" options in static-stub zones; this had been overlooked. [RT #30482] 3362. [bug] Setting some option values to 0 in named.conf could trigger an assertion failure on startup. [RT #27730] 3360. [bug] 'host -w' could die. [RT #18723] 3359. [bug] An improperly-formed TSIG secret could cause a memory leak. [RT #30607] 3357. [port] Add support for libxml2-2.8.x [RT #30440] 3356. [bug] Cap the TTL of signed RRsets when RRSIGs are approaching their expiry, so they don't remain in caches after expiry. [RT #26429] --- 9.8.4b1 released --- 3354. [func] Improve OpenSSL error logging. [RT #29932] 3353. [bug] Use a single task for task exclusive operations. [RT #29872] 3352. [bug] Ensure that learned server attributes timeout of the adb cache. [RT #29856] 3351. [bug] isc_mem_put and isc_mem_putanddetach didn't report caller if either ISC_MEM_DEBUGSIZE or ISC_MEM_DEBUGCTX memory debugging flags are set. [RT #30243] 3350. [bug] Memory read overrun in isc___mem_reallocate if ISC_MEM_DEBUGCTX memory debugging flag is set. [RT #30240] 3348. [bug] Prevent RRSIG data from being cached if a negative record matching the covering type exists at a higher trust level. Such data already can't be retrieved from the cache since change 3218 -- this prevents it being inserted into the cache as well. [RT #26809] 3347. [bug] dnssec-settime: Issue a warning when writing a new private key file would cause a change in the permissions of the existing file. [RT #27724] 3346. [security] Bad-cache data could be used before it was initialized, causing an assert. [RT #30025] 3342. [bug] Change #3314 broke saving of stub zones to disk resulting in excessive cpu usage in some cases. [RT #29952] 3337. [bug] Change #3294 broke support for the multiple keys in controls. [RT #29694] 3335. [func] nslookup: return a nonzero exit code when unable to get an answer. [RT #29492] 3333. [bug] Setting resolver-query-timeout too low can cause named to not recover if it loses connectivity. [RT #29623] 3332. [bug] Re-use cached DS rrsets if possible. [RT #29446] 3331. [security] dns_rdataslab_fromrdataset could produce bad rdataslabs. [RT #29644] 3330. [func] Fix missing signatures on NOERROR results despite RPZ rewriting. Also - add optional "recursive-only yes|no" to the response-policy statement - add optional "max-policy-ttl" to the response-policy statement to limit the false data that "recursive-only no" can introduce into resolvers' caches - add a RPZ performance test to bin/tests/system/rpz when queryperf is available. - the encoding of PASSTHRU action to "rpz-passthru". (The old encoding is still accepted.) [RT #26172] 3329. [bug] Handle RRSIG signer-name case consistently: We generate RRSIG records with the signer-name in lower case. We accept them with any case, but if they fail to validate, we try again in lower case. [RT #27451] 3328. [bug] Fixed inconsistent data checking in dst_parse.c. [RT #29401] 3317. [func] Add ECDSA support (RFC 6605). [RT #21918] --- 9.8.3 released --- 3318. [tuning] Reduce the amount of work performed while holding a bucket lock when finished with a fetch context. [RT #29239] 3314. [bug] The masters list could be updated while stub_callback or refresh_callback were using it. [RT #26732] 3313. [protocol] Add TLSA record type. [RT #28989] 3312. [bug] named-checkconf didn't detect a bad dns64 clients acl. [RT #27631] 3311. [bug] Abort the zone dump if zone->db is NULL in zone.c:zone_gotwritehandle. [RT #29028] 3310. [test] Increase table size for mutex profiling. [RT #28809] 3309. [bug] resolver.c:fctx_finddone() was not thread safe. [RT #27995] 3307. [bug] Add missing ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS. [RT #28956] 3306. [bug] Improve DNS64 reverse zone performance. [RT #28563] 3305. [func] Add wire format lookup method to sdb. [RT #28563] 3304. [bug] Use hmctx, not mctx when freeing rbtdb->heaps. [RT #28571] 3302. [bug] dns_dnssec_findmatchingkeys could fail to find keys if the zone name contained character that required special mappings. [RT #28600] 3301. [contrib] Update queryperf to build on darwin. Add -R flag for non-recursive queries. [RT #28565] 3300. [bug] Named could die if gssapi was enabled in named.conf but was not compiled in. [RT #28338] 3299. [bug] Make SDB handle errors from database drivers better. [RT #28534] 3232. [bug] Zero zone->curmaster before return in dns_zone_setmasterswithkeys(). [RT #26732] 3183. [bug] Added RTLD_GLOBAL flag to dlopen call. [RT #26301] 3197. [bug] Don't try to log the filename and line number when the config parser can't open a file. [RT #22263] --- 9.8.2 released --- 3298. [bug] Named could dereference a NULL pointer in zmgr_start_xfrin_ifquota if the zone was being removed. [RT #28419] 3297. [bug] Named could die on a malformed master file. [RT #28467] 3295. [bug] Adjust isc_time_secondsastimet range check to be more portable. [RT # 26542] 3294. [bug] isccc/cc.c:table_fromwire failed to free alist on error. [RT #28265] 3291. [port] Fixed a build error on systems without ENOTSUP. [RT #28200] 3290. [bug] was not being installed. [RT #28169] 3288. [bug] dlz_destroy() function wasn't correctly registered by the DLZ dlopen driver. [RT #28056] 3287. [port] Update ans.pl to work with Net::DNS 0.68. [RT #28028] 3286. [bug] Managed key maintenance timer could fail to start after 'rndc reconfig'. [RT #26786] --- 9.8.2rc2 released --- 3285. [bug] val-frdataset was incorrectly disassociated in proveunsecure after calling startfinddlvsep. [RT #27928] 3284. [bug] Address race conditions with the handling of rbtnode.deadlink. [RT #27738] 3283. [bug] Raw zones with with more than 512 records in a RRset failed to load. [RT #27863] 3282. [bug] Restrict the TTL of NS RRset to no more than that of the old NS RRset when replacing it. [RT #27792] [RT #27884] 3281. [bug] SOA refresh queries could be treated as cancelled despite succeeding over the loopback interface. [RT #27782] 3280. [bug] Potential double free of a rdataset on out of memory with DNS64. [RT #27762] 3278. [bug] Make sure automatic key maintenance is started when "auto-dnssec maintain" is turned on during "rndc reconfig". [RT #26805] 3276. [bug] win32: ns_os_openfile failed to return NULL on safe_open failure. [RT #27696] 3274. [bug] Log when a zone is not reusable. Only set loadtime on successful loads. [RT #27650] 3273. [bug] AAAA responses could be returned in the additional section even when filter-aaaa-on-v4 was in use. [RT #27292] 3271. [port] darwin: mksymtbl is not always stable, loop several times before giving up. mksymtbl was using non portable perl to covert 64 bit hex strings. [RT #27653] 3268. [bug] Convert RRSIG expiry times to 64 timestamps to work out the earliest expiry time. [RT #23311] 3267. [bug] Memory allocation failures could be mis-reported as unexpected error. New ISC_R_UNSET result code. [RT #27336] 3266. [bug] The maximum number of NSEC3 iterations for a DNSKEY RRset was not being properly computed. [RT #26543] 3262. [bug] Signed responses were handled incorrectly by RPZ. [RT #27316] --- 9.8.2rc1 released --- 3260. [bug] "rrset-order cyclic" could appear not to rotate for some query patterns. [RT #27170/27185] 3259. [bug] named-compilezone: Suppress "dump zone to " message when writing to stdout. [RT #27109] 3258. [test] Add "forcing full sign with unreadable keys" test. [RT #27153] 3257. [bug] Do not generate a error message when calling fsync() in a pipe or socket. [RT #27109] 3256. [bug] Disable empty zones for lwresd -C. [RT #27139] 3254. [bug] Set isc_socket_ipv6only() on the IPv6 control channels. [RT #22249] 3253. [bug] Return DNS_R_SYNTAX when the input to a text field is too long. [RT #26956] 3251. [bug] Enforce a upper bound (65535 bytes) on the amount of memory dns_sdlz_putrr() can allocate per record to prevent run away memory consumption on ISC_R_NOSPACE. [RT #26956] 3250. [func] 'configure --enable-developer'; turn on various configure options, normally off by default, that we want developers to build and test with. [RT #27103] 3249. [bug] Update log message when saving slave zones files for analysis after load failures. [RT #27087] 3248. [bug] Configure options --enable-fixed-rrset and --enable-exportlib were incompatible with each other. [RT #27087] 3247. [bug] 'raw' format zones failed to preserve load order breaking 'fixed' sort order. [RT #27087] 3243. [port] netbsd,bsdi: the thread defaults were not being properly set. 3241. [bug] Address race conditions in the resolver code. [RT #26889] 3240. [bug] DNSKEY state change events could be missed. [RT #26874] 3239. [bug] dns_dnssec_findmatchingkeys needs to use a consistent timestamp. [RT #26883] 3238. [bug] keyrdata was not being reinitialized in lib/dns/rbtdb.c:iszonesecure. [RT#26913] 3237. [bug] dig -6 didn't work with +trace. [RT #26906] --- 9.8.2b1 released --- 3234. [bug] 'make depend' produced invalid makefiles. [RT #26830] 3231. [bug] named could fail to send a incompressible zone. [RT #26796] 3230. [bug] 'dig axfr' failed to properly handle a multi-message axfr with a serial of 0. [RT #26796] 3229. [bug] Fix local variable to struct var assignment found by CLANG warning. 3228. [tuning] Dynamically grow symbol table to improve zone loading performance. [RT #26523] 3227. [bug] Interim fix to make WKS's use of getprotobyname() and getservbyname() self thread safe. [RT #26232] 3226. [bug] Address minor resource leakages. [RT #26624] 3221. [bug] Fixed a potential core dump on shutdown due to referencing fetch context after it's been freed. [RT #26720] 3220. [bug] Change #3186 was incomplete; dns_db_rpz_findips() could fail to set the database version correctly, causing an assertion failure. [RT #26180] 3218. [security] Cache lookup could return RRSIG data associated with nonexistent records, leading to an assertion failure. [RT #26590] 3217. [cleanup] Fix build problem with --disable-static. [RT #26476] 3216. [bug] resolver.c:validated() was not thread-safe. [RT #26478] 3213. [doc] Clarify ixfr-from-differences behavior. [RT #25188] 3212. [bug] rbtdb.c: failed to remove a node from the deadnodes list prior to adding a reference to it leading a possible assertion failure. [RT #23219] 3209. [func] Add "dnssec-lookaside 'no'". [RT #24858] 3208. [bug] 'dig -y' handle unknown tsig algorithm better. [RT #25522] 3207. [contrib] Fixed build error in Berkeley DB DLZ module. [RT #26444] 3206. [cleanup] Add ISC information to log at start time. [RT #25484] 3204. [bug] When a master server that has been marked as unreachable sends a NOTIFY, mark it reachable again. [RT #25960] 3203. [bug] Increase log level to 'info' for validation failures from expired or not-yet-valid RRSIGs. [RT #21796] 3200. [doc] Some rndc functions were undocumented or were missing from 'rndc -h' output. [RT #25555] 3198. [doc] Clarified that dnssec-settime can alter keyfile permissions. [RT #24866] 3196. [bug] nsupdate: return nonzero exit code when target zone doesn't exist. [RT #25783] 3195. [cleanup] Silence "file not found" warnings when loading managed-keys zone. [RT #26340] 3194. [doc] Updated RFC references in the 'empty-zones-enable' documentation. [RT #25203] 3193. [cleanup] Changed MAXZONEKEYS to DNS_MAXZONEKEYS, moved to dnssec.h. [RT #26415] 3192. [bug] A query structure could be used after being freed. [RT #22208] 3191. [bug] Print NULL records using "unknown" format. [RT #26392] 3190. [bug] Underflow in error handling in isc_mutexblock_init. [RT #26397] 3189. [test] Added a summary report after system tests. [RT #25517] 3188. [bug] zone.c:zone_refreshkeys() could fail to detach references correctly when errors occurred, causing a hang on shutdown. [RT #26372] 3187. [port] win32: support for Visual Studio 2008. [RT #26356] 3186. [bug] Version/db mis-match in rpz code. [RT #26180] 3179. [port] kfreebsd: build issues. [RT #26273] 3175. [bug] Fix how DNSSEC positive wildcard responses from a NSEC3 signed zone are validated. Stop sending a unnecessary NSEC3 record when generating such responses. [RT #26200] 3174. [bug] Always compute to revoked key tag from scratch. [RT #26186] 3173. [port] Correctly validate root DS responses. [RT #25726] 3171. [bug] Exclusively lock the task when adding a zone using 'rndc addzone'. [RT #25600] 3170. [func] RPZ update: - fix precedence among competing rules - improve ARM text including documenting rule precedence - try to rewrite CNAME chains until first hit - new "rpz" logging channel - RDATA for CNAME rules can include wildcards - replace "NO-OP" named.conf policy override with "PASSTHRU" and add "DISABLED" override ("NO-OP" is still recognized) [RT #25172] 3169. [func] Catch db/version mis-matches when calling dns_db_*(). [RT #26017] 3167. [bug] Negative answers from forwarders were not being correctly tagged making them appear to not be cached. [RT #25380] 3162. [test] start.pl: modified to allow for "named.args" in ns*/ subdirectory to override stock arguments to named. Largely from RT#26044, but no separate ticket. 3161. [bug] zone.c:del_sigs failed to always reset rdata leading assertion failures. [RT #25880] 3157. [tuning] Reduce the time spent in "rndc reconfig" by parsing the config file before pausing the server. [RT #21373] 3155. [bug] Fixed a build failure when using contrib DLZ drivers (e.g., mysql, postgresql, etc). [RT #25710] 3154. [bug] Attempting to print an empty rdataset could trigger an assert. [RT #25452] 3152. [cleanup] Some versions of gcc and clang failed due to incorrect use of __builtin_expect. [RT #25183] 3151. [bug] Queries for type RRSIG or SIG could be handled incorrectly. [RT #21050] 3148. [bug] Processing of normal queries could be stalled when forwarding a UPDATE message. [RT #24711] 3146. [test] Fixed gcc4.6.0 errors in ATF. [RT #25598] 3145. [test] Capture output of ATF unit tests in "./atf.out" if there were any errors while running them. [RT #25527] 3144. [bug] dns_dbiterator_seek() could trigger an assert when used with a nonexistent database node. [RT #25358] 3143. [bug] Silence clang compiler warnings. [RT #25174] 3139. [test] Added tests from RFC 6234, RFC 2202, and RFC 1321 for the hashing algorithms (md5, sha1 - sha512, and their hmac counterparts). [RT #25067] --- 9.8.1 released --- --- 9.8.1rc1 released --- 3141. [bug] Silence spurious "zone serial (0) unchanged" messages associated with empty zones. [RT #25079] 3138. [bug] Address memory leaks and out-of-order operations when shutting named down. [RT #25210] 3136. [func] Add RFC 1918 reverse zones to the list of built-in empty zones switched on by the 'empty-zones-enable' option. [RT #24990] Note: empty-zones-enable must be "yes;" or a empty zone needs to be disabled in named.conf for RFC 1918 zones to be activated. This requirement may be removed in future releases. 3135. [port] FreeBSD: workaround broken IPV6_USE_MIN_MTU processing. See http://www.freebsd.org/cgi/query-pr.cgi?pr=158307 [RT #24950] 3134. [bug] Improve the accuracy of dnssec-signzone's signing statistics. [RT #16030] --- 9.8.1b3 released --- 3133. [bug] Change #3114 was incomplete. [RT #24577] 3131. [tuning] Improve scalability by allocating one zone task per 100 zones at startup time, rather than using a fixed-size task table. [RT #24406] 3129. [bug] Named could crash on 'rndc reconfig' when allow-new-zones was set to yes and named ACLs were used. [RT #22739] --- 9.8.1b2 released --- 3126. [security] Using DNAME record to generate replacements caused RPZ to exit with a assertion failure. [RT #24766] 3125. [security] Using wildcard CNAME records as a replacement with RPZ caused named to exit with a assertion failure. [RT #24715] 3124. [bug] Use an rdataset attribute flag to indicate negative-cache records rather than using rrtype 0; this will prevent problems when that rrtype is used in actual DNS packets. [RT #24777] 3123. [security] Change #2912 exposed a latent flaw in dns_rdataset_totext() that could cause named to crash with an assertion failure. [RT #24777] 3122. [cleanup] dnssec-settime: corrected usage message. [RT #24664] 3121. [security] An authoritative name server sending a negative response containing a very large RRset could trigger an off-by-one error in the ncache code and crash named. [RT #24650] 3120. [bug] Named could fail to validate zones listed in a DLV that validated insecure without using DLV and had DS records in the parent zone. [RT #24631] 3119. [bug] When rolling to a new DNSSEC key, a private-type record could be created and never marked complete. [RT #23253] 3118. [bug] nsupdate could dump core on shutdown when using SIG(0) keys. [RT #24604] 3117. [cleanup] Remove doc and parser references to the never-implemented 'auto-dnssec create' option. [RT #24533] 3115. [bug] Named could fail to return requested data when following a CNAME that points into the same zone. [RT #24455] 3114. [bug] Retain expired RRSIGs in dynamic zones if key is inactive and there is no replacement key. [RT #23136] 3113. [doc] Document the relationship between serial-query-rate and NOTIFY messages. --- 9.8.1b1 released --- 3112. [doc] Add missing descriptions of the update policy name types "ms-self", "ms-subdomain", "krb5-self" and "krb5-subdomain", which allow machines to update their own records, to the BIND 9 ARM. 3111. [bug] Improved consistency checks for dnssec-enable and dnssec-validation, added test cases to the checkconf system test. [RT #24398] 3110. [bug] dnssec-signzone: Wrong error message could appear when attempting to sign with no KSK. [RT #24369] 3107. [bug] dnssec-signzone: Report the correct number of ZSKs when using -x. [RT #20852] 3105. [bug] GOST support can be suppressed by "configure --without-gost" [RT #24367] 3104. [bug] Better support for cross-compiling. [RT #24367] 3103. [bug] Configuring 'dnssec-validation auto' in a view instead of in the options statement could trigger an assertion failure in named-checkconf. [RT #24382] 3101. [bug] Zones using automatic key maintenance could fail to check the key repository for updates. [RT #23744] 3100. [security] Certain response policy zone configurations could trigger an INSIST when receiving a query of type RRSIG. [RT #24280] 3099. [test] "dlz" system test now runs but gives R:SKIPPED if not compiled with --with-dlz-filesystem. [RT #24146] 3098. [bug] DLZ zones were answering without setting the AA bit. [RT #24146] 3097. [test] Add a tool to test handling of malformed packets. [RT #24096] 3096. [bug] Set KRB5_KTNAME before calling log_cred() in dst_gssapi_acceptctx(). [RT #24004] 3095. [bug] Handle isolated reserved ports in the port range. [RT #23957] 3094. [doc] Expand dns64 documentation. 3093. [bug] Fix gssapi/kerberos dependencies [RT #23836] 3092. [bug] Signatures for records at the zone apex could go stale due to an incorrect timer setting. [RT #23769] 3091. [bug] Fixed a bug in which zone keys that were published and then subsequently activated could fail to trigger automatic signing. [RT #22911] 3090. [func] Make --with-gssapi default [RT #23738] 3088. [bug] Remove bin/tests/system/logfileconfig/ns1/named.conf and add setup.sh in order to resolve changing named.conf issue. [RT #23687] 3087. [bug] DDNS updates using SIG(0) with update-policy match type "external" could cause a crash. [RT #23735] 3086. [bug] Running dnssec-settime -f on an old-style key will now force an update to the new key format even if no other change has been specified, using "-P now -A now" as default values. [RT #22474] 3083. [bug] NOTIFY messages were not being sent when generating a NSEC3 chain incrementally. [RT #23702] 3082. [port] strtok_r is threads only. [RT #23747] 3081. [bug] Failure of DNAME substitution did not return YXDOMAIN. [RT #23591] 3080. [cleanup] Replaced compile time constant by STDTIME_ON_32BITS. [RT #23587] 3079. [bug] Handle isc_event_allocate failures in t_tasks. [RT #23572] 3078. [func] Added a new include file with function typedefs for the DLZ "dlopen" driver. [RT #23629] 3077. [bug] zone.c:zone_refreshkeys() incorrectly called dns_zone_attach(), use zone->irefs instead. [RT #23303] 3075. [bug] dns_dnssec_findzonekeys{2} used a inconsistent timestamp when determining which keys are active. [RT #23642] 3074. [bug] Make the adb cache read through for zone data and glue learn for zone named is authoritative for. [RT #22842] 3073. [bug] managed-keys changes were not properly being recorded. [RT #20256] 3072. [bug] dns_dns64_aaaaok() potential NULL pointer dereference. [RT #20256] 3071. [bug] has_nsec could be used uninitialized in update.c:next_active. [RT #20256] 3070. [bug] dnssec-signzone potential NULL pointer dereference. [RT #20256] 3069. [cleanup] Silence warnings messages from clang static analysis. [RT #20256] 3068. [bug] Named failed to build with a OpenSSL without engine support. [RT #23473] 3067. [bug] ixfr-from-differences {master|slave}; failed to select the master/slave zones. [RT #23580] 3066. [func] The DLZ "dlopen" driver is now built by default, no longer requiring a configure option. To disable it, use "configure --without-dlopen". (Note: driver not supported on win32.) [RT #23467] 3065. [bug] RRSIG could have time stamps too far in the future. [RT #23356] 3064. [bug] powerpc: add sync instructions to the end of atomic operations. [RT #23469] 3063. [contrib] More verbose error reporting from DLZ LDAP. [RT #23402] 3059. [test] Added a regression test for change #3023. 3058. [bug] Cause named to terminate at startup or rndc reconfig/ reload to fail, if a log file specified in the conf file isn't a plain file. [RT #22771] 3057. [bug] "rndc secroots" would abort after the first error and so could miss some views. [RT #23488] 3054. [bug] Added elliptic curve support check in GOST OpenSSL engine detection. [RT #23485] 3053. [bug] Under a sustained high query load with a finite max-cache-size, it was possible for cache memory to be exhausted and not recovered. [RT #23371] 3052. [test] Fixed last autosign test report. [RT #23256] 3051. [bug] NS records obscure DNAME records at the bottom of the zone if both are present. [RT #23035] 3050. [bug] The autosign system test was timing dependent. Wait for the initial autosigning to complete before running the rest of the test. [RT #23035] 3049. [bug] Save and restore the gid when creating creating named.pid at startup. [RT #23290] 3048. [bug] Fully separate view key management. [RT #23419] 3047. [bug] DNSKEY NODATA responses not cached fixed in validator.c. Tests added to dnssec system test. [RT #22908] 3046. [bug] Use RRSIG original TTL to compute validated RRset and RRSIG TTL. [RT #23332] 3044. [bug] Hold the socket manager lock while freeing the socket. [RT #23333] 3043. [test] Merged in the NetBSD ATF test framework (currently version 0.12) for development of future unit tests. Use configure --with-atf to build ATF internally or configure --with-atf=prefix to use an external copy. [RT #23209] 3042. [bug] dig +trace could fail attempting to use IPv6 addresses on systems with only IPv4 connectivity. [RT #23297] 3041. [bug] dnssec-signzone failed to generate new signatures on ttl changes. [RT #23330] 3040. [bug] Named failed to validate insecure zones where a node with a CNAME existed between the trust anchor and the top of the zone. [RT #23338] 3038. [bug] Install . [RT #23342] 3037. [doc] Update COPYRIGHT to contain all the individual copyright notices that cover various parts. 3036. [bug] Check built-in zone arguments to see if the zone is re-usable or not. [RT #21914] 3035. [cleanup] Simplify by using strlcpy. [RT #22521] 3034. [cleanup] nslookup: use strlcpy instead of safecopy. [RT #22521] 3033. [cleanup] Add two INSIST(bucket != DNS_ADB_INVALIDBUCKET). [RT #22521] 3032. [bug] rdatalist.c: add missing REQUIREs. [RT #22521] 3031. [bug] dns_rdataclass_format() handle a zero sized buffer. [RT #22521] 3030. [bug] dns_rdatatype_format() handle a zero sized buffer. [RT #22521] 3029. [bug] isc_netaddr_format() handle a zero sized buffer. [RT #22521] 3028. [bug] isc_sockaddr_format() handle a zero sized buffer. [RT #22521] 3027. [bug] Add documented REQUIREs to cfg_obj_asnetprefix() to catch NULL pointer dereferences before they happen. [RT #22521] 3026. [bug] lib/isc/httpd.c: check that we have enough space after calling grow_headerspace() and if not re-call grow_headerspace() until we do. [RT #22521] --- 9.8.0 released --- 3025. [bug] Fixed a possible deadlock due to zone resigning. [RT #22964] 3024. [func] RTT Banding removed due to minor security increase but major impact on resolver latency. [RT #23310] 3023. [bug] Named could be left in an inconsistent state when receiving multiple AXFR response messages that were not all TSIG-signed. [RT #23254] 3022. [bug] Fixed rpz SERVFAILs after failed zone transfers [RT #23246] 3021. [bug] Change #3010 was incomplete. [RT #22296] 3020. [bug] auto-dnssec failed to correctly update the zone when changing the DNSKEY RRset. [RT #23232] 3019. [test] Test: check apex NSEC3 records after adding DNSKEY record via UPDATE. [RT #23229] --- 9.8.0rc1 released --- 3018. [bug] Named failed to check for the "none;" acl when deciding if a zone may need to be re-signed. [RT #23120] 3017. [doc] dnssec-keyfromlabel -I was not properly documented. [RT #22887] 3016. [bug] rndc usage missing '-b'. [RT #22937] 3015. [port] win32: fix IN6_IS_ADDR_LINKLOCAL and IN6_IS_ADDR_SITELOCAL macros. [RT #22724] 3013. [bug] The DNS64 ttl was not always being set as expected. [RT #23034] 3012. [bug] Remove DNSKEY TTL change pairs before generating signing records for any remaining DNSKEY changes. [RT #22590] 3011. [func] Allow setting this in named.conf using the new 'resolver-query-timeout' option, which specifies a max time in seconds. 0 means 'default' and anything longer than 30 will be silently set to 30. [RT #22852] 3010. [bug] Fixed a bug where "rndc reconfig" stopped the timer for refreshing managed-keys. [RT #22296] 3009. [bug] clients-per-query code didn't work as expected with particular query patterns. [RT #22972] --- 9.8.0b1 released --- 3008. [func] Response policy zones (RPZ) support. [RT #21726] 3007. [bug] Named failed to preserve the case of domain names in rdata which is not compressible when writing master files. [RT #22863] 3006. [func] Allow dynamically generated TSIG keys to be preserved across restarts of named. Initially this is for TSIG keys generated using GSSAPI. [RT #22639] 3005. [port] Solaris: Work around the lack of gsskrb5_register_acceptor_identity() by setting the KRB5_KTNAME environment variable to the contents of tkey-gssapi-keytab. Also fixed test errors on MacOSX. [RT #22853] 3004. [func] DNS64 reverse support. [RT #22769] 3003. [experimental] Added update-policy match type "external", enabling named to defer the decision of whether to allow a dynamic update to an external daemon. (Contributed by Andrew Tridgell.) [RT #22758] 3002. [bug] isc_mutex_init_errcheck() failed to destroy attr. [RT #22766] 3001. [func] Added a default trust anchor for the root zone, which can be switched on by setting "dnssec-validation auto;" in the named.conf options. [RT #21727] 3000. [bug] More TKEY/GSS fixes: - nsupdate can now get the default realm from the user's Kerberos principal - corrected gsstest compilation flags - improved documentation - fixed some NULL dereferences [RT #22795] 2999. [func] Add GOST support (RFC 5933). [RT #20639] 2998. [func] Add isc_task_beginexclusive and isc_task_endexclusive to the task api. [RT #22776] 2997. [func] named -V now reports the OpenSSL and libxml2 verions it was compiled against. [RT #22687] 2996. [security] Temporarily disable SO_ACCEPTFILTER support. [RT #22589] 2995. [bug] The Kerberos realm was not being correctly extracted from the signer's identity. [RT #22770] 2994. [port] NetBSD: use pthreads by default on NetBSD >= 5.0, and do not use threads on earlier versions. Also kill the unproven-pthreads, mit-pthreads, and ptl2 support. 2993. [func] Dynamically grow adb hash tables. [RT #21186] 2992. [contrib] contrib/check-secure-delegation.pl: A simple tool for looking at a secure delegation. [RT #22059] 2991. [contrib] contrib/zone-edit.sh: A simple zone editing tool for dynamic zones. [RT #22365] 2990. [bug] 'dnssec-settime -S' no longer tests prepublication interval validity when the interval is set to 0. [RT #22761] 2989. [func] Added support for writable DLZ zones. (Contributed by Andrew Tridgell of the Samba project.) [RT #22629] 2988. [experimental] Added a "dlopen" DLZ driver, allowing the creation of external DLZ drivers that can be loaded as shared objects at runtime rather than linked with named. Currently this is switched on via a compile-time option, "configure --with-dlz-dlopen". Note: the syntax for configuring DLZ zones is likely to be refined in future releases. (Contributed by Andrew Tridgell of the Samba project.) [RT #22629] 2987. [func] Improve ease of configuring TKEY/GSS updates by adding a "tkey-gssapi-keytab" option. If set, updates will be allowed with any key matching a principal in the specified keytab file. "tkey-gssapi-credential" is no longer required and is expected to be deprecated. (Contributed by Andrew Tridgell of the Samba project.) [RT #22629] 2986. [func] Add new zone type "static-stub". It's like a stub zone, but the nameserver names and/or their IP addresses are statically configured. [RT #21474] 2985. [bug] Add a regression test for change #2896. [RT #21324] 2984. [bug] Don't run MX checks when the target of the MX record is ".". [RT #22645] 2983. [bug] Include "loadkeys" in rndc help output. [RT #22493] --- 9.8.0a1 released --- 2982. [bug] Reference count dst keys. dst_key_attach() can be used increment the reference count. Note: dns_tsigkey_createfromkey() callers should now always call dst_key_free() rather than setting it to NULL on success. [RT #22672] 2981. [func] Partial DNS64 support (AAAA synthesis). [RT #21991] 2980. [bug] named didn't properly handle UPDATES that changed the TTL of the NSEC3PARAM RRset. [RT #22363] 2979. [bug] named could deadlock during shutdown if two "rndc stop" commands were issued at the same time. [RT #22108] 2978. [port] hpux: look for [RT #21919] 2977. [bug] 'nsupdate -l' report if the session key is missing. [RT #21670] 2976. [bug] named could die on exit after negotiating a GSS-TSIG key. [RT #22573] 2975. [bug] rbtdb.c:cleanup_dead_nodes_callback() acquired the wrong lock which could lead to server deadlock. [RT #22614] 2974. [bug] Some valid UPDATE requests could fail due to a consistency check examining the existing version of the zone rather than the new version resulting from the UPDATE. [RT #22413] 2973. [bug] bind.keys.h was being removed by the "make clean" at the end of configure resulting in build failures where there is very old version of perl installed. Move it to "make maintainer-clean". [RT #22230] 2972. [bug] win32: address windows socket errors. [RT #21906] 2971. [bug] Fixed a bug that caused journal files not to be compacted on Windows systems as a result of non-POSIX-compliant rename() semantics. [RT #22434] 2970. [security] Adding a NO DATA negative cache entry failed to clear any matching RRSIG records. A subsequent lookup of of NO DATA cache entry could trigger a INSIST when the unexpected RRSIG was also returned with the NO DATA cache entry. CVE-2010-3613, VU#706148. [RT #22288] 2969. [security] Fix acl type processing so that allow-query works in options and view statements. Also add a new set of tests to verify proper functioning. CVE-2010-3615, VU#510208. [RT #22418] 2968. [security] Named could fail to prove a data set was insecure before marking it as insecure. One set of conditions that can trigger this occurs naturally when rolling DNSKEY algorithms. CVE-2010-3614, VU#837744. [RT #22309] 2967. [bug] 'host -D' now turns on debugging messages earlier. [RT #22361] 2966. [bug] isc_print_vsnprintf() failed to check if there was space available in the buffer when adding a left justified character with a non zero width, (e.g. "%-1c"). [RT #22270] 2965. [func] Test HMAC functions using test data from RFC 2104 and RFC 4634. [RT #21702] 2964. [placeholder] 2963. [security] The allow-query acl was being applied instead of the allow-query-cache acl to cache lookups. [RT #22114] 2962. [port] win32: add more dependencies to BINDBuild.dsw. [RT #22062] 2961. [bug] Be still more selective about the non-authoritative answers we apply change 2748 to. [RT #22074] 2960. [func] Check that named accepts non-authoritative answers. [RT #21594] 2959. [func] Check that named starts with a missing masterfile. [RT #22076] 2958. [bug] named failed to start with a missing master file. [RT #22076] 2957. [bug] entropy_get() and entropy_getpseudo() failed to match the API for RAND_bytes() and RAND_pseudo_bytes() respectively. [RT #21962] 2956. [port] Enable atomic operations on the PowerPC64. [RT #21899] 2955. [func] Provide more detail in the recursing log. [RT #22043] 2954. [bug] contrib: dlz_mysql_driver.c bad error handling on build_sqldbinstance failure. [RT #21623] 2953. [bug] Silence spurious "expected covering NSEC3, got an exact match" message when returning a wildcard no data response. [RT #21744] 2952. [port] win32: named-checkzone and named-checkconf failed to initialize winsock. [RT #21932] 2951. [bug] named failed to generate a correct signed response in a optout, delegation only zone with no secure delegations. [RT #22007] 2950. [bug] named failed to perform a SOA up to date check when falling back to TCP on UDP timeouts when ixfr-from-differences was set. [RT #21595] 2949. [bug] dns_view_setnewzones() contained a memory leak if it was called multiple times. [RT #21942] 2948. [port] MacOS: provide a mechanism to configure the test interfaces at reboot. See bin/tests/system/README for details. 2947. [placeholder] 2946. [doc] Document the default values for the minimum and maximum zone refresh and retry values in the ARM. [RT #21886] 2945. [doc] Update empty-zones list in ARM. [RT #21772] 2944. [maint] Remove ORCHID prefix from built in empty zones. [RT #21772] 2943. [func] Add support to load new keys into managed zones without signing immediately with "rndc loadkeys". Add support to link keys with "dnssec-keygen -S" and "dnssec-settime -S". [RT #21351] 2942. [contrib] zone2sqlite failed to setup the entropy sources. [RT #21610] 2941. [bug] sdb and sdlz (dlz's zone database) failed to support DNAME at the zone apex. [RT #21610] 2940. [port] Remove connection aborted error message on Windows. [RT #21549] 2939. [func] Check that named successfully skips NSEC3 records that fail to match the NSEC3PARAM record currently in use. [RT# 21868] 2938. [bug] When generating signed responses, from a signed zone that uses NSEC3, named would use a uninitialized pointer if it needed to skip a NSEC3 record because it didn't match the selected NSEC3PARAM record for zone. [RT# 21868] 2937. [bug] Worked around an apparent race condition in over memory conditions. Without this fix a DNS cache DB or ADB could incorrectly stay in an over memory state, effectively refusing further caching, which subsequently made a BIND 9 caching server unworkable. This fix prevents this problem from happening by polling the state of the memory context, rather than making a copy of the state, which appeared to cause a race. This is a "workaround" in that it doesn't solve the possible race per se, but several experiments proved this change solves the symptom. Also, the polling overhead hasn't been reported to be an issue. This bug should only affect a caching server that specifies a finite max-cache-size. It's also quite likely that the bug happens only when enabling threads, but it's not confirmed yet. [RT #21818] 2936. [func] Improved configuration syntax and multiple-view support for addzone/delzone feature (see change #2930). Removed "new-zone-file" option, replaced with "allow-new-zones (yes|no)". The new-zone-file for each view is now created automatically, with a filename generated from a hash of the view name. It is no longer necessary to "include" the new-zone-file in named.conf; this happens automatically. Zones that were not added via "rndc addzone" can no longer be removed with "rndc delzone". [RT #19447] 2935. [bug] nsupdate: improve 'file not found' error message. [RT #21871] 2934. [bug] Use ANSI C compliant shift range in lib/isc/entropy.c. [RT #21871] 2933. [bug] 'dig +nsid' used stack memory after it went out of scope. This could potentially result in a unknown, potentially malformed, EDNS option being sent instead of the desired NSID option. [RT #21781] 2932. [cleanup] Corrected a numbering error in the "dnssec" test. [RT #21597] 2931. [bug] Temporarily and partially disable change 2864 because it would cause infinite attempts of RRSIG queries. This is an urgent care fix; we'll revisit the issue and complete the fix later. [RT #21710] 2930. [experimental] New "rndc addzone" and "rndc delzone" commands allow dynamic addition and deletion of zones. To enable this feature, specify a "new-zone-file" option at the view or options level in named.conf. Zone configuration information for the new zones will be written into that file. To make the new zones persist after a restart, "include" the file into named.conf in the appropriate view. (Note: This feature is not yet documented, and its syntax is expected to change.) [RT #19447] 2929. [bug] Improved handling of GSS security contexts: - added LRU expiration for generated TSIGs - added the ability to use a non-default realm - added new "realm" keyword in nsupdate - limited lifetime of generated keys to 1 hour or the lifetime of the context (whichever is smaller) [RT #19737] 2928. [bug] Be more selective about the non-authoritative answer we apply change 2748 to. [RT #21594] 2927. [placeholder] 2926. [placeholder] 2925. [bug] Named failed to accept uncachable negative responses from insecure zones. [RT# 21555] 2924. [func] 'rndc secroots' dump a combined summary of the current managed keys combined with trusted keys. [RT #20904] 2923. [bug] 'dig +trace' could drop core after "connection timeout". [RT #21514] 2922. [contrib] Update zkt to version 1.0. 2921. [bug] The resolver could attempt to destroy a fetch context too soon. [RT #19878] 2920. [func] Allow 'filter-aaaa-on-v4' to be applied selectively to IPv4 clients. New acl 'filter-aaaa' (default any). 2919. [func] Add autosign-ksk and autosign-zsk virtual time tests. [RT #20840] 2918. [maint] Add AAAA address for I.ROOT-SERVERS.NET. 2917. [func] Virtual time test framework. [RT #20801] 2916. [func] Add framework to use IPv6 in tests. fd92:7065:b8e:ffff::1 ... fd92:7065:b8e:ffff::7 2915. [cleanup] Be smarter about which objects we attempt to compile based on configure options. [RT #21444] 2914. [bug] Make the "autosign" system test more portable. [RT #20997] 2913. [func] Add pkcs#11 system tests. [RT #20784] 2912. [func] Windows clients don't like UPDATE responses that clear the zone section. [RT #20986] 2911. [bug] dnssec-signzone didn't handle out of zone records well. [RT #21367] 2910. [func] Sanity check Kerberos credentials. [RT #20986] 2909. [bug] named-checkconf -p could die if "update-policy local;" was specified in named.conf. [RT #21416] 2908. [bug] It was possible for re-signing to stop after removing a DNSKEY. [RT #21384] 2907. [bug] The export version of libdns had undefined references. [RT #21444] 2906. [bug] Address RFC 5011 implementation issues. [RT #20903] 2905. [port] aix: set use_atomic=yes with native compiler. [RT #21402] 2904. [bug] When using DLV, sub-zones of the zones in the DLV, could be incorrectly marked as insecure instead of secure leading to negative proofs failing. This was a unintended outcome from change 2890. [RT# 21392] 2903. [bug] managed-keys-directory missing from namedconf.c. [RT #21370] 2902. [func] Add regression test for change 2897. [RT #21040] 2901. [port] Use AC_C_FLEXIBLE_ARRAY_MEMBER. [RT #21316] 2900. [bug] The placeholder negative caching element was not properly constructed triggering a INSIST in dns_ncache_towire(). [RT #21346] 2899. [port] win32: Support linking against OpenSSL 1.0.0. 2898. [bug] nslookup leaked memory when -domain=value was specified. [RT #21301] 2897. [bug] NSEC3 chains could be left behind when transitioning to insecure. [RT #21040] 2896. [bug] "rndc sign" failed to properly update the zone when adding a DNSKEY for publication only. [RT #21045] 2895. [func] genrandom: add support for the generation of multiple files. [RT #20917] 2894. [contrib] DLZ LDAP support now use '$' not '%'. [RT #21294] 2893. [bug] Improve managed keys support. New named.conf option managed-keys-directory. [RT #20924] 2892. [bug] Handle REVOKED keys better. [RT #20961] 2891. [maint] Update empty-zones list to match draft-ietf-dnsop-default-local-zones-13. [RT# 21099] 2890. [bug] Handle the introduction of new trusted-keys and DS, DLV RRsets better. [RT #21097] 2889. [bug] Elements of the grammar where not properly reported. [RT #21046] 2888. [bug] Only the first EDNS option was displayed. [RT #21273] 2887. [bug] Report the keytag times in UTC in the .key file, local time is presented as a comment within the comment. [RT #21223] 2886. [bug] ctime() is not thread safe. [RT #21223] 2885. [bug] Improve -fno-strict-aliasing support probing in configure. [RT #21080] 2884. [bug] Insufficient validation in dns_name_getlabelsequence(). [RT #21283] 2883. [bug] 'dig +short' failed to handle really large datasets. [RT #21113] 2882. [bug] Remove memory context from list of active contexts before clearing 'magic'. [RT #21274] 2881. [bug] Reduce the amount of time the rbtdb write lock is held when closing a version. [RT #21198] 2880. [cleanup] Make the output of dnssec-keygen and dnssec-revoke consistent. [RT #21078] 2879. [contrib] DLZ bdbhpt driver fails to close correct cursor. [RT #21106] 2878. [func] Incrementally write the master file after performing a AXFR. [RT #21010] 2877. [bug] The validator failed to skip obviously mismatching RRSIGs. [RT #21138] 2876. [bug] Named could return SERVFAIL for negative responses from unsigned zones. [RT #21131] 2875. [bug] dns_time64_fromtext() could accept non digits. [RT #21033] 2874. [bug] Cache lack of EDNS support only after the server successfully responds to the query using plain DNS. [RT #20930] 2873. [bug] Canceling a dynamic update via the dns/client module could trigger an assertion failure. [RT #21133] 2872. [bug] Modify dns/client.c:dns_client_createx() to only require one of IPv4 or IPv6 rather than both. [RT #21122] 2871. [bug] Type mismatch in mem_api.c between the definition and the header file, causing build failure with --enable-exportlib. [RT #21138] 2870. [maint] Add AAAA address for L.ROOT-SERVERS.NET. 2869. [bug] Fix arguments to dns_keytable_findnextkeynode() call. [RT #20877] 2868. [cleanup] Run "make clean" at the end of configure to ensure any changes made by configure are integrated. Use --with-make-clean=no to disable. [RT #20994] 2867. [bug] Don't set GSS_C_SEQUENCE_FLAG as Windows DNS servers don't like it. [RT #20986] 2866. [bug] Windows does not like the TSIG name being compressed. [RT #20986] 2865. [bug] memset to zero event.data. [RT #20986] 2864. [bug] Direct SIG/RRSIG queries were not handled correctly. [RT #21050] 2863. [port] linux: disable IPv6 PMTUD and use network minimum MTU. [RT #21056] 2862. [bug] nsupdate didn't default to the parent zone when updating DS records. [RT #20896] 2861. [doc] dnssec-settime man pages didn't correctly document the inactivation time. [RT #21039] 2860. [bug] named-checkconf's usage was out of date. [RT #21039] 2859. [bug] When canceling validation it was possible to leak memory. [RT #20800] 2858. [bug] RTT estimates were not being adjusted on ICMP errors. [RT #20772] 2857. [bug] named-checkconf did not fail on a bad trusted key. [RT #20705] 2856. [bug] The size of a memory allocation was not always properly recorded. [RT #20927] 2855. [func] nsupdate will now preserve the entered case of domain names in update requests it sends. [RT #20928] 2854. [func] dig: allow the final soa record in a axfr response to be suppressed, dig +onesoa. [RT #20929] 2853. [bug] add_sigs() could run out of scratch space. [RT #21015] 2852. [bug] Handle broken DNSSEC trust chains better. [RT #15619] 2851. [doc] nslookup.1, removed from the docbook source as it produced bad nroff. [RT #21007] 2850. [bug] If isc_heap_insert() failed due to memory shortage the heap would have corrupted entries. [RT #20951] 2849. [bug] Don't treat errors from the xml2 library as fatal. [RT #20945] 2848. [doc] Moved README.dnssec, README.libdns, README.pkcs11 and README.rfc5011 into the ARM. [RT #20899] 2847. [cleanup] Corrected usage message in dnssec-settime. [RT #20921] 2846. [bug] EOF on unix domain sockets was not being handled correctly. [RT #20731] 2845. [bug] RFC 5011 client could crash on shutdown. [RT #20903] 2844. [doc] notify-delay default in ARM was wrong. It should have been five (5) seconds. 2843. [func] Prevent dnssec-keygen and dnssec-keyfromlabel from creating key files if there is a chance that the new key ID will collide with an existing one after either of the keys has been revoked. (To override this in the case of dnssec-keyfromlabel, use the -y option. dnssec-keygen will simply create a different, non-colliding key, so an override is not necessary.) [RT #20838] 2842. [func] Added "smartsign" and improved "autosign" and "dnssec" regression tests. [RT #20865] 2841. [bug] Change 2836 was not complete. [RT #20883] 2840. [bug] Temporary fixed pkcs11-destroy usage check. [RT #20760] 2839. [bug] A KSK revoked by named could not be deleted. [RT #20881] 2838. [placeholder] 2837. [port] Prevent Linux spurious warnings about fwrite(). [RT #20812] 2836. [bug] Keys that were scheduled to become active could be delayed. [RT #20874] 2835. [bug] Key inactivity dates were inadvertently stored in the private key file with the outdated tag "Unpublish" rather than "Inactive". This has been fixed; however, any existing keys that had Inactive dates set will now need to have them reset, using 'dnssec-settime -I'. [RT #20868] 2834. [bug] HMAC-SHA* keys that were longer than the algorithm digest length were used incorrectly, leading to interoperability problems with other DNS implementations. This has been corrected. (Note: If an oversize key is in use, and compatibility is needed with an older release of BIND, the new tool "isc-hmac-fixup" can convert the key secret to a form that will work with all versions.) [RT #20751] 2833. [cleanup] Fix usage messages in dnssec-keygen and dnssec-settime. [RT #20851] 2832. [bug] Modify "struct stat" in lib/export/samples/nsprobe.c to avoid redefinition in some OSs [RT 20831] 2831. [security] Do not attempt to validate or cache out-of-bailiwick data returned with a secure answer; it must be re-fetched from its original source and validated in that context. [RT #20819] 2830. [bug] Changing the OPTOUT setting could take multiple passes. [RT #20813] 2829. [bug] Fixed potential node inconsistency in rbtdb.c. [RT #20808] 2828. [security] Cached CNAME or DNAME RR could be returned to clients without DNSSEC validation. [RT #20737] 2827. [security] Bogus NXDOMAIN could be cached as if valid. [RT #20712] 2826. [bug] NSEC3->NSEC transitions could fail due to a lock not being released. [RT #20740] 2825. [bug] Changing the setting of OPTOUT in a NSEC3 chain that was in the process of being created was not properly recorded in the zone. [RT #20786] 2824. [bug] "rndc sign" was not being run by the correct task. [RT #20759] 2823. [bug] rbtdb.c:getsigningtime() was missing locks. [RT #20781] 2822. [bug] rbtdb.c:loadnode() could return the wrong result. [RT #20802] 2821. [doc] Add note that named-checkconf doesn't automatically read rndc.key and bind.keys [RT #20758] 2820. [func] Handle read access failure of OpenSSL configuration file more user friendly (PKCS#11 engine patch). [RT #20668] 2819. [cleanup] Removed unnecessary DNS_POINTER_MAXHOPS define. [RT #20771] 2818. [cleanup] rndc could return an incorrect error code when a zone was not found. [RT #20767] 2817. [cleanup] Removed unnecessary isc_task_endexclusive() calls. [RT #20768] 2816. [bug] previous_closest_nsec() could fail to return data for NSEC3 nodes [RT #29730] 2815. [bug] Exclusively lock the task when freezing a zone. [RT #19838] 2814. [func] Provide a definitive error message when a master zone is not loaded. [RT #20757] 2813. [bug] Better handling of unreadable DNSSEC key files. [RT #20710] 2812. [bug] Make sure updates can't result in a zone with NSEC-only keys and NSEC3 records. [RT #20748] 2811. [cleanup] Add "rndc sign" to list of commands in rndc usage output. [RT #20733] 2810. [doc] Clarified the process of transitioning an NSEC3 zone to insecure. [RT #20746] 2809. [cleanup] Restored accidentally-deleted text in usage output in dnssec-settime and dnssec-revoke [RT #20739] 2808. [bug] Remove the attempt to install atomic.h from lib/isc. atomic.h is correctly installed by the architecture specific subdirectories. [RT #20722] 2807. [bug] Fixed a possible ASSERT when reconfiguring zone keys. [RT #20720] --- 9.7.0rc1 released --- 2806. [bug] "rdnc sign" could delay re-signing the DNSKEY when it had changed. [RT #20703] 2805. [bug] Fixed namespace problems encountered when building external programs using non-exported BIND9 libraries (i.e., built without --enable-exportlib). [RT #20679] 2804. [bug] Send notifies when a zone is signed with "rndc sign" or as a result of a scheduled key change. [RT #20700] 2803. [port] win32: Install named-journalprint, nsec3hash, arpaname and genrandom under windows. [RT #20670] 2802. [cleanup] Rename journalprint to named-journalprint. [RT #20670] 2801. [func] Detect and report records that are different according to DNSSEC but are semantically equal according to plain DNS. Apply plain DNS comparisons rather than DNSSEC comparisons when processing UPDATE requests. dnssec-signzone now removes such semantically duplicate records prior to signing the RRset. named-checkzone -r {ignore|warn|fail} (default warn) named-compilezone -r {ignore|warn|fail} (default warn) named.conf: check-dup-records {ignore|warn|fail}; 2800. [func] Reject zones which have NS records which refer to CNAMEs, DNAMEs or don't have address record (class IN only). Reject UPDATEs which would cause the zone to fail the above checks if committed. [RT #20678] 2799. [cleanup] Changed the "secure-to-insecure" option to "dnssec-secure-to-insecure", and "dnskey-ksk-only" to "dnssec-dnskey-kskonly", for clarity. [RT #20586] 2798. [bug] Addressed bugs in managed-keys initialization and rollover. [RT #20683] 2797. [bug] Don't decrement the dispatch manager's maxbuffers. [RT #20613] 2796. [bug] Missing dns_rdataset_disassociate() call in dns_nsec3_delnsec3sx(). [RT #20681] 2795. [cleanup] Add text to differentiate "update with no effect" log messages. [RT #18889] 2794. [bug] Install . [RT #20677] 2793. [func] Add "autosign" and "metadata" tests to the automatic tests. [RT #19946] 2792. [func] "filter-aaaa-on-v4" can now be set in view options (if compiled in). [RT #20635] 2791. [bug] The installation of isc-config.sh was broken. [RT #20667] 2790. [bug] Handle DS queries to stub zones. [RT #20440] 2789. [bug] Fixed an INSIST in dispatch.c [RT #20576] 2788. [bug] dnssec-signzone could sign with keys that were not requested [RT #20625] 2787. [bug] Spurious log message when zone keys were dynamically reconfigured. [RT #20659] 2786. [bug] Additional could be promoted to answer. [RT #20663] --- 9.7.0b3 released --- 2785. [bug] Revoked keys could fail to self-sign [RT #20652] 2784. [bug] TC was not always being set when required glue was dropped. [RT #20655] 2783. [func] Return minimal responses to EDNS/UDP queries with a UDP buffer size of 512 or less. [RT #20654] 2782. [port] win32: use getaddrinfo() for hostname lookups. [RT #20650] 2781. [bug] Inactive keys could be used for signing. [RT #20649] 2780. [bug] dnssec-keygen -A none didn't properly unset the activation date in all cases. [RT #20648] 2779. [bug] Dynamic key revocation could fail. [RT #20644] 2778. [bug] dnssec-signzone could fail when a key was revoked without deleting the unrevoked version. [RT #20638] 2777. [contrib] DLZ MYSQL auto reconnect support discovery was wrong. 2776. [bug] Change #2762 was not correct. [RT #20647] 2775. [bug] Accept RSASHA256 and RSASHA512 as NSEC3 compatible in dnssec-keyfromlabel. [RT #20643] 2774. [bug] Existing cache DB wasn't being reused after reconfiguration. [RT #20629] 2773. [bug] In autosigned zones, the SOA could be signed with the KSK. [RT #20628] 2772. [security] When validating, track whether pending data was from the additional section or not and only return it if validates as secure. [RT #20438] 2771. [bug] dnssec-signzone: DNSKEY records could be corrupted when importing from key files [RT #20624] 2770. [cleanup] Add log messages to resolver.c to indicate events causing FORMERR responses. [RT #20526] 2769. [cleanup] Change #2742 was incomplete. [RT #19589] 2768. [bug] dnssec-signzone: -S no longer implies -g [RT #20568] 2767. [bug] named could crash on startup if a zone was configured with auto-dnssec and there was no key-directory. [RT #20615] 2766. [bug] isc_socket_fdwatchpoke() should only update the socketmgr state if the socket is not pending on a read or write. [RT #20603] 2765. [bug] Skip masters for which the TSIG key cannot be found. [RT #20595] 2764. [bug] "rndc-confgen -a" could trigger a REQUIRE. [RT #20610] 2763. [bug] "rndc sign" didn't create an NSEC chain. [RT #20591] 2762. [bug] DLV validation failed with a local slave DLV zone. [RT #20577] 2761. [cleanup] Enable internal symbol table for backtrace only for systems that are known to work. Currently, BSD variants, Linux and Solaris are supported. [RT# 20202] 2760. [cleanup] Corrected named-compilezone usage summary. [RT #20533] 2759. [doc] Add information about .jbk/.jnw files to the ARM. [RT #20303] 2758. [bug] win32: Added a workaround for a windows 2008 bug that could cause the UDP client handler to shut down. [RT #19176] 2757. [bug] dig: assertion failure could occur in connect timeout. [RT #20599] 2756. [bug] Fixed corrupt logfile message in update.c. [RT# 20597] 2755. [placeholder] 2754. [bug] Secure-to-insecure transitions failed when zone was signed with NSEC3. [RT #20587] 2753. [bug] Removed an unnecessary warning that could appear when building an NSEC chain. [RT #20589] 2752. [bug] Locking violation. [RT #20587] 2751. [bug] Fixed a memory leak in dnssec-keyfromlabel. [RT #20588] 2750. [bug] dig: assertion failure could occur when a server didn't have an address. [RT #20579] 2749. [bug] ixfr-from-differences generated a non-minimal ixfr for NSEC3 signed zones. [RT #20452] 2748. [func] Identify bad answers from GTLD servers and treat them as referrals. [RT #18884] 2747. [bug] Journal roll forwards failed to set the re-signing time of RRSIGs correctly. [RT #20541] 2746. [port] hpux: address signed/unsigned expansion mismatch of dns_rbtnode_t.nsec. [RT #20542] 2745. [bug] configure script didn't probe the return type of gai_strerror(3) correctly. [RT #20573] 2744. [func] Log if a query was over TCP. [RT #19961] 2743. [bug] RRSIG could be incorrectly set in the NSEC3 record for a insecure delegation. --- 9.7.0b2 released --- 2742. [cleanup] Clarify some DNSSEC-related log messages in validator.c. [RT #19589] 2741. [func] Allow the dnssec-keygen progress messages to be suppressed (dnssec-keygen -q). Automatically suppress the progress messages when stdin is not a tty. [RT #20474] 2740. [placeholder] 2739. [cleanup] Clean up API for initializing and clearing trust anchors for a view. [RT #20211] 2738. [func] Add RSASHA256 and RSASHA512 tests to the dnssec system test. [RT #20453] 2737. [func] UPDATE requests can leak existence information. [RT #17261] 2736. [func] Improve the performance of NSEC signed zones with more than a normal amount of glue below a delegation. [RT #20191] 2735. [bug] dnssec-signzone could fail to read keys that were specified on the command line with full paths, but weren't in the current directory. [RT #20421] 2734. [port] cygwin: arpaname did not compile. [RT #20473] 2733. [cleanup] Clean up coding style in pkcs11-* tools. [RT #20355] 2732. [func] Add optional filter-aaaa-on-v4 option, available if built with './configure --enable-filter-aaaa'. Filters out AAAA answers to clients connecting via IPv4. (This is NOT recommended for general use.) [RT #20339] 2731. [func] Additional work on change 2709. The key parser will now ignore unrecognized fields when the minor version number of the private key format has been increased. It will reject any key with the major version number increased. [RT #20310] 2730. [func] Have dnssec-keygen display a progress indication a la 'openssl genrsa' on standard error. Note when the first '.' is followed by a long stop one has the choice between slow generation vs. poor random quality, i.e., '-r /dev/urandom'. [RT #20284] 2729. [func] When constructing a CNAME from a DNAME use the DNAME TTL. [RT #20451] 2728. [bug] dnssec-keygen, dnssec-keyfromlabel and dnssec-signzone now warn immediately if asked to write into a nonexistent directory. [RT #20278] 2727. [func] The 'key-directory' option can now specify a relative path. [RT #20154] 2726. [func] Added support for SHA-2 DNSSEC algorithms, RSASHA256 and RSASHA512. [RT #20023] 2725. [doc] Added information about the file "managed-keys.bind" to the ARM. [RT #20235] 2724. [bug] Updates to a existing node in secure zone using NSEC were failing. [RT #20448] 2723. [bug] isc_base32_totext(), isc_base32hex_totext(), and isc_base64_totext(), didn't always mark regions of memory as fully consumed after conversion. [RT #20445] 2722. [bug] Ensure that the memory associated with the name of a node in a rbt tree is not altered during the life of the node. [RT #20431] 2721. [port] Have dst__entropy_status() prime the random number generator. [RT #20369] 2720. [bug] RFC 5011 trust anchor updates could trigger an assert if the DNSKEY record was unsigned. [RT #20406] 2719. [func] Skip trusted/managed keys for unsupported algorithms. [RT #20392] 2718. [bug] The space calculations in opensslrsa_todns() were incorrect. [RT #20394] 2717. [bug] named failed to update the NSEC/NSEC3 record when the last private type record was removed as a result of completing the signing the zone with a key. [RT #20399] 2716. [bug] nslookup debug mode didn't return the ttl. [RT #20414] --- 9.7.0b1 released --- 2715. [bug] Require OpenSSL support to be explicitly disabled. [RT #20288] 2714. [port] aix/powerpc: 'asm("ics");' needs non standard assembler flags. 2713. [bug] powerpc: atomic operations missing asm("ics") / __isync() calls. 2712. [func] New 'auto-dnssec' zone option allows zone signing to be fully automated in zones configured for dynamic DNS. 'auto-dnssec allow;' permits a zone to be signed by creating keys for it in the key-directory and using 'rndc sign '. 'auto-dnssec maintain;' allows that too, plus it also keeps the zone's DNSSEC keys up to date according to their timing metadata. [RT #19943] 2711. [port] win32: Add the bin/pkcs11 tools into the full build. [RT #20372] 2710. [func] New 'dnssec-signzone -x' flag and 'dnskey-ksk-only' zone option cause a zone to be signed with only KSKs signing the DNSKEY RRset, not ZSKs. This reduces the size of a DNSKEY answer. [RT #20340] 2709. [func] Added some data fields, currently unused, to the private key file format, to allow implementation of explicit key rollover in a future release without impairing backward or forward compatibility. [RT #20310] 2708. [func] Insecure to secure and NSEC3 parameter changes via update are now fully supported and no longer require defines to enable. We now no longer overload the NSEC3PARAM flag field, nor the NSEC OPT bit at the apex. Secure to insecure changes are controlled by by the named.conf option 'secure-to-insecure'. Warning: If you had previously enabled support by adding defines at compile time to BIND 9.6 you should ensure that all changes that are in progress have completed prior to upgrading to BIND 9.7. BIND 9.7 is not backwards compatible. 2707. [func] dnssec-keyfromlabel no longer require engine name to be specified in the label if there is a default engine or the -E option has been used. Also, it now uses default algorithms as dnssec-keygen does (i.e., RSASHA1, or NSEC3RSASHA1 if -3 is used). [RT #20371] 2706. [bug] Loading a zone with a very large NSEC3 salt could trigger an assert. [RT #20368] 2705. [placeholder] 2704. [bug] Serial of dynamic and stub zones could be inconsistent with their SOA serial. [RT #19387] 2703. [func] Introduce an OpenSSL "engine" argument with -E for all binaries which can take benefit of crypto hardware. [RT #20230] 2702. [func] Update PKCS#11 tools (bin/pkcs11) [RT #20225 & all] 2701. [doc] Correction to ARM: hmac-md5 is no longer the only supported TSIG key algorithm. [RT #18046] 2700. [doc] The match-mapped-addresses option is discouraged. [RT #12252] 2699. [bug] Missing lock in rbtdb.c. [RT #20037] 2698. [placeholder] 2697. [port] win32: ensure that S_IFMT, S_IFDIR, S_IFCHR and S_IFREG are defined after including . [RT #20309] 2696. [bug] named failed to successfully process some valid acl constructs. [RT #20308] 2695. [func] DHCP/DDNS - update fdwatch code for use by DHCP. Modify the api to isc_sockfdwatch_t (the callback function for isc_socket_fdwatchcreate) to include information about the direction (read or write) and add isc_socket_fdwatchpoke. [RT #20253] 2694. [bug] Reduce default NSEC3 iterations from 100 to 10. [RT #19970] 2693. [port] Add some noreturn attributes. [RT #20257] 2692. [port] win32: 32/64 bit cleanups. [RT #20335] 2691. [func] dnssec-signzone: retain the existing NSEC or NSEC3 chain when re-signing a previously-signed zone. Use -u to modify NSEC3 parameters or switch between NSEC and NSEC3. [RT #20304] 2690. [bug] win32: fix isc_thread_key_getspecific() prototype. [RT #20315] 2689. [bug] Correctly handle snprintf result. [RT #20306] 2688. [bug] Use INTERFACE_F_POINTTOPOINT, not IFF_POINTOPOINT, to decide to fetch the destination address. [RT #20305] 2687. [bug] Fixed dnssec-signzone -S handling of revoked keys. Also, added warnings when revoking a ZSK, as this is not defined by protocol (but is legal). [RT #19943] 2686. [bug] dnssec-signzone should clean the old NSEC chain when signing with NSEC3 and vice versa. [RT #20301] 2685. [contrib] Update contrib/zkt to version 0.99c. [RT #20054] 2684. [cleanup] dig: formalize +ad and +cd as synonyms for +adflag and +cdflag. [RT #19305] 2683. [bug] dnssec-signzone should clean out old NSEC3 chains when the NSEC3 parameters used to sign the zone change. [RT #20246] 2682. [bug] "configure --enable-symtable=all" failed to build. [RT #20282] 2681. [bug] IPSECKEY RR of gateway type 3 was not correctly decoded. [RT #20269] 2680. [func] Move contrib/pkcs11-keygen to bin/pkcs11. [RT #20067] 2679. [func] dig -k can now accept TSIG keys in named.conf format. [RT #20031] 2678. [func] Treat DS queries as if "minimal-response yes;" was set. [RT #20258] 2677. [func] Changes to key metadata behavior: - Keys without "publish" or "active" dates set will no longer be used for smart signing. However, those dates will be set to "now" by default when a key is created; to generate a key but not use it yet, use dnssec-keygen -G. - New "inactive" date (dnssec-keygen/settime -I) sets the time when a key is no longer used for signing but is still published. - The "unpublished" date (-U) is deprecated in favor of "deleted" (-D). [RT #20247] 2676. [bug] --with-export-installdir should have been --with-export-includedir. [RT #20252] 2675. [bug] dnssec-signzone could crash if the key directory did not exist. [RT #20232] --- 9.7.0a3 released --- 2674. [bug] "dnssec-lookaside auto;" crashed if named was built without openssl. [RT #20231] 2673. [bug] The managed-keys.bind zone file could fail to load due to a spurious result from sync_keyzone() [RT #20045] 2672. [bug] Don't enable searching in 'host' when doing reverse lookups. [RT #20218] 2671. [bug] Add support for PKCS#11 providers not returning the public exponent in RSA private keys (OpenCryptoki for instance) in dnssec-keyfromlabel. [RT #19294] 2670. [bug] Unexpected connect failures failed to log enough information to be useful. [RT #20205] 2669. [func] Update PKCS#11 support to support Keyper HSM. Update PKCS#11 patch to be against openssl-0.9.8i. 2668. [func] Several improvements to dnssec-* tools, including: - dnssec-keygen and dnssec-settime can now set key metadata fields 0 (to unset a value, use "none") - dnssec-revoke sets the revocation date in addition to the revoke bit - dnssec-settime can now print individual metadata fields instead of always printing all of them, and can print them in unix epoch time format for use by scripts [RT #19942] 2667. [func] Add support for logging stack backtrace on assertion failure (not available for all platforms). [RT #19780] 2666. [func] Added an 'options' argument to dns_name_fromstring() (API change from 9.7.0a2). [RT #20196] 2665. [func] Clarify syntax for managed-keys {} statement, add ARM documentation about RFC 5011 support. [RT #19874] 2664. [bug] create_keydata() and minimal_update() in zone.c didn't properly check return values for some functions. [RT #19956] 2663. [func] win32: allow named to run as a service using "NT AUTHORITY\LocalService" as the account. [RT #19977] 2662. [bug] lwres_getipnodebyname() and lwres_getipnodebyaddr() returned a misleading error code when lwresd was down. [RT #20028] 2661. [bug] Check whether socket fd exceeds FD_SETSIZE when creating lwres context. [RT #20029] 2660. [func] Add a new set of DNS libraries for non-BIND9 applications. See README.libdns. [RT #19369] 2659. [doc] Clarify dnssec-keygen doc: key name must match zone name for DNSSEC keys. [RT #19938] 2658. [bug] dnssec-settime and dnssec-revoke didn't process key file paths correctly. [RT #20078] 2657. [cleanup] Lower "journal file does not exist, creating it" log level to debug 1. [RT #20058] 2656. [func] win32: add a "tools only" check box to the installer which causes it to only install dig, host, nslookup, nsupdate and relevant DLLs. [RT #19998] 2655. [doc] Document that key-directory does not affect bind.keys, rndc.key or session.key. [RT #20155] 2654. [bug] Improve error reporting on duplicated names for deny-answer-xxx. [RT #20164] 2653. [bug] Treat ENGINE_load_private_key() failures as key not found rather than out of memory. [RT #18033] 2652. [func] Provide more detail about what record is being deleted. [RT #20061] 2651. [bug] Dates could print incorrectly in K*.key files on 64-bit systems. [RT #20076] 2650. [bug] Assertion failure in dnssec-signzone when trying to read keyset-* files. [RT #20075] 2649. [bug] Set the domain for forward only zones. [RT #19944] 2648. [port] win32: isc_time_seconds() was broken. [RT #19900] 2647. [bug] Remove unnecessary SOA updates when a new KSK is added. [RT #19913] 2646. [bug] Incorrect cleanup on error in socket.c. [RT #19987] 2645. [port] "gcc -m32" didn't work on amd64 and x86_64 platforms which default to 64 bits. [RT #19927] --- 9.7.0a2 released --- 2644. [bug] Change #2628 caused a regression on some systems; named was unable to write the PID file and would fail on startup. [RT #20001] 2643. [bug] Stub zones interacted badly with NSEC3 support. [RT #19777] 2642. [bug] nsupdate could dump core on solaris when reading improperly formatted key files. [RT #20015] 2641. [bug] Fixed an error in parsing update-policy syntax, added a regression test to check it. [RT #20007] 2640. [security] A specially crafted update packet will cause named to exit. [RT #20000] 2639. [bug] Silence compiler warnings in gssapi code. [RT #19954] 2638. [bug] Install arpaname. [RT #19957] 2637. [func] Rationalize dnssec-signzone's signwithkey() calling. [RT #19959] 2636. [func] Simplify zone signing and key maintenance with the dnssec-* tools. Major changes: - all dnssec-* tools now take a -K option to specify a directory in which key files will be stored - DNSSEC can now store metadata indicating when they are scheduled to be published, activated, revoked or removed; these values can be set by dnssec-keygen or overwritten by the new dnssec-settime command - dnssec-signzone -S (for "smart") option reads key metadata and uses it to determine automatically which keys to publish to the zone, use for signing, revoke, or remove from the zone [RT #19816] 2635. [bug] isc_inet_ntop() incorrectly handled 0.0/16 addresses. [RT #19716] 2634. [port] win32: Add support for libxml2, enable statschannel. [RT #19773] 2633. [bug] Handle 15 bit rand() functions. [RT #19783] 2632. [func] util/kit.sh: warn if documentation appears to be out of date. [RT #19922] 2631. [bug] Handle "//", "/./" and "/../" in mkdirpath(). [RT #19926 ] 2630. [func] Improved syntax for DDNS autoconfiguration: use "update-policy local;" to switch on local DDNS in a zone. (The "ddns-autoconf" option has been removed.) [RT #19875] 2629. [port] Check for seteuid()/setegid(), use setresuid()/ setresgid() if not present. [RT #19932] 2628. [port] linux: Allow /var/run/named/named.pid to be opened at startup with reduced capabilities in operation. [RT #19884] 2627. [bug] Named aborted if the same key was included in trusted-keys more than once. [RT #19918] 2626. [bug] Multiple trusted-keys could trigger an assertion failure. [RT #19914] 2625. [bug] Missing UNLOCK in rbtdb.c. [RT #19865] 2624. [func] 'named-checkconf -p' will print out the parsed configuration. [RT #18871] 2623. [bug] Named started searches for DS non-optimally. [RT #19915] 2622. [bug] Printing of named.conf grammar was broken. [RT #19919] 2621. [doc] Made copyright boilerplate consistent. [RT #19833] 2620. [bug] Delay thawing the zone until the reload of it has completed successfully. [RT #19750] 2619. [func] Add support for RFC 5011, automatic trust anchor maintenance. The new "managed-keys" statement can be used in place of "trusted-keys" for zones which support this protocol. (Note: this syntax is expected to change prior to 9.7.0 final.) [RT #19248] 2618. [bug] The sdb and sdlz db_interator_seek() methods could loop infinitely. [RT #19847] 2617. [bug] ifconfig.sh failed to emit an error message when run from the wrong location. [RT #19375] 2616. [bug] 'host' used the nameservers from resolv.conf even when a explicit nameserver was specified. [RT #19852] 2615. [bug] "__attribute__((unused))" was in the wrong place for ia64 gcc builds. [RT #19854] 2614. [port] win32: 'named -v' should automatically be executed in the foreground. [RT #19844] 2613. [placeholder] --- 9.7.0a1 released --- 2612. [func] Add default values for the arguments to dnssec-keygen. Without arguments, it will now generate a 1024-bit RSASHA1 zone-signing key, or with the -f KSK option, a 2048-bit RSASHA1 key-signing key. [RT #19300] 2611. [func] Add -l option to dnssec-dsfromkey to generate DLV records instead of DS records. [RT #19300] 2610. [port] sunos: Change #2363 was not complete. [RT #19796] 2609. [func] Simplify the configuration of dynamic zones: - add ddns-confgen command to generate configuration text for named.conf - add zone option "ddns-autoconf yes;", which causes named to generate a TSIG session key and allow updates to the zone using that key - add '-l' (localhost) option to nsupdate, which causes nsupdate to connect to a locally-running named process using the session key generated by named [RT #19284] 2608. [func] Perform post signing verification checks in dnssec-signzone. These can be disabled with -P. The post sign verification test ensures that for each algorithm in use there is at least one non revoked self signed KSK key. That all revoked KSK keys are self signed. That all records in the zone are signed by the algorithm. [RT #19653] 2607. [bug] named could incorrectly delete NSEC3 records for empty nodes when processing a update request. [RT #19749] 2606. [bug] "delegation-only" was not being accepted in delegation-only type zones. [RT #19717] 2605. [bug] Accept DS responses from delegation only zones. [RT # 19296] 2604. [func] Add support for DNS rebinding attack prevention through new options, deny-answer-addresses and deny-answer-aliases. Based on contributed code from JD Nurmi, Google. [RT #18192] 2603. [port] win32: handle .exe extension of named-checkzone and named-comilezone argv[0] names under windows. [RT #19767] 2602. [port] win32: fix debugging command line build of libisccfg. [RT #19767] 2601. [doc] Mention file creation mode mask in the named manual page. 2600. [doc] ARM: miscellaneous reformatting for different page widths. [RT #19574] 2599. [bug] Address rapid memory growth when validation fails. [RT #19654] 2598. [func] Reserve the -F flag. [RT #19657] 2597. [bug] Handle a validation failure with a insecure delegation from a NSEC3 signed master/slave zone. [RT #19464] 2596. [bug] Stale tree nodes of cache/dynamic rbtdb could stay long, leading to inefficient memory usage or rejecting newer cache entries in the worst case. [RT #19563] 2595. [bug] Fix unknown extended rcodes in dig. [RT #19625] 2594. [func] Have rndc warn if using its default configuration file when the key file also exists. [RT #19424] 2593. [bug] Improve a corner source of SERVFAILs [RT #19632] 2592. [bug] Treat "any" as a type in nsupdate. [RT #19455] 2591. [bug] named could die when processing a update in removed_orphaned_ds(). [RT #19507] 2590. [func] Report zone/class of "update with no effect". [RT #19542] 2589. [bug] dns_db_unregister() failed to clear '*dbimp'. [RT #19626] 2588. [bug] SO_REUSEADDR could be set unconditionally after failure of bind(2) call. This should be rare and mostly harmless, but may cause interference with other processes that happen to use the same port. [RT #19642] 2587. [func] Improve logging by reporting serial numbers for when zone serial has gone backwards or unchanged. [RT #19506] 2586. [bug] Missing cleanup of SIG rdataset in searching a DLZ DB or SDB. [RT #19577] 2585. [bug] Uninitialized socket name could be referenced via a statistics channel, triggering an assertion failure in XML rendering. [RT #19427] 2584. [bug] alpha: gcc optimization could break atomic operations. [RT #19227] 2583. [port] netbsd: provide a control to not add the compile date to the version string, -DNO_VERSION_DATE. 2582. [bug] Don't emit warning log message when we attempt to remove non-existent journal. [RT #19516] 2581. [contrib] dlz/mysql set MYSQL_OPT_RECONNECT option on connection. Requires MySQL 5.0.19 or later. [RT #19084] 2580. [bug] UpdateRej statistics counter could be incremented twice for one rejection. [RT #19476] 2579. [bug] DNSSEC lookaside validation failed to handle unknown algorithms. [RT #19479] 2578. [bug] Changed default sig-signing-type to 65534, because 65535 turns out to be reserved. [RT #19477] 2577. [doc] Clarified some statistics counters. [RT #19454] 2576. [bug] NSEC record were not being correctly signed when a zone transitions from insecure to secure. Handle such incorrectly signed zones. [RT #19114] 2575. [func] New functions dns_name_fromstring() and dns_name_tostring(), to simplify conversion of a string to a dns_name structure and vice versa. [RT #19451] 2574. [doc] Document nsupdate -g and -o. [RT #19351] 2573. [bug] Replacing a non-CNAME record with a CNAME record in a single transaction in a signed zone failed. [RT #19397] 2572. [func] Simplify DLV configuration, with a new option "dnssec-lookaside auto;" This is the equivalent of "dnssec-lookaside . trust-anchor dlv.isc.org;" plus setting a trusted-key for dlv.isc.org. Note: The trusted key is hard-coded into named, but is also stored in (and can be overridden by) $sysconfdir/bind.keys. As the ISC DLV key rolls over it can be kept up to date by replacing the bind.keys file with a key downloaded from https://www.isc.org/solutions/dlv. [RT #18685] 2571. [func] Add a new tool "arpaname" which translates IP addresses to the corresponding IN-ADDR.ARPA or IP6.ARPA name. [RT #18976] 2570. [func] Log the destination address the query was sent to. [RT #19209] 2569. [func] Move journalprint, nsec3hash, and genrandom commands from bin/tests into bin/tools; "make install" will put them in $sbindir. [RT #19301] 2568. [bug] Report when the write to indicate a otherwise successful start fails. [RT #19360] 2567. [bug] dst__privstruct_writefile() could miss write errors. write_public_key() could miss write errors. dnssec-dsfromkey could miss write errors. [RT #19360] 2566. [cleanup] Clarify logged message when an insecure DNSSEC response arrives from a zone thought to be secure: "insecurity proof failed" instead of "not insecure". [RT #19400] 2565. [func] Add support for HIP record. Includes new functions dns_rdata_hip_first(), dns_rdata_hip_next() and dns_rdata_hip_current(). [RT #19384] 2564. [bug] Only take EDNS fallback steps when processing timeouts. [RT #19405] 2563. [bug] Dig could leak a socket causing it to wait forever to exit. [RT #19359] 2562. [doc] ARM: miscellaneous improvements, reorganization, and some new content. 2561. [doc] Add isc-config.sh(1) man page. [RT #16378] 2560. [bug] Add #include to iptable.c. [RT #18258] 2559. [bug] dnssec-dsfromkey could compute bad DS records when reading from a K* files. [RT #19357] 2558. [func] Set the ownership of missing directories created for pid-file if -u has been specified on the command line. [RT #19328] 2557. [cleanup] PCI compliance: * new libisc log module file * isc_dir_chroot() now also changes the working directory to "/". * additional INSISTs * additional logging when files can't be removed. 2556. [port] Solaris: mkdir(2) on tmpfs filesystems does not do the error checks in the correct order resulting in the wrong error code sometimes being returned. [RT #19249] 2555. [func] dig: when emitting a hex dump also display the corresponding characters. [RT #19258] 2554. [bug] Validation of uppercase queries from NSEC3 zones could fail. [RT #19297] 2553. [bug] Reference leak on DNSSEC validation errors. [RT #19291] 2552. [bug] zero-no-soa-ttl-cache was not being honored. [RT #19340] 2551. [bug] Potential Reference leak on return. [RT #19341] 2550. [bug] Check --with-openssl= finds . [RT #19343] 2549. [port] linux: define NR_OPEN if not currently defined. [RT #19344] 2548. [bug] Install iterated_hash.h. [RT #19335] 2547. [bug] openssl_link.c:mem_realloc() could reference an out-of-range area of the source buffer. New public function isc_mem_reallocate() was introduced to address this bug. [RT #19313] 2546. [func] Add --enable-openssl-hash configure flag to use OpenSSL (in place of internal routine) for hash functions (MD5, SHA[12] and HMAC). [RT #18815] 2545. [doc] ARM: Legal hostname checking (check-names) is for SRV RDATA too. [RT #19304] 2544. [cleanup] Removed unused structure members in adb.c. [RT #19225] 2543. [contrib] Update contrib/zkt to version 0.98. [RT #19113] 2542. [doc] Update the description of dig +adflag. [RT #19290] 2541. [bug] Conditionally update dispatch manager statistics. [RT #19247] 2540. [func] Add a nibble mode to $GENERATE. [RT #18872] 2539. [security] Update the interaction between recursion, allow-query, allow-query-cache and allow-recursion. [RT #19198] 2538. [bug] cache/ADB memory could grow over max-cache-size, especially with threads and smaller max-cache-size values. [RT #19240] 2537. [func] Added more statistics counters including those on socket I/O events and query RTT histograms. [RT #18802] 2536. [cleanup] Silence some warnings when -Werror=format-security is specified. [RT #19083] 2535. [bug] dig +showsearch and +trace interacted badly. [RT #19091] 2534. [func] Check NAPTR records regular expressions and replacement strings to ensure they are syntactically valid and consistent. [RT #18168] 2533. [doc] ARM: document @ (at-sign). [RT #17144] 2532. [bug] dig: check the question section of the response to see if it matches the asked question. [RT #18495] 2531. [bug] Change #2207 was incomplete. [RT #19098] 2530. [bug] named failed to reject insecure to secure transitions via UPDATE. [RT #19101] 2529. [cleanup] Upgrade libtool to silence complaints from recent version of autoconf. [RT #18657] 2528. [cleanup] Silence spurious configure warning about --datarootdir [RT #19096] 2527. [placeholder] 2526. [func] New named option "attach-cache" that allows multiple views to share a single cache to save memory and improve lookup efficiency. Based on contributed code from Barclay Osborn, Google. [RT #18905] 2525. [func] New logging category "query-errors" to provide detailed internal information about query failures, especially about server failures. [RT #19027] 2524. [port] sunos: dnssec-signzone needs strtoul(). [RT #19129] 2523. [bug] Random type rdata freed by dns_nsec_typepresent(). [RT #19112] 2522. [security] Handle -1 from DSA_do_verify() and EVP_VerifyFinal(). 2521. [bug] Improve epoll cross compilation support. [RT #19047] 2520. [bug] Update xml statistics version number to 2.0 as change #2388 made the schema incompatible to the previous version. [RT #19080] 2519. [bug] dig/host with -4 or -6 didn't work if more than two nameserver addresses of the excluded address family preceded in resolv.conf. [RT #19081] 2518. [func] Add support for the new CERT types from RFC 4398. [RT #19077] 2517. [bug] dig +trace with -4 or -6 failed when it chose a nameserver address of the excluded address type. [RT #18843] 2516. [bug] glue sort for responses was performed even when not needed. [RT #19039] 2515. [port] win32: build dnssec-dsfromkey and dnssec-keyfromlabel. [RT #19063] 2514. [bug] dig/host failed with -4 or -6 when resolv.conf contains a nameserver of the excluded address family. [RT #18848] 2513. [bug] Fix windows cli build. [RT #19062] 2512. [func] Print a summary of the cached records which make up the negative response. [RT #18885] 2511. [cleanup] dns_rdata_tofmttext() add const to linebreak. [RT #18885] 2510. [bug] "dig +sigchase" could trigger REQUIRE failures. [RT #19033] 2509. [bug] Specifying a fixed query source port was broken. [RT #19051] 2508. [placeholder] 2507. [func] Log the recursion quota values when killing the oldest query or refusing to recurse due to quota. [RT #19022] 2506. [port] solaris: Check at configure time if hack_shutup_pthreadonceinit is needed. [RT #19037] 2505. [port] Treat amd64 similarly to x86_64 when determining atomic operation support. [RT #19031] 2504. [bug] Address race condition in the socket code. [RT #18899] 2503. [port] linux: improve compatibility with Linux Standard Base. [RT #18793] 2502. [cleanup] isc_radix: Improve compliance with coding style, document function in . [RT #18534] 2501. [func] $GENERATE now supports all rdata types. Multi-field rdata types need to be quoted. See the ARM for details. [RT #18368] 2500. [contrib] contrib/sdb/pgsql/zonetodb.c called non-existent function. [RT #18582] 2499. [port] solaris: lib/lwres/getaddrinfo.c namespace clash. [RT #18837] --- 9.6.0rc1 released --- 2498. [bug] Removed a bogus function argument used with ISC_SOCKET_USE_POLLWATCH: it could cause compiler warning or crash named with the debug 1 level of logging. [RT #18917] 2497. [bug] Don't add RRSIG bit to NSEC3 bit map for insecure delegation. 2496. [bug] Add sanity length checks to NSID option. [RT #18813] 2495. [bug] Tighten RRSIG checks. [RT #18795] 2494. [bug] isc/radix.h, dns/sdlz.h and dns/dlz.h were not being installed. [RT #18826] 2493. [bug] The linux capabilities code was not correctly cleaning up after itself. [RT #18767] 2492. [func] Rndc status now reports the number of cpus discovered and the number of worker threads when running multi-threaded. [RT #18273] 2491. [func] Attempt to re-use a local port if we are already using the port. [RT #18548] 2490. [port] aix: work around a kernel bug where IPV6_RECVPKTINFO is cleared when IPV6_V6ONLY is set. [RT #18785] 2489. [port] solaris: Workaround Solaris's kernel bug about /dev/poll: http://bugs.opensolaris.org/view_bug.do?bug_id=6724237 Define ISC_SOCKET_USE_POLLWATCH at build time to enable this workaround. [RT #18870] 2488. [func] Added a tool, dnssec-dsfromkey, to generate DS records from keyset and .key files. [RT #18694] 2487. [bug] Give TCP connections longer to complete. [RT #18675] 2486. [func] The default locations for named.pid and lwresd.pid are now /var/run/named/named.pid and /var/run/lwresd/lwresd.pid respectively. This allows the owner of the containing directory to be set, for "named -u" support, and allows there to be a permanent symbolic link in the path, for "named -t" support. [RT #18306] 2485. [bug] Change update's the handling of obscured RRSIG records. Not all orphaned DS records were being removed. [RT #18828] 2484. [bug] It was possible to trigger a REQUIRE failure when adding NSEC3 proofs to the response in query_addwildcardproof(). [RT #18828] 2483. [port] win32: chroot() is not supported. [RT #18805] 2482. [port] libxml2: support versions 2.7.* in addition to 2.6.*. [RT #18806] --- 9.6.0b1 released --- 2481. [bug] rbtdb.c:matchparams() failed to handle NSEC3 chain collisions. [RT #18812] 2480. [bug] named could fail to emit all the required NSEC3 records. [RT #18812] 2479. [bug] xfrout:covers was not properly initialized. [RT #18801] 2478. [bug] 'addresses' could be used uninitialized in configure_forward(). [RT #18800] 2477. [bug] dig: the global option to print the command line is +cmd not print_cmd. Update the output to reflect this. [RT #17008] 2476. [doc] ARM: improve documentation for max-journal-size and ixfr-from-differences. [RT #15909] [RT #18541] 2475. [bug] LRU cache cleanup under overmem condition could purge particular entries more aggressively. [RT #17628] 2474. [bug] ACL structures could be allocated with insufficient space, causing an array overrun. [RT #18765] 2473. [port] linux: raise the limit on open files to the possible maximum value before spawning threads; 'files' specified in named.conf doesn't seem to work with threads as expected. [RT #18784] 2472. [port] linux: check the number of available cpu's before calling chroot as it depends on "/proc". [RT #16923] 2471. [bug] named-checkzone was not reporting missing mandatory glue when sibling checks were disabled. [RT #18768] 2470. [bug] Elements of the isc_radix_node_t could be incorrectly overwritten. [RT# 18719] 2469. [port] solaris: Work around Solaris's select() limitations. [RT #18769] 2468. [bug] Resolver could try unreachable servers multiple times. [RT #18739] 2467. [bug] Failure of fcntl(F_DUPFD) wasn't logged. [RT #18740] 2466. [doc] ARM: explain max-cache-ttl 0 SERVFAIL issue. [RT #18302] 2465. [bug] Adb's handling of lame addresses was different for IPv4 and IPv6. [RT #18738] 2464. [port] linux: check that a capability is present before trying to set it. [RT #18135] 2463. [port] linux: POSIX doesn't include the IPv6 Advanced Socket API and glibc hides parts of the IPv6 Advanced Socket API as a result. This is stupid as it breaks how the two halves (Basic and Advanced) of the IPv6 Socket API were designed to be used but we have to live with it. Define _GNU_SOURCE to pull in the IPv6 Advanced Socket API. [RT #18388] 2462. [doc] Document -m (enable memory usage debugging) option for dig. [RT #18757] 2461. [port] sunos: Change #2363 was not complete. [RT #17513] --- 9.6.0a1 released --- 2460. [bug] Don't call dns_db_getnsec3parameters() on the cache. [RT #18697] 2459. [contrib] Import dnssec-zkt to contrib/zkt. [RT #18448] 2458. [doc] ARM: update and correction for max-cache-size. [RT #18294] 2457. [tuning] max-cache-size is reverted to 0, the previous default. It should be safe because expired cache entries are also purged. [RT #18684] 2456. [bug] In ACLs, ::/0 and 0.0.0.0/0 would both match any address, regardless of family. They now correctly distinguish IPv4 from IPv6. [RT #18559] 2455. [bug] Stop metadata being transferred via axfr/ixfr. [RT #18639] 2454. [func] nsupdate: you can now set a default ttl. [RT #18317] 2453. [bug] Remove NULL pointer dereference in dns_journal_print(). [RT #18316] 2452. [func] Improve bin/test/journalprint. [RT #18316] 2451. [port] solaris: handle runtime linking better. [RT #18356] 2450. [doc] Fix lwresd docbook problem for manual page. [RT #18672] 2449. [placeholder] 2448. [func] Add NSEC3 support. [RT #15452] 2447. [cleanup] libbind has been split out as a separate product. 2446. [func] Add a new log message about build options on startup. A new command-line option '-V' for named is also provided to show this information. [RT# 18645] 2445. [doc] ARM out-of-date on empty reverse zones (list includes RFC1918 address, but these are not yet compiled in). [RT #18578] 2444. [port] Linux, FreeBSD, AIX: Turn off path mtu discovery (clear DF) for UDP responses and requests. 2443. [bug] win32: UDP connect() would not generate an event, and so connected UDP sockets would never clean up. Fix this by doing an immediate WSAConnect() rather than an io completion port type for UDP. 2442. [bug] A lock could be destroyed twice. [RT# 18626] 2441. [bug] isc_radix_insert() could copy radix tree nodes incompletely. [RT #18573] 2440. [bug] named-checkconf used an incorrect test to determine if an ACL was set to none. 2439. [bug] Potential NULL dereference in dns_acl_isanyornone(). [RT #18559] 2438. [bug] Timeouts could be logged incorrectly under win32. 2437. [bug] Sockets could be closed too early, leading to inconsistent states in the socket module. [RT #18298] 2436. [security] win32: UDP client handler can be shutdown. [RT #18576] 2435. [bug] Fixed an ACL memory leak affecting win32. 2434. [bug] Fixed a minor error-reporting bug in lib/isc/win32/socket.c. 2433. [tuning] Set initial timeout to 800ms. 2432. [bug] More Windows socket handling improvements. Stop using I/O events and use IO Completion Ports throughout. Rewrite the receive path logic to make it easier to support multiple simultaneous requesters in the future. Add stricter consistency checking as a compile-time option (define ISC_SOCKET_CONSISTENCY_CHECKS; defaults to off). 2431. [bug] Acl processing could leak memory. [RT #18323] 2430. [bug] win32: isc_interval_set() could round down to zero if the input was less than NS_INTERVAL nanoseconds. Round up instead. [RT #18549] 2429. [doc] nsupdate should be in section 1 of the man pages. [RT #18283] 2428. [bug] dns_iptable_merge() mishandled merges of negative tables. [RT #18409] 2427. [func] Treat DNSKEY queries as if "minimal-response yes;" was set. [RT #18528] 2426. [bug] libbind: inet_net_pton() can sometimes return the wrong value if excessively large net masks are supplied. [RT #18512] 2425. [bug] named didn't detect unavailable query source addresses at load time. [RT #18536] 2424. [port] configure now probes for a working epoll implementation. Allow the use of kqueue, epoll and /dev/poll to be selected at compile time. [RT #18277] 2423. [security] Randomize server selection on queries, so as to make forgery a little more difficult. Instead of always preferring the server with the lowest RTT, pick a server with RTT within the same 128 millisecond band. [RT #18441] 2422. [bug] Handle the special return value of a empty node as if it was a NXRRSET in the validator. [RT #18447] 2421. [func] Add new command line option '-S' for named to specify the max number of sockets. [RT #18493] Use caution: this option may not work for some operating systems without rebuilding named. 2420. [bug] Windows socket handling cleanup. Let the io completion event send out canceled read/write done events, which keeps us from writing to memory we no longer have ownership of. Add debugging socket_log() function. Rework TCP socket handling to not leak sockets. 2419. [cleanup] Document that isc_socket_create() and isc_socket_open() should not be used for isc_sockettype_fdwatch sockets. [RT #18521] 2418. [bug] AXFR request on a DLZ could trigger a REQUIRE failure [RT #18430] 2417. [bug] Connecting UDP sockets for outgoing queries could unexpectedly fail with an 'address already in use' error. [RT #18411] 2416. [func] Log file descriptors that cause exceeding the internal maximum. [RT #18460] 2415. [bug] 'rndc dumpdb' could trigger various assertion failures in rbtdb.c. [RT #18455] 2414. [bug] A masterdump context held the database lock too long, causing various troubles such as dead lock and recursive lock acquisition. [RT #18311, #18456] 2413. [bug] Fixed an unreachable code path in socket.c. [RT #18442] 2412. [bug] win32: address a resource leak. [RT #18374] 2411. [bug] Allow using a larger number of sockets than FD_SETSIZE for select(). To enable this, set ISC_SOCKET_MAXSOCKETS at compilation time. [RT #18433] Note: with changes #2469 and #2421 above, there is no need to tweak ISC_SOCKET_MAXSOCKETS at compilation time any more. 2410. [bug] Correctly delete m_versionInfo. [RT #18432] 2409. [bug] Only log that we disabled EDNS processing if we were subsequently successful. [RT #18029] 2408. [bug] A duplicate TCP dispatch event could be sent, which could then trigger an assertion failure in resquery_response(). [RT #18275] 2407. [port] hpux: test for sys/dyntune.h. [RT #18421] 2406. [placeholder] 2405. [cleanup] The default value for dnssec-validation was changed to "yes" in 9.5.0-P1 and all subsequent releases; this was inadvertently omitted from CHANGES at the time. 2404. [port] hpux: files unlimited support. 2403. [bug] TSIG context leak. [RT #18341] 2402. [port] Support Solaris 2.11 and over. [RT #18362] 2401. [bug] Expect to get E[MN]FILE errno internal_accept() (from accept() or fcntl() system calls). [RT #18358] 2400. [bug] Log if kqueue()/epoll_create()/open(/dev/poll) fails. [RT #18297] 2399. [placeholder] 2398. [bug] Improve file descriptor management. New, temporary, named.conf option reserved-sockets, default 512. [RT #18344] 2397. [bug] gssapi_functions had too many elements. [RT #18355] 2396. [bug] Don't set SO_REUSEADDR for randomized ports. [RT #18336] 2395. [port] Avoid warning and no effect from "files unlimited" on Linux when running as root. [RT #18335] 2394. [bug] Default configuration options set the limit for open files to 'unlimited' as described in the documentation. [RT #18331] 2393. [bug] nested acls containing keys could trigger an assertion in acl.c. [RT #18166] 2392. [bug] remove 'grep -q' from acl test script, some platforms don't support it. [RT #18253] 2391. [port] hpux: cover additional recvmsg() error codes. [RT #18301] 2390. [bug] dispatch.c could make a false warning on 'odd socket'. [RT #18301]. 2389. [bug] Move the "working directory writable" check to after the ns_os_changeuser() call. [RT #18326] 2388. [bug] Avoid using tables for layout purposes in statistics XSL [RT #18159]. 2387. [bug] Silence compiler warnings in lib/isc/radix.c. [RT #18147] [RT #18258] 2386. [func] Add warning about too small 'open files' limit. [RT #18269] 2385. [bug] A condition variable in socket.c could leak in rare error handling [RT #17968]. 2384. [security] Fully randomize UDP query ports to improve forgery resilience. [RT #17949, #18098] 2383. [bug] named could double queries when they resulted in SERVFAIL due to overkilling EDNS0 failure detection. [RT #18182] 2382. [doc] Add descriptions of DHCID, IPSECKEY, SPF and SSHFP to ARM. 2381. [port] dlz/mysql: support multiple install layouts for mysql. /include/{,mysql/}mysql.h and /lib/{,mysql/}. [RT #18152] 2380. [bug] dns_view_find() was not returning NXDOMAIN/NXRRSET proofs which, in turn, caused validation failures for insecure zones immediately below a secure zone the server was authoritative for. [RT #18112] 2379. [contrib] queryperf/gen-data-queryperf.py: removed redundant TLDs and supported RRs with TTLs [RT #17972] 2378. [bug] gssapi_functions{} had a redundant member in BIND 9.5. [RT #18169] 2377. [bug] Address race condition in dnssec-signzone. [RT #18142] 2376. [bug] Change #2144 was not complete. 2375. [placeholder] 2374. [bug] "blackhole" ACLs could cause named to segfault due to some uninitialized memory. [RT #18095] 2373. [bug] Default values of zone ACLs were re-parsed each time a new zone was configured, causing an overconsumption of memory. [RT #18092] 2372. [bug] Fixed incorrect TAG_HMACSHA256_BITS value [RT #18047] 2371. [doc] Add +nsid option to dig man page. [RT #18039] 2370. [bug] "rndc freeze" could trigger an assertion in named when called on a nonexistent zone. [RT #18050] 2369. [bug] libbind: Array bounds overrun on read in bitncmp(). [RT #18054] 2368. [port] Linux: use libcap for capability management if possible. [RT# 18026] 2367. [bug] Improve counting of dns_resstatscounter_retry [RT #18030] 2366. [bug] Adb shutdown race. [RT #18021] 2365. [bug] Fix a bug that caused dns_acl_isany() to return spurious results. [RT #18000] 2364. [bug] named could trigger a assertion when serving a malformed signed zone. [RT #17828] 2363. [port] sunos: pre-set "lt_cv_sys_max_cmd_len=4096;". [RT #17513] 2362. [cleanup] Make "rrset-order fixed" a compile-time option. settable by "./configure --enable-fixed-rrset". Disabled by default. [RT #17977] 2361. [bug] "recursion" statistics counter could be counted multiple times for a single query. [RT #17990] 2360. [bug] Fix a condition where we release a database version (which may acquire a lock) while holding the lock. 2359. [bug] Fix NSID bug. [RT #17942] 2358. [doc] Update host's default query description. [RT #17934] 2357. [port] Don't use OpenSSL's engine support in versions before OpenSSL 0.9.7f. [RT #17922] 2356. [bug] Built in mutex profiler was not scalable enough. [RT #17436] 2355. [func] Extend the number statistics counters available. [RT #17590] 2354. [bug] Failed to initialize some rdatasetheader_t elements. [RT #17927] 2353. [func] Add support for Name Server ID (RFC 5001). 'dig +nsid' requests NSID from server. 'request-nsid yes;' causes recursive server to send NSID requests to upstream servers. Server responds to NSID requests with the string configured by 'server-id' option. [RT #17091] 2352. [bug] Various GSS_API fixups. [RT #17729] 2351. [bug] convertxsl.pl generated very long lines. [RT #17906] 2350. [port] win32: IPv6 support. [RT #17797] 2349. [func] Provide incremental re-signing support for secure dynamic zones. [RT #1091] 2348. [func] Use the EVP interface to OpenSSL. Add PKCS#11 support. Documentation is in the new README.pkcs11 file. New tool, dnssec-keyfromlabel, which takes the label of a key pair in a HSM and constructs a DNS key pair for use by named and dnssec-signzone. [RT #16844] 2347. [bug] Delete now traverses the RB tree in the canonical order. [RT #17451] 2346. [func] Memory statistics now cover all active memory contexts in increased detail. [RT #17580] 2345. [bug] named-checkconf failed to detect when forwarders were set at both the options/view level and in a root zone. [RT #17671] 2344. [bug] Improve "logging{ file ...; };" documentation. [RT #17888] 2343. [bug] (Seemingly) duplicate IPv6 entries could be created in ADB. [RT #17837] 2342. [func] Use getifaddrs() if available under Linux. [RT #17224] 2341. [bug] libbind: add missing -I../include for off source tree builds. [RT #17606] 2340. [port] openbsd: interface configuration. [RT #17700] 2339. [port] tru64: support for libbind. [RT #17589] 2338. [bug] check_ds() could be called with a non DS rdataset. [RT #17598] 2337. [bug] BUILD_LDFLAGS was not being correctly set. [RT #17614] 2336. [func] If "named -6" is specified then listen on all IPv6 interfaces if there are not listen-on-v6 clauses in named.conf. [RT #17581] 2335. [port] sunos: libbind and *printf() support for long long. [RT #17513] 2334. [bug] Bad REQUIRES in fromstruct_in_naptr(), off by one bug in fromstruct_txt(). [RT #17609] 2333. [bug] Fix off by one error in isc_time_nowplusinterval(). [RT #17608] 2332. [contrib] query-loc-0.4.0. [RT #17602] 2331. [bug] Failure to regenerate any signatures was not being reported nor being past back to the UPDATE client. [RT #17570] 2330. [bug] Remove potential race condition when handling over memory events. [RT #17572] WARNING: API CHANGE: over memory callback function now needs to call isc_mem_waterack(). See for details. 2329. [bug] Clearer help text for dig's '-x' and '-i' options. 2328. [maint] Add AAAA addresses for A.ROOT-SERVERS.NET, F.ROOT-SERVERS.NET, H.ROOT-SERVERS.NET, J.ROOT-SERVERS.NET, K.ROOT-SERVERS.NET and M.ROOT-SERVERS.NET. 2327. [bug] It was possible to dereference a NULL pointer in rbtdb.c. Implement dead node processing in zones as we do for caches. [RT #17312] 2326. [bug] It was possible to trigger a INSIST in the acache processing. 2325. [port] Linux: use capset() function if available. [RT #17557] 2324. [bug] Fix IPv6 matching against "any;". [RT #17533] 2323. [port] tru64: namespace clash. [RT #17547] 2322. [port] MacOS: work around the limitation of setrlimit() for RLIMIT_NOFILE. [RT #17526] 2321. [placeholder] 2320. [func] Make statistics counters thread-safe for platforms that support certain atomic operations. [RT #17466] 2319. [bug] Silence Coverity warnings in lib/dns/rdata/in_1/apl_42.c. [RT #17469] 2318. [port] sunos fixes for libbind. [RT #17514] 2317. [bug] "make distclean" removed bind9.xsl.h. [RT #17518] 2316. [port] Missing #include in lib/dns/gssapictx.c. [RT #17513] 2315. [bug] Used incorrect address family for mapped IPv4 addresses in acl.c. [RT #17519] 2314. [bug] Uninitialized memory use on error path in bin/named/lwdnoop.c. [RT #17476] 2313. [cleanup] Silence Coverity warnings. Handle private stacks. [RT #17447] [RT #17478] 2312. [cleanup] Silence Coverity warning in lib/isc/unix/socket.c. [RT #17458] 2311. [bug] IPv6 addresses could match IPv4 ACL entries and vice versa. [RT #17462] 2310. [bug] dig, host, nslookup: flush stdout before emitting debug/fatal messages. [RT #17501] 2309. [cleanup] Fix Coverity warnings in lib/dns/acl.c and iptable.c. [RT #17455] 2308. [cleanup] Silence Coverity warning in bin/named/controlconf.c. [RT #17495] 2307. [bug] Remove infinite loop from lib/dns/sdb.c. [RT #17496] 2306. [bug] Remove potential race from lib/dns/resolver.c. [RT #17470] 2305. [security] inet_network() buffer overflow. CVE-2008-0122. 2304. [bug] Check returns from all dns_rdata_tostruct() calls. [RT #17460] 2303. [bug] Remove unnecessary code from bin/named/lwdgnba.c. [RT #17471] 2302. [bug] Fix memset() calls in lib/tests/t_api.c. [RT #17472] 2301. [bug] Remove resource leak and fix error messages in bin/tests/system/lwresd/lwtest.c. [RT #17474] 2300. [bug] Fixed failure to close open file in bin/tests/names/t_names.c. [RT #17473] 2299. [bug] Remove unnecessary NULL check in bin/nsupdate/nsupdate.c. [RT #17475] 2298. [bug] isc_mutex_lock() failure not caught in bin/tests/timers/t_timers.c. [RT #17468] 2297. [bug] isc_entropy_createfilesource() failure not caught in bin/tests/dst/t_dst.c. [RT #17467] 2296. [port] Allow docbook stylesheet location to be specified to configure. [RT #17457] 2295. [bug] Silence static overrun error in bin/named/lwaddr.c. [RT #17459] 2294. [func] Allow the experimental statistics channels to have multiple connections and ACL. Note: the stats-server and stats-server-v6 options available in the previous beta releases are replaced with the generic statistics-channels statement. 2293. [func] Add ACL regression test. [RT #17375] 2292. [bug] Log if the working directory is not writable. [RT #17312] 2291. [bug] PR_SET_DUMPABLE may be set too late. Also report failure to set PR_SET_DUMPABLE. [RT #17312] 2290. [bug] Let AD in the query signal that the client wants AD set in the response. [RT #17301] 2289. [func] named-checkzone now reports the out-of-zone CNAME found. [RT #17309] 2288. [port] win32: mark service as running when we have finished loading. [RT #17441] 2287. [bug] Use 'volatile' if the compiler supports it. [RT #17413] 2286. [func] Allow a TCP connection to be used as a weak authentication method for reverse zones. New update-policy methods tcp-self and 6to4-self. [RT #17378] 2285. [func] Test framework for client memory context management. [RT #17377] 2284. [bug] Memory leak in UPDATE prerequisite processing. [RT #17377] 2283. [bug] TSIG keys were not attaching to the memory context. TSIG keys should use the rings memory context rather than the clients memory context. [RT #17377] 2282. [bug] Acl code fixups. [RT #17346] [RT #17374] 2281. [bug] Attempts to use undefined acls were not being logged. [RT #17307] 2280. [func] Allow the experimental http server to be reached over IPv6 as well as IPv4. [RT #17332] 2279. [bug] Use setsockopt(SO_NOSIGPIPE), when available, to protect applications from receiving spurious SIGPIPE signals when using the resolver. 2278. [bug] win32: handle the case where Windows returns no search list or DNS suffix. [RT #17354] 2277. [bug] Empty zone names were not correctly being caught at in the post parse checks. [RT #17357] 2276. [bug] Install . [RT# 17359] 2275. [func] Add support to dig to perform IXFR queries over UDP. [RT #17235] 2274. [func] Log zone transfer statistics. [RT #17336] 2273. [bug] Adjust log level to WARNING when saving inconsistent stub/slave master and journal files. [RT# 17279] 2272. [bug] Handle illegal dnssec-lookaside trust-anchor names. [RT #17262] 2271. [bug] Fix a memory leak in http server code [RT #17100] 2270. [bug] dns_db_closeversion() version->writer could be reset before it is tested. [RT #17290] 2269. [contrib] dbus memory leaks and missing va_end calls. [RT #17232] 2268. [bug] 0.IN-ADDR.ARPA was missing from the empty zones list. --- 9.5.0b1 released --- 2267. [bug] Radix tree node_num value could be set incorrectly, causing positive ACL matches to look like negative ones. [RT #17311] 2266. [bug] client.c:get_clientmctx() returned the same mctx once the pool of mctx's was filled. [RT #17218] 2265. [bug] Test that the memory context's basic_table is non NULL before freeing. [RT #17265] 2264. [bug] Server prefix length was being ignored. [RT #17308] 2263. [bug] "named-checkconf -z" failed to set default value for "check-integrity". [RT #17306] 2262. [bug] Error status from all but the last view could be lost. [RT #17292] 2261. [bug] Fix memory leak with "any" and "none" ACLs [RT #17272] 2260. [bug] Reported wrong clients-per-query when increasing the value. [RT #17236] 2259. [placeholder] --- 9.5.0a7 released --- 2258. [bug] Fallback from IXFR/TSIG to SOA/AXFR/TSIG broken. [RT #17241] 2257. [bug] win32: Use the full path to vcredist_x86.exe when calling it. [RT #17222] 2256. [bug] win32: Correctly register the installation location of bindevt.dll. [RT #17159] 2255. [maint] L.ROOT-SERVERS.NET is now 199.7.83.42. 2254. [bug] timer.c:dispatch() failed to lock timer->lock when reading timer->idle allowing it to see intermediate values as timer->idle was reset by isc_timer_touch(). [RT #17243] 2253. [func] "max-cache-size" defaults to 32M. "max-acache-size" defaults to 16M. 2252. [bug] Fixed errors in sortlist code [RT #17216] 2251. [placeholder] 2250. [func] New flag 'memstatistics' to state whether the memory statistics file should be written or not. Additionally named's -m option will cause the statistics file to be written. [RT #17113] 2249. [bug] Only set Authentic Data bit if client requested DNSSEC, per RFC 3655 [RT #17175] 2248. [cleanup] Fix several errors reported by Coverity. [RT #17160] 2247. [doc] Sort doc/misc/options. [RT #17067] 2246. [bug] Make the startup of test servers (ans.pl) more robust. [RT #17147] 2245. [bug] Validating lack of DS records at trust anchors wasn't working. [RT #17151] 2244. [func] Allow the check of nameserver names against the SOA MNAME field to be disabled by specifying 'notify-to-soa yes;'. [RT #17073] 2243. [func] Configuration files without a newline at the end now parse without error. [RT #17120] 2242. [bug] nsupdate: GSS-TSIG support using the Heimdal Kerberos library could require a source of random data. [RT #17127] 2241. [func] nsupdate: add a interactive 'help' command. [RT #17099] 2240. [bug] Cleanup nsupdates GSS-TSIG support. Convert a number of INSIST()s into plain fatal() errors which report the triggering result code. The 'key' command wasn't disabling GSS-TSIG. [RT #17099] 2239. [func] Ship a pre built bin/named/bind9.xsl.h. [RT #17114] 2238. [bug] It was possible to trigger a REQUIRE when a validation was canceled. [RT #17106] 2237. [bug] libbind: res_init() was not thread aware. [RT #17123] 2236. [bug] dnssec-signzone failed to preserve the case of of wildcard owner names. [RT #17085] 2235. [bug] was not being installed. [RT #17135] 2234. [port] Correct some compiler warnings on SCO OSr5 [RT #17134] 2233. [func] Add support for O(1) ACL processing, based on radix tree code originally written by Kevin Brintnall. [RT #16288] 2232. [bug] dns_adb_findaddrinfo() could fail and return ISC_R_SUCCESS. [RT #17137] 2231. [bug] Building dlzbdb (contrib/dlz/bin/dlzbdb) was broken. [RT #17088] 2230. [bug] We could INSIST reading a corrupted journal. [RT #17132] 2229. [bug] Null pointer dereference on query pool creation failure. [RT #17133] 2228. [contrib] contrib: Change 2188 was incomplete. 2227. [cleanup] Tidied up the FAQ. [RT #17121] 2226. [placeholder] 2225. [bug] More support for systems with no IPv4 addresses. [RT #17111] 2224. [bug] Defer journal compaction if a xfrin is in progress. [RT #17119] 2223. [bug] Make a new journal when compacting. [RT #17119] 2222. [func] named-checkconf now checks server key references. [RT #17097] 2221. [bug] Set the event result code to reflect the actual record turned to caller when a cache update is rejected due to a more credible answer existing. [RT #17017] 2220. [bug] win32: Address a race condition in final shutdown of the Windows socket code. [RT #17028] 2219. [bug] Apply zone consistency checks to additions, not removals, when updating. [RT #17049] 2218. [bug] Remove unnecessary REQUIRE from dns_validator_create(). [RT #16976] 2217. [func] Adjust update log levels. [RT #17092] 2216. [cleanup] Fix a number of errors reported by Coverity. [RT #17094] 2215. [bug] Bad REQUIRE check isc_hmacsha1_verify(). [RT #17094] 2214. [bug] Deregister OpenSSL lock callback when cleaning up. Reorder OpenSSL cleanup so that RAND_cleanup() is called before the locks are destroyed. [RT #17098] 2213. [bug] SIG0 diagnostic failure messages were looking at the wrong status code. [RT #17101] 2212. [func] 'host -m' now causes memory statistics and active memory to be printed at exit. [RT 17028] 2211. [func] Update "dynamic update temporarily disabled" message. [RT #17065] 2210. [bug] Deleting class specific records via UPDATE could fail. [RT #17074] 2209. [port] osx: linking against user supplied static OpenSSL libraries failed as the system ones were still being found. [RT #17078] 2208. [port] win32: make sure both build methods produce the same output. [RT #17058] 2207. [port] Some implementations of getaddrinfo() fail to set ai_canonname correctly. [RT #17061] --- 9.5.0a6 released --- 2206. [security] "allow-query-cache" and "allow-recursion" now cross inherit from each other. If allow-query-cache is not set in named.conf then allow-recursion is used if set, otherwise allow-query is used if set, otherwise the default (localnets; localhost;) is used. If allow-recursion is not set in named.conf then allow-query-cache is used if set, otherwise allow-query is used if set, otherwise the default (localnets; localhost;) is used. [RT #16987] 2205. [bug] libbind: change #2119 broke thread support. [RT #16982] 2204. [bug] "rndc flushanme name unknown-view" caused named to crash. [RT #16984] 2203. [security] Query id generation was cryptographically weak. [RT # 16915] 2202. [security] The default acls for allow-query-cache and allow-recursion were not being applied. [RT #16960] 2201. [bug] The build failed in a separate object directory. [RT #16943] 2200. [bug] The search for cached NSEC records was stopping to early leading to excessive DLV queries. [RT #16930] 2199. [bug] win32: don't call WSAStartup() while loading dlls. [RT #16911] 2198. [bug] win32: RegCloseKey() could be called when RegOpenKeyEx() failed. [RT #16911] 2197. [bug] Add INSIST to catch negative responses which are not setting the event result code appropriately. [RT #16909] 2196. [port] win32: yield processor while waiting for once to to complete. [RT #16958] 2195. [func] dnssec-keygen now defaults to nametype "ZONE" when generating DNSKEYs. [RT #16954] 2194. [bug] Close journal before calling 'done' in xfrin.c. --- 9.5.0a5 released --- 2193. [port] win32: BINDInstall.exe is now linked statically. [RT #16906] 2192. [port] win32: use vcredist_x86.exe to install Visual Studio's redistributable dlls if building with Visual Stdio 2005 or later. 2191. [func] named-checkzone now allows dumping to stdout (-). named-checkconf now has -h for help. named-checkzone now has -h for help. rndc now has -h for help. Better handling of '-?' for usage summaries. [RT #16707] 2190. [func] Make fallback to plain DNS from EDNS due to timeouts more visible. New logging category "edns-disabled". [RT #16871] 2189. [bug] Handle socket() returning EINTR. [RT #15949] 2188. [contrib] queryperf: autoconf changes to make the search for libresolv or libbind more robust. [RT #16299] 2187. [bug] query_addds(), query_addwildcardproof() and query_addnxrrsetnsec() should take a version argument. [RT #16368] 2186. [port] cygwin: libbind: check for struct sockaddr_storage independently of IPv6. [RT #16482] 2185. [port] sunos: libbind: check for ssize_t, memmove() and memchr(). [RT #16463] 2184. [bug] bind9.xsl.h didn't build out of the source tree. [RT #16830] 2183. [bug] dnssec-signzone didn't handle offline private keys well. [RT #16832] 2182. [bug] dns_dispatch_createtcp() and dispatch_createudp() could return ISC_R_SUCCESS when they ran out of memory. [RT #16365] 2181. [port] sunos: libbind: add paths.h from BIND 8. [RT #16462] 2180. [cleanup] Remove bit test from 'compress_test' as they are no longer needed. [RT #16497] 2179. [func] 'rndc command zone' will now find 'zone' if it is unique to all the views. [RT #16821] 2178. [bug] 'rndc reload' of a slave or stub zone resulted in a reference leak. [RT #16867] 2177. [bug] Array bounds overrun on read (rcodetext) at debug level 10+. [RT #16798] 2176. [contrib] dbus update to handle race condition during initialization (Bugzilla 235809). [RT #16842] 2175. [bug] win32: windows broadcast condition variable support was broken. [RT #16592] 2174. [bug] I/O errors should always be fatal when reading master files. [RT #16825] 2173. [port] win32: When compiling with MSVS 2005 SP1 we also need to ship Microsoft.VC80.MFCLOC. --- 9.5.0a4 released --- 2172. [bug] query_addsoa() was being called with a non zone db. [RT #16834] 2171. [bug] Handle breaks in DNSSEC trust chains where the parent servers are not DS aware (DS queries to the parent return a referral to the child). 2170. [func] Add acache processing to test suite. [RT #16711] 2169. [bug] host, nslookup: when reporting NXDOMAIN report the given name and not the last name searched for. [RT #16763] 2168. [bug] nsupdate: in non-interactive mode treat syntax errors as fatal errors. [RT #16785] 2167. [bug] When re-using a automatic zone named failed to attach it to the new view. [RT #16786] --- 9.5.0a3 released --- 2166. [bug] When running in batch mode, dig could misinterpret a server address as a name to be looked up, causing unexpected output. [RT #16743] 2165. [func] Allow the destination address of a query to determine if we will answer the query or recurse. allow-query-on, allow-recursion-on and allow-query-cache-on. [RT #16291] 2164. [bug] The code to determine how named-checkzone / named-compilezone was called failed under windows. [RT #16764] 2163. [bug] If only one of query-source and query-source-v6 specified a port the query pools code broke (change 2129). [RT #16768] 2162. [func] Allow "rrset-order fixed" to be disabled at compile time. [RT #16665] 2161. [bug] Fix which log messages are emitted for 'rndc flush'. [RT #16698] 2160. [bug] libisc wasn't handling NULL ifa_addr pointers returned from getifaddrs(). [RT #16708] --- 9.5.0a2 released --- 2159. [bug] Array bounds overrun in acache processing. [RT #16710] 2158. [bug] ns_client_isself() failed to initialize key leading to a REQUIRE failure. [RT #16688] 2157. [func] dns_db_transfernode() created. [RT #16685] 2156. [bug] Fix node reference leaks in lookup.c:lookup_find(), resolver.c:validated() and resolver.c:cache_name(). Fix a memory leak in rbtdb.c:free_noqname(). Make lookup.c:lookup_find() robust against event leaks. [RT #16685] 2155. [contrib] SQLite sdb module from jaboydjr@netwalk.com. [RT #16694] 2154. [func] Scoped (e.g. IPv6 link-local) addresses may now be matched in acls by omitting the scope. [RT #16599] 2153. [bug] nsupdate could leak memory. [RT #16691] 2152. [cleanup] Use sizeof(buf) instead of fixed number in dighost.c:get_trusted_key(). [RT #16678] 2151. [bug] Missing newline in usage message for journalprint. [RT #16679] 2150. [bug] 'rrset-order cyclic' uniformly distribute the starting point for the first response for a given RRset. [RT #16655] 2149. [bug] isc_mem_checkdestroyed() failed to abort on if there were still active memory contexts. [RT #16672] 2148. [func] Add positive logging for rndc commands. [RT #14623] 2147. [bug] libbind: remove potential buffer overflow from hmac_link.c. [RT #16437] 2146. [cleanup] Silence Linux's spurious "obsolete setsockopt SO_BSDCOMPAT" message. [RT #16641] 2145. [bug] Check DS/DLV digest lengths for known digests. [RT #16622] 2144. [cleanup] Suppress logging of SERVFAIL from forwarders. [RT #16619] 2143. [bug] We failed to restart the IPv6 client when the kernel failed to return the destination the packet was sent to. [RT #16613] 2142. [bug] Handle master files with a modification time that matches the epoch. [RT# 16612] 2141. [bug] dig/host should not be setting IDN_ASCCHECK (IDN equivalent of LDH checks). [RT #16609] 2140. [bug] libbind: missing unlock on pthread_key_create() failures. [RT #16654] 2139. [bug] dns_view_find() was being called with wrong type in adb.c. [RT #16670] 2138. [bug] Lock order reversal in resolver.c. [RT #16653] 2137. [port] Mips little endian and/or mips 64 bit are now supported for atomic operations. [RT#16648] 2136. [bug] nslookup/host looped if there was no search list and the host didn't exist. [RT #16657] 2135. [bug] Uninitialized rdataset in sdlz.c. [RT# 16656] 2134. [func] Additional statistics support. [RT #16666] 2133. [port] powerpc: Support both IBM and MacOS Power PC assembler syntaxes. [RT #16647] 2132. [bug] Missing unlock on out of memory in dns_dispatchmgr_setudp(). 2131. [contrib] dlz/mysql: AXFR was broken. [RT #16630] 2130. [func] Log if CD or DO were set. [RT #16640] 2129. [func] Provide a pool of UDP sockets for queries to be made over. See use-queryport-pool, queryport-pool-ports and queryport-pool-updateinterval. [RT #16415] 2128. [doc] xsltproc --nonet, update DTD versions. [RT #16635] 2127. [port] Improved OpenSSL 0.9.8 support. [RT #16563] 2126. [security] Serialize validation of type ANY responses. [RT #16555] 2125. [bug] dns_zone_getzeronosoattl() REQUIRE failure if DLZ was defined. [RT #16574] 2124. [security] It was possible to dereference a freed fetch context. [RT #16584] --- 9.5.0a1 released --- 2123. [func] Use Doxygen to generate internal documentation. [RT #11398] 2122. [func] Experimental http server and statistics support for named via xml. 2121. [func] Add a 10 slot dead masters cache (LRU) with a 600 second timeout. [RT #16553] 2120. [doc] Fix markup on nsupdate man page. [RT #16556] 2119. [compat] libbind: allow res_init() to succeed enough to return the default domain even if it was unable to allocate memory. 2118. [bug] Handle response with long chains of domain name compression pointers which point to other compression pointers. [RT #16427] 2117. [bug] DNSSEC fixes: named could fail to cache NSEC records which could lead to validation failures. named didn't handle negative DS responses that were in the process of being validated. Check CNAME bit before accepting NODATA proof. To be able to ignore a child NSEC there must be SOA (and NS) set in the bitmap. [RT #16399] 2116. [bug] 'rndc reload' could cause the cache to continually be cleaned. [RT #16401] 2115. [bug] 'rndc reconfig' could trigger a INSIST if the number of masters for a zone was reduced. [RT #16444] 2114. [bug] dig/host/nslookup: searches for names with multiple labels were failing. [RT #16447] 2113. [bug] nsupdate: if a zone is specified it should be used for server discover. [RT# 16455] 2112. [security] Warn if weak RSA exponent is used. [RT #16460] 2111. [bug] Fix a number of errors reported by Coverity. [RT #16507] 2110. [bug] "minimal-responses yes;" interacted badly with BIND 8 priming queries. [RT #16491] 2109. [port] libbind: silence aix 5.3 compiler warnings. [RT #16502] 2108. [func] DHCID support. [RT #16456] 2107. [bug] dighost.c: more cleanup of buffers. [RT #16499] 2106. [func] 'rndc status' now reports named's version. [RT #16426] 2105. [func] GSS-TSIG support (RFC 3645). 2104. [port] Fix Solaris SMF error message. 2103. [port] Add /usr/sfw to list of locations for OpenSSL under Solaris. 2102. [port] Silence Solaris 10 warnings. 2101. [bug] OpenSSL version checks were not quite right. [RT #16476] 2100. [port] win32: copy libeay32.dll to Build\Debug. Copy Debug\named-checkzone to Debug\named-compilezone. 2099. [port] win32: more manifest issues. 2098. [bug] Race in rbtdb.c:no_references(), which occasionally triggered an INSIST failure about the node lock reference. [RT #16411] 2097. [bug] named could reference a destroyed memory context after being reloaded / reconfigured. [RT #16428] 2096. [bug] libbind: handle applications that fail to detect res_init() failures better. 2095. [port] libbind: alway prototype inet_cidr_ntop_ipv6() and net_cidr_ntop_ipv6(). [RT #16388] 2094. [contrib] Update named-bootconf. [RT# 16404] 2093. [bug] named-checkzone -s was broken. 2092. [bug] win32: dig, host, nslookup. Use registry config if resolv.conf does not exist or no nameservers listed. [RT #15877] 2091. [port] dighost.c: race condition on cleanup. [RT #16417] 2090. [port] win32: Visual C++ 2005 command line manifest support. [RT #16417] 2089. [security] Raise the minimum safe OpenSSL versions to OpenSSL 0.9.7l and OpenSSL 0.9.8d. Versions prior to these have known security flaws which are (potentially) exploitable in named. [RT #16391] 2088. [security] Change the default RSA exponent from 3 to 65537. [RT #16391] 2087. [port] libisc failed to compile on OS's w/o a vsnprintf. [RT #16382] 2086. [port] libbind: FreeBSD now has get*by*_r() functions. [RT #16403] 2085. [doc] win32: added index.html and README to zip. [RT #16201] 2084. [contrib] dbus update for 9.3.3rc2. 2083. [port] win32: Visual C++ 2005 support. 2082. [doc] Document 'cache-file' as a test only option. 2081. [port] libbind: minor 64-bit portability fix in memcluster.c. [RT #16360] 2080. [port] libbind: res_init.c did not compile on older versions of Solaris. [RT #16363] 2079. [bug] The lame cache was not handling multiple types correctly. [RT #16361] 2078. [bug] dnssec-checkzone output style "default" was badly named. It is now called "relative". [RT #16326] 2077. [bug] 'dnssec-signzone -O raw' wasn't outputting the complete signed zone. [RT #16326] 2076. [bug] Several files were missing #include causing build failures on OSF. [RT #16341] 2075. [bug] The spillat timer event hander could leak memory. [RT #16357] 2074. [bug] dns_request_createvia2(), dns_request_createvia3(), dns_request_createraw2() and dns_request_createraw3() failed to send multiple UDP requests. [RT #16349] 2073. [bug] Incorrect semantics check for update policy "wildcard". [RT #16353] 2072. [bug] We were not generating valid HMAC SHA digests. [RT #16320] 2071. [port] Test whether gcc accepts -fno-strict-aliasing. [RT #16324] 2070. [bug] The remote address was not always displayed when reporting dispatch failures. [RT #16315] 2069. [bug] Cross compiling was not working. [RT #16330] 2068. [cleanup] Lower incremental tuning message to debug 1. [RT #16319] 2067. [bug] 'rndc' could close the socket too early triggering a INSIST under Windows. [RT #16317] 2066. [security] Handle SIG queries gracefully. [RT #16300] 2065. [bug] libbind: probe for HPUX prototypes for endprotoent_r() and endservent_r(). [RT 16313] 2064. [bug] libbind: silence AIX compiler warnings. [RT #16218] 2063. [bug] Change #1955 introduced a bug which caused the first 'rndc flush' call to not free memory. [RT #16244] 2062. [bug] 'dig +nssearch' was reusing a buffer before it had been returned by the socket code. [RT #16307] 2061. [bug] Accept expired wildcard message reversed. [RT #16296] 2060. [bug] Enabling DLZ support could leave views partially configured. [RT #16295] 2059. [bug] Search into cache rbtdb could trigger an INSIST failure while cleaning up a stale rdataset. [RT #16292] 2058. [bug] Adjust how we calculate rtt estimates in the presence of authoritative servers that drop EDNS and/or CD requests. Also fallback to EDNS/512 and plain DNS faster for zones with less than 3 servers. [RT #16187] 2057. [bug] Make setting "ra" dependent on both allow-query-cache and allow-recursion. [RT #16290] 2056. [bug] dig: ixfr= was not being treated case insensitively at all times. [RT #15955] 2055. [bug] Missing goto after dropping multicast query. [RT #15944] 2054. [port] freebsd: do not explicitly link against -lpthread. [RT #16170] 2053. [port] netbsd:libbind: silence compiler warnings. [RT #16220] 2052. [bug] 'rndc' improve connect failed message to report the failing address. [RT #15978] 2051. [port] More strtol() fixes. [RT #16249] 2050. [bug] Parsing of NSAP records was not case insensitive. [RT #16287] 2049. [bug] Restore SOA before AXFR when falling back from a attempted IXFR when transferring in a zone. Allow a initial SOA query before attempting a AXFR to be requested. [RT #16156] 2048. [bug] It was possible to loop forever when using avoid-v4-udp-ports / avoid-v6-udp-ports when the OS always returned the same local port. [RT #16182] 2047. [bug] Failed to initialize the interface flags to zero. [RT #16245] 2046. [bug] rbtdb.c:rdataset_setadditional() could cause duplicate cleanup [RT #16247]. 2045. [func] Use lock buckets for acache entries to limit memory consumption. [RT #16183] 2044. [port] Add support for atomic operations for Itanium. [RT #16179] 2043. [port] nsupdate/nslookup: Force the flushing of the prompt for interactive sessions. [RT#16148] 2042. [bug] named-checkconf was incorrectly rejecting the logging category "config". [RT #16117] 2041. [bug] "configure --with-dlz-bdb=yes" produced a bad set of libraries to be linked. [RT #16129] 2040. [bug] rbtdb no_references() could trigger an INSIST failure with --enable-atomic. [RT #16022] 2039. [func] Check that all buffers passed to the socket code have been retrieved when the socket event is freed. [RT #16122] 2038. [bug] dig/nslookup/host was unlinking from wrong list when handling errors. [RT #16122] 2037. [func] When unlinking the first or last element in a list check that the list head points to the element to be unlinked. [RT #15959] 2036. [bug] 'rndc recursing' could cause trigger a REQUIRE. [RT #16075] 2035. [func] Make falling back to TCP on UDP refresh failure optional. Default "try-tcp-refresh yes;" for BIND 8 compatibility. [RT #16123] 2034. [bug] gcc: set -fno-strict-aliasing. [RT #16124] 2033. [bug] We weren't creating multiple client memory contexts on demand as expected. [RT #16095] 2032. [bug] Remove a INSIST in query_addadditional2(). [RT #16074] 2031. [bug] Emit a error message when "rndc refresh" is called on a non slave/stub zone. [RT # 16073] 2030. [bug] We were being overly conservative when disabling openssl engine support. [RT #16030] 2029. [bug] host printed out the server multiple times when specified on the command line. [RT #15992] 2028. [port] linux: socket.c compatibility for old systems. [RT #16015] 2027. [port] libbind: Solaris x86 support. [RT #16020] 2026. [bug] Rate limit the two recursive client exceeded messages. [RT #16044] 2025. [func] Update "zone serial unchanged" message. [RT #16026] 2024. [bug] named emitted spurious "zone serial unchanged" messages on reload. [RT #16027] 2023. [bug] "make install" should create ${localstatedir}/run and ${sysconfdir} if they do not exist. [RT #16033] 2022. [bug] If dnssec validation is disabled only assert CD if CD was requested. [RT #16037] 2021. [bug] dnssec-enable no; triggered a REQUIRE. [RT #16037] 2020. [bug] rdataset_setadditional() could leak memory. [RT #16034] 2019. [tuning] Reduce the amount of work performed per quantum when cleaning the cache. [RT #15986] 2018. [bug] Checking if the HMAC MD5 private file was broken. [RT #15960] 2017. [bug] allow-query default was not correct. [RT #15946] 2016. [bug] Return a partial answer if recursion is not allowed but requested and we had the answer to the original qname. [RT #15945] 2015. [cleanup] use-additional-cache is now acache-enable for consistency. Default acache-enable off in BIND 9.4 as it requires memory usage to be configured. It may be enabled by default in BIND 9.5 once we have more experience with it. 2014. [func] Statistics about acache now recorded and sent to log. [RT #15976] 2013. [bug] Handle unexpected TSIGs on unsigned AXFR/IXFR responses more gracefully. [RT #15941] 2012. [func] Don't insert new acache entries if acache is full. [RT #15970] 2011. [func] dnssec-signzone can now update the SOA record of the signed zone, either as an increment or as the system time(). [RT #15633] 2010. [placeholder] rt15958 2009. [bug] libbind: Coverity fixes. [RT #15808] 2008. [func] It is now possible to enable/disable DNSSEC validation from rndc. This is useful for the mobile hosts where the current connection point breaks DNSSEC (firewall/proxy). [RT #15592] rndc validation newstate [view] 2007. [func] It is now possible to explicitly enable DNSSEC validation. default dnssec-validation no; to be changed to yes in 9.5.0. [RT #15674] 2006. [security] Allow-query-cache and allow-recursion now default to the built in acls "localnets" and "localhost". This is being done to make caching servers less attractive as reflective amplifying targets for spoofed traffic. This still leave authoritative servers exposed. The best fix is for full BCP 38 deployment to remove spoofed traffic. 2005. [bug] libbind: Retransmission timeouts should be based on which attempt it is to the nameserver and not the nameserver itself. [RT #13548] 2004. [bug] dns_tsig_sign() could pass a NULL pointer to dst_context_destroy() when cleaning up after a error. [RT #15835] 2003. [bug] libbind: The DNS name/address lookup functions could occasionally follow a random pointer due to structures not being completely zeroed. [RT #15806] 2002. [bug] libbind: tighten the constraints on when struct addrinfo._ai_pad exists. [RT #15783] 2001. [func] Check the KSK flag when updating a secure dynamic zone. New zone option "update-check-ksk yes;". [RT #15817] 2000. [bug] memmove()/strtol() fix was incomplete. [RT #15812] 1999. [func] Implement "rrset-order fixed". [RT #13662] 1998. [bug] Restrict handling of fifos as sockets to just SunOS. This allows named to connect to entropy gathering daemons that use fifos instead of sockets. [RT #15840] 1997. [bug] Named was failing to replace negative cache entries when a positive one for the type was learnt. [RT #15818] 1996. [bug] nsupdate: if a zone has been specified it should appear in the output of 'show'. [RT #15797] 1995. [bug] 'host' was reporting multiple "is an alias" messages. [RT #15702] 1994. [port] OpenSSL 0.9.8 support. [RT #15694] 1993. [bug] Log messages, via syslog, were missing the space after the timestamp if "print-time yes" was specified. [RT #15844] 1992. [bug] Not all incoming zone transfer messages included the view. [RT #15825] 1991. [cleanup] The configuration data, once read, should be treated as read only. Expand the use of const to enforce this at compile time. [RT #15813] 1990. [bug] libbind: isc's override of broken gettimeofday() implementations was not always effective. [RT #15709] 1989. [bug] win32: don't check the service password when re-installing. [RT #15882] 1988. [bug] Remove a bus error from the SHA256/SHA512 support. [RT #15878] 1987. [func] DS/DLV SHA256 digest algorithm support. [RT #15608] 1986. [func] Report when a zone is removed. [RT #15849] 1985. [protocol] DLV has now been assigned a official type code of 32769. [RT #15807] Note: care should be taken to ensure you upgrade both named and dnssec-signzone at the same time for zones with DLV records where named is the master server for the zone. Also any zones that contain DLV records should be removed when upgrading a slave zone. You do not however have to upgrade all servers for a zone with DLV records simultaneously. 1984. [func] dig, nslookup and host now advertise a 4096 byte EDNS UDP buffer size by default. [RT #15855] 1983. [func] Two new update policies. "selfsub" and "selfwild". [RT #12895] 1982. [bug] DNSKEY was being accepted on the parent side of a delegation. KEY is still accepted there for RFC 3007 validated updates. [RT #15620] 1981. [bug] win32: condition.c:wait() could fail to reattain the mutex lock. 1980. [func] dnssec-signzone: output the SOA record as the first record in the signed zone. [RT #15758] 1979. [port] linux: allow named to drop core after changing user ids. [RT #15753] 1978. [port] Handle systems which have a broken recvmsg(). [RT #15742] 1977. [bug] Silence noisy log message. [RT #15704] 1976. [bug] Handle systems with no IPv4 addresses. [RT #15695] 1975. [bug] libbind: isc_gethexstring() could misparse multi-line hex strings with comments. [RT #15814] 1974. [doc] List each of the zone types and associated zone options separately in the ARM. 1973. [func] TSIG HMACSHA1, HMACSHA224, HMACSHA256, HMACSHA384 and HMACSHA512 support. [RT #13606] 1972. [contrib] DBUS dynamic forwarders integration from Jason Vas Dias . 1971. [port] linux: make detection of missing IF_NAMESIZE more robust. [RT #15443] 1970. [bug] nsupdate: adjust UDP timeout when falling back to unsigned SOA query. [RT #15775] 1969. [bug] win32: the socket code was freeing the socket structure too early. [RT #15776] 1968. [bug] Missing lock in resolver.c:validated(). [RT #15739] 1967. [func] dig/nslookup/host: warn about missing "QR". [RT #15779] 1966. [bug] Don't set CD when we have fallen back to plain DNS. [RT #15727] 1965. [func] Suppress spurious "recursion requested but not available" warning with 'dig +qr'. [RT #15780]. 1964. [func] Separate out MX and SRV to CNAME checks. [RT #15723] 1963. [port] Tru64 4.0E doesn't support send() and recv(). [RT #15586] 1962. [bug] Named failed to clear old update-policy when it was removed. [RT #15491] 1961. [bug] Check the port and address of responses forwarded to dispatch. [RT #15474] 1960. [bug] Update code should set NSEC ttls from SOA MINIMUM. [RT #15465] 1959. [func] Control the zeroing of the negative response TTL to a soa query. Defaults "zero-no-soa-ttl yes;" and "zero-no-soa-ttl-cache no;". [RT #15460] 1958. [bug] Named failed to update the zone's secure state until the zone was reloaded. [RT #15412] 1957. [bug] Dig mishandled responses to class ANY queries. [RT #15402] 1956. [bug] Improve cross compile support, 'gen' is now built by native compiler. See README for additional cross compile support information. [RT #15148] 1955. [bug] Pre-allocate the cache cleaning iterator. [RT #14998] 1954. [func] Named now falls back to advertising EDNS with a 512 byte receive buffer if the initial EDNS queries fail. [RT #14852] 1953. [func] The maximum EDNS UDP response named will send can now be set in named.conf (max-udp-size). This is independent of the advertised receive buffer (edns-udp-size). [RT #14852] 1952. [port] hpux: tell the linker to build a runtime link path "-Wl,+b:". [RT #14816]. 1951. [security] Drop queries from particular well known ports. Don't return FORMERR to queries from particular well known ports. [RT #15636] 1950. [port] Solaris 2.5.1 and earlier cannot bind() then connect() a TCP socket. This prevents the source address being set for TCP connections. [RT #15628] 1949. [func] Addition memory leakage checks. [RT #15544] 1948. [bug] If was possible to trigger a REQUIRE failure in xfrin.c:maybe_free() if named ran out of memory. [RT #15568] 1947. [func] It is now possible to configure named to accept expired RRSIGs. Default "dnssec-accept-expired no;". Setting "dnssec-accept-expired yes;" leaves named vulnerable to replay attacks. [RT #14685] 1946. [bug] resume_dslookup() could trigger a REQUIRE failure when using forwarders. [RT #15549] 1945. [cleanup] dnssec-keygen: RSA (RSAMD5) is no longer recommended. To generate a RSAMD5 key you must explicitly request RSAMD5. [RT #13780] 1944. [cleanup] isc_hash_create() does not need a read/write lock. [RT #15522] 1943. [bug] Set the loadtime after rolling forward the journal. [RT #15647] 1942. [bug] If the name of a DNSKEY match that of one in trusted-keys do not attempt to validate the DNSKEY using the parents DS RRset. [RT #15649] 1941. [bug] ncache_adderesult() should set eresult even if no rdataset is passed to it. [RT #15642] 1940. [bug] Fixed a number of error conditions reported by Coverity. 1939. [bug] The resolver could dereference a null pointer after validation if all the queries have timed out. [RT #15528] 1938. [bug] The validator was not correctly handling unsecure negative responses at or below a SEP. [RT #15528] 1937. [bug] sdlz doesn't handle RRSIG records. [RT #15564] 1936. [bug] The validator could leak memory. [RT #15544] 1935. [bug] 'acache' was DO sensitive. [RT #15430] 1934. [func] Validate pending NS RRsets, in the authority section, prior to returning them if it can be done without requiring DNSKEYs to be fetched. [RT #15430] 1933. [bug] dump_rdataset_raw() had a incorrect INSIST. [RT #15534] 1932. [bug] hpux: LDFLAGS was getting corrupted. [RT #15530] 1931. [bug] Per-client mctx could require a huge amount of memory, particularly for a busy caching server. [RT #15519] 1930. [port] HPUX: ia64 support. [RT #15473] 1929. [port] FreeBSD: extend use of PTHREAD_SCOPE_SYSTEM. 1928. [bug] Race in rbtdb.c:currentversion(). [RT #15517] 1927. [bug] Access to soanode or nsnode in rbtdb violated the lock order rule and could cause a dead lock. [RT# 15518] 1926. [bug] The Windows installer did not check for empty passwords. BINDinstall was being installed in the wrong place. [RT #15483] 1925. [port] All outer level AC_TRY_RUNs need cross compiling defaults. [RT #15469] 1924. [port] libbind: hpux ia64 support. [RT #15473] 1923. [bug] ns_client_detach() called too early. [RT #15499] 1922. [bug] check-tool.c:setup_logging() missing call to dns_log_setcontext(). 1921. [bug] Client memory contexts were not using internal malloc. [RT# 15434] 1920. [bug] The cache rbtdb lock array was too small to have the desired performance characteristics. [RT #15454] 1919. [contrib] queryperf: a set of new features: collecting/printing response delays, printing intermediate results, and adjusting query rate for the "target" qps. 1918. [bug] Memory leak when checking acls. [RT #15391] 1917. [doc] funcsynopsisinfo wasn't being treated as verbatim when generating man pages. [RT #15385] 1916. [func] Integrate contributed IDN code from JPNIC. [RT #15383] 1915. [bug] dig +ndots was broken. [RT #15215] 1914. [protocol] DS is required to accept mnemonic algorithms (RFC 4034). Still emit numeric algorithms for compatibility with RFC 3658. [RT #15354] 1913. [func] Integrate contributed DLZ code into named. [RT #11382] 1912. [port] aix: atomic locking for powerpc. [RT #15020] 1911. [bug] Update windows socket code. [RT #14965] 1910. [bug] dig's +sigchase code overhauled. [RT #14933] 1909. [bug] The DLV code has been re-worked to make no longer query order sensitive. [RT #14933] 1908. [func] dig now warns if 'RA' is not set in the answer when 'RD' was set in the query. host/nslookup skip servers that fail to set 'RA' when 'RD' is set unless a server is explicitly set. [RT #15005] 1907. [func] host/nslookup now continue (default)/fail on SERVFAIL. [RT #15006] 1906. [func] dig now has a '-q queryname' and '+showsearch' options. [RT #15034] 1905. [bug] Strings returned from cfg_obj_asstring() should be treated as read-only. The prototype for cfg_obj_asstring() has been updated to reflect this. [RT #15256] 1904. [func] Automatic empty zone creation for D.F.IP6.ARPA and friends. Note: RFC 1918 zones are not yet covered by this but are likely to be in a future release. New options: empty-server, empty-contact, empty-zones-enable and disable-empty-zone. 1903. [func] ISC string copy API. 1902. [func] Attempt to make the amount of work performed in a iteration self tuning. The covers nodes clean from the cache per iteration, nodes written to disk when rewriting a master file and nodes destroyed per iteration when destroying a zone or a cache. [RT #14996] 1901. [cleanup] Don't add DNSKEY records to the additional section. 1900. [bug] ixfr-from-differences failed to ensure that the serial number increased. [RT #15036] 1899. [func] named-checkconf now validates update-policy entries. [RT #14963] 1898. [bug] Extend ISC_SOCKADDR_FORMATSIZE and ISC_NETADDR_FORMATSIZE to allow for scope details. 1897. [func] x86 and x86_64 now have separate atomic locking implementations. 1896. [bug] Recursive clients soft quota support wasn't working as expected. [RT #15103] 1895. [bug] A escaped character is, potentially, converted to the output character set too early. [RT #14666] 1894. [doc] Review ARM for BIND 9.4. 1893. [port] Use uintptr_t if available. [RT #14606] 1892. [func] Support for SPF rdata type. [RT #15033] 1891. [port] freebsd: pthread_mutex_init can fail if it runs out of memory. [RT #14995] 1890. [func] Raise the UDP receive buffer size to 32k if it is less than 32k. [RT #14953] 1889. [port] sunos: non blocking i/o support. [RT #14951] 1888. [func] Support for IPSECKEY rdata type. [RT #14967] 1887. [bug] The cache could delete expired records too fast for clients with a virtual time in the past. [RT #14991] 1886. [bug] fctx_create() could return success even though it failed. [RT #14993] 1885. [func] dig: report the number of extra bytes still left in the packet after processing all the records. 1884. [cleanup] dighost.c: move external declarations into . 1883. [bug] dnssec-signzone, dnssec-keygen: handle negative debug levels. [RT #14962] 1882. [func] Limit the number of recursive clients that can be waiting for a single query () to resolve. New options clients-per-query and max-clients-per-query. 1881. [func] Add a system test for named-checkconf. [RT #14931] 1880. [func] The lame cache is now done on a basis as some servers only appear to be lame for certain query types. [RT #14916] 1879. [func] "USE INTERNAL MALLOC" is now runtime selectable. [RT #14892] 1878. [func] Detect duplicates of UDP queries we are recursing on and drop them. New stats category "duplicate". [RT #2471] 1877. [bug] Fix unreasonably low quantum on call to dns_rbt_destroy2(). Remove unnecessary unhash_node() call. [RT #14919] 1876. [func] Additional memory debugging support to track size and mctx arguments. [RT #14814] 1875. [bug] process_dhtkey() was using the wrong memory context to free some memory. [RT #14890] 1874. [port] sunos: portability fixes. [RT #14814] 1873. [port] win32: isc__errno2result() now reports its caller. [RT #13753] 1872. [port] win32: Handle ERROR_NETNAME_DELETED. [RT #13753] 1871. [placeholder] 1870. [func] Added framework for handling multiple EDNS versions. [RT #14873] 1869. [func] dig can now specify the EDNS version when making a query. [RT #14873] 1868. [func] edns-udp-size can now be overridden on a per server basis. [RT #14851] 1867. [bug] It was possible to trigger a INSIST in dlv_validatezonekey(). [RT #14846] 1866. [bug] resolv.conf parse errors were being ignored by dig/host/nslookup. [RT #14841] 1865. [bug] Silently ignore nameservers in /etc/resolv.conf with bad addresses. [RT #14841] 1864. [bug] Don't try the alternative transfer source if you got a answer / transfer with the main source address. [RT #14802] 1863. [bug] rrset-order "fixed" error messages not complete. 1862. [func] Add additional zone data constancy checks. named-checkzone has extended checking of NS, MX and SRV record and the hosts they reference. named has extended post zone load checks. New zone options: check-mx and integrity-check. [RT #4940] 1861. [bug] dig could trigger a INSIST on certain malformed responses. [RT #14801] 1860. [port] solaris 2.8: hack_shutup_pthreadmutexinit was incorrectly set. [RT #14775] 1859. [func] Add support for CH A record. [RT #14695] 1858. [bug] The flush-zones-on-shutdown option wasn't being parsed. [RT #14686] 1857. [bug] named could trigger a INSIST() if reconfigured / reloaded too fast. [RT #14673] 1856. [doc] Switch Docbook toolchain from DSSSL to XSL. [RT #11398] 1855. [bug] ixfr-from-differences was failing to detect changes of ttl due to dns_diff_subtract() was ignoring the ttl of records. [RT #14616] 1854. [bug] lwres also needs to know the print format for (long long). [RT #13754] 1853. [bug] Rework how DLV interacts with proveunsecure(). [RT #13605] 1852. [cleanup] Remove last vestiges of dnssec-signkey and dnssec-makekeyset (removed from Makefile years ago). 1851. [doc] Doxygen comment markup. [RT #11398] 1850. [bug] Memory leak in lwres_getipnodebyaddr(). [RT #14591] 1849. [doc] All forms of the man pages (docbook, man, html) should have consistent copyright dates. 1848. [bug] Improve SMF integration. [RT #13238] 1847. [bug] isc_ondestroy_init() is called too late in dns_rbtdb_create()/dns_rbtdb64_create(). [RT #13661] 1846. [contrib] query-loc-0.3.0 from Stephane Bortzmeyer . 1845. [bug] Improve error reporting to distinguish between accept()/fcntl() and socket()/fcntl() errors. [RT #13745] 1844. [bug] inet_pton() accepted more that 4 hexadecimal digits for each 16 bit piece of the IPv6 address. The text representation of a IPv6 address has been tightened to disallow this (draft-ietf-ipv6-addr-arch-v4-02.txt). [RT #5662] 1843. [cleanup] CINCLUDES takes precedence over CFLAGS. This helps when CFLAGS contains "-I /usr/local/include" resulting in old header files being used. 1842. [port] cmsg_len() could produce incorrect results on some platform. [RT #13744] 1841. [bug] "dig +nssearch" now makes a recursive query to find the list of nameservers to query. [RT #13694] 1840. [func] dnssec-signzone can now randomize signature end times (dnssec-signzone -j jitter). [RT #13609] 1839. [bug] was not being installed. 1838. [cleanup] Don't allow Linux capabilities to be inherited. [RT #13707] 1837. [bug] Compile time option ISC_FACILITY was not effective for 'named -u '. [RT #13714] 1836. [cleanup] Silence compiler warnings in hash_test.c. 1835. [bug] Update dnssec-signzone's usage message. [RT #13657] 1834. [bug] Bad memset in rdata_test.c. [RT #13658] 1833. [bug] Race condition in isc_mutex_lock_profile(). [RT #13660] 1832. [bug] named fails to return BADKEY on unknown TSIG algorithm. [RT #13620] 1831. [doc] Update named-checkzone documentation. [RT#13604] 1830. [bug] adb lame cache has sence of test reversed. [RT #13600] 1829. [bug] win32: "pid-file none;" broken. [RT #13563] 1828. [bug] isc_rwlock_init() failed to properly cleanup if it encountered a error. [RT #13549] 1827. [bug] host: update usage message for '-a'. [RT #37116] 1826. [bug] Missing DESTROYLOCK() in isc_mem_createx() on out of memory error. [RT #13537] 1825. [bug] Missing UNLOCK() on out of memory error from in rbtdb.c:subtractrdataset(). [RT #13519] 1824. [bug] Memory leak on dns_zone_setdbtype() failure. [RT #13510] 1823. [bug] Wrong macro used to check for point to point interface. [RT#13418] 1822. [bug] check-names test for RT was reversed. [RT #13382] 1821. [placeholder] 1820. [bug] Gracefully handle acl loops. [RT #13659] 1819. [bug] The validator needed to check both the algorithm and digest types of the DS to determine if it could be used to introduce a secure zone. [RT #13593] 1818. [bug] 'named-checkconf -z' triggered an INSIST. [RT #13599] 1817. [func] Add support for additional zone file formats for improving loading performance. The masterfile-format option in named.conf can be used to specify a non-default format. A separate command named-compilezone was provided to generate zone files in the new format. Additionally, the -I and -O options for dnssec-signzone specify the input and output formats. 1816. [port] UnixWare: failed to compile lib/isc/unix/net.c. [RT #13597] 1815. [bug] nsupdate triggered a REQUIRE if the server was set without also setting the zone and it encountered a CNAME and was using TSIG. [RT #13086] 1814. [func] UNIX domain controls are now supported. 1813. [func] Restructured the data locking framework using architecture dependent atomic operations (when available), improving response performance on multi-processor machines significantly. x86, x86_64, alpha, powerpc, and mips are currently supported. 1812. [port] win32: IN6_IS_ADDR_UNSPECIFIED macro is incorrect. [RT #13453] 1811. [func] Preserve the case of domain names in rdata during zone transfers. [RT #13547] 1810. [bug] configure, lib/bind/configure make different default decisions about whether to do a threaded build. [RT #13212] 1809. [bug] "make distclean" failed for libbind if the platform is not supported. 1808. [bug] zone.c:notify_zone() contained a race condition, zone->db could change underneath it. [RT #13511] 1807. [bug] When forwarding (forward only) set the active domain from the forward zone name. [RT #13526] 1806. [bug] The resolver returned the wrong result when a CNAME / DNAME was encountered when fetching glue from a secure namespace. [RT #13501] 1805. [bug] Pending status was not being cleared when DLV was active. [RT #13501] 1804. [bug] Ensure that if we are queried for glue that it fits in the additional section or TC is set to tell the client to retry using TCP. [RT #10114] 1803. [bug] dnssec-signzone sometimes failed to remove old RRSIGs. [RT #13483] 1802. [bug] Handle connection resets better. [RT #11280] 1801. [func] Report differences between hints and real NS rrset and associated address records. 1800. [bug] Changes #1719 allowed a INSIST to be triggered. [RT #13428] 1799. [bug] 'rndc flushname' failed to flush negative cache entries. [RT #13438] 1798. [func] The server syntax has been extended to support a range of servers. [RT #11132] 1797. [func] named-checkconf now check acls to verify that they only refer to existing acls. [RT #13101] 1796. [func] "rndc freeze/thaw" now freezes/thaws all zones. 1795. [bug] "rndc dumpdb" was not fully documented. Minor formating issues with "rndc dumpdb -all". [RT #13396] 1794. [func] Named and named-checkzone can now both check for non-terminal wildcard records. 1793. [func] Extend adjusting TTL warning messages. [RT #13378] 1792. [func] New zone option "notify-delay". Specify a minimum delay between sets of NOTIFY messages. 1791. [bug] 'host -t a' still printed out AAAA and MX records. [RT #13230] 1790. [cleanup] Move lib/dns/sec/dst up into lib/dns. This should allow parallel make to succeed. 1789. [bug] Prerequisite test for tkey and dnssec could fail with "configure --with-libtool". 1788. [bug] libbind9.la/libbind9.so needs to link against libisccfg.la/libisccfg.so. 1787. [port] HPUX: both "cc" and "gcc" need -Wl,+vnocompatwarnings. 1786. [port] AIX: libt_api needs to be taught to look for T_testlist in the main executable (--with-libtool). [RT #13239] 1785. [bug] libbind9.la/libbind9.so needs to link against libisc.la/libisc.so. 1784. [cleanup] "libtool -allow-undefined" is the default. Leave hooks in configure to allow it to be set if needed in the future. 1783. [cleanup] We only need one copy of libtool.m4, ltmain.sh in the source tree. 1782. [port] OSX: --with-libtool + --enable-libbind broke on __evOptMonoTime. [RT #13219] 1781. [port] FreeBSD 5.3: set PTHREAD_SCOPE_SYSTEM. [RT #12810] 1780. [bug] Update libtool to 1.5.10. 1779. [port] OSF 5.1: libtool didn't handle -pthread correctly. 1778. [port] HUX 11.11: fix broken IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT macros. 1777. [port] OSF 5.1: fix broken IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT macros. 1776. [port] Solaris 2.9: fix broken IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT macros. 1775. [bug] Only compile getnetent_r.c when threaded. [RT #13205] 1774. [port] Aix: Silence compiler warnings / build failures. [RT #13154] 1773. [bug] Fast retry on host / net unreachable. [RT #13153] 1772. [placeholder] 1771. [placeholder] 1770. [bug] named-checkconf failed to report missing a missing file clause for rbt{64} master/hint zones. [RT#13009] 1769. [port] win32: change compiler flags /MTd ==> /MDd, /MT ==> /MD. 1768. [bug] nsecnoexistnodata() could be called with a non-NSEC rdataset. [RT #12907] 1767. [port] Builds on IPv6 platforms without IPv6 Advanced API support for (struct in6_pktinfo) failed. [RT #13077] 1766. [bug] Update the master file timestamp on successful refresh as well as the journal's timestamp. [RT# 13062] 1765. [bug] configure --with-openssl=auto failed. [RT #12937] 1764. [bug] dns_zone_replacedb failed to emit a error message if there was no SOA record in the replacement db. [RT #13016] 1763. [func] Perform sanity checks on NS records which refer to 'in zone' names. [RT #13002] 1762. [bug] isc_interfaceiter_create() could return ISC_R_SUCCESS even when it failed. [RT #12995] 1761. [bug] 'rndc dumpdb' didn't report unassociated entries. [RT #12971] 1760. [bug] Host / net unreachable was not penalising rtt estimates. [RT #12970] 1759. [bug] Named failed to startup if the OS supported IPv6 but had no IPv6 interfaces configured. [RT #12942] 1758. [func] Don't send notify messages to self. [RT #12933] 1757. [func] host now can turn on memory debugging flags with '-m'. 1756. [func] named-checkconf now checks the logging configuration. [RT #12352] 1755. [func] allow-update is now settable at the options / view level. [RT #6636] 1754. [bug] We weren't always attempting to query the parent server for the DS records at the zone cut. [RT #12774] 1753. [bug] Don't serve a slave zone which has no NS records. [RT #12894] 1752. [port] Move isc_app_start() to after ns_os_daemonise() as some fork() implementations unblock the signals that are blocked by isc_app_start(). [RT #12810] 1751. [bug] --enable-getifaddrs failed under linux. [RT #12867] 1750. [port] lib/bind/make/rules.in:subdirs was not bash friendly. [RT #12864] 1749. [bug] 'check-names response ignore;' failed to ignore. [RT #12866] 1748. [func] dig now returns the byte count for axfr/ixfr. 1747. [bug] BIND 8 compatibility: named/named-checkconf failed to parse "host-statistics-max" in named.conf. 1746. [func] Make public the function to read a key file, dst_key_read_public(). [RT #12450] 1745. [bug] Dig/host/nslookup accept replies from link locals regardless of scope if no scope was specified when query was sent. [RT #12745] 1744. [bug] If tuple2msgname() failed to convert a tuple to a name a REQUIRE could be triggered. [RT #12796] 1743. [bug] If isc_taskmgr_create() was not able to create the requested number of worker threads then destruction of the manager would trigger an INSIST() failure. [RT #12790] 1742. [bug] Deleting all records at a node then adding a previously existing record, in a single UPDATE transaction, failed to leave / regenerate the associated RRSIG records. [RT #12788] 1741. [bug] Deleting all records at a node in a secure zone using a update-policy grant failed. [RT #12787] 1740. [bug] Replace rbt's hash algorithm as it performed badly with certain zones. [RT #12729] NOTE: a hash context now needs to be established via isc_hash_create() if the application was not already doing this. 1739. [bug] dns_rbt_deletetree() could incorrectly return ISC_R_QUOTA. [RT #12695] 1738. [bug] Enable overrun checking by default. [RT #12695] 1737. [bug] named failed if more than 16 masters were specified. [RT #12627] 1736. [bug] dst_key_fromnamedfile() could fail to read a public key. [RT #12687] 1735. [bug] 'dig +sigtrace' could die with a REQUIRE failure. [RE #12688] 1734. [cleanup] 'rndc-confgen -a -t' remove extra '/' in path. [RT #12588] 1733. [bug] Return non-zero exit status on initial load failure. [RT #12658] 1732. [bug] 'rrset-order name "*"' wasn't being applied to ".". [RT #12467] 1731. [port] darwin: relax version test in ifconfig.sh. [RT #12581] 1730. [port] Determine the length type used by the socket API. [RT #12581] 1729. [func] Improve check-names error messages. 1728. [doc] Update check-names documentation. 1727. [bug] named-checkzone: check-names support didn't match documentation. 1726. [port] aix5: add support for aix5. 1725. [port] linux: update error message on interaction of threads, capabilities and setuid support (named -u). [RT #12541] 1724. [bug] Look for DNSKEY records with "dig +sigtrace". [RT #12557] 1723. [cleanup] Silence compiler warnings from t_tasks.c. [RT #12493] 1722. [bug] Don't commit the journal on malformed ixfr streams. [RT #12519] 1721. [bug] Error message from the journal processing were not always identifying the relevant journal. [RT #12519] 1720. [bug] 'dig +chase' did not terminate on a RFC 2308 Type 1 negative response. [RT #12506] 1719. [bug] named was not correctly caching a RFC 2308 Type 1 negative response. [RT #12506] 1718. [bug] nsupdate was not handling RFC 2308 Type 3 negative responses when looking for the zone / master server. [RT #12506] 1717. [port] solaris: ifconfig.sh did not support Solaris 10. "ifconfig.sh down" didn't work for Solaris 9. 1716. [doc] named.conf(5) was being installed in the wrong location. [RT# 12441] 1715. [func] 'dig +trace' now randomly selects the next servers to try. Report if there is a bad delegation. 1714. [bug] dig/host/nslookup were only trying the first address when a nameserver was specified by name. [RT #12286] 1713. [port] linux: extend capset failure message to say: please ensure that the capset kernel module is loaded. see insmod(8) 1712. [bug] Missing FULLCHECK for "trusted-key" in dig. 1711. [func] 'rndc unfreeze' has been deprecated by 'rndc thaw'. 1710. [func] 'rndc notify zone [class [view]]' resend the NOTIFY messages for the specified zone. [RT #9479] 1709. [port] solaris: add SMF support from Sun. 1708. [cleanup] Replaced dns_fullname_hash() with dns_name_fullhash() for conformance to the name space convention. Binary backward compatibility to the old function name is provided. [RT #12376] 1707. [contrib] sdb/ldap updated to version 1.0-beta. 1706. [bug] 'rndc stop' failed to cause zones to be flushed sometimes. [RT #12328] 1705. [func] Allow the journal's name to be changed via named.conf. 1704. [port] lwres needed a snprintf() implementation for platforms without snprintf(). Add missing "#include ". [RT #12321] 1703. [bug] named would loop sending NOTIFY messages when it failed to receive a response. [RT #12322] 1702. [bug] also-notify should not be applied to built in zones. [RT #12323] 1701. [doc] A minimal named.conf man page. 1700. [func] nslookup is no longer to be treated as deprecated. Remove "deprecated" warning message. Add man page. 1699. [bug] dnssec-signzone can generate "not exact" errors when resigning. [RT #12281] 1698. [doc] Use reserved IPv6 documentation prefix. 1697. [bug] xxx-source{,-v6} was not effective when it specified one of listening addresses and a different port than the listening port. [RT #12257] 1696. [bug] dnssec-signzone failed to clean out nodes that consisted of only NSEC and RRSIG records. [RT #12154] 1695. [bug] DS records when forwarding require special handling. [RT #12133] 1694. [bug] Report if the builtin views of "_default" / "_bind" are defined in named.conf. [RT #12023] 1693. [bug] max-journal-size was not effective for master zones with ixfr-from-differences set. [RT# 12024] 1692. [bug] Don't set -I, -L and -R flags when libcrypto is in /usr/lib. [RT #11971] 1691. [bug] sdb's attachversion was not complete. [RT #11990] 1690. [bug] Delay detaching view from the client until UPDATE processing completes when shutting down. [RT #11714] 1689. [bug] DNS_NAME_TOREGION() and DNS_NAME_SPLIT() macros contained gratuitous semicolons. [RT #11707] 1688. [bug] LDFLAGS was not supported. 1687. [bug] Race condition in dispatch. [RT #10272] 1686. [bug] Named sent a extraneous NOTIFY when it received a redundant UPDATE request. [RT #11943] 1685. [bug] Change #1679 loop tests weren't quite right. 1684. [func] ixfr-from-differences now takes master and slave in addition to yes and no at the options and view levels. 1683. [bug] dig +sigchase could leak memory. [RT #11445] 1682. [port] Update configure test for (long long) printf format. [RT #5066] 1681. [bug] Only set SO_REUSEADDR when a port is specified in isc_socket_bind(). [RT #11742] 1680. [func] rndc: the source address can now be specified. 1679. [bug] When there was a single nameserver with multiple addresses for a zone not all addresses were tried. [RT #11706] 1678. [bug] RRSIG should use TYPEXXXXX for unknown types. 1677. [bug] dig: +aaonly didn't work, +aaflag undocumented. 1676. [func] New option "allow-query-cache". This lets allow-query be used to specify the default zone access level rather than having to have every zone override the global value. allow-query-cache can be set at both the options and view levels. If allow-query-cache is not set allow-query applies. 1675. [bug] named would sometimes add extra NSEC records to the authority section. 1674. [port] linux: increase buffer size used to scan /proc/net/if_inet6. 1673. [port] linux: issue a error messages if IPv6 interface scans fails. 1672. [cleanup] Tests which only function in a threaded build now return R:THREADONLY (rather than R:UNTESTED) in a non-threaded build. 1671. [contrib] queryperf: add NAPTR to the list of known types. 1670. [func] Log UPDATE requests to slave zones without an acl as "disabled" at debug level 3. [RT# 11657] 1669. [placeholder] 1668. [bug] DIG_SIGCHASE was making bin/dig/host dump core. 1667. [port] linux: not all versions have IF_NAMESIZE. 1666. [bug] The optional port on hostnames in dual-stack-servers was being ignored. 1665. [func] rndc now allows addresses to be set in the server clauses. 1664. [bug] nsupdate needed KEY for SIG(0), not DNSKEY. 1663. [func] Look for OpenSSL by default. 1662. [bug] Change #1658 failed to change one use of 'type' to 'keytype'. 1661. [bug] Restore dns_name_concatenate() call in adb.c:set_target(). [RT #11582] 1660. [bug] win32: connection_reset_fix() was being called unconditionally. [RT #11595] 1659. [cleanup] Cleanup some messages that were referring to KEY vs DNSKEY, NXT vs NSEC and SIG vs RRSIG. 1658. [func] Update dnssec-keygen to default to KEY for HMAC-MD5 and DH. Tighten which options apply to KEY and DNSKEY records. 1657. [doc] ARM: document query log output. 1656. [doc] Update DNSSEC description in ARM to cover DS, NSEC DNSKEY and RRSIG. [RT #11542] 1655. [bug] Logging multiple versions w/o a size was broken. [RT #11446] 1654. [bug] isc_result_totext() contained array bounds read error. 1653. [func] Add key type checking to dst_key_fromfilename(), DST_TYPE_KEY should be used to read TSIG, TKEY and SIG(0) keys. 1652. [bug] TKEY still uses KEY. 1651. [bug] dig: process multiple dash options. 1650. [bug] dig, nslookup: flush standard out after each command. 1649. [bug] Silence "unexpected non-minimal diff" message. [RT #11206] 1648. [func] Update dnssec-lookaside named.conf syntax to support multiple dnssec-lookaside namespaces (not yet implemented). 1647. [bug] It was possible trigger a INSIST when chasing a DS record that required walking back over a empty node. [RT #11445] 1646. [bug] win32: logging file versions didn't work with non-UNC filenames. [RT#11486] 1645. [bug] named could trigger a REQUIRE failure if multiple masters with keys are specified. 1644. [bug] Update the journal modification time after a successful refresh query. [RT #11436] 1643. [bug] dns_db_closeversion() could leak memory / node references. [RT #11163] 1642. [port] Support OpenSSL implementations which don't have DSA support. [RT #11360] 1641. [bug] Update the check-names description in ARM. [RT #11389] 1640. [bug] win32: isc_socket_cancel(ISC_SOCKCANCEL_ACCEPT) was incorrectly closing the socket. [RT #11291] 1639. [func] Initial dlv system test. 1638. [bug] "ixfr-from-differences" could generate a REQUIRE failure if the journal open failed. [RT #11347] 1637. [bug] Node reference leak on error in addnoqname(). 1636. [bug] The dump done callback could get ISC_R_SUCCESS even if a error had occurred. The database version no longer matched the version of the database that was dumped. 1635. [bug] Memory leak on error in query_addds(). 1634. [bug] named didn't supply a useful error message when it detected duplicate views. [RT #11208] 1633. [bug] named should return NOTIMP to update requests to a slaves without a allow-update-forwarding acl specified. [RT #11331] 1632. [bug] nsupdate failed to send prerequisite only UPDATE messages. [RT #11288] 1631. [bug] dns_journal_compact() could sometimes corrupt the journal. [RT #11124] 1630. [contrib] queryperf: add support for IPv6 transport. 1629. [func] dig now supports IPv6 scoped addresses with the extended format in the local-server part. [RT #8753] 1628. [bug] Typo in Compaq Trucluster support. [RT# 11264] 1627. [bug] win32: sockets were not being closed when the last external reference was removed. [RT# 11179] 1626. [bug] --enable-getifaddrs was broken. [RT#11259] 1625. [bug] named failed to load/transfer RFC2535 signed zones which contained CNAMES. [RT# 11237] 1624. [bug] zonemgr_putio() call should be locked. [RT# 11163] 1623. [bug] A serial number of zero was being displayed in the "sending notifies" log message when also-notify was used. [RT #11177] 1622. [func] probe the system to see if IPV6_(RECV)PKTINFO is available, and suppress wildcard binding if not. 1621. [bug] match-destinations did not work for IPv6 TCP queries. [RT# 11156] 1620. [func] When loading a zone report if it is signed. [RT #11149] 1619. [bug] Missing ISC_LIST_UNLINK in end_reserved_dispatches(). [RT# 11118] 1618. [bug] Fencepost errors in dns_name_ishostname() and dns_name_ismailbox() could trigger a INSIST(). 1617. [port] win32: VC++ 6.0 support. 1616. [compat] Ensure that named's version is visible in the core dump. [RT #11127] 1615. [port] Define ISC_SOCKADDR_LEN_T based on _BSD_SOCKLEN_T_ if it is defined. 1614. [port] win32: silence resource limit messages. [RT# 11101] 1613. [bug] Builds would fail on machines w/o a if_nametoindex(). Missing #ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX/#endif. [RT #11119] 1612. [bug] check-names at the option/view level could trigger an INSIST. [RT# 11116] 1611. [bug] solaris: IPv6 interface scanning failed to cope with no active IPv6 interfaces. 1610. [bug] On dual stack machines "dig -b" failed to set the address type to be looked up with "@server". [RT #11069] 1609. [func] dig now has support to chase DNSSEC signature chains. Requires -DDIG_SIGCHASE=1 to be set in STD_CDEFINES. DNSSEC validation code in dig coded by Olivier Courtay (olivier.courtay@irisa.fr) for the IDsA project (http://idsa.irisa.fr). 1608. [func] dig and host now accept -4/-6 to select IP transport to use when making queries. 1607. [bug] dig, host and nslookup were still using random() to generate query ids. [RT# 11013] 1606. [bug] DLV insecurity proof was failing. 1605. [func] New dns_db_find() option DNS_DBFIND_COVERINGNSEC. 1604. [bug] A xfrout_ctx_create() failure would result in xfrout_ctx_destroy() being called with a partially initialized structure. 1603. [bug] nsupdate: set interactive based on isatty(). [RT# 10929] 1602. [bug] Logging to a file failed unless a size was specified. [RT# 10925] 1601. [bug] Silence spurious warning 'both "recursion no;" and "allow-recursion" active' warning from view "_bind". [RT# 10920] 1600. [bug] Duplicate zone pre-load checks were not case insensitive. 1599. [bug] Fix memory leak on error path when checking named.conf. 1598. [func] Specify that certain parts of the namespace must be secure (dnssec-must-be-secure). 1597. [func] Allow notify-source and query-source to be specified on a per server basis similar to transfer-source. [RT #6496] 1596. [func] Accept 'notify-source' style syntax for query-source. 1595. [func] New notify type 'master-only'. Enable notify for master zones only. 1594. [bug] 'rndc dumpdb' could prevent named from answering queries while the dump was in progress. [RT #10565] 1593. [bug] rndc should return "unknown command" to unknown commands. [RT# 10642] 1592. [bug] configure_view() could leak a dispatch. [RT# 10675] 1591. [bug] libbind: updated to BIND 8.4.5. 1590. [port] netbsd: update thread support. 1589. [func] DNSSEC lookaside validation. 1588. [bug] win32: TCP sockets could become blocked. [RT #10115] 1587. [bug] dns_message_settsigkey() failed to clear existing key. [RT #10590] 1586. [func] "check-names" is now implemented. 1585. [placeholder] 1584. [bug] "make test" failed with a read only source tree. [RT #10461] 1583. [bug] Records add via UPDATE failed to get the correct trust level. [RT #10452] 1582. [bug] rrset-order failed to work on RRsets with more than 32 elements. [RT #10381] 1581. [func] Disable DNSSEC support by default. To enable DNSSEC specify "dnssec-enable yes;" in named.conf. 1580. [bug] Zone destruction on final detach takes a long time. [RT #3746] 1579. [bug] Multiple task managers could not be created. 1578. [bug] Don't use CLASS E IPv4 addresses when resolving. [RT #10346] 1577. [bug] Use isc_uint32_t in ultrasparc optimizer bug workaround code. [RT #10331] 1576. [bug] Race condition in dns_dispatch_addresponse(). [RT# 10272] 1575. [func] Log TSIG name on TSIG verify failure. [RT #4404] 1574. [bug] Don't attempt to open the controls socket(s) when running tests. [RT #9091] 1573. [port] linux: update to libtool 1.5.2 so that "make install DESTDIR=/xx" works with "configure --with-libtool". [RT #9941] 1572. [bug] nsupdate: sign the soa query to find the enclosing zone if the server is specified. [RT #10148] 1571. [bug] rbt:hash_node() could fail leaving the hash table in an inconsistent state. [RT #10208] 1570. [bug] nsupdate failed to handle classes other than IN. New keyword 'class' which sets the default class. [RT #10202] 1569. [func] nsupdate new command 'answer' which displays the complete answer message to the last update. 1568. [bug] nsupdate now reports that the update failed in interactive mode. [RT# 10236] 1567. [maint] B.ROOT-SERVERS.NET is now 192.228.79.201. 1566. [port] Support for the cmsg framework on Solaris and HP/UX. This also solved the problem that match-destinations for IPv6 addresses did not work on these systems. [RT #10221] 1565. [bug] CD flag should be copied to outgoing queries unless the query is under a secure entry point in which case CD should be set. 1564. [func] Attempt to provide a fallback entropy source to be used if named is running chrooted and named is unable to open entropy source within the chroot area. [RT #10133] 1563. [bug] Gracefully fail when unable to obtain neither an IPv4 nor an IPv6 dispatch. [RT #10230] 1562. [bug] isc_socket_create() and isc_socket_accept() could leak memory under error conditions. [RT #10230] 1561. [bug] It was possible to release the same name twice if named ran out of memory. [RT #10197] 1560. [port] FreeBSD: work around FreeBSD 5.2 mapping EAI_NODATA and EAI_NONAME to the same value. 1559. [port] named should ignore SIGFSZ. 1558. [func] New DNSSEC 'disable-algorithms'. Support entry into child zones for which we don't have a supported algorithm. Such child zones are treated as unsigned. 1557. [func] Implement missing DNSSEC tests for * NOQNAME proof with wildcard answers. * NOWILDARD proof with NXDOMAIN. Cache and return NOQNAME with wildcard answers. 1556. [bug] nsupdate now treats all names as fully qualified. [RT #6427] 1555. [func] 'rrset-order cyclic' no longer has a random starting point per query. [RT #7572] 1554. [bug] dig, host, nslookup failed when no nameservers were specified in /etc/resolv.conf. [RT #8232] 1553. [bug] The windows socket code could stop accepting connections. [RT#10115] 1552. [bug] Accept NOTIFY requests from mapped masters if matched-mapped is set. [RT #10049] 1551. [port] Open "/dev/null" before calling chroot(). 1550. [port] Call tzset(), if available, before calling chroot(). 1549. [func] named-checkzone can now write out the zone contents in a easily parsable format (-D and -o). 1548. [bug] When parsing APL records it was possible to silently accept out of range ADDRESSFAMILY values. [RT# 9979] 1547. [bug] Named wasted memory recording duplicate lame zone entries. [RT #9341] 1546. [bug] We were rejecting valid secure CNAME to negative answers. 1545. [bug] It was possible to leak memory if named was unable to bind to the specified transfer source and TSIG was being used. [RT #10120] 1544. [bug] Named would logged a single entry to a file despite it being over the specified size limit. 1543. [bug] Logging using "versions unlimited" did not work. 1542. [placeholder] 1541. [func] NSEC now uses new bitmap format. 1540. [bug] "rndc reload " was silently accepted. [RT #8934] 1539. [bug] Open UDP sockets for notify-source and transfer-source that use reserved ports at startup. [RT #9475] 1538. [placeholder] rt9997 1537. [func] New option "querylog". If set specify whether query logging is to be enabled or disabled at startup. 1536. [bug] Windows socket code failed to log a error description when returning ISC_R_UNEXPECTED. [RT #9998] 1535. [placeholder] 1534. [bug] Race condition when priming cache. [RT# 9940] 1533. [func] Warn if both "recursion no;" and "allow-recursion" are active. [RT# 4389] 1532. [port] netbsd: the configure test for requires . 1531. [port] AIX more libtool fixes. 1530. [bug] It was possible to trigger a INSIST() failure if a slave master file was removed at just the correct moment. [RT #9462] 1529. [bug] "notify explicit;" failed to log that NOTIFY messages were being sent for the zone. [RT# 9442] 1528. [cleanup] Simplify some dns_name_ functions based on the deprecation of bitstring labels. 1527. [cleanup] Reduce the number of gettimeofday() calls without losing necessary timer granularity. 1526. [func] Implemented "additional section caching (or acache)", an internal cache framework for additional section content to improve response performance. Several configuration options were provided to control the behavior. 1525. [bug] dns_cache_create() could trigger a REQUIRE failure in isc_mem_put() during error cleanup. [RT# 9360] 1524. [port] AIX needs to be able to resolve all symbols when creating shared libraries (--with-libtool). 1523. [bug] Fix race condition in rbtdb. [RT# 9189] 1522. [bug] dns_db_findnode() relax the requirements on 'name'. [RT# 9286] 1521. [bug] dns_view_createresolver() failed to check the result from isc_mem_create(). [RT# 9294] 1520. [protocol] Add SSHFP (SSH Finger Print) type. 1519. [bug] dnssec-signzone:nsec_setbit() computed the wrong length of the new bitmap. 1518. [bug] dns_nsec_buildrdata(), and hence dns_nsec_build(), contained a off-by-one error when working out the number of octets in the bitmap. 1517. [port] Support for IPv6 interface scanning on HP/UX and TrueUNIX 5.1. 1516. [func] Roll the DNSSEC types to RRSIG, NSEC and DNSKEY. 1515. [func] Allow transfer source to be set in a server statement. [RT #6496] 1514. [bug] named: isc_hash_destroy() was being called too early. [RT #9160] 1513. [doc] Add "US" to root-delegation-only exclude list. 1512. [bug] Extend the delegation-only logging to return query type, class and responding nameserver. 1511. [bug] delegation-only was generating false positives on negative answers from sub-zones. 1510. [func] New view option "root-delegation-only". Apply delegation-only check to all TLDs and root. Note there are some TLDs that are NOT delegation only (e.g. DE, LV, US and MUSEUM) these can be excluded from the checks by using exclude. root-delegation-only exclude { "DE"; "LV"; "US"; "MUSEUM"; }; 1509. [bug] Hint zones should accept delegation-only. Forward zone should not accept delegation-only. 1508. [bug] Don't apply delegation-only checks to answers from forwarders. 1507. [bug] Handle BIND 8 style returns to NS queries to parents when making delegation-only checks. 1506. [bug] Wrong return type for dns_view_isdelegationonly(). 1505. [bug] Uninitialized rdataset in sdb. [RT #8750] 1504. [func] New zone type "delegation-only". 1503. [port] win32: install libeay32.dll outside of system32. 1502. [bug] nsupdate: adjust timeouts for UPDATE requests over TCP. 1501. [func] Allow TCP queue length to be specified via named.conf, tcp-listen-queue. 1500. [bug] host failed to lookup MX records. Also look up AAAA records. 1499. [bug] isc_random need to be seeded better if arc4random() is not used. 1498. [port] bsdos: 5.x support. 1497. [placeholder] 1496. [port] test for pthread_attr_setstacksize(). 1495. [cleanup] Replace hash functions with universal hash. 1494. [security] Turn on RSA BLINDING as a precaution. 1493. [placeholder] 1492. [cleanup] Preserve rwlock quota context when upgrading / downgrading. [RT #5599] 1491. [bug] dns_master_dump*() would produce extraneous $ORIGIN lines. [RT #6206] 1490. [bug] Accept reading state as well as working state in ns_client_next(). [RT #6813] 1489. [compat] Treat 'allow-update' on slave zones as a warning. [RT #3469] 1488. [bug] Don't override trust levels for glue addresses. [RT #5764] 1487. [bug] A REQUIRE() failure could be triggered if a zone was queued for transfer and the zone was then removed. [RT #6189] 1486. [bug] isc_print_snprintf() '%%' consumed one too many format characters. [RT# 8230] 1485. [bug] gen failed to handle high type values. [RT #6225] 1484. [bug] The number of records reported after a AXFR was wrong. [RT #6229] 1483. [bug] dig axfr failed if the message id in the answer failed to match that in the request. Only the id in the first message is required to match. [RT #8138] 1482. [bug] named could fail to start if the kernel supports IPv6 but no interfaces are configured. Similarly for IPv4. [RT #6229] 1481. [bug] Refresh and stub queries failed to use masters keys if specified. [RT #7391] 1480. [bug] Provide replay protection for rndc commands. Full replay protection requires both rndc and named to be updated. Partial replay protection (limited exposure after restart) is provided if just named is updated. 1479. [bug] cfg_create_tuple() failed to handle out of memory cleanup. parse_list() would leak memory on syntax errors. 1478. [port] ifconfig.sh didn't account for other virtual interfaces. It now takes a optional argument to specify the first interface number. [RT #3907] 1477. [bug] memory leak using stub zones and TSIG. 1476. [placeholder] 1475. [port] Probe for old sprintf(). 1474. [port] Provide strtoul() and memmove() for platforms without them. 1473. [bug] create_map() and create_string() failed to handle out of memory cleanup. [RT #6813] 1472. [contrib] idnkit-1.0 from JPNIC, replaces mdnkit. 1471. [bug] libbind: updated to BIND 8.4.0. 1470. [bug] Incorrect length passed to snprintf. [RT #5966] 1469. [func] Log end of outgoing zone transfer at same level as the start of transfer is logged. [RT #4441] 1468. [func] Internal zones are no longer counted for 'rndc status'. [RT #4706] 1467. [func] $GENERATES now supports optional class and ttl. 1466. [bug] lwresd configuration errors resulted in memory and lock leaks. [RT #5228] 1465. [bug] isc_base64_decodestring() and isc_base64_tobuffer() failed to check that trailing bits were zero allowing some invalid base64 strings to be accepted. [RT #5397] 1464. [bug] Preserve "out of zone" data for outgoing zone transfers. [RT #5192] 1463. [bug] dns_rdata_from{wire,struct}() failed to catch bad NXT bit maps. [RT #5577] 1462. [bug] parse_sizeval() failed to check the token type. [RT #5586] 1461. [bug] Remove deadlock from rbtdb code. [RT #5599] 1460. [bug] inet_pton() failed to reject certain malformed IPv6 literals. 1459. [placeholder] 1458. [cleanup] sprintf() -> snprintf(). 1457. [port] Provide strlcat() and strlcpy() for platforms without them. 1456. [contrib] gen-data-queryperf.py from Stephane Bortzmeyer. 1455. [bug] missing from server grammar in doc/misc/options. [RT #5616] 1454. [port] Use getifaddrs() if available for interface scanning. --disable-getifaddrs to override. Glibc currently has a getifaddrs() that does not support IPv6. Use --enable-getifaddrs=glibc to force the use of this version under linux machines. 1453. [doc] ARM: $GENERATE example wasn't accurate. [RT #5298] 1452. [placeholder] 1451. [bug] rndc-confgen didn't exit with a error code for all failures. [RT #5209] 1450. [bug] Fetching expired glue failed under certain circumstances. [RT #5124] 1449. [bug] query_addbestns() didn't handle running out of memory gracefully. 1448. [bug] Handle empty wildcards labels. 1447. [bug] We were casting (unsigned int) to and from (void *). rdataset->private4 is now rdataset->privateuint4 to reflect a type change. 1446. [func] Implemented undocumented alternate transfer sources from BIND 8. See use-alt-transfer-source, alt-transfer-source and alt-transfer-source-v6. SECURITY: use-alt-transfer-source is ENABLED unless you are using views. This may cause a security risk resulting in accidental disclosure of wrong zone content if the master supplying different source content based on IP address. If you are not certain ISC recommends setting use-alt-transfer-source no; 1445. [bug] DNS_ADBFIND_STARTATROOT broke stub zones. This has been replaced with DNS_ADBFIND_STARTATZONE which causes the search to start using the closest zone. 1444. [func] dns_view_findzonecut2() allows you to specify if the cache should be searched for zone cuts. 1443. [func] Masters lists can now be specified and referenced in zone masters clauses and other masters lists. 1442. [func] New functions for manipulating port lists: dns_portlist_create(), dns_portlist_add(), dns_portlist_remove(), dns_portlist_match(), dns_portlist_attach() and dns_portlist_detach(). 1441. [func] It is now possible to tell dig to bind to a specific source port. 1440. [func] It is now possible to tell named to avoid using certain source ports (avoid-v4-udp-ports, avoid-v6-udp-ports). 1439. [bug] Named could return NOERROR with certain NOTIFY failures. Return NOTAUTH if the NOTIFY zone is not being served. 1438. [func] Log TSIG (if any) when logging NOTIFY requests. 1437. [bug] Leave space for stdio to work in. [RT #5033] 1436. [func] dns_zonemgr_resumexfrs() can be used to restart stalled transfers. 1435. [bug] zmgr_resume_xfrs() was being called read locked rather than write locked. zmgr_resume_xfrs() was not being called if the zone was being shutdown. 1434. [bug] "rndc reconfig" failed to initiate the initial zone transfer of new slave zones. 1433. [bug] named could trigger a REQUIRE failure if it could not get a file descriptor when attempting to write a master file. [RT #4347] 1432. [func] The advertised EDNS UDP buffer size can now be set via named.conf (edns-udp-size). 1431. [bug] isc_print_snprintf() "%s" with precision could walk off end of argument. [RT #5191] 1430. [port] linux: IPv6 interface scanning support. 1429. [bug] Prevent the cache getting locked to old servers. 1428. [placeholder] 1427. [bug] Race condition in adb with threaded build. 1426. [placeholder] 1425. [port] linux/libbind: define __USE_MISC when testing *_r() function prototypes in netdb.h. [RT #4921] 1424. [bug] EDNS version not being correctly printed. 1423. [contrib] queryperf: added A6 and SRV. 1422. [func] Log name/type/class when denying a query. [RT #4663] 1421. [func] Differentiate updates that don't succeed due to prerequisites (unsuccessful) vs other reasons (failed). 1420. [port] solaris: work around gcc optimizer bug. 1419. [port] openbsd: use /dev/arandom. [RT #4950] 1418. [bug] 'rndc reconfig' did not cause new slaves to load. 1417. [func] ID.SERVER/CHAOS is now a built in zone. See "server-id" for how to configure. 1416. [bug] Empty node should return NOERROR NODATA, not NXDOMAIN. [RT #4715] 1415. [func] DS TTL now derived from NS ttl. NXT TTL now derived from SOA MINIMUM. 1414. [func] Support for KSK flag. 1413. [func] Explicitly request the (re-)generation of DS records from keysets (dnssec-signzone -g). 1412. [func] You can now specify servers to be tried if a nameserver has IPv6 address and you only support IPv4 or the reverse. See dual-stack-servers. 1411. [bug] empty nodes should stop wildcard matches. [RT #4802] 1410. [func] Handle records that live in the parent zone, e.g. DS. 1409. [bug] DS should have attribute DNS_RDATATYPEATTR_DNSSEC. 1408. [bug] "make distclean" was not complete. [RT #4700] 1407. [bug] lfsr incorrectly implements the shift register. [RT #4617] 1406. [bug] dispatch initializes one of the LFSR's with a incorrect polynomial. [RT #4617] 1405. [func] Use arc4random() if available. 1404. [bug] libbind: ns_name_ntol() could overwrite a zero length buffer. 1403. [func] dnssec-signzone, dnssec-keygen, dnssec-makekeyset dnssec-signkey now report their version in the usage message. 1402. [cleanup] A6 has been moved to experimental and is no longer fully supported. 1401. [bug] adb wasn't clearing state when the timer expired. 1400. [bug] Block the addition of wildcard NS records by IXFR or UPDATE. [RT #3502] 1399. [bug] Use serial number arithmetic when testing SIG timestamps. [RT #4268] 1398. [doc] ARM: notify-also should have been also-notify. [RT #4345] 1397. [maint] J.ROOT-SERVERS.NET is now 192.58.128.30. 1396. [func] dnssec-signzone: adjust the default signing time by 1 hour to allow for clock skew. 1395. [port] OpenSSL 0.9.7 defines CRYPTO_LOCK_ENGINE but doesn't have a working implementation. [RT #4079] 1394. [func] It is now possible to check if a particular element is in a acl. Remove duplicate entries from the localnets acl. 1393. [port] Bind to individual IPv6 interfaces if IPV6_IPV6ONLY is not available in the kernel to prevent accidently listening on IPv4 interfaces. 1392. [bug] named-checkzone: update usage. 1391. [func] Add support for IPv6 scoped addresses in named. 1390. [func] host now supports ixfr. 1389. [bug] named could fail to rotate long log files. [RT #3666] 1388. [port] irix: check for sys/sysctl.h and NET_RT_IFLIST before defining HAVE_IFLIST_SYSCTL. [RT #3770] 1387. [bug] named could crash due to an access to invalid memory space (which caused an assertion failure) in incremental cleaning. [RT #3588] 1386. [bug] named-checkzone -z stopped on errors in a zone. [RT #3653] 1385. [bug] Setting serial-query-rate to 10 would trigger a REQUIRE failure. 1384. [bug] host was incompatible with BIND 8 in its exit code and in the output with the -l option. [RT #3536] 1383. [func] Track the serial number in a IXFR response and log if a mismatch occurs. This is a more specific error than "not exact". [RT #3445] 1382. [bug] make install failed with --enable-libbind. [RT #3656] 1381. [bug] named failed to correctly process answers that contained DNAME records where the resulting CNAME resulted in a negative answer. 1380. [func] 'rndc recursing' dump recursing queries to 'recursing-file = "named.recursing";'. 1379. [func] 'rndc status' now reports tcp and recursion quota states. 1378. [func] Improved positive feedback for 'rndc {reload|refresh}. 1377. [func] dns_zone_load{new}() now reports if the zone was loaded, queued for loading to up to date. 1376. [func] New function dns_zone_logc() to log to specified category. 1375. [func] 'rndc dumpdb' now dumps the adb cache along with the data cache. 1374. [func] dns_adb_dump() now logs the lame zones associated with each server. 1373. [bug] Recovery from expired glue failed under certain circumstances. 1372. [bug] named crashes with an assertion failure on exit when sharing the same port for listening and querying, and changing listening addresses several times. [RT# 3509] 1371. [bug] notify-source-v6, transfer-source-v6 and query-source-v6 with explicit addresses and using the same ports as named was listening on could interfere with named's ability to answer queries sent to those addresses. 1370. [bug] dig '+[no]recurse' was incorrectly documented. 1369. [bug] Adding an NS record as the lexicographically last record in a secure zone didn't work. 1368. [func] remove support for bitstring labels. 1367. [func] Use response times to select forwarders. 1366. [contrib] queryperf usage was incomplete. Add '-h' for help. 1365. [func] "localhost" and "localnets" acls now include IPv6 addresses / prefixes. 1364. [func] Log file name when unable to open memory statistics and dump database files. [RT# 3437] 1363. [func] Listen-on-v6 now supports specific addresses. 1362. [bug] remove IFF_RUNNING test when scanning interfaces. 1361. [func] log the reason for rejecting a server when resolving queries. 1360. [bug] --enable-libbind would fail when not built in the source tree for certain OS's. 1359. [security] Support patches OpenSSL libraries. http://www.cert.org/advisories/CA-2002-23.html 1358. [bug] It was possible to trigger a INSIST when debugging large dynamic updates. [RT #3390] 1357. [bug] nsupdate was extremely wasteful of memory. 1356. [tuning] Reduce the number of events / quantum for zone tasks. 1355. [bug] Fix DNSSEC wildcard proof for CNAME/DNAME. 1354. [doc] lwres man pages had illegal nroff. 1353. [contrib] sdb/ldap to version 0.9. 1352. [bug] dig, host, nslookup when falling back to TCP use the current search entry (if any). [RT #3374] 1351. [bug] lwres_getipnodebyname() returned the wrong name when given a IPv4 literal, af=AF_INET6 and AI_MAPPED was set. 1350. [bug] dns_name_fromtext() failed to handle too many labels gracefully. 1349. [security] Minimum OpenSSL version now 0.9.6e (was 0.9.5a). http://www.cert.org/advisories/CA-2002-23.html 1348. [port] win32: Rewrote code to use I/O Completion Ports in socket.c and eliminating a host of socket errors. Performance is enhanced. 1347. [placeholder] 1346. [placeholder] 1345. [port] Use a explicit -Wformat with gcc. Not all versions include it in -Wall. 1344. [func] Log if the serial number on the master has gone backwards. If you have multiple machines specified in the masters clause you may want to set 'multi-master yes;' to suppress this warning. 1343. [func] Log successful notifies received (info). Adjust log level for failed notifies to notice. 1342. [func] Log remote address with TCP dispatch failures. 1341. [func] Allow a rate limiter to be stalled. 1340. [bug] Delay and spread out the startup refresh load. 1339. [func] dig, host and nslookup now use IP6.ARPA for nibble lookups. Bit string lookups are no longer attempted. 1338. [placeholder] 1337. [placeholder] 1336. [func] Nibble lookups under IP6.ARPA are now supported by dns_byaddr_create(). dns_byaddr_createptrname() is deprecated, use dns_byaddr_createptrname2() instead. 1335. [bug] When performing a nonexistence proof, the validator should discard parent NXTs from higher in the DNS. 1334. [bug] When signing/verifying rdatasets, duplicate rdatas need to be suppressed. 1333. [contrib] queryperf now reports a summary of returned rcodes (-c), rcodes are printed in mnemonic form (-v). 1332. [func] Report the current serial with periodic commits when rolling forward the journal. 1331. [func] Generate DNSSEC wildcard proofs. 1330. [bug] When processing events (non-threaded) only allow the task one chance to use to use its quantum. 1329. [func] named-checkzone will now check if nameservers that appear to be IP addresses. Available modes "fail", "warn" (default) and "ignore" the results of the check. 1328. [bug] The validator could incorrectly verify an invalid negative proof. 1327. [bug] The validator would incorrectly mark data as insecure when seeing a bogus signature before a correct signature. 1326. [bug] DNAME/CNAME signatures were not being cached when validation was not being performed. [RT #3284] 1325. [bug] If the tcpquota was exhausted it was possible to to trigger a INSIST() failure. 1324. [port] darwin: ifconfig.sh now supports darwin. 1323. [port] linux: Slackware 4.0 needs . [RT #3205] 1322. [bug] dnssec-signzone usage message was misleading. 1321. [bug] If the last RRset in a zone is glue, dnssec-signzone would incorrectly duplicate its output and sign it. 1320. [doc] query-source-v6 was missing from options section. [RT #3218] 1319. [func] libbind: log attempts to exploit #1318. 1318. [bug] libbind: Remote buffer overrun. 1317. [port] libbind: TrueUNIX 5.1 does not like __align as a element name. 1316. [bug] libbind: gethostans() could get out of sync parsing the response if there was a very long CNAME chain. 1315. [bug] Options should apply to the internal _bind view. 1314. [port] Handle ECONNRESET from sendmsg() [unix]. 1313. [func] Query log now says if the query was signed (S) or if EDNS was used (E). 1312. [func] Log TSIG key used w/ outgoing zone transfers. 1311. [bug] lwres_getrrsetbyname leaked memory. [RT #3159] 1310. [bug] 'rndc stop' failed to cause zones to be flushed sometimes. [RT #3157] 1309. [func] Log that a zone transfer was covered by a TSIG. 1308. [func] DS (delegation signer) support. 1307. [bug] nsupdate: allow white space base64 key data. 1306. [bug] Badly encoded LOC record when the size, horizontal precision or vertical precision was 0.1m. 1305. [bug] Document that internal zones are included in the rndc status results. 1304. [func] New function: dns_zone_name(). 1303. [func] Option 'flush-zones-on-shutdown ;'. 1302. [func] Extended rndc dumpdb to support dumping of zones and view selection: 'dumpdb [-all|-zones|-cache] [view]'. 1301. [func] New category 'update-security'. 1300. [port] Compaq Trucluster support. 1299. [bug] Set AI_ADDRCONFIG when looking up addresses via getaddrinfo() (affects dig, host, nslookup, rndc and nsupdate). 1298. [bug] The CINCLUDES macro in lib/dns/sec/dst/Makefile could be left with a trailing "\" after configure has been run. 1297. [port] linux: make handling EINVAL from socket() no longer conditional on #ifdef LINUX. 1296. [bug] isc_log_closefilelogs() needed to lock the log context. 1295. [bug] isc_log_setdebuglevel() needed to lock the log context. 1294. [func] libbind: no longer attempts bit string labels for IPv6 reverse resolution. Try IP6.ARPA then IP6.INT for nibble style resolution. 1293. [func] Entropy can now be retrieved from EGDs. [RT #2438] 1292. [func] Enable IPv6 support when using ioctl style interface scanning and OS supports SIOCGLIFADDR using struct if_laddrreq. 1291. [func] Enable IPv6 support when using sysctl style interface scanning. 1290. [func] "dig axfr" now reports the number of messages as well as the number of records. 1289. [port] See if -ldl is required for OpenSSL? [RT #2672] 1288. [bug] Adjusted REQUIRE's in lib/dns/name.c to better reflect written requirements. 1287. [bug] REQUIRE that DNS_DBADD_MERGE only be set when adding a rdataset to a zone db in the rbtdb implementation of addrdataset. 1286. [bug] dns_name_downcase() enforce requirement that target != NULL or name->buffer != NULL. 1285. [func] lwres: probe the system to see what address families are currently in use. 1284. [bug] The RTT estimate on unused servers was not aged. [RT #2569] 1283. [func] Use "dataready" accept filter if available. 1282. [port] libbind: hpux 11.11 interface scanning. 1281. [func] Log zone when unable to get private keys to update zone. Log zone when NXT records are missing from secure zone. 1280. [bug] libbind: escape '(' and ')' when converting to presentation form. 1279. [port] Darwin uses (unsigned long) for size_t. [RT #2590] 1278. [func] dig: now supports +[no]cl +[no]ttlid. 1277. [func] You can now create your own customized printing styles: dns_master_stylecreate() and dns_master_styledestroy(). 1276. [bug] libbind: const pointer conflicts in res_debug.c. 1275. [port] libbind: hpux: treat all hpux systems as BIG_ENDIAN. 1274. [bug] Memory leak in lwres_gnbarequest_parse(). 1273. [port] libbind: solaris: 64 bit binary compatibility. 1272. [contrib] Berkeley DB 4.0 sdb implementation from Nuno Miguel Rodrigues . 1271. [bug] "recursion available: {denied,approved}" was too confusing. 1270. [bug] Check that system inet_pton() and inet_ntop() support AF_INET6. 1269. [port] Openserver: ifconfig.sh support. 1268. [port] Openserver: the value FD_SETSIZE depends on whether is included or not. Be consistent. 1267. [func] isc_file_openunique() now creates file using mode 0666 rather than 0600. 1266. [bug] ISC_LINK_INIT, ISC_LINK_UNLINK, ISC_LIST_DEQUEUE, __ISC_LINK_UNLINKUNSAFE and __ISC_LIST_DEQUEUEUNSAFE are not C++ compatible, use *_TYPE versions instead. 1265. [bug] libbind: LINK_INIT and UNLINK were not compatible with C++, use LINK_INIT_TYPE and UNLINK_TYPE instead. 1264. [placeholder] 1263. [bug] Reference after free error if dns_dispatchmgr_create() failed. 1262. [bug] ns_server_destroy() failed to set *serverp to NULL. 1261. [func] libbind: ns_sign2() and ns_sign_tcp() now provide support for compressed TSIG owner names. 1260. [func] libbind: res_update can now update IPv6 servers, new function res_findzonecut2(). 1259. [bug] libbind: get_salen() IPv6 support was broken for OSs w/o sa_len. 1258. [bug] libbind: res_nametotype() and res_nametoclass() were broken. 1257. [bug] Failure to write pid-file should not be fatal on reload. [RT #2861] 1256. [contrib] 'queryperf' now has EDNS (-e) + DNSSEC DO (-D) support. 1255. [bug] When verifying that an NXT proves nonexistence, check the rcode of the message and only do the matching NXT check. That is, for NXDOMAIN responses, check that the name is in the range between the NXT owner and next name, and for NOERROR NODATA responses, check that the type is not present in the NXT bitmap. 1254. [func] preferred-glue option from BIND 8.3. 1253. [bug] The dnssec system test failed to remove the correct files. 1252. [bug] Dig, host and nslookup were not checking the address the answer was coming from against the address it was sent to. [RT# 2692] 1251. [port] win32: a make file contained absolute version specific references. 1250. [func] Nsupdate will report the address the update was sent to. 1249. [bug] Missing masters clause was not handled gracefully. [RT #2703] 1248. [bug] DESTDIR was not being propagated between makes. 1247. [bug] Don't reset the interface index for link/site local addresses. [RT #2576] 1246. [func] New functions isc_sockaddr_issitelocal(), isc_sockaddr_islinklocal(), isc_netaddr_issitelocal() and isc_netaddr_islinklocal(). 1245. [bug] Treat ENOBUFS, ENOMEM and ENFILE as soft errors for accept(). 1244. [bug] Receiving a TCP message from a blackhole address would prevent further messages being received over that interface. 1243. [bug] It was possible to trigger a REQUIRE() in dns_message_findtype(). [RT #2659] 1242. [bug] named-checkzone failed if a journal existed. [RT #2657] 1241. [bug] Drop received UDP messages with a zero source port as these are invariably forged. [RT #2621] 1240. [bug] It was possible to leak zone references by specifying an incorrect zone to rndc. 1239. [bug] Under certain circumstances named could continue to use a name after it had been freed triggering INSIST() failures. [RT #2614] 1238. [bug] It is possible to lockup the server when shutting down if notifies were being processed. [RT #2591] 1237. [bug] nslookup: "set q=type" failed. 1236. [bug] dns_rdata{class,type}_fromtext() didn't handle non NULL terminated text regions. [RT #2588] 1235. [func] Report 'out of memory' errors from openssl. 1234. [bug] contrib/sdb: 'zonetodb' failed to call dns_result_register(). DNS_R_SEENINCLUDE should not be fatal. 1233. [bug] The flags field of a KEY record can be expressed in hex as well as decimal. 1232. [bug] unix/errno2result() didn't handle EADDRNOTAVAIL. 1231. [port] HPUX 11.11 recvmsg() can return spurious EADDRNOTAVAIL. 1230. [bug] isccc_cc_isreply() and isccc_cc_isack() were broken. 1229. [bug] named would crash if it received a TSIG signed query as part of an AXFR response. [RT #2570] 1228. [bug] 'make install' did not depend on 'make all'. [RT #2559] 1227. [bug] dns_lex_getmastertoken() now returns ISC_R_BADNUMBER if a number was expected and some other token was found. [RT#2532] 1226. [func] Use EDNS for zone refresh queries. [RT #2551] 1225. [func] dns_message_setopt() no longer requires that dns_message_renderbegin() to have been called. 1224. [bug] 'rrset-order' and 'sortlist' should be additive not exclusive. 1223. [func] 'rrset-order' partially works 'cyclic' and 'random' are supported. 1222. [bug] Specifying 'port *' did not always result in a system selected (non-reserved) port being used. [RT #2537] 1221. [bug] Zone types 'master', 'slave' and 'stub' were not being compared case insensitively. [RT #2542] 1220. [func] Support for APL rdata type. 1219. [func] Named now reports the TSIG extended error code when signature verification fails. [RT #1651] 1218. [bug] Named incorrectly returned SERVFAIL rather than NOTAUTH when there was a TSIG BADTIME error. [RT #2519] 1217. [func] Report locations of previous key definition when a duplicate is detected. 1216. [bug] Multiple server clauses for the same server were not reported. [RT #2514] 1215. [port] solaris: add support to ifconfig.sh for x86 2.5.1 1214. [bug] Win32: isc_file_renameunique() could leave zero length files behind. 1213. [func] Report view associated with client if it is not a standard view (_default or _bind). 1212. [port] libbind: 64k answer buffers were causing stack space to be exceeded for certain OS. Use heap space instead. 1211. [bug] dns_name_fromtext() incorrectly handled certain valid octal bitlabels. [RT #2483] 1210. [bug] libbind: getnameinfo() failed to lookup IPv4 mapped / compatible addresses. [RT #2461] 1209. [bug] Dig, host, nslookup were not checking the message ids on the responses. [RT #2454] 1208. [bug] dns_master_load*() failed to log a error message if an error was detected when parsing the ownername of a record. [RT #2448] 1207. [bug] libbind: getaddrinfo() could call freeaddrinfo() with an invalid pointer. 1206. [bug] SERVFAIL and NOTIMP responses to an EDNS query should trigger a non-EDNS retry. 1205. [bug] OPT, TSIG and TKEY cannot be used to set the "class" of the message. [RT #2449] 1204. [bug] libbind: res_nupdate() failed to update the name server addresses before sending the update. 1203. [func] Report locations of previous acl and zone definitions when a duplicate is detected. 1202. [func] New functions: cfg_obj_line() and cfg_obj_file(). 1201. [bug] Require that if 'callbacks' is passed to dns_rdata_fromtext(), callbacks->error and callbacks->warn are initialized. 1200. [bug] Log 'errno' that we are unable to convert to isc_result_t. [RT #2404] 1199. [doc] ARM reference to RFC 2157 should have been RFC 1918. [RT #2436] 1198. [bug] OPT printing style was not consistent with the way the header fields are printed. The DO bit was not reported if set. Report if any of the MBZ bits are set. 1197. [bug] Attempts to define the same acl multiple times were not detected. 1196. [contrib] update mdnkit to 2.2.3. 1195. [bug] Attempts to redefine builtin acls should be caught. [RT #2403] 1194. [bug] Not all duplicate zone definitions were being detected at the named.conf checking stage. [RT #2431] 1193. [bug] dig +besteffort parsing didn't handle packet truncation. dns_message_parse() has new flag DNS_MESSAGE_IGNORETRUNCATION. 1192. [bug] The seconds fields in LOC records were restricted to three decimal places. More decimal places should be allowed but warned about. 1191. [bug] A dynamic update removing the last non-apex name in a secure zone would fail. [RT #2399] 1190. [func] Add the "rndc freeze" and "rndc unfreeze" commands. [RT #2394] 1189. [bug] On some systems, malloc(0) returns NULL, which could cause the caller to report an out of memory error. [RT #2398] 1188. [bug] Dynamic updates of a signed zone would fail if some of the zone private keys were unavailable. 1187. [bug] named was incorrectly returning DNSSEC records in negative responses when the DO bit was not set. 1186. [bug] isc_hex_tobuffer(,,length = 0) failed to unget the EOL token when reading to end of line. 1185. [bug] libbind: don't assume statp->_u._ext.ext is valid unless RES_INIT is set when calling res_*init(). 1184. [bug] libbind: call res_ndestroy() if RES_INIT is set when res_*init() is called. 1183. [bug] Handle ENOSR error when writing to the internal control pipe. [RT #2395] 1182. [bug] The server could throw an assertion failure when constructing a negative response packet. 1181. [func] Add the "key-directory" configuration statement, which allows the server to look for online signing keys in alternate directories. 1180. [func] dnssec-keygen should always generate keys with protocol 3 (DNSSEC), since it's less confusing that way. 1179. [func] Add SIG(0) support to nsupdate. 1178. [bug] Follow and cache (if appropriate) A6 and other data chains to completion in the additional section. 1177. [func] Report view when loading zones if it is not a standard view (_default or _bind). [RT #2270] 1176. [doc] Document that allow-v6-synthesis is only performed for clients that are supplied recursive service. [RT #2260] 1175. [bug] named-checkzone and named-checkconf failed to call dns_result_register() at startup which could result in runtime exceptions when printing "out of memory" errors. [RT #2335] 1174. [bug] Win32: add WSAECONNRESET to the expected errors from connect(). [RT #2308] 1173. [bug] Potential memory leaks in isc_log_create() and isc_log_settag(). [RT #2336] 1172. [doc] Add CERT, GPOS, KX, NAPTR, NSAP, PX and TXT to table of RR types in ARM. 1171. [func] Added function isc_region_compare(), updated files in lib/dns to use this function instead of local one. 1170. [bug] Don't attempt to print the token when a I/O error occurs when parsing named.conf. [RT #2275] 1169. [func] Identify recursive queries in the query log. 1168. [bug] Empty also-notify clauses were not handled. [RT #2309] 1167. [contrib] nslint-2.1a3 (from author). 1166. [bug] "Not Implemented" should be reported as NOTIMP, not NOTIMPL. [RT #2281] 1165. [bug] We were rejecting notify-source{-v6} in zone clauses. 1164. [bug] Empty masters clauses in slave / stub zones were not handled gracefully. [RT #2262] 1163. [func] isc_time_formattimestamp() now includes the year. 1162. [bug] The allow-notify option was not accepted in slave zone statements. 1161. [bug] named-checkzone looped on unbalanced brackets. [RT #2248] 1160. [bug] Generating Diffie-Hellman keys longer than 1024 bits could fail. [RT #2241] 1159. [bug] MD and MF are not permitted to be loaded by RFC1123. 1158. [func] Report the client's address when logging notify messages. 1157. [func] match-clients and match-destinations now accept keys. [RT #2045] 1156. [port] The configure test for strsep() incorrectly succeeded on certain patched versions of AIX 4.3.3. [RT #2190] 1155. [func] Recover from master files being removed from under us. 1154. [bug] Don't attempt to obtain the netmask of a interface if there is no address configured. [RT #2176] 1153. [func] 'rndc {stop|halt} -p' now reports the process id of the instance of named being shutdown. 1152. [bug] libbind: read buffer overflows. 1151. [bug] nslookup failed to check that the arguments to the port, timeout, and retry options were valid integers and in range. [RT #2099] 1150. [bug] named incorrectly accepted TTL values containing plus or minus signs, such as 1d+1h-1s. 1149. [func] New function isc_parse_uint32(). 1148. [func] 'rndc-confgen -a' now provides positive feedback. 1147. [func] Set IPV6_V6ONLY on IPv6 sockets if supported by the OS. listen-on-v6 { any; }; should no longer result in IPv4 queries be accepted. Similarly control { inet :: ... }; should no longer result in IPv4 connections being accepted. This can be overridden at compile time by defining ISC_ALLOW_MAPPED=1. 1146. [func] Allow IPV6_IPV6ONLY to be set/cleared on a socket if supported by the OS by a new function isc_socket_ipv6only(). 1145. [func] "host" no longer reports a NOERROR/NODATA response by printing nothing. [RT #2065] 1144. [bug] rndc-confgen would crash if both the -a and -t options were specified. [RT #2159] 1143. [bug] When a trusted-keys statement was present and named was built without crypto support, it would leak memory. 1142. [bug] dnssec-signzone would fail to delete temporary files in some failure cases. [RT #2144] 1141. [bug] When named rejected a control message, it would leak a file descriptor and memory. It would also fail to respond, causing rndc to hang. [RT #2139, #2164] 1140. [bug] rndc-confgen did not accept IPv6 addresses as arguments to the -s option. [RT #2138] 1139. [func] It is now possible to flush a given name from the cache(s) via 'rndc flushname name [view]'. [RT #2051] 1138. [func] It is now possible to flush a given name from the cache by calling the new function dns_cache_flushname(). 1137. [func] It is now possible to flush a given name from the ADB by calling the new function dns_adb_flushname(). 1136. [bug] CNAME records synthesized from DNAMEs did not have a TTL of zero as required by RFC2672. [RT #2129] 1135. [func] You can now override the default syslog() facility for named/lwresd at compile time. [RT #1982] 1134. [bug] Multi-threaded servers could deadlock in ferror() when reloading zone files. [RT #1951, #1998] 1133. [bug] IN6_IS_ADDR_LOOPBACK was not portably defined on platforms without IN6_IS_ADDR_LOOPBACK. [RT #2106] 1132. [func] Improve UPDATE prerequisite failure diagnostic messages. 1131. [bug] The match-destinations view option did not work with IPv6 destinations. [RT #2073, #2074] 1130. [bug] Log messages reporting an out-of-range serial number did not include the out-of-range number but the following token. [RT #2076] 1129. [bug] Multi-threaded servers could crash under heavy resolution load due to a race condition. [RT #2018] 1128. [func] sdb drivers can now provide RR data in either text or wire format, the latter using the new functions dns_sdb_putrdata() and dns_sdb_putnamedrdata(). 1127. [func] rndc: If the server to contact has multiple addresses, try all of them. 1126. [bug] The server could access a freed event if shut down while a client start event was pending delivery. [RT #2061] 1125. [bug] rndc: -k option was missing from usage message. [RT #2057] 1124. [doc] dig: +[no]dnssec, +[no]besteffort and +[no]fail are now documented. [RT #2052] 1123. [bug] dig +[no]fail did not match description. [RT #2052] 1122. [tuning] Resolution timeout reduced from 90 to 30 seconds. [RT #2046] 1121. [bug] The server could attempt to access a NULL zone table if shut down while resolving. [RT #1587, #2054] 1120. [bug] Errors in options were not fatal. [RT #2002] 1119. [func] Added support in Win32 for NTFS file/directory ACL's for access control. 1118. [bug] On multi-threaded servers, a race condition could cause an assertion failure in resolver.c during resolver shutdown. [RT #2029] 1117. [port] The configure check for in6addr_loopback incorrectly succeeded on AIX 4.3 when compiling with -O2 because the test code was optimized away. [RT #2016] 1116. [bug] Setting transfers in a server clause, transfers-in, or transfers-per-ns to a value greater than 2147483647 disabled transfers. [RT #2002] 1115. [func] Set maximum values for cleaning-interval, heartbeat-interval, interface-interval, max-transfer-idle-in, max-transfer-idle-out, max-transfer-time-in, max-transfer-time-out, statistics-interval of 28 days and sig-validity-interval of 3660 days. [RT #2002] 1114. [port] Ignore more accept() errors. [RT #2021] 1113. [bug] The allow-update-forwarding option was ignored when specified in a view. [RT #2014] 1112. [placeholder] 1111. [bug] Multi-threaded servers could deadlock processing recursive queries due to a locking hierarchy violation in adb.c. [RT #2017] 1110. [bug] dig should only accept valid abbreviations of +options. [RT #2003] 1109. [bug] nsupdate accepted illegal ttl values. 1108. [bug] On Win32, rndc was hanging when named was not running due to failure to select for exceptional conditions in select(). [RT #1870] 1107. [bug] nsupdate could catch an assertion failure if an invalid domain name was given as the argument to the "zone" command. 1106. [bug] After seeing an out of range TTL, nsupdate would treat all TTLs as out of range. [RT #2001] 1105. [port] OpenUNIX 8 enable threads by default. [RT #1970] 1104. [bug] Invalid arguments to the transfer-format option could cause an assertion failure. [RT #1995] 1103. [port] OpenUNIX 8 support (ifconfig.sh). [RT #1970] 1102. [doc] Note that query logging is enabled by directing the queries category to a channel. 1101. [bug] Array bounds read error in lwres_gai_strerror. 1100. [bug] libbind: DNSSEC key ids were computed incorrectly. 1099. [cleanup] libbind: defining REPORT_ERRORS in lib/bind/dst caused compile time errors. 1098. [bug] libbind: HMAC-MD5 key files are now mode 0600. 1097. [func] libbind: RES_PRF_TRUNC for dig. 1096. [func] libbind: "DNSSEC OK" (DO) support. 1095. [func] libbind: resolver option: no-tld-query. disables trying unqualified as a tld. no_tld_query is also supported for FreeBSD compatibility. 1094. [func] libbind: add support gcc's format string checking. 1093. [doc] libbind: miscellaneous nroff fixes. 1092. [bug] libbind: get*by*() failed to check if res_init() had been called. 1091. [bug] libbind: misplaced va_end(). 1090. [bug] libbind: dns_ho.c:add_hostent() was not returning the amount of memory consumed resulting in garbage address being returned. Alignment calculations were wasting space. We weren't suppressing duplicate addresses. 1089. [func] libbind: inet_{cidr,net}_{pton,ntop}() now have IPv6 support. 1088. [port] libbind: MPE/iX C.70 (incomplete) 1087. [bug] libbind: struct __res_state too large on 64 bit arch. 1086. [port] libbind: sunos: old sprintf. 1085. [port] libbind: solaris: sys_nerr and sys_errlist do not exist when compiling in 64 bit mode. 1084. [cleanup] libbind: gai_strerror() rewritten. 1083. [bug] The default control channel listened on the wildcard address, not the loopback as documented. [RT #1975] 1082. [bug] The -g option to named incorrectly caused logging to be sent to syslog in addition to stderr. [RT #1974] 1081. [bug] Multicast queries were incorrectly identified based on the source address, not the destination address. 1080. [bug] BIND 8 compatibility: accept bare IP prefixes as the second element of a two-element top level sort list statement. [RT #1964] 1079. [bug] BIND 8 compatibility: accept bare elements at top level of sort list treating them as if they were a single element list. [RT #1963] 1078. [bug] We failed to correct bad tv_usec values in one case. [RT #1966] 1077. [func] Do not accept further recursive clients when the total number of recursive lookups being processed exceeds max-recursive-clients, even if some of the lookups are internally generated. [RT #1915, #1938] 1076. [bug] A badly defined global key could trigger an assertion on load/reload if views were used. [RT #1947] 1075. [bug] Out-of-range network prefix lengths were not reported. [RT #1954] 1074. [bug] Running out of memory in dump_rdataset() could cause an assertion failure. [RT #1946] 1073. [bug] The ADB cache cleaning should also be space driven. [RT #1915, #1938] 1072. [bug] The TCP client quota could be exceeded when recursion occurred. [RT #1937] 1071. [bug] Sockets listening for TCP DNS connections specified an excessive listen backlog. [RT #1937] 1070. [bug] Copy DNSSEC OK (DO) to response as specified by draft-ietf-dnsext-dnssec-okbit-03.txt. 1069. [placeholder] 1068. [bug] errno could be overwritten by catgets(). [RT #1921] 1067. [func] Allow quotas to be soft, isc_quota_soft(). 1066. [bug] Provide a thread safe wrapper for strerror(). [RT #1689] 1065. [func] Runtime support to select new / old style interface scanning using ioctls. 1064. [bug] Do not shut down active network interfaces if we are unable to scan the interface list. [RT #1921] 1063. [bug] libbind: "make install" was failing on IRIX. [RT #1919] 1062. [bug] If the control channel listener socket was shut down before server exit, the listener object could be freed twice. [RT #1916] 1061. [bug] If periodic cache cleaning happened to start while cleaning due to reaching the configured maximum cache size was in progress, the server could catch an assertion failure. [RT #1912] 1060. [func] Move refresh, stub and notify UDP retry processing into dns_request. 1059. [func] dns_request now support will now retry UDP queries, dns_request_createvia2() and dns_request_createraw2(). 1058. [func] Limited lifetime ticker timers are now available, isc_timertype_limited. 1057. [bug] Reloading the server after adding a "file" clause to a zone statement could cause the server to crash due to a typo in change 1016. 1056. [bug] Rndc could catch an assertion failure on SIGINT due to an uninitialized variable. [RT #1908] 1055. [func] Version and hostname queries can now be disabled using "version none;" and "hostname none;", respectively. 1054. [bug] On Win32, cfg_categories and cfg_modules need to be exported from the libisccfg DLL. 1053. [bug] Dig did not increase its timeout when receiving AXFRs unless the +time option was used. [RT #1904] 1052. [bug] Journals were not being created in binary mode resulting in "journal format not recognized" error under Win32. [RT #1889] 1051. [bug] Do not ignore a network interface completely just because it has a noncontiguous netmask. Instead, omit it from the localnets ACL and issue a warning. [RT #1891] 1050. [bug] Log messages reporting malformed IP addresses in address lists such as that of the forwarders option failed to include the correct error code, file name, and line number. [RT #1890] 1049. [func] "pid-file none;" will disable writing a pid file. [RT #1848] 1048. [bug] Servers built with -DISC_MEM_USE_INTERNAL_MALLOC=1 didn't work. 1047. [bug] named was incorrectly refusing all requests signed with a TSIG key derived from an unsigned TKEY negotiation with a NOERROR response. [RT #1886] 1046. [bug] The help message for the --with-openssl configure option was inaccurate. [RT #1880] 1045. [bug] It was possible to skip saving glue for a nameserver for a stub zone. 1044. [bug] Specifying allow-transfer, notify-source, or notify-source-v6 in a stub zone was not treated as an error. 1043. [bug] Specifying a transfer-source or transfer-source-v6 option in the zone statement for a master zone was not treated as an error. [RT #1876] 1042. [bug] The "config" logging category did not work properly. [RT #1873] 1041. [bug] Dig/host/nslookup could catch an assertion failure on SIGINT due to an uninitialized variable. [RT #1867] 1040. [bug] Multiple listen-on-v6 options with different ports were not accepted. [RT #1875] 1039. [bug] Negative responses with CNAMEs in the answer section were cached incorrectly. [RT #1862] 1038. [bug] In servers configured with a tkey-domain option, TKEY queries with an owner name other than the root could cause an assertion failure. [RT #1866, #1869] 1037. [bug] Negative responses whose authority section contain SOA or NS records whose owner names are not equal equal to or parents of the query name should be rejected. [RT #1862] 1036. [func] Silently drop requests received via multicast as long as there is no final multicast DNS standard. 1035. [bug] If we respond to multicast queries (which we currently do not), respond from a unicast address as specified in RFC 1123. [RT #137] 1034. [bug] Ignore the RD bit on multicast queries as specified in RFC 1123. [RT #137] 1033. [bug] Always respond to requests with an unsupported opcode with NOTIMP, even if we don't have a matching view or cannot determine the class. 1032. [func] hostname.bind/txt/chaos now returns the name of the machine hosting the nameserver. This is useful in diagnosing problems with anycast servers. 1031. [bug] libbind.a: isc__gettimeofday() infinite recursion. [RT #1858] 1030. [bug] On systems with no resolv.conf file, nsupdate exited with an error rather than defaulting to using the loopback address. [RT #1836] 1029. [bug] Some named.conf errors did not cause the loading of the configuration file to return a failure status even though they were logged. [RT #1847] 1028. [bug] On Win32, dig/host/nslookup looked for resolv.conf in the wrong directory. [RT #1833] 1027. [bug] RRs having the reserved type 0 should be rejected. [RT #1471] 1026. [placeholder] 1025. [bug] Don't use multicast addresses to resolve iterative queries. [RT #101] 1024. [port] Compilation failed on HP-UX 11.11 due to incompatible use of the SIOCGLIFCONF macro name. [RT #1831] 1023. [func] Accept hints without TTLs. 1022. [bug] Don't report empty root hints as "extra data". [RT #1802] 1021. [bug] On Win32, log message timestamps were one month later than they should have been, and the server would exhibit unspecified behavior in December. 1020. [bug] IXFR log messages did not distinguish between true IXFRs, AXFR-style IXFRs, and mere version polls. [RT #1811] 1019. [bug] The value of the lame-ttl option was limited to 18000 seconds, not 1800 seconds as documented. [RT #1803] 1018. [bug] The default log channel was not always initialized correctly. [RT #1813] 1017. [bug] When specifying TSIG keys to dig and nsupdate using the -k option, they must be HMAC-MD5 keys. [RT #1810] 1016. [bug] Slave zones with no backup file were re-transferred on every server reload. 1015. [bug] Log channels that had a "versions" option but no "size" option failed to create numbered log files. [RT #1783] 1014. [bug] Some queries would cause statistics counters to increment more than once or not at all. [RT #1321] 1013. [bug] It was possible to cancel a query twice when marking a server as bogus or by having a blackhole acl. [RT #1776] 1012. [bug] The -p option to named did not behave as documented. 1011. [cleanup] Removed isc_dir_current(). 1010. [bug] The server could attempt to execute a command channel command after initiating server shutdown, causing an assertion failure. [RT #1766] 1009. [port] OpenUNIX 8 support. [RT #1728] 1008. [port] libtool.m4, ltmain.sh from libtool-1.4.2. 1007. [port] config.guess, config.sub from autoconf-2.52. 1006. [bug] If a KEY RR was found missing during DNSSEC validation, an assertion failure could subsequently be triggered in the resolver. [RT #1763] 1005. [bug] Don't copy nonzero RCODEs from request to response. [RT #1765] 1004. [port] Deal with recvfrom() returning EHOSTDOWN. [RT #1770] 1003. [func] Add the +retry option to dig. 1002. [bug] When reporting an unknown class name in named.conf, including the file name and line number. [RT #1759] 1001. [bug] win32 socket code doio_recv was not catching a WSACONNRESET error when a client was timing out the request and closing its socket. [RT #1745] 1000. [bug] BIND 8 compatibility: accept "HESIOD" as an alias for class "HS". [RT #1759] 999. [func] "rndc retransfer zone [class [view]]" added. [RT #1752] 998. [func] named-checkzone now has arguments to specify the chroot directory (-t) and working directory (-w). [RT #1755] 997. [func] Add support for RSA-SHA1 keys (RFC3110). 996. [func] Issue warning if the configuration filename contains the chroot path. 995. [bug] dig, host, nslookup: using a raw IPv6 address as a target address should be fatal on a IPv4 only system. 994. [func] Treat non-authoritative responses to queries for type NS as referrals even if the NS records are in the answer section, because BIND 8 servers incorrectly send them that way. This is necessary for DNSSEC validation of the NS records of a secure zone to succeed when the parent is a BIND 8 server. [RT #1706] 993. [func] dig: -v now reports the version. 992. [doc] dig: ~/.digrc is now documented. 991. [func] Lower UDP refresh timeout messages to level debug 1. 990. [bug] The rndc-confgen man page was not installed. 989. [bug] Report filename if $INCLUDE fails for file related errors. [RT #1736] 988. [bug] 'additional-from-auth no;' did not work reliably in the case of queries answered from the cache. [RT #1436] 987. [bug] "dig -help" didn't show "+[no]stats". 986. [bug] "dig +noall" failed to clear stats and command printing. 985. [func] Consider network interfaces to be up iff they have a nonzero IP address rather than based on the IFF_UP flag. [RT #1160] 984. [bug] Multi-threading should be enabled by default on Solaris 2.7 and newer, but it wasn't. 983. [func] The server now supports generating IXFR difference sequences for non-dynamic zones by comparing zone versions, when enabled using the new config option "ixfr-from-differences". [RT #1727] 982. [func] If "memstatistics-file" is set in options the memory statistics will be written to it. 981. [func] The dnssec tools can now take multiple '-r randomfile' arguments. 980. [bug] Incoming zone transfers restarting after an error could trigger an assertion failure. [RT #1692] 979. [func] Incremental master file dumping. dns_master_dumpinc(), dns_master_dumptostreaminc(), dns_dumpctx_attach(), dns_dumpctx_detach(), dns_dumpctx_cancel(), dns_dumpctx_db() and dns_dumpctx_version(). 978. [bug] dns_db_attachversion() had an invalid REQUIRE() condition. 977. [bug] Improve "not at top of zone" error message. 976. [func] named-checkconf can now test load master zones (named-checkconf -z). [RT #1468] 975. [bug] "max-cache-size default;" as a view option caused an assertion failure. 974. [bug] "max-cache-size unlimited;" as a global option was not accepted. 973. [bug] Failed to log the question name when logging: "bad zone transfer request: non-authoritative zone (NOTAUTH)". 972. [bug] The file modification time code in zone.c was using the wrong epoch. [RT #1667] 971. [placeholder] 970. [func] 'max-journal-size' can now be used to set a target size for a journal. 969. [func] dig now supports the undocumented dig 8 feature of allowing arbitrary labels, not just dotted decimal quads, with the -x option. This can be used to conveniently look up RFC2317 names as in "dig -x 10.0.0.0-127". [RT #827, #1576, #1598] 968. [bug] On win32, the isc_time_now() function was unnecessarily calling strtime(). [RT #1671] 967. [bug] On win32, the link for bindevt was not including the required resource file to enable the event viewer to interpret the error messages in the event log, [RT #1668] 966. [placeholder] 965. [bug] Including data other than root server NS and A records in the root hint file could cause a rbtdb node reference leak. [RT #1581, #1618] 964. [func] Warn if data other than root server NS and A records are found in the root hint file. [RT #1581, #1618] 963. [bug] Bad ISC_LANG_ENDDECLS. [RT #1645] 962. [bug] libbind: bad "#undef", don't attempt to install non-existent nlist.h. [RT #1640] 961. [bug] Tried to use a IPV6 feature when ISC_PLATFORM_HAVEIPV6 was not defined. [RT #1482] 960. [port] liblwres failed to build on systems with support for getrrsetbyname() in the OS. [RT #1592] 959. [port] On FreeBSD, determine the number of CPUs by calling sysctlbyname(). [RT #1584] 958. [port] ssize_t is not available on all platforms. [RT #1607] 957. [bug] sys/select.h inclusion was broken on older platforms. [RT #1607] 956. [bug] ns_g_autorndcfile changed to ns_g_keyfile in named/win32/os.c due to code changes in change #953. win32 .make file for rndc-confgen updated to add include path for os.h header. --- 9.2.0rc1 released --- 955. [bug] When using views, the zone's class was not being inherited from the view's class. [RT #1583] 954. [bug] When requesting AXFRs or IXFRs using dig, host, or nslookup, the RD bit should not be set as zone transfers are inherently non-recursive. [RT #1575] 953. [func] The /var/run/named.key file from change #843 has been replaced by /etc/rndc.key. Both named and rndc will look for this file and use it to configure a default control channel key if not already configured using a different method (rndc.conf / controls). Unlike named.key, rndc.key is not created automatically; it must be created by manually running "rndc-confgen -a". 952. [bug] The server required manual intervention to serve the affected zones if it died between creating a journal and committing the first change to it. 951. [bug] CFLAGS was not passed to the linker when linking some of the test programs under bin/tests. [RT #1555]. 950. [bug] Explicit TTLs did not properly override $TTL due to a bug in change 834. [RT #1558] 949. [bug] host was unable to print records larger than 512 bytes. [RT #1557] --- 9.2.0b2 released --- 948. [port] Integrated support for building on Windows NT / Windows 2000. 947. [bug] dns_rdata_soa_t had a badly named element "mname" which was really the RNAME field from RFC1035. To avoid confusion and silent errors that would occur it the "origin" and "mname" elements were given their correct names "mname" and "rname" respectively, the "mname" element is renamed to "contact". 946. [cleanup] doc/misc/options is now machine-generated from the configuration parser syntax tables, and therefore more likely to be correct. 945. [func] Add the new view-specific options "match-destinations" and "match-recursive-only". 944. [func] Check for expired signatures on load. 943. [bug] The server could crash when receiving a command via rndc if the configuration file listed only nonexistent keys in the controls statement. [RT #1530] 942. [port] libbind: GETNETBYADDR_ADDR_T was not correctly defined on some platforms. 941. [bug] The configuration checker crashed if a slave zone didn't contain a masters statement. [RT #1514] 940. [bug] Double zone locking failure on error path. [RT #1510] --- 9.2.0b1 released --- 939. [port] Add the --disable-linux-caps option to configure for systems that manage capabilities outside of named. [RT #1503] 938. [placeholder] 937. [bug] A race when shutting down a zone could trigger a INSIST() failure. [RT #1034] 936. [func] Warn about IPv4 addresses that are not complete dotted quads. [RT #1084] 935. [bug] inet_pton failed to reject leading zeros. 934. [port] Deal with systems where accept() spuriously returns ECONNRESET. 933. [bug] configure failed doing libbind on platforms not supported by BIND 8. [RT #1496] --- 9.2.0a3 released --- 932. [bug] Use INSTALL_SCRIPT, not INSTALL_PROGRAM, when installing isc-config.sh. [RT #198, #1466] 931. [bug] The controls statement only attempted to verify messages using the first key in the key list. (9.2.0a1/a2 only). 930. [func] Query performance testing tool added as contrib/queryperf. 929. [placeholder] 928. [bug] nsupdate would send empty update packets if the send (or empty line) command was run after another send but before any new updates or prerequisites were specified. It should simply ignore this command. 927. [bug] Don't hold the zone lock for the entire dump to disk. [RT #1423] 926. [bug] The resolver could deadlock with the ADB when shutting down (multi-threaded builds only). [RT #1324] 925. [cleanup] Remove openssl from the distribution; require that --with-openssl be specified if DNSSEC is needed. 924. [port] Extend support for pre-RFC2133 IPv6 implementation. [RT #987] 923. [bug] Multiline TSIG secrets (and other multiline strings) were not accepted in named.conf. [RT #1469] 922. [func] Added two new lwres_getrrsetbyname() result codes, ERR_NONAME and ERR_NODATA. 921. [bug] lwres returned an incorrect error code if it received a truncated message. 920. [func] Increase the lwres receive buffer size to 16K. [RT #1451] 919. [placeholder] 918. [func] In nsupdate, TSIG errors are no longer treated as fatal errors. 917. [func] New nsupdate command 'key', allowing TSIG keys to be specified in the nsupdate command stream rather than the command line. 916. [bug] Specifying type ixfr to dig without specifying a serial number failed in unexpected ways. 915. [func] The named-checkconf and named-checkzone programs now have a '-v' option for printing their version. [RT #1151] 914. [bug] Global 'server' statements were rejected when using views, even though they were accepted in 9.1. [RT #1368] 913. [bug] Cache cleaning was not sufficiently aggressive. [RT #1441, #1444] 912. [bug] Attempts to set the 'additional-from-cache' or 'additional-from-auth' option to 'no' in a server with recursion enabled will now be ignored and cause a warning message. [RT #1145] 911. [placeholder] 910. [port] Some pre-RFC2133 IPv6 implementations do not define IN6ADDR_ANY_INIT. [RT #1416] 909. [placeholder] 908. [func] New program, rndc-confgen, to simplify setting up rndc. 907. [func] The ability to get entropy from either the random device, a user-provided file or from the keyboard was migrated from the DNSSEC tools to libisc as isc_entropy_usebestsource(). 906. [port] Separated the system independent portion of lib/isc/unix/entropy.c into lib/isc/entropy.c and added lib/isc/win32/entropy.c. 905. [bug] Configuring a forward "zone" for the root domain did not work. [RT #1418] 904. [bug] The server would leak memory if attempting to use an expired TSIG key. [RT #1406] 903. [bug] dig should not crash when receiving a TCP packet of length 0. 902. [bug] The -d option was ignored if both -t and -g were also specified. 901. [placeholder] 900. [bug] A config.guess update changed the system identification string of FreeBSD systems; configure and bin/tests/system/ifconfig.sh now recognize the new string. --- 9.2.0a2 released --- 899. [bug] lib/dns/soa.c failed to compile on many platforms due to inappropriate use of a void value. [RT #1372, #1373, #1386, #1387, #1395] 898. [bug] "dig" failed to set a nonzero exit status on UDP query timeout. [RT #1323] 897. [bug] A config.guess update changed the system identification string of UnixWare systems; configure now recognizes the new string. 896. [bug] If a configuration file is set on named's command line and it has a relative pathname, the current directory (after any possible jailing resulting from named -t) will be prepended to it so that reloading works properly even when a directory option is present. 895. [func] New function, isc_dir_current(), akin to POSIX's getcwd(). 894. [bug] When using the DNSSEC tools, a message intended to warn when the keyboard was being used because of the lack of a suitable random device was not being printed. 893. [func] Removed isc_file_test() and added isc_file_exists() for the basic functionality that was being added with isc_file_test(). 892. [placeholder] 891. [bug] Return an error when a SIG(0) signed response to an unsigned query is seen. This should actually do the verification, but it's not currently possible. [RT #1391] 890. [cleanup] The man pages no longer require the mandoc macros and should now format cleanly using most versions of nroff, and HTML versions of the man pages have been added. Both are generated from DocBook source. 889. [port] Eliminated blank lines before .TH in nroff man pages since they cause problems with some versions of nroff. [RT #1390] 888. [bug] Don't die when using TKEY to delete a nonexistent TSIG key. [RT #1392] 887. [port] Detect broken compilers that can't call static functions from inline functions. [RT #1212] 886. [placeholder] 885. [placeholder] 884. [placeholder] 883. [placeholder] 882. [placeholder] 881. [placeholder] 880. [placeholder] 879. [placeholder] 878. [placeholder] 877. [placeholder] 876. [placeholder] 875. [placeholder] 874. [placeholder] 873. [placeholder] 872. [placeholder] 871. [placeholder] 870. [placeholder] 869. [placeholder] 868. [placeholder] 867. [placeholder] 866. [func] Close debug only file channels when debug is set to zero. [RT #1246] 865. [bug] The new configuration parser did not allow the optional debug level in a "severity debug" clause of a logging channel to be omitted. This is now allowed and treated as "severity debug 1;" like it does in BIND 8.2.4, not as "severity debug 0;" like it did in BIND 9.1. [RT #1367] 864. [cleanup] Multi-threading is now enabled by default on OSF1, Solaris 2.7 and newer, AIX, IRIX, and HP-UX. 863. [bug] If an error occurred while an outgoing zone transfer was starting up, the server could access a domain name that had already been freed when logging a message saying that the transfer was starting. [RT #1383] 862. [bug] Use after realloc(), non portable pointer arithmetic in grmerge(). 861. [port] Add support for Mac OS X, by making it equivalent to Darwin. This was derived from the config.guess file shipped with Mac OS X. [RT #1355] 860. [func] Drop cross class glue in zone transfers. 859. [bug] Cache cleaning now won't swamp the CPU if there is a persistent over limit condition. 858. [func] isc_mem_setwater() no longer requires that when the callback function is non-NULL then its hi_water argument must be greater than its lo_water argument (they can now be equal) or that they be non-zero. 857. [cleanup] Use ISC_MAGIC() to define all magic numbers for structs, for our friends in EBCDIC-land. 856. [func] Allow partial rdatasets to be returned in answer and authority sections to help non-TCP capable clients recover from truncation. [RT #1301] 855. [bug] Stop spurious "using RFC 1035 TTL semantics" warnings. 854. [bug] The config parser didn't properly handle config options that were specified in units of time other than seconds. [RT #1372] 853. [bug] configure_view_acl() failed to detach existing acls. [RT #1374] 852. [bug] Handle responses from servers which do not know about IXFR. 851. [cleanup] The obsolete support-ixfr option was not properly ignored. --- 9.2.0a1 released --- 850. [bug] dns_rbt_findnode() would not find nodes that were split on a bitstring label somewhere other than in the last label of the node. [RT #1351] 849. [func] will ensure INADDR_LOOPBACK is defined. 848. [func] A minimum max-cache-size of two megabytes is enforced by the cache cleaner. 847. [func] Added isc_file_test(), which currently only has some very basic functionality to test for the existence of a file, whether a pathname is absolute, or whether a pathname is the fundamental representation of the current directory. It is intended that this function can be expanded to test other things a programmer might want to know about a file. 846. [func] A non-zero 'param' to dst_key_generate() when making an hmac-md5 key means that good entropy is not required. 845. [bug] The access rights on the public file of a symmetric key are now restricted as soon as the file is opened, rather than after it has been written and closed. 844. [func] will ensure INADDR_LOOPBACK is defined, just as does. 843. [func] If no controls statement is present in named.conf, or if any inet phrase of a controls statement is lacking a keys clause, then a key will be automatically generated by named and an rndc.conf-style file named named.key will be written that uses it. rndc will use this file only if its normal configuration file, or one provided on the command line, does not exist. 842. [func] 'rndc flush' now takes an optional view. 841. [bug] When sdb modules were not declared threadsafe, their create and destroy functions were not serialized. 840. [bug] The config file parser could print the wrong file name if an error was detected after an included file was parsed. [RT #1353] 839. [func] Dump packets for which there was no view or that the class could not be determined to category "unmatched". 838. [port] UnixWare 7.x.x is now suported by bin/tests/system/ifconfig.sh. 837. [cleanup] Multi-threading is now enabled by default only on OSF1, Solaris 2.7 and newer, and AIX. 836. [func] Upgraded libtool to 1.4. 835. [bug] The dispatcher could enter a busy loop if it got an I/O error receiving on a UDP socket. [RT #1293] 834. [func] Accept (but warn about) master files beginning with an SOA record without an explicit TTL field and lacking a $TTL directive, by using the SOA MINTTL as a default TTL. This is for backwards compatibility with old versions of BIND 8, which accepted such files without warning although they are illegal according to RFC1035. 833. [cleanup] Moved dns_soa_*() from to , and extended them to support all the integer-valued fields of the SOA RR. 832. [bug] The default location for named.conf in named-checkconf should depend on --sysconfdir like it does in named. [RT #1258] 831. [placeholder] 830. [func] Implement 'rndc status'. 829. [bug] The DNS_R_ZONECUT result code should only be returned when an ANY query is made with DNS_DBFIND_GLUEOK set. In all other ANY query cases, returning the delegation is better. 828. [bug] The errno value from recvfrom() could be overwritten by logging code. [RT #1293] 827. [bug] When an IXFR protocol error occurs, the slave should retry with AXFR. 826. [bug] Some IXFR protocol errors were not detected. 825. [bug] zone.c:ns_query() detached from the wrong zone reference. [RT #1264] 824. [bug] Correct line numbers reported by dns_master_load(). [RT #1263] 823. [func] The output of "dig -h" now goes to stdout so that it can easily be piped through "more". [RT #1254] 822. [bug] Sending nxrrset prerequisites would crash nsupdate. [RT #1248] 821. [bug] The program name used when logging to syslog should be stripped of leading path components. [RT #1178, #1232] 820. [bug] Name server address lookups failed to follow A6 chains into the glue of local authoritative zones. 819. [bug] In certain cases, the resolver's attempts to restart an address lookup at the root could cause the fetch to deadlock (with itself) instead of restarting. [RT #1225] 818. [bug] Certain pathological responses to ANY queries could cause an assertion failure. [RT #1218] 817. [func] Adjust timeouts for dialup zone queries. 816. [bug] Report potential problems with log file accessibility at configuration time, since such problems can't reliably be reported at the time they actually occur. 815. [bug] If a log file was specified with a path separator character (i.e. "/") in its name and the directory did not exist, the log file's name was treated as though it were the directory name. [RT #1189] 814. [bug] Socket objects left over from accept() failures were incorrectly destroyed, causing corruption of socket manager data structures. 813. [bug] File descriptors exceeding FD_SETSIZE were handled badly. [RT #1192] 812. [bug] dig sometimes printed incomplete IXFR responses due to an uninitialized variable. [RT #1188] 811. [bug] Parentheses were not quoted in zone dumps. [RT #1194] 810. [bug] The signer name in SIG records was not properly down-cased when signing/verifying records. [RT #1186] 809. [bug] Configuring a non-local address as a transfer-source could cause an assertion failure during load. 808. [func] Add 'rndc flush' to flush the server's cache. 807. [bug] When setting up TCP connections for incoming zone transfers, the transfer-source port was not ignored like it should be. 806. [bug] DNS_R_SEENINCLUDE was failing to propagate back up the calling stack to the zone maintenance level, causing zones to not reload when an included file was touched but the top-level zone file was not. 805. [bug] When using "forward only", missing root hints should not cause queries to fail. [RT #1143] 804. [bug] Attempting to obtain entropy could fail in some situations. This would be most common on systems with user-space threads. [RT #1131] 803. [bug] Treat all SIG queries as if they have the CD bit set, otherwise no data will be returned [RT #749] 802. [bug] DNSSEC key tags were computed incorrectly in almost all cases. [RT #1146] 801. [bug] nsupdate should treat lines beginning with ';' as comments. [RT #1139] 800. [bug] dnssec-signzone produced incorrect statistics for large zones. [RT #1133] 799. [bug] The ADB didn't find AAAA glue in a zone unless A6 glue was also present. 798. [bug] nsupdate should be able to reject bad input lines and continue. [RT #1130] 797. [func] Issue a warning if the 'directory' option contains a relative path. [RT #269] 796. [func] When a size limit is associated with a log file, only roll it when the size is reached, not every time the log file is opened. [RT #1096] 795. [func] Add the +multiline option to dig. [RT #1095] 794. [func] Implement the "port" and "default-port" statements in rndc.conf. 793. [cleanup] The DNSSEC tools could create filenames that were illegal or contained shell meta-characters. They now use a different text encoding of names that doesn't have these problems. [RT #1101] 792. [cleanup] Replace the OMAPI command channel protocol with a simpler one. 791. [bug] The command channel now works over IPv6. 790. [bug] Wildcards created using dynamic update or IXFR could fail to match. [RT #1111] 789. [bug] The "localhost" and "localnets" ACLs did not match when used as the second element of a two-element sortlist item. 788. [func] Add the "match-mapped-addresses" option, which causes IPv6 v4mapped addresses to be treated as IPv4 addresses for the purpose of acl matching. 787. [bug] The DNSSEC tools failed to downcase domain names when mapping them into file names. 786. [bug] When DNSSEC signing/verifying data, owner names were not properly down-cased. 785. [bug] A race condition in the resolver could cause an assertion failure. [RT #673, #872, #1048] 784. [bug] nsupdate and other programs would not quit properly if some signals were blocked by the caller. [RT #1081] 783. [bug] Following CNAMEs could cause an assertion failure when either using an sdb database or under very rare conditions. 782. [func] Implement the "serial-query-rate" option. 781. [func] Avoid error packet loops by dropping duplicate FORMERR responses. [RT #1006] 780. [bug] Error handling code dealing with out of memory or other rare errors could lead to assertion failures by calling functions on uninitialized names. [RT #1065] 779. [func] Added the "minimal-responses" option. 778. [bug] When starting cache cleaning, cleaning_timer_action() returned without first pausing the iterator, which could cause deadlock. [RT #998] 777. [bug] An empty forwarders list in a zone failed to override global forwarders. [RT #995] 776. [func] Improved error reporting in denied messages. [RT #252] 775. [placeholder] 774. [func] max-cache-size is implemented. 773. [func] Added isc_rwlock_trylock() to attempt to lock without blocking. 772. [bug] Owner names could be incorrectly omitted from cache dumps in the presence of negative caching entries. [RT #991] 771. [cleanup] TSIG errors related to unsynchronized clocks are logged better. [RT #919] 770. [func] Add the "edns yes_or_no" statement to the server clause. [RT #524] 769. [func] Improved error reporting when parsing rdata. [RT #740] 768. [bug] The server did not emit an SOA when a CNAME or DNAME chain ended in NXDOMAIN in an authoritative zone. 767. [placeholder] 766. [bug] A few cases in query_find() could leak fname. This would trigger the mpctx->allocated == 0 assertion when the server exited. [RT #739, #776, #798, #812, #818, #821, #845, #892, #935, #966] 765. [func] ACL names are once again case insensitive, like in BIND 8. [RT #252] 764. [func] Configuration files now allow "include" directives in more places, such as inside the "view" statement. [RT #377, #728, #860] 763. [func] Configuration files no longer have reserved words. [RT #731, #753] 762. [cleanup] The named.conf and rndc.conf file parsers have been completely rewritten. 761. [bug] _REENTRANT was still defined when building with --disable-threads. 760. [contrib] Significant enhancements to the pgsql sdb driver. 759. [bug] The resolver didn't turn off "avoid fetches" mode when restarting, possibly causing resolution to fail when it should not. This bug only affected platforms which support both IPv4 and IPv6. [RT #927] 758. [bug] The "avoid fetches" code did not treat negative cache entries correctly, causing fetches that would be useful to be avoided. This bug only affected platforms which support both IPv4 and IPv6. [RT #927] 757. [func] Log zone transfers. 756. [bug] dns_zone_load() could "return" success when no master file was configured. 755. [bug] Fix incorrectly formatted log messages in zone.c. 754. [bug] Certain failure conditions sending UDP packets could cause the server to retry the transmission indefinitely. [RT #902] 753. [bug] dig, host, and nslookup would fail to contact a remote server if getaddrinfo() returned an IPv6 address on a system that doesn't support IPv6. [RT #917] 752. [func] Correct bad tv_usec elements returned by gettimeofday(). 751. [func] Log successful zone loads / transfers. [RT #898] 750. [bug] A query should not match a DNAME whose trust level is pending. [RT #916] 749. [bug] When a query matched a DNAME in a secure zone, the server did not return the signature of the DNAME. [RT #915] 748. [doc] List supported RFCs in doc/misc/rfc-compliance. [RT #781] 747. [bug] The code to determine whether an IXFR was possible did not properly check for a database that could not have a journal. [RT #865, #908] 746. [bug] The sdb didn't clone rdatasets properly, causing a crash when the server followed delegations. [RT #905] 745. [func] Report the owner name of records that fail semantic checks while loading. 744. [bug] When returning DNS_R_CNAME or DNS_R_DNAME as the result of an ANY or SIG query, the resolver failed to setup the return event's rdatasets, causing an assertion failure in the query code. [RT #881] 743. [bug] Receiving a large number of certain malformed answers could cause named to stop responding. [RT #861] 742. [placeholder] 741. [port] Support openssl-engine. [RT #709] 740. [port] Handle openssl library mismatches slightly better. 739. [port] Look for /dev/random in configure, rather than assuming it will be there for only a predefined set of OSes. 738. [bug] If a non-threadsafe sdb driver supported AXFR and received an AXFR request, it would deadlock or die with an assertion failure. [RT #852] 737. [port] stdtime.c failed to compile on certain platforms. 736. [func] New functions isc_task_{begin,end}exclusive(). 735. [doc] Add BIND 4 migration notes. 734. [bug] An attempt to re-lock the zone lock could occur if the server was shutdown during a zone transfer. [RT #830] 733. [bug] Reference counts of dns_acl_t objects need to be locked but were not. [RT #801, #821] 732. [bug] Glue with 0 TTL could also cause SERVFAIL. [RT #828] 731. [bug] Certain zone errors could cause named-checkzone to fail ungracefully. [RT #819] 730. [bug] lwres_getaddrinfo() returns the correct result when it fails to contact a server. [RT #768] 729. [port] pthread_setconcurrency() needs to be called on Solaris. 728. [bug] Fix comment processing on master file directives. [RT# 757] 727. [port] Work around OS bug where accept() succeeds but fails to fill in the peer address of the accepted connection, by treating it as an error rather than an assertion failure. [RT #809] 726. [func] Implement the "trace" and "notrace" commands in rndc. 725. [bug] Installing man pages could fail. 724. [func] New libisc functions isc_netaddr_any(), isc_netaddr_any6(). 723. [bug] Referrals whose NS RRs had a 0 TTL caused the resolver to return DNS_R_SERVFAIL. [RT #783] 722. [func] Allow incremental loads to be canceled. 721. [cleanup] Load manager and dns_master_loadfilequota() are no more. 720. [bug] Server could enter infinite loop in dispatch.c:do_cancel(). [RT #733] 719. [bug] Rapid reloads could trigger an assertion failure. [RT #743, #763] 718. [cleanup] "internal" is no longer a reserved word in named.conf. [RT #753, #731] 717. [bug] Certain TKEY processing failure modes could reference an uninitialized variable, causing the server to crash. [RT #750] 716. [bug] The first line of a $INCLUDE master file was lost if an origin was specified. [RT #744] 715. [bug] Resolving some A6 chains could cause an assertion failure in adb.c. [RT #738] 714. [bug] Preserve interval timers across reloads unless changed. [RT# 729] 713. [func] named-checkconf takes '-t directory' similar to named. [RT #726] 712. [bug] Sending a large signed update message caused an assertion failure. [RT #718] 711. [bug] The libisc and liblwres implementations of inet_ntop contained an off by one error. 710. [func] The forwarders statement now takes an optional port. [RT #418] 709. [bug] ANY or SIG queries for data with a TTL of 0 would return SERVFAIL. [RT #620] 708. [bug] When building with --with-openssl, the openssl headers included with BIND 9 should not be used. [RT #702] 707. [func] The "filename" argument to named-checkzone is no longer optional, to reduce confusion. [RT #612] 706. [bug] Zones with an explicit "allow-update { none; };" were considered dynamic and therefore not reloaded on SIGHUP or "rndc reload". 705. [port] Work out resource limit type for use where rlim_t is not available. [RT #695] 704. [port] RLIMIT_NOFILE is not available on all platforms. [RT #695] 703. [port] sys/select.h is needed on older platforms. [RT #695] 702. [func] If the address 0.0.0.0 is seen in resolv.conf, use 127.0.0.1 instead. [RT #693] 701. [func] Root hints are now fully optional. Class IN views use compiled-in hints by default, as before. Non-IN views with no root hints now provide authoritative service but not recursion. A warning is logged if a view has neither root hints nor authoritative data for the root. [RT #696] 700. [bug] $GENERATE range check was wrong. [RT #688] 699. [bug] The lexer mishandled empty quoted strings. [RT #694] 698. [bug] Aborting nsupdate with ^C would lead to several race conditions. 697. [bug] nsupdate was not compatible with the undocumented BIND 8 behavior of ignoring TTLs in "update delete" commands. [RT #693] 696. [bug] lwresd would die with an assertion failure when passed a zero-length name. [RT #692] 695. [bug] If the resolver attempted to query a blackholed or bogus server, the resolution would fail immediately. 694. [bug] $GENERATE did not produce the last entry. [RT #682, #683] 693. [bug] An empty lwres statement in named.conf caused the server to crash while loading. 692. [bug] Deal with systems that have getaddrinfo() but not gai_strerror(). [RT #679] 691. [bug] Configuring per-view forwarders caused an assertion failure. [RT #675, #734] 690. [func] $GENERATE now supports DNAME. [RT #654] 689. [doc] man pages are now installed. [RT #210] 688. [func] "make tags" now works on systems with the "Exuberant Ctags" etags. 687. [bug] Only say we have IPv6, with sufficient functionality, if it has actually been tested. [RT #586] 686. [bug] dig and nslookup can now be properly aborted during blocking operations. [RT #568] 685. [bug] nslookup should use the search list/domain options from resolv.conf by default. [RT #405, #630] 684. [bug] Memory leak with view forwarders. [RT #656] 683. [bug] File descriptor leak in isc_lex_openfile(). 682. [bug] nslookup displayed SOA records incorrectly. [RT #665] 681. [bug] $GENERATE specifying output format was broken. [RT #653] 680. [bug] dns_rdata_fromstruct() mishandled options bigger than 255 octets. 679. [bug] $INCLUDE could leak memory and file descriptors on reload. [RT #639] 678. [bug] "transfer-format one-answer;" could trigger an assertion failure. [RT #646] 677. [bug] dnssec-signzone would occasionally use the wrong ttl for database operations and fail. [RT #643] 676. [bug] Log messages about lame servers to category 'lame-servers' rather than 'resolver', so as not to be gratuitously incompatible with BIND 8. 675. [bug] TKEY queries could cause the server to leak memory. 674. [func] Allow messages to be TSIG signed / verified using a offset from the current time. 673. [func] The server can now convert RFC1886-style recursive lookup requests into RFC2874-style lookups, when enabled using the new option "allow-v6-synthesis". 672. [bug] The wrong time was in the "time signed" field when replying with BADTIME error. 671. [bug] The message code was failing to parse a message with no question section and a TSIG record. [RT #628] 670. [bug] The lwres replacements for getaddrinfo and getipnodebyname didn't properly check for the existence of the sockaddr sa_len field. 669. [bug] dnssec-keygen now makes the public key file non-world-readable for symmetric keys. [RT #403] 668. [func] named-checkzone now reports multiple errors in master files. 667. [bug] On Linux, running named with the -u option and a non-world-readable configuration file didn't work. [RT #626] 666. [bug] If a request sent by dig is longer than 512 bytes, use TCP. 665. [bug] Signed responses were not sent when the size of the TSIG + question exceeded the maximum message size. [RT #628] 664. [bug] The t_tasks and t_timers module tests are now skipped when building without threads, since they require threads. 663. [func] Accept a size_spec, not just an integer, in the (unimplemented and ignored) max-ixfr-log-size option for compatibility with recent versions of BIND 8. [RT #613] 662. [bug] dns_rdata_fromtext() failed to log certain errors. 661. [bug] Certain UDP IXFR requests caused an assertion failure (mpctx->allocated == 0). [RT #355, #394, #623] 660. [port] Detect multiple CPUs on HP-UX and IRIX. 659. [performance] Rewrite the name compression code to be much faster. 658. [cleanup] Remove all vestiges of 16 bit global compression. 657. [bug] When a listen-on statement in an lwres block does not specify a port, use 921, not 53. Also update the listen-on documentation. [RT #616] 656. [func] Treat an unescaped newline in a quoted string as an error. This means that TXT records with missing close quotes should have meaningful errors printed. 655. [bug] Improve error reporting on unexpected eof when loading zones. [RT #611] 654. [bug] Origin was being forgotten in TCP retries in dig. [RT #574] 653. [bug] +defname option in dig was reversed in sense. [RT #549] 652. [bug] zone_saveunique() did not report the new name. 651. [func] The AD bit in responses now has the meaning specified in . 650. [bug] SIG(0) records were being generated and verified incorrectly. [RT #606] 649. [bug] It was possible to join to an already running fctx after it had "cloned" its events, but before it sent them. In this case, the event of the newly joined fetch would not contain the answer, and would trigger the INSIST() in fctx_sendevents(). In BIND 9.0, this bug did not trigger an INSIST(), but caused the fetch to fail with a SERVFAIL result. [RT #588, #597, #605, #607] 648. [port] Add support for pre-RFC2133 IPv6 implementations. 647. [bug] Resolver queries sent after following multiple referrals had excessively long retransmission timeouts due to incorrectly counting the referrals as "restarts". 646. [bug] The UnixWare ISC_PLATFORM_FIXIN6INADDR fix in isc/net.h didn't _cleanly_ fix the problem it was trying to fix. 645. [port] BSD/OS 3.0 needs pthread_init(). [RT #603] 644. [bug] #622 needed more work. [RT #562] 643. [bug] xfrin error messages made more verbose, added class of the zone. [RT# 599] 642. [bug] Break the exit_check() race in the zone module. [RT #598] --- 9.1.0b2 released --- 641. [bug] $GENERATE caused a uninitialized link to be used. [RT #595] 640. [bug] Memory leak in error path could cause "mpctx->allocated == 0" failure. [RT #584] 639. [bug] Reading entropy from the keyboard would sometimes fail. [RT #591] 638. [port] lib/isc/random.c needed to explicitly include time.h to get a prototype for time() when pthreads was not being used. [RT #592] 637. [port] Use isc_u?int64_t instead of (unsigned) long long in lib/isc/print.c. Also allow lib/isc/print.c to be compiled even if the platform does not need it. [RT #592] 636. [port] Shut up MSVC++ about a possible loss of precision in the ISC__BUFFER_PUTUINT*() macros. [RT #592] 635. [bug] Reloading a server with a configured blackhole list would cause an assertion. [RT #590] 634. [bug] A log file will completely stop being written when it reaches the maximum size in all cases, not just when versioning is also enabled. [RT #570] 633. [port] Cope with rlim_t missing on BSD/OS systems. [RT #575] 632. [bug] The index array of the journal file was corrupted as it was written to disk. 631. [port] Build without thread support on systems without pthreads. 630. [bug] Locking failure in zone code. [RT #582] 629. [bug] 9.1.0b1 dereferenced a null pointer and crashed when responding to a UDP IXFR request. 628. [bug] If the root hints contained only AAAA addresses, named would be unable to perform resolution. 627. [bug] The EDNS0 blackhole detection code of change 324 waited for three retransmissions to each server, which takes much too long when a domain has many name servers and all of them drop EDNS0 queries. Now we retry without EDNS0 after three consecutive timeouts, even if they are all from different servers. [RT #143] 626. [bug] The lightweight resolver daemon no longer crashes when asked for a SIG rrset. [RT #558] 625. [func] Zones now inherit their class from the enclosing view. 624. [bug] The zone object could get timer events after it had been destroyed, causing a server crash. [RT #571] 623. [func] Added "named-checkconf" and "named-checkzone" program for syntax checking named.conf files and zone files, respectively. 622. [bug] A canceled request could be destroyed before dns_request_destroy() was called. [RT #562] 621. [port] Disable IPv6 at runtime if IPv6 sockets are unusable. This mostly affects Red Hat Linux 7.0, which has conflicts between libc and the kernel. 620. [bug] dns_master_load*inc() now require 'task' and 'load' to be non-null. Also 'done' will not be called if dns_master_load*inc() fails immediately. [RT #565] 619. [placeholder] 618. [bug] Queries to a signed zone could sometimes cause an assertion failure. 617. [bug] When using dynamic update to add a new RR to an existing RRset with a different TTL, the journal entries generated from the update did not include explicit deletions and re-additions of the existing RRs to update their TTL to the new value. 616. [func] dnssec-signzone -t output now includes performance statistics. 615. [bug] dnssec-signzone did not like child keysets signed by multiple keys. 614. [bug] Checks for uninitialized link fields were prone to false positives, causing assertion failures. The checks are now disabled by default and may be re-enabled by defining ISC_LIST_CHECKINIT. 613. [bug] "rndc reload zone" now reloads primary zones. It previously only updated slave and stub zones, if an SOA query indicated an out of date serial. 612. [cleanup] Shutup a ridiculously noisy HP-UX compiler that complains relentlessly about how its treatment of 'const' has changed as well as how casting sometimes tightens alignment constraints. 611. [func] allow-notify can be used to permit processing of notify messages from hosts other than a slave's masters. 610. [func] rndc dumpdb is now supported. 609. [bug] getrrsetbyname() would crash lwresd if the server found more SIGs than answers. [RT #554] 608. [func] dnssec-signzone now adds a comment to the zone with the time the file was signed. 607. [bug] nsupdate would fail if it encountered a CNAME or DNAME in a response to an SOA query. [RT #515] 606. [bug] Compiling with --disable-threads failed due to isc_thread_self() being incorrectly defined as an integer rather than a function. 605. [func] New function isc_lex_getlasttokentext(). 604. [bug] The named.conf parser could print incorrect line numbers when long comments were present. 603. [bug] Make dig handle multiple types or classes on the same query more correctly. 602. [func] Cope automatically with UnixWare's broken IN6_IS_ADDR_* macros. [RT #539] 601. [func] Return a non-zero exit code if an update fails in nsupdate. 600. [bug] Reverse lookups sometimes failed in dig, etc... 599. [func] Added four new functions to the libisc log API to support i18n messages. isc_log_iwrite(), isc_log_ivwrite(), isc_log_iwrite1() and isc_log_ivwrite1() were added. 598. [bug] An update-policy statement would cause the server to assert while loading. [RT #536] 597. [func] dnssec-signzone is now multi-threaded. 596. [bug] DNS_RDATASLAB_FORCE and DNS_RDATASLAB_EXACT are not mutually exclusive. 595. [port] On Linux 2.2, socket() returns EINVAL when it should return EAFNOSUPPORT. Work around this. [RT #531] 594. [func] sdb drivers are now assumed to not be thread-safe unless the DNS_SDBFLAG_THREADSAFE flag is supplied. 593. [bug] If a secure zone was missing all its NXTs and a dynamic update was attempted, the server entered an infinite loop. 592. [bug] The sig-validity-interval option now specifies a number of days, not seconds. This matches the documentation. [RT #529] --- 9.1.0b1 released --- 591. [bug] Work around non-reentrancy in openssl by disabling pre-computation in keys. 590. [doc] There are now man pages for the lwres library in doc/man/lwres. 589. [bug] The server could deadlock if a zone was updated while being transferred out. 588. [bug] ctx->in_use was not being correctly initialized when when pushing a file for $INCLUDE. [RT #523] 587. [func] A warning is now printed if the "allow-update" option allows updates based on the source IP address, to alert users to the fact that this is insecure and becoming increasingly so as servers capable of update forwarding are being deployed. 586. [bug] multiple views with the same name were fatal. [RT #516] 585. [func] dns_db_addrdataset() and and dns_rdataslab_merge() now support 'exact' additions in a similar manner to dns_db_subtractrdataset() and dns_rdataslab_subtract(). 584. [func] You can now say 'notify explicit'; to suppress notification of the servers listed in NS records and notify only those servers listed in the 'also-notify' option. 583. [func] "rndc querylog" will now toggle logging of queries, like "ndc querylog" in BIND 8. 582. [bug] dns_zone_idetach() failed to lock the zone. [RT #199, #463] 581. [bug] log severity was not being correctly processed. [RT #485] 580. [func] Ignore trailing garbage on incoming DNS packets, for interoperability with broken server implementations. [RT #491] 579. [bug] nsupdate did not take a filename to read update from. [RT #492] 578. [func] New config option "notify-source", to specify the source address for notify messages. 577. [func] Log illegal RDATA combinations. e.g. multiple singleton types, cname and other data. 576. [doc] isc_log_create() description did not match reality. 575. [bug] isc_log_create() was not setting internal state correctly to reflect the default channels created. 574. [bug] TSIG signed queries sent by the resolver would fail to have their responses validated and would leak memory. 573. [bug] The journal files of IXFRed slave zones were inadvertently discarded on server reload, causing "journal out of sync with zone" errors on subsequent reloads. [RT #482] 572. [bug] Quoted strings were not accepted as key names in address match lists. 571. [bug] It was possible to create an rdataset of singleton type which had more than one rdata. [RT #154] [RT #279] 570. [bug] rbtdb.c allowed zones containing nodes which had both a CNAME and "other data". [RT #154] 569. [func] The DNSSEC AD bit will not be set on queries which have not requested a DNSSEC response. 568. [func] Add sample simple database drivers in contrib/sdb. 567. [bug] Setting the zone transfer timeout to zero caused an assertion failure. [RT #302] 566. [func] New public function dns_timer_setidle(). 565. [func] Log queries more like BIND 8: query logging is now done to category "queries", level "info". [RT #169] 564. [func] Add sortlist support to lwresd. 563. [func] New public functions dns_rdatatype_format() and dns_rdataclass_format(), for convenient formatting of rdata type/class mnemonics in log messages. 562. [cleanup] Moved lib/dns/*conf.c to bin/named where they belong. 561. [func] The 'datasize', 'stacksize', 'coresize' and 'files' clauses of the options{} statement are now implemented. 560. [bug] dns_name_split did not properly the resulting prefix when a maximal length bitstring label was split which was preceded by another bitstring label. [RT #429] 559. [bug] dns_name_split did not properly create the suffix when splitting within a maximal length bitstring label. 558. [func] New functions, isc_resource_getlimit and isc_resource_setlimit. 557. [func] Symbolic constants for libisc integral types. 556. [func] The DNSSEC OK bit in the EDNS extended flags is now implemented. Responses to queries without this bit set will not contain any DNSSEC records. 555. [bug] A slave server attempting a zone transfer could crash with an assertion failure on certain malformed responses from the master. [RT #457] 554. [bug] In some cases, not all of the dnssec tools were properly installed. 553. [bug] Incoming zone transfers deferred due to quota were not started when quota was increased but only when a transfer in progress finished. [RT #456] 552. [bug] We were not correctly detecting the end of all c-style comments. [RT #455] 551. [func] Implemented the 'sortlist' option. 550. [func] Support unknown rdata types and classes. 549. [bug] "make" did not immediately abort the build when a subdirectory make failed [RT #450]. 548. [func] The lexer now ungets tokens more correctly. 547. [placeholder] 546. [func] Option 'lame-ttl' is now implemented. 545. [func] Name limit and counting options removed from dig; they didn't work properly, and cannot be correctly implemented without significant changes. 544. [func] Add statistics option, enable statistics-file option, add RNDC option "dump-statistics" to write out a query statistics file. 543. [doc] The 'port' option is now documented. 542. [func] Add support for update forwarding as required for full compliance with RFC2136. It is turned off by default and can be enabled using the 'allow-update-forwarding' option. 541. [func] Add bogus server support. 540. [func] Add dialup support. 539. [func] Support the blackhole option. 538. [bug] fix buffer overruns by 1 in lwres_getnameinfo(). 537. [placeholder] 536. [func] Use transfer-source{-v6} when sending refresh queries. Transfer-source{-v6} now take a optional port parameter for setting the UDP source port. The port parameter is ignored for TCP. 535. [func] Use transfer-source{-v6} when forwarding update requests. 534. [func] Ancestors have been removed from RBT chains. Ancestor information can be discerned via node parent pointers. 533. [func] Incorporated name hashing into the RBT database to improve search speed. 532. [func] Implement DNS UPDATE pseudo records using DNS_RDATA_UPDATE flag. 531. [func] Rdata really should be initialized before being assigned to (dns_rdata_fromwire(), dns_rdata_fromtext(), dns_rdata_clone(), dns_rdata_fromregion()), check that it is. 530. [func] New function dns_rdata_invalidate(). 529. [bug] 521 contained a bug which caused zones to always reload. [RT #410] 528. [func] The ISC_LIST_XXXX macros now perform sanity checks on their arguments. ISC_LIST_XXXXUNSAFE can be use to skip the checks however use with caution. 527. [func] New function dns_rdata_clone(). 526. [bug] nsupdate incorrectly refused to add RRs with a TTL of 0. 525. [func] New arguments 'options' for dns_db_subtractrdataset(), and 'flags' for dns_rdataslab_subtract() allowing you to request that the RR's must exist prior to deletion. DNS_R_NOTEXACT is returned if the condition is not met. 524. [func] The 'forward' and 'forwarders' statement in non-forward zones should work now. 523. [doc] The source to the Administrator Reference Manual is now an XML file using the DocBook DTD, and is included in the distribution. The plain text version of the ARM is temporarily unavailable while we figure out how to generate readable plain text from the XML. 522. [func] The lightweight resolver daemon can now use a real configuration file, and its functionality can be provided by a name server. Also, the -p and -P options to lwresd have been reversed. 521. [bug] Detect master files which contain $INCLUDE and always reload. [RT #196] 520. [bug] Upgraded libtool to 1.3.5, which makes shared library builds almost work on AIX (and possibly others). 519. [bug] dns_name_split() would improperly split some bitstring labels, zeroing a few of the least significant bits in the prefix part. When such an improperly created prefix was returned to the RBT database, the bogus label was dutifully stored, corrupting the tree. [RT #369] 518. [bug] The resolver did not realize that a DNAME which was "the answer" to the client's query was "the answer", and such queries would fail. [RT #399] 517. [bug] The resolver's DNAME code would trigger an assertion if there was more than one DNAME in the chain. [RT #399] 516. [bug] Cache lookups which had a NULL node pointer, e.g. those by dns_view_find(), and which would match a DNAME, would trigger an INSIST(!search.need_cleanup) assertion. [RT #399] 515. [bug] The ssu table was not being attached / detached by dns_zone_[sg]etssutable. [RT#397] 514. [func] Retry refresh and notify queries if they timeout. [RT #388] 513. [func] New functionality added to rdnc and server to allow individual zones to be refreshed or reloaded. 512. [bug] The zone transfer code could throw an exception with an invalid IXFR stream. 511. [bug] The message code could throw an assertion on an out of memory failure. [RT #392] 510. [bug] Remove spurious view notify warning. [RT #376] 509. [func] Add support for write of zone files on shutdown. 508. [func] dns_message_parse() can now do a best-effort attempt, which should allow dig to print more invalid messages. 507. [func] New functions dns_zone_flush(), dns_zt_flushanddetach() and dns_view_flushanddetach(). 506. [func] Do not fail to start on errors in zone files. 505. [bug] nsupdate was printing "unknown result code". [RT #373] 504. [bug] The zone was not being marked as dirty when updated via IXFR. 503. [bug] dumptime was not being set along with DNS_ZONEFLG_NEEDDUMP. 502. [func] On a SERVFAIL reply, DiG will now try the next server in the list, unless the +fail option is specified. 501. [bug] Incorrect port numbers were being displayed by nslookup. [RT #352] 500. [func] Nearly useless +details option removed from DiG. 499. [func] In DiG, specifying a class with -c or type with -t changes command-line parsing so that classes and types are only recognized if following -c or -t. This allows hosts with the same name as a class or type to be looked up. 498. [doc] There is now a man page for "dig" in doc/man/bin/dig.1. 497. [bug] The error messages printed when an IP match list contained a network address with a nonzero host part where not sufficiently detailed. [RT #365] 496. [bug] named didn't sanity check numeric parameters. [RT #361] 495. [bug] nsupdate was unable to handle large records. [RT #368] 494. [func] Do not cache NXDOMAIN responses for SOA queries. 493. [func] Return non-cachable (ttl = 0) NXDOMAIN responses for SOA queries. This makes it easier to locate the containing zone without polluting intermediate caches. 492. [bug] attempting to reload a zone caused the server fail to shutdown cleanly. [RT #360] 491. [bug] nsupdate would segfault when sending certain prerequisites with empty RDATA. [RT #356] 490. [func] When a slave/stub zone has not yet successfully obtained an SOA containing the zone's configured retry time, perform the SOA query retries using exponential backoff. [RT #337] 489. [func] The zone manager now has a "i/o" queue. 488. [bug] Locks weren't properly destroyed in some cases. 487. [port] flockfile() is not defined on all systems. 486. [bug] nslookup: "set all" and "server" commands showed the incorrect port number if a port other than 53 was specified. [RT #352] 485. [func] When dig had more than one server to query, it would send all of the messages at the same time. Add rate limiting of the transmitted messages. 484. [bug] When the server was reloaded after removing addresses from the named.conf "listen-on" statement, sockets were still listening on the removed addresses due to reference count loops. [RT #325] 483. [bug] nslookup: "set all" showed a "search" option but it was not settable. 482. [bug] nslookup: a plain "server" or "lserver" should be treated as a lookup. 481. [bug] nslookup:get_next_command() stack size could exceed per thread limit. 480. [bug] strtok() is not thread safe. [RT #349] 479. [func] The test suite can now be run by typing "make check" or "make test" at the top level. 478. [bug] "make install" failed if the directory specified with --prefix did not already exist. 477. [bug] The the isc-config.sh script could be installed before its directory was created. [RT #324] 476. [bug] A zone could expire while a zone transfer was in progress triggering a INSIST failure. [RT #329] 475. [bug] query_getzonedb() sometimes returned a non-null version on failure. This caused assertion failures when generating query responses where names subject to additional section processing pointed to a zone to which access had been denied by means of the allow-query option. [RT #336] 474. [bug] The mnemonic of the CHAOS class is CH according to RFC1035, but it was printed and read only as CHAOS. We now accept both forms as input, and print it as CH. [RT #305] 473. [bug] nsupdate overran the end of the list of name servers when no servers could be reached, typically causing it to print the error message "dns_request_create: not implemented". 472. [bug] Off-by-one error caused isc_time_add() to sometimes produce invalid time values. 471. [bug] nsupdate didn't compile on HP/UX 10.20 470. [func] $GENERATE is now supported. See also doc/misc/migration. 469. [bug] "query-source address * port 53;" now works. 468. [bug] dns_master_load*() failed to report file and line number in certain error conditions. 467. [bug] dns_master_load*() failed to log an error if pushfile() failed. 466. [bug] dns_master_load*() could return success when it failed. 465. [cleanup] Allow 0 to be set as an omapi_value_t value by omapi_value_storeint(). 464. [cleanup] Build with openssl's RSA code instead of dnssafe. 463. [bug] nsupdate sent malformed SOA queries to the second and subsequent name servers in resolv.conf if the query sent to the first one failed. 462. [bug] --disable-ipv6 should work now. 461. [bug] Specifying an unknown key in the "keys" clause of the "controls" statement caused a NULL pointer dereference. [RT #316] 460. [bug] Much of the DNSSEC code only worked with class IN. 459. [bug] Nslookup processed the "set" command incorrectly. 458. [bug] Nslookup didn't properly check class and type values. [RT #305] 457. [bug] Dig/host/hslookup didn't properly handle connect timeouts in certain situations, causing an unnecessary warning message to be printed. 456. [bug] Stub zones were not resetting the refresh and expire counters, loadtime or clearing the DNS_ZONE_REFRESH (refresh in progress) flag upon successful update. This disabled further refreshing of the stub zone, causing it to eventually expire. [RT #300] 455. [doc] Document IPv4 prefix notation does not require a dotted decimal quad but may be just dotted decimal. 454. [bug] Enforce dotted decimal and dotted decimal quad where documented as such in named.conf. [RT #304, RT #311] 453. [bug] Warn if the obsolete option "maintain-ixfr-base" is specified in named.conf. [RT #306] 452. [bug] Warn if the unimplemented option "statistics-file" is specified in named.conf. [RT #301] 451. [func] Update forwarding implemented. 450. [func] New function ns_client_sendraw(). 449. [bug] isc_bitstring_copy() only works correctly if the two bitstrings have the same lsb0 value, but this requirement was not documented, nor was there a REQUIRE for it. 448. [bug] Host output formatting change, to match v8. [RT #255] 447. [bug] Dig didn't properly retry in TCP mode after a truncated reply. [RT #277] 446. [bug] Confusing notify log message. [RT #298] 445. [bug] Doing a 0 bit isc_bitstring_copy() of an lsb0 bitstring triggered a REQUIRE statement. The REQUIRE statement was incorrect. [RT #297] 444. [func] "recursion denied" messages are always logged at debug level 1, now, rather than sometimes at ERROR. This silences these warnings in the usual case, where some clients set the RD bit in all queries. 443. [bug] When loading a master file failed because of an unrecognized RR type name, the error message did not include the file name and line number. [RT #285] 442. [bug] TSIG signed messages that did not match any view crashed the server. [RT #290] 441. [bug] Nodes obscured by a DNAME were inaccessible even when DNS_DBFIND_GLUEOK was set. 440. [func] New function dns_zone_forwardupdate(). 439. [func] New function dns_request_createraw(). 438. [func] New function dns_message_getrawmessage(). 437. [func] Log NOTIFY activity to the notify channel. 436. [bug] If recvmsg() returned EHOSTUNREACH or ENETUNREACH, which sometimes happens on Linux, named would enter a busy loop. Also, unexpected socket errors were not logged at a high enough logging level to be useful in diagnosing this situation. [RT #275] 435. [bug] dns_zone_dump() overwrote existing zone files rather than writing to a temporary file and renaming. This could lead to empty or partial zone files being left around in certain error conditions involving the initial transfer of a slave zone, interfering with subsequent server startup. [RT #282] 434. [func] New function isc_file_isabsolute(). 433. [func] isc_base64_decodestring() now accepts newlines within the base64 data. This makes it possible to break up the key data in a "trusted-keys" statement into multiple lines. [RT #284] 432. [func] Added refresh/retry jitter. The actual refresh/ retry time is now a random value between 75% and 100% of the configured value. 431. [func] Log at ISC_LOG_INFO when a zone is successfully loaded. 430. [bug] Rewrote the lightweight resolver client management code to handle shutdown correctly and general cleanup. 429. [bug] The space reserved for a TSIG record in a response was 2 bytes too short, leading to message generation failures. 428. [bug] rbtdb.c:find_closest_nxt() erroneously returned DNS_R_BADDB for nodes which had neither NXT nor SIG NXT (e.g. glue). This could cause SERVFAILs when generating negative responses in a secure zone. 427. [bug] Avoid going into an infinite loop when the validator gets a negative response to a key query where the records are signed by the missing key. 426. [bug] Attempting to generate an oversized RSA key could cause dnssec-keygen to dump core. 425. [bug] Warn about the auth-nxdomain default value change if there is no auth-nxdomain statement in the config file. [RT #287] 424. [bug] notify_createmessage() could trigger an assertion failure when creating the notify message failed, e.g. due to corrupt zones with multiple SOA records. [RT #279] 423. [bug] When responding to a recursive query, errors that occur after following a CNAME should cause the query to fail. [RT #274] 422. [func] get rid of isc_random_t, and make isc_random_get() and isc_random_jitter() use rand() internally instead of local state. Note that isc_random_*() functions are only for weak, non-critical "randomness" such as timing jitter and such. 421. [bug] nslookup would exit when given a blank line as input. 420. [bug] nslookup failed to implement the "exit" command. 419. [bug] The certificate type PKIX was misspelled as SKIX. 418. [bug] At debug levels >= 10, getting an unexpected socket receive error would crash the server while trying to log the error message. 417. [func] Add isc_app_block() and isc_app_unblock(), which allow an application to handle signals while blocking. 416. [bug] Slave zones with no master file tried to use a NULL pointer for a journal file name when they received an IXFR. [RT #273] 415. [bug] The logging code leaked file descriptors. 414. [bug] Server did not shut down until all incoming zone transfers were finished. 413. [bug] Notify could attempt to use the zone database after it had been unloaded. [RT#267] 412. [bug] named -v didn't print the version. 411. [bug] A typo in the HS A code caused an assertion failure. 410. [bug] lwres_gethostbyname() and company set lwres_h_errno to a random value on success. 409. [bug] If named was shut down early in the startup process, ns_omapi_shutdown() would attempt to lock an uninitialized mutex. [RT #262] 408. [bug] stub zones could leak memory and reference counts if all the masters were unreachable. 407. [bug] isc_rwlock_lock() would needlessly block readers when it reached the read quota even if no writers were waiting. 406. [bug] Log messages were occasionally lost or corrupted due to a race condition in isc_log_doit(). 405. [func] Add support for selective forwarding (forward zones) 404. [bug] The request library didn't completely work with IPv6. 403. [bug] "host" did not use the search list. 402. [bug] Treat undefined acls as errors, rather than warning and then later throwing an assertion. [RT #252] 401. [func] Added simple database API. 400. [bug] SIG(0) signing and verifying was done incorrectly. [RT #249] 399. [bug] When reloading the server with a config file containing a syntax error, it could catch an assertion failure trying to perform zone maintenance on, or sending notifies from, tentatively created zones whose views were never fully configured and lacked an address database and request manager. 398. [bug] "dig" sometimes caught an assertion failure when using TSIG, depending on the key length. 397. [func] Added utility functions dns_view_gettsig() and dns_view_getpeertsig(). 396. [doc] There is now a man page for "nsupdate" in doc/man/bin/nsupdate.8. 395. [bug] nslookup printed incorrect RR type mnemonics for RRs of type >= 21 [RT #237]. 394. [bug] Current name was not propagated via $INCLUDE. 393. [func] Initial answer while loading (awl) support. Entry points: dns_master_loadfileinc(), dns_master_loadstreaminc(), dns_master_loadbufferinc(). Note: calls to dns_master_load*inc() should be rate be rate limited so as to not use up all file descriptors. 392. [func] Add ISC_R_FAMILYNOSUPPORT. Returned when OS does not support the given address family requested. 391. [clarity] ISC_R_FAMILY -> ISC_R_FAMILYMISMATCH. 390. [func] The function dns_zone_setdbtype() now takes an argc/argv style vector of words and sets both the zone database type and its arguments, making the functions dns_zone_adddbarg() and dns_zone_cleardbargs() unnecessary. 389. [bug] Attempting to send a request over IPv6 using dns_request_create() on a system without IPv6 support caused an assertion failure [RT #235]. 388. [func] dig and host can now do reverse ipv6 lookups. 387. [func] Add dns_byaddr_createptrname(), which converts an address into the name used by a PTR query. 386. [bug] Missing strdup() of ACL name caused random ACL matching failures [RT #228]. 385. [cleanup] Removed functions dns_zone_equal(), dns_zone_print(), and dns_zt_print(). 384. [bug] nsupdate was incorrectly limiting TTLs to 65535 instead of 2147483647. 383. [func] When writing a master file, print the SOA and NS records (and their SIGs) before other records. 382. [bug] named -u failed on many Linux systems where the libc provided kernel headers do not match the current kernel. 381. [bug] Check for IPV6_RECVPKTINFO and use it instead of IPV6_PKTINFO if found. [RT #229] 380. [bug] nsupdate didn't work with IPv6. 379. [func] New library function isc_sockaddr_anyofpf(). 378. [func] named and lwresd will log the command line arguments they were started with in the "starting ..." message. 377. [bug] When additional data lookups were refused due to "allow-query", the databases were still being attached causing reference leaks. 376. [bug] The server should always use good entropy when performing cryptographic functions needing entropy. 375. [bug] Per-zone "allow-query" did not properly override the view/global one for CNAME targets and additional data [RT #220]. 374. [bug] SOA in authoritative negative responses had wrong TTL. 373. [func] nslookup is now installed by "make install". 372. [bug] Deal with Microsoft DNS servers appending two bytes of garbage to zone transfer requests. 371. [bug] At high debug levels, doing an outgoing zone transfer of a very large RRset could cause an assertion failure during logging. 370. [bug] The error messages for roll-forward failures were overly terse. 369. [func] Support new named.conf options, view and zone statements: max-retry-time, min-retry-time, max-refresh-time, min-refresh-time. 368. [func] Restructure the internal ".bind" view so that more zones can be added to it. 367. [bug] Allow proper selection of server on nslookup command line. 366. [func] Allow use of '-' batch file in dig for stdin. 365. [bug] nsupdate -k leaked memory. 364. [func] Added additional-from-{cache,auth} 363. [placeholder] 362. [bug] rndc no longer aborts if the configuration file is missing an options statement. [RT #209] 361. [func] When the RBT find or chain functions set the name and origin for a node that stores the root label the name is now set to an empty name, instead of ".", to simplify later use of the name and origin by dns_name_concatenate(), dns_name_totext() or dns_name_format(). 360. [func] dns_name_totext() and dns_name_format() now allow an empty name to be passed, which is formatted as "@". 359. [bug] dnssec-signzone occasionally signed glue records. 358. [cleanup] Rename the intermediate files used by the dnssec programs. 357. [bug] The zone file parser crashed if the argument to $INCLUDE was a quoted string. 356. [cleanup] isc_task_send no longer requires event->sender to be non-null. 355. [func] Added isc_dir_createunique(), similar to mkdtemp(). 354. [doc] Man pages for the dnssec tools are now included in the distribution, in doc/man/dnssec. 353. [bug] double increment in lwres/gethost.c:copytobuf(). [RT# 187] 352. [bug] Race condition in dns_client_t startup could cause an assertion failure. 351. [bug] Constructing a response with rcode SERVFAIL to a TSIG signed query could crash the server. 350. [bug] Also-notify lists specified in the global options block were not correctly reference counted, causing a memory leak. 349. [bug] Processing a query with the CD bit set now works as expected. 348. [func] New boolean named.conf options 'additional-from-auth' and 'additional-from-cache' now supported in view and global options statement. 347. [bug] Don't crash if an argument is left off options in dig. 346. [placeholder] 345. [bug] Large-scale changes/cleanups to dig: * Significantly improve structure handling * Don't pre-load entire batch files * Add name/rr counting/limiting * Fix SIGINT handling * Shorten timeouts to match v8's behavior 344. [bug] When shutting down, lwresd sometimes tried to shut down its client tasks twice, triggering an assertion. 343. [bug] Although zone maintenance SOA queries and notify requests were signed with TSIG keys when configured for the server in case, the TSIG was not verified on the response. 342. [bug] The wrong name was being passed to dns_name_dup() when generating a TSIG key using TKEY. 341. [func] Support 'key' clause in named.conf zone masters statement to allow authentication via TSIG keys: masters { 10.0.0.1 port 5353 key "foo"; 10.0.0.2 ; }; 340. [bug] The top-level COPYRIGHT file was missing from the distribution. 339. [bug] DNSSEC validation of the response to an ANY query at a name with a CNAME RR in a secure zone triggered an assertion failure. 338. [bug] lwresd logged to syslog as named, not lwresd. 337. [bug] "dig" did not recognize "nsap-ptr" as an RR type on the command line. 336. [bug] "dig -f" used 64 k of memory for each line in the file. It now uses much less, though still proportionally to the file size. 335. [bug] named would occasionally attempt recursion when it was disallowed or undesired. 334. [func] Added hmac-md5 to libisc. 333. [bug] The resolver incorrectly accepted referrals to domains that were not parents of the query name, causing assertion failures. 332. [func] New function dns_name_reset(). 331. [bug] Only log "recursion denied" if RD is set. [RT #178] 330. [bug] Many debugging messages were partially formatted even when debugging was turned off, causing a significant decrease in query performance. 329. [func] omapi_auth_register() now takes a size_t argument for the length of a key's secret data. Previously OMAPI only stored secrets up to the first NUL byte. 328. [func] Added isc_base64_decodestring(). 327. [bug] rndc.conf parser wasn't correctly recognizing an IP address where a host specification was required. 326. [func] 'keys' in an 'inet' control statement is now required and must have at least one item in it. A "not supported" warning is now issued if a 'unix' control channel is defined. 325. [bug] isc_lex_gettoken was processing octal strings when ISC_LEXOPT_CNUMBER was not set. 324. [func] In the resolver, turn EDNS0 off if there is no response after a number of retransmissions. This is to allow queries some chance of succeeding even if all the authoritative servers of a zone silently discard EDNS0 requests instead of sending an error response like they ought to. 323. [bug] dns_rbt_findname() did not ignore empty rbt nodes. Because of this, servers authoritative for a parent and grandchild zone but not authoritative for the intervening child zone did not correctly issue referrals to the servers of the child zone. 322. [bug] Queries for KEY RRs are now sent to the parent server before the authoritative one, making DNSSEC insecurity proofs work in many cases where they previously didn't. 321. [bug] When synthesizing a CNAME RR for a DNAME response, query_addcname() failed to initialize the type and class of the CNAME dns_rdata_t, causing random failures. 320. [func] Multiple rndc changes: parses an rndc.conf file, uses authentication to talk to named, command line syntax changed. This will all be described in the ARM. 319. [func] The named.conf "controls" statement is now used to configure the OMAPI command channel. 318. [func] dns_c_ndcctx_destroy() could never return anything except ISC_R_SUCCESS; made it have void return instead. 317. [func] Use callbacks from libomapi to determine if a new connection is valid, and if a key requested to be used with that connection is valid. 316. [bug] Generate a warning if we detect an unexpected but treat as . 315. [bug] Handle non-empty blanks lines. [RT #163] 314. [func] The named.conf controls statement can now have more than one key specified for the inet clause. 313. [bug] When parsing resolv.conf, don't terminate on an error. Instead, parse as much as possible, but still return an error if one was found. 312. [bug] Increase the number of allowed elements in the resolv.conf search path from 6 to 8. If there are more than this, ignore the remainder rather than returning a failure in lwres_conf_parse. 311. [bug] lwres_conf_parse failed when the first line of resolv.conf was empty or a comment. 310. [func] Changes to named.conf "controls" statement (inet subtype only) - support "keys" clause controls { inet * port 1024 allow { any; } keys { "foo"; } } - allow "port xxx" to be left out of statement, in which case it defaults to omapi's default port of 953. 309. [bug] When sending a referral, the server did not look for name server addresses as glue in the zone holding the NS RRset in the case where this zone was not the same as the one where it looked for name server addresses as authoritative data. 308. [bug] Treat a SOA record not at top of zone as an error when loading a zone. [RT #154] 307. [bug] When canceling a query, the resolver didn't check for isc_socket_sendto() calls that did not yet have their completion events posted, so it could (rarely) end up destroying the query context and then want to use it again when the send event posted, triggering an assertion as it tried to cancel an already-canceled query. [RT #77] 306. [bug] Reading HMAC-MD5 private key files didn't work. 305. [bug] When reloading the server with a config file containing a syntax error, it could catch an assertion failure trying to perform zone maintenance on tentatively created zones whose views were never fully configured and lacked an address database. 304. [bug] If more than LWRES_CONFMAXNAMESERVERS servers are listed in resolv.conf, silently ignore them instead of returning failure. 303. [bug] Add additional sanity checks to differentiate a AXFR response vs a IXFR response. [RT #157] 302. [bug] In dig, host, and nslookup, MXNAME should be large enough to hold any legal domain name in presentation format + terminating NULL. 301. [bug] Uninitialized pointer in host:printmessage(). [RT #159] 300. [bug] Using both and didn't work on platforms lacking IPv6 because each included their own ipv6 header file for the missing definitions. Now each library's ipv6.h defines the wrapper symbol of the other (ISC_IPV6_H and LWRES_IPV6_H). 299. [cleanup] Get the user and group information before changing the root directory, so the administrator does not need to keep a copy of the user and group databases in the chroot'ed environment. Suggested by Hakan Olsson. 298. [bug] A mutex deadlock occurred during shutdown of the interface manager under certain conditions. Digital Unix systems were the most affected. 297. [bug] Specifying a key name that wasn't fully qualified in certain parts of the config file could cause an assertion failure. 296. [bug] "make install" from a separate build directory failed unless configure had been run in the source directory, too. 295. [bug] When invoked with type==CNAME and a message not constructed by dns_message_parse(), dns_message_findname() failed to find anything due to checking for attribute bits that are set only in dns_message_parse(). This caused an infinite loop when constructing the response to an ANY query at a CNAME in a secure zone. 294. [bug] If we run out of space in while processing glue when reading a master file and commit "current name" reverts to "name_current" instead of staying as "name_glue". 293. [port] Add support for FreeBSD 4.0 system tests. 292. [bug] Due to problems with the way some operating systems handle simultaneous listening on IPv4 and IPv6 addresses, the server no longer listens on IPv6 addresses by default. To revert to the previous behavior, specify "listen-on-v6 { any; };" in the config file. 291. [func] Caching servers no longer send outgoing queries over TCP just because the incoming recursive query was a TCP one. 290. [cleanup] +twiddle option to dig (for testing only) removed. 289. [cleanup] dig is now installed in $bindir instead of $sbindir. host is now installed in $bindir. (Be sure to remove any $sbindir/dig from a previous release.) 288. [func] rndc is now installed by "make install" into $sbindir. 287. [bug] rndc now works again as "rndc 127.1 reload" (for only that task). Parsing its configuration file and using digital signatures for authentication has been disabled until named supports the "controls" statement, post-9.0.0. 286. [bug] On Solaris 2, when named inherited a signal state where SIGHUP had the SIG_IGN action, SIGHUP would be ignored rather than causing the server to reload its configuration. 285. [bug] A change made to the dst API for beta4 inadvertently broke OMAPI's creation of a dst key from an incoming message, causing an assertion to be triggered. Fixed. 284. [func] The DNSSEC key generation and signing tools now generate randomness from keyboard input on systems that lack /dev/random. 283. [cleanup] The 'lwresd' program is now a link to 'named'. 282. [bug] The lexer now returns ISC_R_RANGE if parsed integer is too big for an unsigned long. 281. [bug] Fixed list of recognized config file category names. 280. [func] Add isc-config.sh, which can be used to more easily build applications that link with our libraries. 279. [bug] Private omapi function symbols shared between two or more files in libomapi.a were not namespace protected using the ISC convention of starting with the library name and two underscores ("omapi__"...) 278. [bug] bin/named/logconf.c:category_fromconf() didn't take note of when isc_log_categorybyname() wasn't able to find the category name and would then apply the channel list of the unknown category to all categories. 277. [bug] isc_log_categorybyname() and isc_log_modulebyname() would fail to find the first member of any category or module array apart from the internal defaults. Thus, for example, the "notify" category was improperly configured by named. 276. [bug] dig now supports maximum sized TCP messages. 275. [bug] The definition of lwres_gai_strerror() was missing the lwres_ prefix. 274. [bug] TSIG AXFR verify failed when talking to a BIND 8 server. 273. [func] The default for the 'transfer-format' option is now 'many-answers'. This will break zone transfers to BIND 4.9.5 and older unless there is an explicit 'one-answer' configuration. 272. [bug] The sending of large TCP responses was canceled in mid-transmission due to a race condition caused by the failure to set the client object's "newstate" variable correctly when transitioning to the "working" state. 271. [func] Attempt to probe the number of cpus in named if unspecified rather than defaulting to 1. 270. [func] Allow maximum sized TCP answers. 269. [bug] Failed DNSSEC validations could cause an assertion failure by causing clone_results() to be called with with hevent->node == NULL. 268. [doc] A plain text version of the Administrator Reference Manual is now included in the distribution, as doc/arm/Bv9ARM.txt. 267. [func] Nsupdate is now provided in the distribution. 266. [bug] zone.c:save_nsrrset() node was not initialized. 265. [bug] dns_request_create() now works for TCP. 264. [func] Dispatch can not take TCP sockets in connecting state. Set DNS_DISPATCHATTR_CONNECTED when calling dns_dispatch_createtcp() for connected TCP sockets or call dns_dispatch_starttcp() when the socket is connected. 263. [func] New logging channel type 'stderr' channel some-name { stderr; severity error; } 262. [bug] 'master' was not initialized in zone.c:stub_callback(). 261. [func] Add dns_zone_markdirty(). 260. [bug] Running named as a non-root user failed on Linux kernels new enough to support retaining capabilities after setuid(). 259. [func] New random-device and random-seed-file statements for global options block of named.conf. Both accept a single string argument. 258. [bug] Fixed printing of lwres_addr_t.address field. 257. [bug] The server detached the last zone manager reference too early, while it could still be in use by queries. This manifested itself as assertion failures during the shutdown process for busy name servers. [RT #133] 256. [func] isc_ratelimiter_t now has attach/detach semantics, and isc_ratelimiter_shutdown guarantees that the rate limiter is detached from its task. 255. [func] New function dns_zonemgr_attach(). 254. [bug] Suppress "query denied" messages on additional data lookups. --- 9.0.0b4 released --- 253. [func] resolv.conf parser now recognizes ';' and '#' as comments (anywhere in line, not just as the beginning). 252. [bug] resolv.conf parser mishandled masks on sortlists. It also aborted when an unrecognized keyword was seen, now it silently ignores the entire line. 251. [bug] lwresd caught an assertion failure on startup. 250. [bug] fixed handling of size+unit when value would be too large for internal representation. 249. [cleanup] max-cache-size config option now takes a size-spec like 'datasize', except 'default' is not allowed. 248. [bug] global lame-ttl option was not being printed when config structures were written out. 247. [cleanup] Rename cache-size config option to max-cache-size. 246. [func] Rename global option cachesize to cache-size and add corresponding option to view statement. 245. [bug] If an uncompressed name will take more than 255 bytes and the buffer is sufficiently long, dns_name_fromwire should return DNS_R_FORMERR, not ISC_R_NOSPACE. This bug caused cause the server to catch an assertion failure when it received a query for a name longer than 255 bytes. 244. [bug] empty named.conf file and empty options statement are now parsed properly. 243. [func] new cachesize option for named.conf 242. [cleanup] fixed incorrect warning about auth-nxdomain usage. 241. [cleanup] nscount and soacount have been removed from the dns_master_*() argument lists. 240. [func] databases now come in three flavours: zone, cache and stub. 239. [func] If ISC_MEM_DEBUG is enabled, the variable isc_mem_debugging controls whether messages are printed or not. 238. [cleanup] A few more compilation warnings have been quieted: + missing sigwait prototype on BSD/OS 4.0/4.0.1. + PTHREAD_ONCE_INIT unbraced initializer warnings on Solaris 2.8. + IN6ADDR_ANY_INIT unbraced initializer warnings on BSD/OS 4.*, Linux and Solaris 2.8. 237. [bug] If connect() returned ENOBUFS when the resolver was initiating a TCP query, the socket didn't get destroyed, and the server did not shut down cleanly. 236. [func] Added new listen-on-v6 config file statement. 235. [func] Consider it a config file error if a listen-on statement has an IPv6 address in it, or a listen-on-v6 statement has an IPv4 address in it. 234. [bug] Allow a trusted-key's first field (domain-name) be either a quoted or an unquoted string, instead of requiring a quoted string. 233. [cleanup] Convert all config structure integer values to unsigned integer (isc_uint32_t) to match grammar. 232. [bug] Allow slave zones to not have a file. 231. [func] Support new 'port' clause in config file options section. Causes 'listen-on', 'masters' and 'also-notify' statements to use its value instead of default (53). 230. [func] Replace the dst sign/verify API with a cleaner one. 229. [func] Support config file sig-validity-interval statement in options, views and zone statements (master zones only). 228. [cleanup] Logging messages in config module stripped of trailing period. 227. [cleanup] The enumerated identifiers dns_rdataclass_*, dns_rcode_*, dns_opcode_*, and dns_trust_* are also now cast to their appropriate types, as with dns_rdatatype_* in item number 225 below. 226. [func] dns_name_totext() now always prints the root name as '.', even when omit_final_dot is true. 225. [cleanup] The enumerated dns_rdatatype_* identifiers are now cast to dns_rdatatype_t via macros of their same name so that they are of the proper integral type wherever a dns_rdatatype_t is needed. 224. [cleanup] The entire project builds cleanly with gcc's -Wcast-qual and -Wwrite-strings warnings enabled, which is now the default when using gcc. (Warnings from confparser.c, because of yacc's code, are unfortunately to be expected.) 223. [func] Several functions were re-prototyped to qualify one or more of their arguments with "const". Similarly, several functions that return pointers now have those pointers qualified with const. 222. [bug] The global 'also-notify' option was ignored. 221. [bug] An uninitialized variable was sometimes passed to dns_rdata_freestruct() when loading a zone, causing an assertion failure. 220. [cleanup] Set the default outgoing port in the view, and set it in sockaddrs returned from the ADB. [31-May-2000 explorer] 219. [bug] Signed truncated messages more correctly follow the respective specs. 218. [func] When an rdataset is signed, its ttl is normalized based on the signature validity period. 217. [func] Also-notify and trusted-keys can now be used in the 'view' statement. 216. [func] The 'max-cache-ttl' and 'max-ncache-ttl' options now work. 215. [bug] Failures at certain points in request processing could cause the assertion INSIST(client->lockview == NULL) to be triggered. 214. [func] New public function isc_netaddr_format(), for formatting network addresses in log messages. 213. [bug] Don't leak memory when reloading the zone if an update-policy clause was present in the old zone. 212. [func] Added dns_message_get/settsigkey, to make TSIG key management reasonable. 211. [func] The 'key' and 'server' statements can now occur inside 'view' statements. 210. [bug] The 'allow-transfer' option was ignored for slave zones, and the 'transfers-per-ns' option was was ignored for all zones. 209. [cleanup] Upgraded openssl files to new version 0.9.5a 208. [func] Added ISC_OFFSET_MAXIMUM for the maximum value of an isc_offset_t. 207. [func] The dnssec tools properly use the logging subsystem. 206. [cleanup] dst now stores the key name as a dns_name_t, not a char *. 205. [cleanup] On IRIX, turn off the mostly harmless warnings 1692 ("prototyped function redeclared without prototype") and 1552 ("variable ... set but not used") when compiling in the lib/dns/sec/{dnssafe,openssl} directories, which contain code imported from outside sources. 204. [cleanup] On HP/UX, pass +vnocompatwarnings to the linker to quiet the warnings that "The linked output may not run on a PA 1.x system." 203. [func] notify and zone soa queries are now tsig signed when appropriate. 202. [func] isc_lex_getsourceline() changed from returning int to returning unsigned long, the type of its underlying counter. 201. [cleanup] Removed the test/sdig program, it has been replaced by bin/dig/dig. --- 9.0.0b3 released --- 200. [bug] Failures in sending query responses to clients (e.g., running out of network buffers) were not logged. 199. [bug] isc_heap_delete() sometimes violated the heap invariant, causing timer events not to be posted when due. 198. [func] Dispatch managers hold memory pools which any managed dispatcher may use. This allows us to avoid dipping into the memory context for most allocations. [19-May-2000 explorer] 197. [bug] When an incoming AXFR or IXFR completes, the zone's internal state is refreshed from the SOA data. [19-May-2000 explorer] 196. [func] Dispatchers can be shared easily between views and/or interfaces. [19-May-2000 explorer] 195. [bug] Including the NXT record of the root domain in a negative response caused an assertion failure. 194. [doc] The PDF version of the Administrator's Reference Manual is no longer included in the ISC BIND9 distribution. 193. [func] changed dst_key_free() prototype. 192. [bug] Zone configuration validation is now done at end of config file parsing, and before loading callbacks. 191. [func] Patched to compile on UnixWare 7.x. This platform is not directly supported by the ISC. 190. [cleanup] The DNSSEC tools have been moved to a separate directory dnssec/ and given the following new, more descriptive names: dnssec-keygen dnssec-signzone dnssec-signkey dnssec-makekeyset Their command line arguments have also been changed to be more consistent. dnssec-keygen now prints the name of the generated key files (sans extension) on standard output to simplify its use in automated scripts. 189. [func] isc_time_secondsastimet(), a new function, will ensure that the number of seconds in an isc_time_t does not exceed the range of a time_t, or return ISC_R_RANGE. Similarly, isc_time_now(), isc_time_nowplusinterval(), isc_time_add() and isc_time_subtract() now check the range for overflow/underflow. In the case of isc_time_subtract, this changed a calling requirement (ie, something that could generate an assertion) into merely a condition that returns an error result. isc_time_add() and isc_time_subtract() were void- valued before but now return isc_result_t. 188. [func] Log a warning message when an incoming zone transfer contains out-of-zone data. 187. [func] isc_ratelimiter_enqueue() has an additional argument 'task'. 186. [func] dns_request_getresponse() has an additional argument 'preserve_order'. 185. [bug] Fixed up handling of ISC_MEMCLUSTER_LEGACY. Several public functions did not have an isc__ prefix, and referred to functions that had previously been renamed. 184. [cleanup] Variables/functions which began with two leading underscores were made to conform to the ANSI/ISO standard, which says that such names are reserved. 183. [func] ISC_LOG_PRINTTAG option for log channels. Useful for logging the program name or other identifier. 182. [cleanup] New command-line parameters for dnssec tools 181. [func] Added dst_key_buildfilename and dst_key_parsefilename 180. [func] New isc_result_t ISC_R_RANGE. Supersedes DNS_R_RANGE. 179. [func] options named.conf statement *must* now come before any zone or view statements. 178. [func] Post-load of named.conf check verifies a slave zone has non-empty list of masters defined. 177. [func] New per-zone boolean: enable-zone yes | no ; intended to let a zone be disabled without having to comment out the entire zone statement. 176. [func] New global and per-view option: max-cache-ttl number 175. [func] New global and per-view option: additional-data internal | minimal | maximal; 174. [func] New public function isc_sockaddr_format(), for formatting socket addresses in log messages. 173. [func] Keep a queue of zones waiting for zone transfer quota so that a new transfer can be dispatched immediately whenever quota becomes available. 172. [bug] $TTL directive was sometimes missing from dumped master files because totext_ctx_init() failed to initialize ctx->current_ttl_valid. 171. [cleanup] On NetBSD systems, the mit-pthreads or unproven-pthreads library is now always used unless --with-ptl2 is explicitly specified on the configure command line. The --with-mit-pthreads option is no longer needed and has been removed. 170. [cleanup] Remove inter server consistency checks from zone, these should return as a separate module in 9.1. dns_zone_checkservers(), dns_zone_checkparents(), dns_zone_checkchildren(), dns_zone_checkglue(). Remove dns_zone_setadb(), dns_zone_setresolver(), dns_zone_setrequestmgr() these should now be found via the view. 169. [func] ratelimiter can now process N events per interval. 168. [bug] include statements in named.conf caused syntax errors due to not consuming the semicolon ending the include statement before switching input streams. 167. [bug] Make lack of masters for a slave zone a soft error. 166. [bug] Keygen was overwriting existing keys if key_id conflicted, now it will retry, and non-null keys with key_id == 0 are not generated anymore. Key was not able to generate NOAUTHCONF DSA key, increased RSA key size to 2048 bits. 165. [cleanup] Silence "end-of-loop condition not reached" warnings from Solaris compiler. 164. [func] Added functions isc_stdio_open(), isc_stdio_close(), isc_stdio_seek(), isc_stdio_read(), isc_stdio_write(), isc_stdio_flush(), isc_stdio_sync(), isc_file_remove() to encapsulate nonportable usage of errno and sync. 163. [func] Added result codes ISC_R_FILENOTFOUND and ISC_R_FILEEXISTS. 162. [bug] Ensure proper range for arguments to ctype.h functions. 161. [cleanup] error in yyparse prototype that only HPUX caught. 160. [cleanup] getnet*() are not going to be implemented at this stage. 159. [func] Redefinition of config file elements is now an error (instead of a warning). 158. [bug] Log channel and category list copy routines weren't assigning properly to output parameter. 157. [port] Fix missing prototype for getopt(). 156. [func] Support new 'database' statement in zone. database "quoted-string"; 155. [bug] ns_notify_start() was not detaching the found zone. 154. [func] The signer now logs libdns warnings to stderr even when not verbose, and in a nicer format. 153. [func] dns_rdata_tostruct() 'mctx' is now optional. If 'mctx' is NULL then you need to preserve the 'rdata' until you have finished using the structure as there may be references to the associated memory. If 'mctx' is non-NULL it is guaranteed that there are no references to memory associated with 'rdata'. dns_rdata_freestruct() must be called if 'mctx' was non-NULL and may safely be called if 'mctx' was NULL. 152. [bug] keygen dumped core if domain name argument was omitted from command line. 151. [func] Support 'disabled' statement in zone config (causes zone to be parsed and then ignored). Currently must come after the 'type' clause. 150. [func] Support optional ports in masters and also-notify statements: masters [ port xxx ] { y.y.y.y [ port zzz ] ; } 149. [cleanup] Removed unused argument 'olist' from dns_c_view_unsetordering(). 148. [cleanup] Stop issuing some warnings about some configuration file statements that were not implemented, but now are. 147. [bug] Changed yacc union size to be smaller for yaccs that put yacc-stack on the real stack. 146. [cleanup] More general redundant header file cleanup. Rather than continuing to itemize every header which changed, this changelog entry just notes that if a header file did not need another header file that it was including in order to provide its advertised functionality, the inclusion of the other header file was removed. See util/check-includes for how this was tested. 145. [cleanup] Added and ISC_LANG_BEGINDECLS/ ISC_LANG_ENDDECLS to header files that had function prototypes, and removed it from those that did not. 144. [cleanup] libdns header files too numerous to name were made to conform to the same style for multiple inclusion protection. 143. [func] Added function dns_rdatatype_isknown(). 142. [cleanup] does not need or . 141. [bug] Corrupt requests with multiple questions could cause an assertion failure. 140. [cleanup] does not need or . 139. [cleanup] now includes instead of and . 138. [cleanup] isc_strtouq moved from str.[ch] to string.[ch] and renamed isc_string_touint64. isc_strsep moved from strsep.c to string.c and renamed isc_string_separate. 137. [cleanup] , , , and made to conform to the same style for multiple inclusion protection. 136. [cleanup] , , and Win32's needed ISC_LANG_BEGINDECLS/ISC_LANG_ENDDECLS. 135. [cleanup] Win32's did not need or , now uses in place of , and needed ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS. 134. [cleanup] does not need . 133. [cleanup] needs . 132. [cleanup] does not need , but does need . 131. [cleanup] and need for ISC_R_* codes used in macros. 130. [cleanup] does not need or , and now includes instead of . 129. [bug] The 'default_debug' log channel was not set up when 'category default' was present in the config file 128. [cleanup] had ISC_LANG_BEGINDECLS instead of ISC_LANG_ENDDECLS at end of header. 127. [cleanup] The contracts for the comparison routines dns_name_fullcompare(), dns_name_compare(), dns_name_rdatacompare(), and dns_rdata_compare() now specify that the order value returned is < 0, 0, or > 0 instead of -1, 0, or 1. 126. [cleanup] and need . 125. [cleanup] , , , , , , and do not need . 124. [func] signer now imports parent's zone key signature and creates null keys/sets zone status bit for children when necessary 123. [cleanup] does not need . 122. [cleanup] does not need or . 121. [cleanup] does not need or . Multiple inclusion protection symbol fixed from ISC_SYMBOL_H to ISC_SYMTAB_H. isc_symtab_t moved to . 120. [cleanup] does not need , , , or . 119. [cleanup] structure definitions for generic rdata structures do not have _generic_ in their names. 118. [cleanup] libdns.a is now namespace-clean, on NetBSD, excepting YACC crust (yyparse, etc) [2000-apr-27 explorer] 117. [cleanup] libdns.a changes: dns_zone_clearnotify() and dns_zone_addnotify() are replaced by dns_zone_setnotifyalso(). dns_zone_clearmasters() and dns_zone_addmaster() are replaced by dns_zone_setmasters(). 116. [func] Added for isc_offset_t (aka off_t on Unix systems). 115. [port] Shut up the -Wmissing-declarations warning about 's __sputaux on BSD/OS pre-4.1. 114. [cleanup] does not need or . 113. [func] Utility programs dig and host added. 112. [cleanup] does not need . 111. [cleanup] does not need or . 110. [cleanup] does not need or . 109. [bug] "make depend" did nothing for bin/tests/{db,mem,sockaddr,tasks,timers}/. 108. [cleanup] DNS_SETBIT/DNS_GETBIT/DNS_CLEARBIT moved from to and renamed to DNS_BIT_SET/DNS_BIT_GET/DNS_BIT_CLEAR. 107. [func] Add keysigner and keysettool. 106. [func] Allow dnssec verifications to ignore the validity period. Used by several of the dnssec tools. 105. [doc] doc/dev/coding.html expanded with other implicit conventions the developers have used. 104. [bug] Made compress_add and compress_find static to lib/dns/compress.c. 103. [func] libisc buffer API changes for : Added: isc_buffer_base(b) (pointer) isc_buffer_current(b) (pointer) isc_buffer_active(b) (pointer) isc_buffer_used(b) (pointer) isc_buffer_length(b) (int) isc_buffer_usedlength(b) (int) isc_buffer_consumedlength(b) (int) isc_buffer_remaininglength(b) (int) isc_buffer_activelength(b) (int) isc_buffer_availablelength(b) (int) Removed: ISC_BUFFER_USEDCOUNT(b) ISC_BUFFER_AVAILABLECOUNT(b) isc_buffer_type(b) Changed names: isc_buffer_used(b, r) -> isc_buffer_usedregion(b, r) isc_buffer_available(b, r) -> isc_buffer_available_region(b, r) isc_buffer_consumed(b, r) -> isc_buffer_consumedregion(b, r) isc_buffer_active(b, r) -> isc_buffer_activeregion(b, r) isc_buffer_remaining(b, r) -> isc_buffer_remainingregion(b, r) Buffer types were removed, so the ISC_BUFFERTYPE_* macros are no more, and the type argument to isc_buffer_init and isc_buffer_allocate were removed. isc_buffer_putstr is now void (instead of isc_result_t) and requires that the caller ensure that there is enough available buffer space for the string. 102. [port] Correctly detect inet_aton, inet_pton and inet_ptop on BSD/OS 4.1. 101. [cleanup] Quieted EGCS warnings from lib/isc/print.c. 100. [cleanup] does not need or . isc_random_t moved to . 99. [cleanup] Rate limiter now has separate shutdown() and destroy() functions, and it guarantees that all queued events are delivered even in the shutdown case. 98. [cleanup] does not need or unless ISC_PLATFORM_NEEDVSNPRINTF is defined. 97. [cleanup] does not need or . 96. [cleanup] does not need . 95. [cleanup] does not need . 94. [cleanup] Some installed header files did not compile as C++. 93. [cleanup] does not need . 92. [cleanup] does not need , , or . 91. [cleanup] does not need or . 90. [cleanup] Removed unneeded ISC_LANG_BEGINDECLS/ISC_LANG_ENDDECLS from . 89. [cleanup] does not need . 88. [cleanup] does not need or . isc_interface_t and isc_interfaceiter_t moved to . 87. [cleanup] does not need , or . 86. [cleanup] isc_bufferlist_t moved from to . 85. [cleanup] does not need , , , or . 84. [func] allow-query ACL checks now apply to all data added to a response. 83. [func] If the server is authoritative for both a delegating zone and its (nonsecure) delegatee, and a query is made for a KEY RR at the top of the delegatee, then the server will look for a KEY in the delegator if it is not found in the delegatee. 82. [cleanup] does not need . 81. [cleanup] and do not need . 80. [cleanup] does not need or . 79. [cleanup] does not need . 78. [cleanup] lwres_conftest renamed to lwresconf_test for consistency with other *_test programs. 77. [cleanup] typedef of isc_time_t and isc_interval_t moved from to . 76. [cleanup] Rewrote keygen. 75. [func] Don't load a zone if its database file is older than the last time the zone was loaded. 74. [cleanup] Removed mktemplate.o and ufile.o from libisc.a, subsumed by file.o. 73. [func] New "file" API in libisc, including new function isc_file_getmodtime, isc_mktemplate renamed to isc_file_mktemplate and isc_ufile renamed to isc_file_openunique. By no means an exhaustive API, it is just what's needed for now. 72. [func] DNS_RBTFIND_NOPREDECESSOR and DNS_RBTFIND_NOOPTIONS added for dns_rbt_findnode, the former to disable the setting of the chain to the predecessor, and the latter to make clear when no options are set. 71. [cleanup] Made explicit the implicit REQUIREs of isc_time_seconds, isc_time_nanoseconds, and isc_time_subtract. 70. [func] isc_time_set() added. 69. [bug] The zone object's master and also-notify lists grew longer with each server reload. 68. [func] Partial support for SIG(0) on incoming messages. 67. [performance] Allow use of alternate (compile-time supplied) OpenSSL libraries/headers. 66. [func] Data in authoritative zones should have a trust level beyond secure. 65. [cleanup] Removed obsolete typedef of dns_zone_callbackarg_t from . 64. [func] The RBT, DB, and zone table APIs now allow the caller find the most-enclosing superdomain of a name. 63. [func] Generate NOTIFY messages. 62. [func] Add UDP refresh support. 61. [cleanup] Use single quotes consistently in log messages. 60. [func] Catch and disallow singleton types on message parse. 59. [bug] Cause net/host unreachable to be a hard error when sending and receiving. 58. [bug] bin/named/query.c could sometimes trigger the (client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) == 0 assertion in query_newname(). 57. [func] Added dns_nxt_typepresent() 56. [bug] SIG records were not properly returned in cached negative answers. 55. [bug] Responses containing multiple names in the authority section were not negatively cached. 54. [bug] If a fetch with sigrdataset==NULL joined one with sigrdataset!=NULL or vice versa, the resolver could catch an assertion or lose signature data, respectively. 53. [port] freebsd 4.0: lib/isc/unix/socket.c requires . 52. [bug] rndc: taskmgr and socketmgr were not initialized to NULL. 51. [cleanup] dns/compress.h and dns/zt.h did not need to include dns/rbt.h; it was needed only by compress.c and zt.c. 50. [func] RBT deletion no longer requires a valid chain to work, and dns_rbt_deletenode was added. 49. [func] Each cache now has its own mctx. 48. [func] isc_task_create() no longer takes an mctx. isc_task_mem() has been eliminated. 47. [func] A number of modules now use memory context reference counting. 46. [func] Memory contexts are now reference counted. Added isc_mem_inuse() and isc_mem_preallocate(). Renamed isc_mem_destroy_check() to isc_mem_setdestroycheck(). 45. [bug] The trusted-key statement incorrectly loaded keys. 44. [bug] Don't include authority data if it would force us to unset the AD bit in the message. 43. [bug] DNSSEC verification of cached rdatasets was failing. 42. [cleanup] Simplified logging of messages with embedded domain names by introducing a new convenience function dns_name_format(). 41. [func] Use PR_SET_KEEPCAPS on Linux 2.3.99-pre3 and later to allow 'named' to run as a non-root user while retaining the ability to bind() to privileged ports. 40. [func] Introduced new logging category "dnssec" and logging module "dns/validator". 39. [cleanup] Moved the typedefs for isc_region_t, isc_textregion_t, and isc_lex_t to . 38. [bug] TSIG signed incoming zone transfers work now. 37. [bug] If the first RR in an incoming zone transfer was not an SOA, the server died with an assertion failure instead of just reporting an error. 36. [cleanup] Change DNS_R_SUCCESS (and others) to ISC_R_SUCCESS 35. [performance] Log messages which are of a level too high to be logged by any channel in the logging configuration will not cause the log mutex to be locked. 34. [bug] Recursion was allowed even with 'recursion no'. 33. [func] The RBT now maintains a parent pointer at each node. 32. [cleanup] bin/lwresd/client.c needs for memset() prototype. 31. [bug] Use ${LIBTOOL} to compile bin/named/main.@O@. 30. [func] config file grammar change to support optional class type for a view. 29. [func] support new config file view options: auth-nxdomain recursion query-source query-source-v6 transfer-source transfer-source-v6 max-transfer-time-out max-transfer-idle-out transfer-format request-ixfr provide-ixfr cleaning-interval fetch-glue notify rfc2308-type1 lame-ttl max-ncache-ttl min-roots 28. [func] support lame-ttl, min-roots and serial-queries config global options. 27. [bug] Only include on BSD/OS 4.[01]*. Including it on other platforms (eg, NetBSD) can cause a forced #error from the C preprocessor. 26. [func] new match-clients statement in config file view. 25. [bug] make install failed to install and . 24. [cleanup] Eliminate some unnecessary #includes of header files from header files. 23. [cleanup] Provide more context in log messages about client requests, using a new function ns_client_log(). 22. [bug] SIGs weren't returned in the answer section when the query resulted in a fetch. 21. [port] Look at STD_CINCLUDES after CINCLUDES during compilation, so additional system include directories can be searched but header files in the bind9 source tree with conflicting names take precedence. This avoids issues with installed versions of dnssafe and openssl. 20. [func] Configuration file post-load validation of zones failed if there were no zones. 19. [bug] dns_zone_notifyreceive() failed to unlock the zone lock in certain error cases. 18. [bug] Use AC_TRY_LINK rather than AC_TRY_COMPILE in configure.in to check for presence of in6addr_any. 17. [func] Do configuration file post-load validation of zones. 16. [bug] put quotes around key names on config file output to avoid possible keyword clashes. 15. [func] Add dns_name_dupwithoffsets(). This function is improves comparison performance for duped names. 14. [bug] free_rbtdb() could have 'put' unallocated memory in an unlikely error path. 13. [bug] lib/dns/master.c and lib/dns/xfrin.c didn't ignore out-of-zone data. 12. [bug] Fixed possible uninitialized variable error. 11. [bug] axfr_rrstream_first() didn't check the result code of db_rr_iterator_first(), possibly causing an assertion to be triggered later. 10. [bug] A bug in the code which makes EDNS0 OPT records in bin/named/client.c and lib/dns/resolver.c could trigger an assertion. 9. [cleanup] replaced bit-setting code in confctx.c and replaced repeated code with macro calls. 8. [bug] Shutdown of incoming zone transfer accessed freed memory. 7. [cleanup] removed 'listen-on' from view statement. 6. [bug] quote RR names when generating config file to prevent possible clash with config file keywords (such as 'key'). 5. [func] syntax change to named.conf file: new ssu grant/deny statements must now be enclosed by an 'update-policy' block. 4. [port] bin/named/unix/os.c didn't compile on systems with linux 2.3 kernel includes due to conflicts between C library includes and the kernel includes. We now get only what we need from , and avoid pulling in other linux kernel .h files. 3. [bug] TKEYs go in the answer section of responses, not the additional section. 2. [bug] Generating cryptographic randomness failed on systems without /dev/random. 1. [bug] The installdirs rule in lib/isc/unix/include/isc/Makefile.in had a typo which prevented the isc directory from being created if it didn't exist. --- 9.0.0b2 released --- # This tells Emacs to use hard tabs in this file. # Local Variables: # indent-tabs-mode: t # End: Index: stable/8/contrib/bind9/bin/named/config.c =================================================================== --- stable/8/contrib/bind9/bin/named/config.c (revision 275668) +++ stable/8/contrib/bind9/bin/named/config.c (revision 275669) @@ -1,854 +1,854 @@ /* * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2001-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: config.c,v 1.113.16.2 2011/02/28 01:19:58 tbox Exp $ */ - /*! \file */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bind.keys.h" /*% default configuration */ static char defaultconf[] = "\ options {\n\ # blackhole {none;};\n" #ifndef WIN32 " coresize default;\n\ datasize default;\n\ files unlimited;\n\ stacksize default;\n" #endif "# session-keyfile \"" NS_LOCALSTATEDIR "/run/named/session.key\";\n\ session-keyname local-ddns;\n\ session-keyalg hmac-sha256;\n\ deallocate-on-exit true;\n\ # directory \n\ dump-file \"named_dump.db\";\n\ fake-iquery no;\n\ has-old-clients false;\n\ heartbeat-interval 60;\n\ host-statistics no;\n\ interface-interval 60;\n\ listen-on {any;};\n\ listen-on-v6 {none;};\n\ match-mapped-addresses no;\n\ memstatistics-file \"named.memstats\";\n\ multiple-cnames no;\n\ # named-xfer ;\n\ # pid-file \"" NS_LOCALSTATEDIR "/run/named/named.pid\"; /* or /lwresd.pid */\n\ bindkeys-file \"" NS_SYSCONFDIR "/bind.keys\";\n\ port 53;\n\ recursing-file \"named.recursing\";\n\ secroots-file \"named.secroots\";\n\ " #ifdef PATH_RANDOMDEV "\ random-device \"" PATH_RANDOMDEV "\";\n\ " #endif "\ recursive-clients 1000;\n\ resolver-query-timeout 10;\n\ rrset-order {type NS order random; order cyclic; };\n\ serial-queries 20;\n\ serial-query-rate 20;\n\ server-id none;\n\ statistics-file \"named.stats\";\n\ statistics-interval 60;\n\ tcp-clients 100;\n\ tcp-listen-queue 10;\n\ # tkey-dhkey \n\ # tkey-gssapi-credential \n\ # tkey-domain \n\ transfers-per-ns 2;\n\ transfers-in 10;\n\ transfers-out 10;\n\ treat-cr-as-space true;\n\ use-id-pool true;\n\ use-ixfr true;\n\ edns-udp-size 4096;\n\ max-udp-size 4096;\n\ request-nsid false;\n\ reserved-sockets 512;\n\ \n\ /* DLV */\n\ dnssec-lookaside . trust-anchor dlv.isc.org;\n\ \n\ /* view */\n\ allow-notify {none;};\n\ allow-update-forwarding {none;};\n\ allow-query-cache { localnets; localhost; };\n\ allow-query-cache-on { any; };\n\ allow-recursion { localnets; localhost; };\n\ allow-recursion-on { any; };\n\ # allow-v6-synthesis ;\n\ # sortlist \n\ # topology \n\ auth-nxdomain false;\n\ minimal-responses false;\n\ recursion true;\n\ provide-ixfr true;\n\ request-ixfr true;\n\ fetch-glue no;\n\ rfc2308-type1 no;\n\ additional-from-auth true;\n\ additional-from-cache true;\n\ query-source address *;\n\ query-source-v6 address *;\n\ notify-source *;\n\ notify-source-v6 *;\n\ cleaning-interval 0; /* now meaningless */\n\ min-roots 2;\n\ lame-ttl 600;\n\ max-ncache-ttl 10800; /* 3 hours */\n\ max-cache-ttl 604800; /* 1 week */\n\ transfer-format many-answers;\n\ max-cache-size 0;\n\ check-names master fail;\n\ check-names slave warn;\n\ check-names response ignore;\n\ check-dup-records warn;\n\ check-mx warn;\n\ check-spf warn;\n\ acache-enable no;\n\ acache-cleaning-interval 60;\n\ max-acache-size 16M;\n\ dnssec-enable yes;\n\ dnssec-validation yes; \n\ dnssec-accept-expired no;\n\ clients-per-query 10;\n\ max-clients-per-query 100;\n\ + max-recursion-depth 7;\n\ + max-recursion-queries 50;\n\ zero-no-soa-ttl-cache no;\n\ nsec3-test-zone no;\n\ allow-new-zones no;\n\ " #ifdef ALLOW_FILTER_AAAA_ON_V4 " filter-aaaa-on-v4 no;\n\ filter-aaaa { any; };\n\ " #endif " /* zone */\n\ allow-query {any;};\n\ allow-query-on {any;};\n\ allow-transfer {any;};\n\ notify yes;\n\ # also-notify \n\ notify-delay 5;\n\ notify-to-soa no;\n\ dialup no;\n\ # forward \n\ # forwarders \n\ maintain-ixfr-base no;\n\ # max-ixfr-log-size \n\ transfer-source *;\n\ transfer-source-v6 *;\n\ alt-transfer-source *;\n\ alt-transfer-source-v6 *;\n\ max-transfer-time-in 120;\n\ max-transfer-time-out 120;\n\ max-transfer-idle-in 60;\n\ max-transfer-idle-out 60;\n\ max-retry-time 1209600; /* 2 weeks */\n\ min-retry-time 500;\n\ max-refresh-time 2419200; /* 4 weeks */\n\ min-refresh-time 300;\n\ multi-master no;\n\ dnssec-secure-to-insecure no;\n\ sig-validity-interval 30; /* days */\n\ sig-signing-nodes 100;\n\ sig-signing-signatures 10;\n\ sig-signing-type 65534;\n\ zone-statistics false;\n\ max-journal-size unlimited;\n\ ixfr-from-differences false;\n\ check-wildcard yes;\n\ check-sibling yes;\n\ check-integrity yes;\n\ check-mx-cname warn;\n\ check-srv-cname warn;\n\ zero-no-soa-ttl yes;\n\ update-check-ksk yes;\n\ dnssec-dnskey-kskonly no;\n\ try-tcp-refresh yes; /* BIND 8 compat */\n\ };\n\ " "#\n\ # Zones in the \"_bind\" view are NOT counted in the count of zones.\n\ #\n\ view \"_bind\" chaos {\n\ recursion no;\n\ notify no;\n\ allow-new-zones no;\n\ \n\ zone \"version.bind\" chaos {\n\ type master;\n\ database \"_builtin version\";\n\ };\n\ \n\ zone \"hostname.bind\" chaos {\n\ type master;\n\ database \"_builtin hostname\";\n\ };\n\ \n\ zone \"authors.bind\" chaos {\n\ type master;\n\ database \"_builtin authors\";\n\ };\n\ \n\ zone \"id.server\" chaos {\n\ type master;\n\ database \"_builtin id\";\n\ };\n\ };\n\ " "#\n\ # Default trusted key(s) for builtin DLV support\n\ # (used if \"dnssec-lookaside auto;\" is set and\n\ # sysconfdir/bind.keys doesn't exist).\n\ #\n\ # BEGIN MANAGED KEYS\n" /* Imported from bind.keys.h: */ MANAGED_KEYS "# END MANAGED KEYS\n\ "; isc_result_t ns_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf) { isc_buffer_t b; isc_buffer_init(&b, defaultconf, sizeof(defaultconf) - 1); isc_buffer_add(&b, sizeof(defaultconf) - 1); return (cfg_parse_buffer(parser, &b, &cfg_type_namedconf, conf)); } isc_result_t ns_config_get(const cfg_obj_t **maps, const char *name, const cfg_obj_t **obj) { int i; for (i = 0;; i++) { if (maps[i] == NULL) return (ISC_R_NOTFOUND); if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS) return (ISC_R_SUCCESS); } } isc_result_t ns_checknames_get(const cfg_obj_t **maps, const char *which, const cfg_obj_t **obj) { const cfg_listelt_t *element; const cfg_obj_t *checknames; const cfg_obj_t *type; const cfg_obj_t *value; int i; for (i = 0;; i++) { if (maps[i] == NULL) return (ISC_R_NOTFOUND); checknames = NULL; if (cfg_map_get(maps[i], "check-names", &checknames) == ISC_R_SUCCESS) { /* * Zone map entry is not a list. */ if (checknames != NULL && !cfg_obj_islist(checknames)) { *obj = checknames; return (ISC_R_SUCCESS); } for (element = cfg_list_first(checknames); element != NULL; element = cfg_list_next(element)) { value = cfg_listelt_value(element); type = cfg_tuple_get(value, "type"); if (strcasecmp(cfg_obj_asstring(type), which) == 0) { *obj = cfg_tuple_get(value, "mode"); return (ISC_R_SUCCESS); } } } } } int ns_config_listcount(const cfg_obj_t *list) { const cfg_listelt_t *e; int i = 0; for (e = cfg_list_first(list); e != NULL; e = cfg_list_next(e)) i++; return (i); } isc_result_t ns_config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass, dns_rdataclass_t *classp) { isc_textregion_t r; isc_result_t result; if (!cfg_obj_isstring(classobj)) { *classp = defclass; return (ISC_R_SUCCESS); } DE_CONST(cfg_obj_asstring(classobj), r.base); r.length = strlen(r.base); result = dns_rdataclass_fromtext(classp, &r); if (result != ISC_R_SUCCESS) cfg_obj_log(classobj, ns_g_lctx, ISC_LOG_ERROR, "unknown class '%s'", r.base); return (result); } isc_result_t ns_config_gettype(const cfg_obj_t *typeobj, dns_rdatatype_t deftype, dns_rdatatype_t *typep) { isc_textregion_t r; isc_result_t result; if (!cfg_obj_isstring(typeobj)) { *typep = deftype; return (ISC_R_SUCCESS); } DE_CONST(cfg_obj_asstring(typeobj), r.base); r.length = strlen(r.base); result = dns_rdatatype_fromtext(typep, &r); if (result != ISC_R_SUCCESS) cfg_obj_log(typeobj, ns_g_lctx, ISC_LOG_ERROR, "unknown type '%s'", r.base); return (result); } dns_zonetype_t ns_config_getzonetype(const cfg_obj_t *zonetypeobj) { dns_zonetype_t ztype = dns_zone_none; const char *str; str = cfg_obj_asstring(zonetypeobj); if (strcasecmp(str, "master") == 0) ztype = dns_zone_master; else if (strcasecmp(str, "slave") == 0) ztype = dns_zone_slave; else if (strcasecmp(str, "stub") == 0) ztype = dns_zone_stub; else if (strcasecmp(str, "static-stub") == 0) ztype = dns_zone_staticstub; else INSIST(0); return (ztype); } isc_result_t ns_config_getiplist(const cfg_obj_t *config, const cfg_obj_t *list, in_port_t defport, isc_mem_t *mctx, isc_sockaddr_t **addrsp, isc_uint32_t *countp) { int count, i = 0; const cfg_obj_t *addrlist; const cfg_obj_t *portobj; const cfg_listelt_t *element; isc_sockaddr_t *addrs; in_port_t port; isc_result_t result; INSIST(addrsp != NULL && *addrsp == NULL); INSIST(countp != NULL); addrlist = cfg_tuple_get(list, "addresses"); count = ns_config_listcount(addrlist); portobj = cfg_tuple_get(list, "port"); if (cfg_obj_isuint32(portobj)) { isc_uint32_t val = cfg_obj_asuint32(portobj); if (val > ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port '%u' out of range", val); return (ISC_R_RANGE); } port = (in_port_t) val; } else if (defport != 0) port = defport; else { result = ns_config_getport(config, &port); if (result != ISC_R_SUCCESS) return (result); } addrs = isc_mem_get(mctx, count * sizeof(isc_sockaddr_t)); if (addrs == NULL) return (ISC_R_NOMEMORY); for (element = cfg_list_first(addrlist); element != NULL; element = cfg_list_next(element), i++) { INSIST(i < count); addrs[i] = *cfg_obj_assockaddr(cfg_listelt_value(element)); if (isc_sockaddr_getport(&addrs[i]) == 0) isc_sockaddr_setport(&addrs[i], port); } INSIST(i == count); *addrsp = addrs; *countp = count; return (ISC_R_SUCCESS); } void ns_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp, isc_uint32_t count) { INSIST(addrsp != NULL && *addrsp != NULL); isc_mem_put(mctx, *addrsp, count * sizeof(isc_sockaddr_t)); *addrsp = NULL; } static isc_result_t get_masters_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) { isc_result_t result; const cfg_obj_t *masters = NULL; const cfg_listelt_t *elt; result = cfg_map_get(cctx, "masters", &masters); if (result != ISC_R_SUCCESS) return (result); for (elt = cfg_list_first(masters); elt != NULL; elt = cfg_list_next(elt)) { const cfg_obj_t *list; const char *listname; list = cfg_listelt_value(elt); listname = cfg_obj_asstring(cfg_tuple_get(list, "name")); if (strcasecmp(listname, name) == 0) { *ret = list; return (ISC_R_SUCCESS); } } return (ISC_R_NOTFOUND); } isc_result_t ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list, isc_mem_t *mctx, isc_sockaddr_t **addrsp, dns_name_t ***keysp, isc_uint32_t *countp) { isc_uint32_t addrcount = 0, keycount = 0, i = 0; isc_uint32_t listcount = 0, l = 0, j; isc_uint32_t stackcount = 0, pushed = 0; isc_result_t result; const cfg_listelt_t *element; const cfg_obj_t *addrlist; const cfg_obj_t *portobj; in_port_t port; dns_fixedname_t fname; isc_sockaddr_t *addrs = NULL; dns_name_t **keys = NULL; struct { const char *name; } *lists = NULL; struct { const cfg_listelt_t *element; in_port_t port; } *stack = NULL; REQUIRE(addrsp != NULL && *addrsp == NULL); REQUIRE(keysp != NULL && *keysp == NULL); REQUIRE(countp != NULL); newlist: addrlist = cfg_tuple_get(list, "addresses"); portobj = cfg_tuple_get(list, "port"); if (cfg_obj_isuint32(portobj)) { isc_uint32_t val = cfg_obj_asuint32(portobj); if (val > ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port '%u' out of range", val); result = ISC_R_RANGE; goto cleanup; } port = (in_port_t) val; } else { result = ns_config_getport(config, &port); if (result != ISC_R_SUCCESS) goto cleanup; } result = ISC_R_NOMEMORY; element = cfg_list_first(addrlist); resume: for ( ; element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *addr; const cfg_obj_t *key; const char *keystr; isc_buffer_t b; addr = cfg_tuple_get(cfg_listelt_value(element), "masterselement"); key = cfg_tuple_get(cfg_listelt_value(element), "key"); if (!cfg_obj_issockaddr(addr)) { const char *listname = cfg_obj_asstring(addr); isc_result_t tresult; /* Grow lists? */ if (listcount == l) { void * new; isc_uint32_t newlen = listcount + 16; size_t newsize, oldsize; newsize = newlen * sizeof(*lists); oldsize = listcount * sizeof(*lists); new = isc_mem_get(mctx, newsize); if (new == NULL) goto cleanup; if (listcount != 0) { memmove(new, lists, oldsize); isc_mem_put(mctx, lists, oldsize); } lists = new; listcount = newlen; } /* Seen? */ for (j = 0; j < l; j++) if (strcasecmp(lists[j].name, listname) == 0) break; if (j < l) continue; tresult = get_masters_def(config, listname, &list); if (tresult == ISC_R_NOTFOUND) { cfg_obj_log(addr, ns_g_lctx, ISC_LOG_ERROR, "masters \"%s\" not found", listname); result = tresult; goto cleanup; } if (tresult != ISC_R_SUCCESS) goto cleanup; lists[l++].name = listname; /* Grow stack? */ if (stackcount == pushed) { void * new; isc_uint32_t newlen = stackcount + 16; size_t newsize, oldsize; newsize = newlen * sizeof(*stack); oldsize = stackcount * sizeof(*stack); new = isc_mem_get(mctx, newsize); if (new == NULL) goto cleanup; if (stackcount != 0) { memmove(new, stack, oldsize); isc_mem_put(mctx, stack, oldsize); } stack = new; stackcount = newlen; } /* * We want to resume processing this list on the * next element. */ stack[pushed].element = cfg_list_next(element); stack[pushed].port = port; pushed++; goto newlist; } if (i == addrcount) { void * new; isc_uint32_t newlen = addrcount + 16; size_t newsize, oldsize; newsize = newlen * sizeof(isc_sockaddr_t); oldsize = addrcount * sizeof(isc_sockaddr_t); new = isc_mem_get(mctx, newsize); if (new == NULL) goto cleanup; if (addrcount != 0) { memmove(new, addrs, oldsize); isc_mem_put(mctx, addrs, oldsize); } addrs = new; addrcount = newlen; newsize = newlen * sizeof(dns_name_t *); oldsize = keycount * sizeof(dns_name_t *); new = isc_mem_get(mctx, newsize); if (new == NULL) goto cleanup; if (keycount != 0) { memmove(new, keys, oldsize); isc_mem_put(mctx, keys, oldsize); } keys = new; keycount = newlen; } addrs[i] = *cfg_obj_assockaddr(addr); if (isc_sockaddr_getport(&addrs[i]) == 0) isc_sockaddr_setport(&addrs[i], port); keys[i] = NULL; i++; /* Increment here so that cleanup on error works. */ if (!cfg_obj_isstring(key)) continue; keys[i - 1] = isc_mem_get(mctx, sizeof(dns_name_t)); if (keys[i - 1] == NULL) goto cleanup; dns_name_init(keys[i - 1], NULL); keystr = cfg_obj_asstring(key); isc_buffer_constinit(&b, keystr, strlen(keystr)); isc_buffer_add(&b, strlen(keystr)); dns_fixedname_init(&fname); result = dns_name_fromtext(dns_fixedname_name(&fname), &b, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_name_dup(dns_fixedname_name(&fname), mctx, keys[i - 1]); if (result != ISC_R_SUCCESS) goto cleanup; } if (pushed != 0) { pushed--; element = stack[pushed].element; port = stack[pushed].port; goto resume; } if (i < addrcount) { void * new; size_t newsize, oldsize; newsize = i * sizeof(isc_sockaddr_t); oldsize = addrcount * sizeof(isc_sockaddr_t); if (i != 0) { new = isc_mem_get(mctx, newsize); if (new == NULL) goto cleanup; memmove(new, addrs, newsize); } else new = NULL; isc_mem_put(mctx, addrs, oldsize); addrs = new; addrcount = i; newsize = i * sizeof(dns_name_t *); oldsize = keycount * sizeof(dns_name_t *); if (i != 0) { new = isc_mem_get(mctx, newsize); if (new == NULL) goto cleanup; memmove(new, keys, newsize); } else new = NULL; isc_mem_put(mctx, keys, oldsize); keys = new; keycount = i; } if (lists != NULL) isc_mem_put(mctx, lists, listcount * sizeof(*lists)); if (stack != NULL) isc_mem_put(mctx, stack, stackcount * sizeof(*stack)); INSIST(keycount == addrcount); *addrsp = addrs; *keysp = keys; *countp = addrcount; return (ISC_R_SUCCESS); cleanup: if (addrs != NULL) isc_mem_put(mctx, addrs, addrcount * sizeof(isc_sockaddr_t)); if (keys != NULL) { for (j = 0; j < i; j++) { if (keys[j] == NULL) continue; if (dns_name_dynamic(keys[j])) dns_name_free(keys[j], mctx); isc_mem_put(mctx, keys[j], sizeof(dns_name_t)); } isc_mem_put(mctx, keys, keycount * sizeof(dns_name_t *)); } if (lists != NULL) isc_mem_put(mctx, lists, listcount * sizeof(*lists)); if (stack != NULL) isc_mem_put(mctx, stack, stackcount * sizeof(*stack)); return (result); } void ns_config_putipandkeylist(isc_mem_t *mctx, isc_sockaddr_t **addrsp, dns_name_t ***keysp, isc_uint32_t count) { unsigned int i; dns_name_t **keys = *keysp; INSIST(addrsp != NULL && *addrsp != NULL); isc_mem_put(mctx, *addrsp, count * sizeof(isc_sockaddr_t)); for (i = 0; i < count; i++) { if (keys[i] == NULL) continue; if (dns_name_dynamic(keys[i])) dns_name_free(keys[i], mctx); isc_mem_put(mctx, keys[i], sizeof(dns_name_t)); } isc_mem_put(mctx, *keysp, count * sizeof(dns_name_t *)); *addrsp = NULL; *keysp = NULL; } isc_result_t ns_config_getport(const cfg_obj_t *config, in_port_t *portp) { const cfg_obj_t *maps[3]; const cfg_obj_t *options = NULL; const cfg_obj_t *portobj = NULL; isc_result_t result; int i; (void)cfg_map_get(config, "options", &options); i = 0; if (options != NULL) maps[i++] = options; maps[i++] = ns_g_defaults; maps[i] = NULL; result = ns_config_get(maps, "port", &portobj); INSIST(result == ISC_R_SUCCESS); if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port '%u' out of range", cfg_obj_asuint32(portobj)); return (ISC_R_RANGE); } *portp = (in_port_t)cfg_obj_asuint32(portobj); return (ISC_R_SUCCESS); } struct keyalgorithms { const char *str; enum { hmacnone, hmacmd5, hmacsha1, hmacsha224, hmacsha256, hmacsha384, hmacsha512 } hmac; unsigned int type; isc_uint16_t size; } algorithms[] = { { "hmac-md5", hmacmd5, DST_ALG_HMACMD5, 128 }, { "hmac-md5.sig-alg.reg.int", hmacmd5, DST_ALG_HMACMD5, 0 }, { "hmac-md5.sig-alg.reg.int.", hmacmd5, DST_ALG_HMACMD5, 0 }, { "hmac-sha1", hmacsha1, DST_ALG_HMACSHA1, 160 }, { "hmac-sha224", hmacsha224, DST_ALG_HMACSHA224, 224 }, { "hmac-sha256", hmacsha256, DST_ALG_HMACSHA256, 256 }, { "hmac-sha384", hmacsha384, DST_ALG_HMACSHA384, 384 }, { "hmac-sha512", hmacsha512, DST_ALG_HMACSHA512, 512 }, { NULL, hmacnone, DST_ALG_UNKNOWN, 0 } }; isc_result_t ns_config_getkeyalgorithm(const char *str, dns_name_t **name, isc_uint16_t *digestbits) { return (ns_config_getkeyalgorithm2(str, name, NULL, digestbits)); } isc_result_t ns_config_getkeyalgorithm2(const char *str, dns_name_t **name, unsigned int *typep, isc_uint16_t *digestbits) { int i; size_t len = 0; isc_uint16_t bits; isc_result_t result; for (i = 0; algorithms[i].str != NULL; i++) { len = strlen(algorithms[i].str); if (strncasecmp(algorithms[i].str, str, len) == 0 && (str[len] == '\0' || (algorithms[i].size != 0 && str[len] == '-'))) break; } if (algorithms[i].str == NULL) return (ISC_R_NOTFOUND); if (str[len] == '-') { result = isc_parse_uint16(&bits, str + len + 1, 10); if (result != ISC_R_SUCCESS) return (result); if (bits > algorithms[i].size) return (ISC_R_RANGE); } else if (algorithms[i].size == 0) bits = 128; else bits = algorithms[i].size; if (name != NULL) { switch (algorithms[i].hmac) { case hmacmd5: *name = dns_tsig_hmacmd5_name; break; case hmacsha1: *name = dns_tsig_hmacsha1_name; break; case hmacsha224: *name = dns_tsig_hmacsha224_name; break; case hmacsha256: *name = dns_tsig_hmacsha256_name; break; case hmacsha384: *name = dns_tsig_hmacsha384_name; break; case hmacsha512: *name = dns_tsig_hmacsha512_name; break; default: INSIST(0); } } if (typep != NULL) *typep = algorithms[i].type; if (digestbits != NULL) *digestbits = bits; return (ISC_R_SUCCESS); } Index: stable/8/contrib/bind9/bin/named/query.c =================================================================== --- stable/8/contrib/bind9/bin/named/query.c (revision 275668) +++ stable/8/contrib/bind9/bin/named/query.c (revision 275669) @@ -1,7450 +1,7449 @@ /* * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: query.c,v 1.353.8.24 2012/02/07 01:14:39 marka Exp $ */ /*! \file */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 0 /* * It has been recommended that DNS64 be changed to return excluded * AAAA addresses if DNS64 synthesis does not occur. This minimises * the impact on the lookup results. While most DNS AAAA lookups are * done to send IP packets to a host, not all of them are and filtering * excluded addresses has a negative impact on those uses. */ #define dns64_bis_return_excluded_addresses 1 #endif /*% Partial answer? */ #define PARTIALANSWER(c) (((c)->query.attributes & \ NS_QUERYATTR_PARTIALANSWER) != 0) /*% Use Cache? */ #define USECACHE(c) (((c)->query.attributes & \ NS_QUERYATTR_CACHEOK) != 0) /*% Recursion OK? */ #define RECURSIONOK(c) (((c)->query.attributes & \ NS_QUERYATTR_RECURSIONOK) != 0) /*% Recursing? */ #define RECURSING(c) (((c)->query.attributes & \ NS_QUERYATTR_RECURSING) != 0) /*% Cache glue ok? */ #define CACHEGLUEOK(c) (((c)->query.attributes & \ NS_QUERYATTR_CACHEGLUEOK) != 0) /*% Want Recursion? */ #define WANTRECURSION(c) (((c)->query.attributes & \ NS_QUERYATTR_WANTRECURSION) != 0) /*% Want DNSSEC? */ #define WANTDNSSEC(c) (((c)->attributes & \ NS_CLIENTATTR_WANTDNSSEC) != 0) /*% Want WANTAD? */ #define WANTAD(c) (((c)->attributes & \ NS_CLIENTATTR_WANTAD) != 0) /*% No authority? */ #define NOAUTHORITY(c) (((c)->query.attributes & \ NS_QUERYATTR_NOAUTHORITY) != 0) /*% No additional? */ #define NOADDITIONAL(c) (((c)->query.attributes & \ NS_QUERYATTR_NOADDITIONAL) != 0) /*% Secure? */ #define SECURE(c) (((c)->query.attributes & \ NS_QUERYATTR_SECURE) != 0) /*% DNS64 A lookup? */ #define DNS64(c) (((c)->query.attributes & \ NS_QUERYATTR_DNS64) != 0) #define DNS64EXCLUDE(c) (((c)->query.attributes & \ NS_QUERYATTR_DNS64EXCLUDE) != 0) /*% No QNAME Proof? */ #define NOQNAME(r) (((r)->attributes & \ DNS_RDATASETATTR_NOQNAME) != 0) #if 0 #define CTRACE(m) isc_log_write(ns_g_lctx, \ NS_LOGCATEGORY_CLIENT, \ NS_LOGMODULE_QUERY, \ ISC_LOG_DEBUG(3), \ "client %p: %s", client, (m)) #define QTRACE(m) isc_log_write(ns_g_lctx, \ NS_LOGCATEGORY_GENERAL, \ NS_LOGMODULE_QUERY, \ ISC_LOG_DEBUG(3), \ "query %p: %s", query, (m)) #else #define CTRACE(m) ((void)m) #define QTRACE(m) ((void)m) #endif #define DNS_GETDB_NOEXACT 0x01U #define DNS_GETDB_NOLOG 0x02U #define DNS_GETDB_PARTIAL 0x04U #define DNS_GETDB_IGNOREACL 0x08U #define PENDINGOK(x) (((x) & DNS_DBFIND_PENDINGOK) != 0) typedef struct client_additionalctx { ns_client_t *client; dns_rdataset_t *rdataset; } client_additionalctx_t; static isc_result_t query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype); static isc_boolean_t validate(ns_client_t *client, dns_db_t *db, dns_name_t *name, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset); static void query_findclosestnsec3(dns_name_t *qname, dns_db_t *db, dns_dbversion_t *version, ns_client_t *client, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, dns_name_t *fname, isc_boolean_t exact, dns_name_t *found); static inline void log_queryerror(ns_client_t *client, isc_result_t result, int line, int level); static void rpz_st_clear(ns_client_t *client); /*% * Increment query statistics counters. */ static inline void inc_stats(ns_client_t *client, isc_statscounter_t counter) { dns_zone_t *zone = client->query.authzone; isc_stats_increment(ns_g_server->nsstats, counter); if (zone != NULL) { isc_stats_t *zonestats = dns_zone_getrequeststats(zone); if (zonestats != NULL) isc_stats_increment(zonestats, counter); } } static void query_send(ns_client_t *client) { isc_statscounter_t counter; if ((client->message->flags & DNS_MESSAGEFLAG_AA) == 0) inc_stats(client, dns_nsstatscounter_nonauthans); else inc_stats(client, dns_nsstatscounter_authans); if (client->message->rcode == dns_rcode_noerror) { if (ISC_LIST_EMPTY(client->message->sections[DNS_SECTION_ANSWER])) { if (client->query.isreferral) { counter = dns_nsstatscounter_referral; } else { counter = dns_nsstatscounter_nxrrset; } } else { counter = dns_nsstatscounter_success; } } else if (client->message->rcode == dns_rcode_nxdomain) { counter = dns_nsstatscounter_nxdomain; } else { /* We end up here in case of YXDOMAIN, and maybe others */ counter = dns_nsstatscounter_failure; } inc_stats(client, counter); ns_client_send(client); } static void query_error(ns_client_t *client, isc_result_t result, int line) { int loglevel = ISC_LOG_DEBUG(3); switch (result) { case DNS_R_SERVFAIL: loglevel = ISC_LOG_DEBUG(1); inc_stats(client, dns_nsstatscounter_servfail); break; case DNS_R_FORMERR: inc_stats(client, dns_nsstatscounter_formerr); break; default: inc_stats(client, dns_nsstatscounter_failure); break; } log_queryerror(client, result, line, loglevel); ns_client_error(client, result); } static void query_next(ns_client_t *client, isc_result_t result) { if (result == DNS_R_DUPLICATE) inc_stats(client, dns_nsstatscounter_duplicate); else if (result == DNS_R_DROP) inc_stats(client, dns_nsstatscounter_dropped); else inc_stats(client, dns_nsstatscounter_failure); ns_client_next(client, result); } static inline void query_freefreeversions(ns_client_t *client, isc_boolean_t everything) { ns_dbversion_t *dbversion, *dbversion_next; unsigned int i; for (dbversion = ISC_LIST_HEAD(client->query.freeversions), i = 0; dbversion != NULL; dbversion = dbversion_next, i++) { dbversion_next = ISC_LIST_NEXT(dbversion, link); /* * If we're not freeing everything, we keep the first three * dbversions structures around. */ if (i > 3 || everything) { ISC_LIST_UNLINK(client->query.freeversions, dbversion, link); isc_mem_put(client->mctx, dbversion, sizeof(*dbversion)); } } } void ns_query_cancel(ns_client_t *client) { LOCK(&client->query.fetchlock); if (client->query.fetch != NULL) { dns_resolver_cancelfetch(client->query.fetch); client->query.fetch = NULL; } UNLOCK(&client->query.fetchlock); } static inline void query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) { dns_rdataset_t *rdataset = *rdatasetp; CTRACE("query_putrdataset"); if (rdataset != NULL) { if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); dns_message_puttemprdataset(client->message, rdatasetp); } CTRACE("query_putrdataset: done"); } static inline void query_reset(ns_client_t *client, isc_boolean_t everything) { isc_buffer_t *dbuf, *dbuf_next; ns_dbversion_t *dbversion, *dbversion_next; /*% * Reset the query state of a client to its default state. */ /* * Cancel the fetch if it's running. */ ns_query_cancel(client); /* * Cleanup any active versions. */ for (dbversion = ISC_LIST_HEAD(client->query.activeversions); dbversion != NULL; dbversion = dbversion_next) { dbversion_next = ISC_LIST_NEXT(dbversion, link); dns_db_closeversion(dbversion->db, &dbversion->version, ISC_FALSE); dns_db_detach(&dbversion->db); ISC_LIST_INITANDAPPEND(client->query.freeversions, dbversion, link); } ISC_LIST_INIT(client->query.activeversions); if (client->query.authdb != NULL) dns_db_detach(&client->query.authdb); if (client->query.authzone != NULL) dns_zone_detach(&client->query.authzone); if (client->query.dns64_aaaa != NULL) query_putrdataset(client, &client->query.dns64_aaaa); if (client->query.dns64_sigaaaa != NULL) query_putrdataset(client, &client->query.dns64_sigaaaa); if (client->query.dns64_aaaaok != NULL) { isc_mem_put(client->mctx, client->query.dns64_aaaaok, client->query.dns64_aaaaoklen * sizeof(isc_boolean_t)); client->query.dns64_aaaaok = NULL; client->query.dns64_aaaaoklen = 0; } query_freefreeversions(client, everything); for (dbuf = ISC_LIST_HEAD(client->query.namebufs); dbuf != NULL; dbuf = dbuf_next) { dbuf_next = ISC_LIST_NEXT(dbuf, link); if (dbuf_next != NULL || everything) { ISC_LIST_UNLINK(client->query.namebufs, dbuf, link); isc_buffer_free(&dbuf); } } if (client->query.restarts > 0) { /* * client->query.qname was dynamically allocated. */ dns_message_puttempname(client->message, &client->query.qname); } client->query.qname = NULL; client->query.attributes = (NS_QUERYATTR_RECURSIONOK | NS_QUERYATTR_CACHEOK | NS_QUERYATTR_SECURE); client->query.restarts = 0; client->query.timerset = ISC_FALSE; if (client->query.rpz_st != NULL) { rpz_st_clear(client); if (everything) { isc_mem_put(client->mctx, client->query.rpz_st, sizeof(*client->query.rpz_st)); client->query.rpz_st = NULL; } } client->query.origqname = NULL; client->query.dboptions = 0; client->query.fetchoptions = 0; client->query.gluedb = NULL; client->query.authdbset = ISC_FALSE; client->query.isreferral = ISC_FALSE; client->query.dns64_options = 0; client->query.dns64_ttl = ISC_UINT32_MAX; } static void query_next_callback(ns_client_t *client) { query_reset(client, ISC_FALSE); } void ns_query_free(ns_client_t *client) { query_reset(client, ISC_TRUE); } static inline isc_result_t query_newnamebuf(ns_client_t *client) { isc_buffer_t *dbuf; isc_result_t result; CTRACE("query_newnamebuf"); /*% * Allocate a name buffer. */ dbuf = NULL; result = isc_buffer_allocate(client->mctx, &dbuf, 1024); if (result != ISC_R_SUCCESS) { CTRACE("query_newnamebuf: isc_buffer_allocate failed: done"); return (result); } ISC_LIST_APPEND(client->query.namebufs, dbuf, link); CTRACE("query_newnamebuf: done"); return (ISC_R_SUCCESS); } static inline isc_buffer_t * query_getnamebuf(ns_client_t *client) { isc_buffer_t *dbuf; isc_result_t result; isc_region_t r; CTRACE("query_getnamebuf"); /*% * Return a name buffer with space for a maximal name, allocating * a new one if necessary. */ if (ISC_LIST_EMPTY(client->query.namebufs)) { result = query_newnamebuf(client); if (result != ISC_R_SUCCESS) { CTRACE("query_getnamebuf: query_newnamebuf failed: done"); return (NULL); } } dbuf = ISC_LIST_TAIL(client->query.namebufs); INSIST(dbuf != NULL); isc_buffer_availableregion(dbuf, &r); if (r.length < 255) { result = query_newnamebuf(client); if (result != ISC_R_SUCCESS) { CTRACE("query_getnamebuf: query_newnamebuf failed: done"); return (NULL); } dbuf = ISC_LIST_TAIL(client->query.namebufs); isc_buffer_availableregion(dbuf, &r); INSIST(r.length >= 255); } CTRACE("query_getnamebuf: done"); return (dbuf); } static inline void query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) { isc_region_t r; CTRACE("query_keepname"); /*% * 'name' is using space in 'dbuf', but 'dbuf' has not yet been * adjusted to take account of that. We do the adjustment. */ REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) != 0); dns_name_toregion(name, &r); isc_buffer_add(dbuf, r.length); dns_name_setbuffer(name, NULL); client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED; } static inline void query_releasename(ns_client_t *client, dns_name_t **namep) { dns_name_t *name = *namep; /*% * 'name' is no longer needed. Return it to our pool of temporary * names. If it is using a name buffer, relinquish its exclusive * rights on the buffer. */ CTRACE("query_releasename"); if (dns_name_hasbuffer(name)) { INSIST((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) != 0); client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED; } dns_message_puttempname(client->message, namep); CTRACE("query_releasename: done"); } static inline dns_name_t * query_newname(ns_client_t *client, isc_buffer_t *dbuf, isc_buffer_t *nbuf) { dns_name_t *name; isc_region_t r; isc_result_t result; REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) == 0); CTRACE("query_newname"); name = NULL; result = dns_message_gettempname(client->message, &name); if (result != ISC_R_SUCCESS) { CTRACE("query_newname: dns_message_gettempname failed: done"); return (NULL); } isc_buffer_availableregion(dbuf, &r); isc_buffer_init(nbuf, r.base, r.length); dns_name_init(name, NULL); dns_name_setbuffer(name, nbuf); client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED; CTRACE("query_newname: done"); return (name); } static inline dns_rdataset_t * query_newrdataset(ns_client_t *client) { dns_rdataset_t *rdataset; isc_result_t result; CTRACE("query_newrdataset"); rdataset = NULL; result = dns_message_gettemprdataset(client->message, &rdataset); if (result != ISC_R_SUCCESS) { CTRACE("query_newrdataset: " "dns_message_gettemprdataset failed: done"); return (NULL); } dns_rdataset_init(rdataset); CTRACE("query_newrdataset: done"); return (rdataset); } static inline isc_result_t query_newdbversion(ns_client_t *client, unsigned int n) { unsigned int i; ns_dbversion_t *dbversion; for (i = 0; i < n; i++) { dbversion = isc_mem_get(client->mctx, sizeof(*dbversion)); if (dbversion != NULL) { dbversion->db = NULL; dbversion->version = NULL; ISC_LIST_INITANDAPPEND(client->query.freeversions, dbversion, link); } else { /* * We only return ISC_R_NOMEMORY if we couldn't * allocate anything. */ if (i == 0) return (ISC_R_NOMEMORY); else return (ISC_R_SUCCESS); } } return (ISC_R_SUCCESS); } static inline ns_dbversion_t * query_getdbversion(ns_client_t *client) { isc_result_t result; ns_dbversion_t *dbversion; if (ISC_LIST_EMPTY(client->query.freeversions)) { result = query_newdbversion(client, 1); if (result != ISC_R_SUCCESS) return (NULL); } dbversion = ISC_LIST_HEAD(client->query.freeversions); INSIST(dbversion != NULL); ISC_LIST_UNLINK(client->query.freeversions, dbversion, link); return (dbversion); } isc_result_t ns_query_init(ns_client_t *client) { isc_result_t result; ISC_LIST_INIT(client->query.namebufs); ISC_LIST_INIT(client->query.activeversions); ISC_LIST_INIT(client->query.freeversions); client->query.restarts = 0; client->query.timerset = ISC_FALSE; client->query.rpz_st = NULL; client->query.qname = NULL; result = isc_mutex_init(&client->query.fetchlock); if (result != ISC_R_SUCCESS) return (result); client->query.fetch = NULL; client->query.authdb = NULL; client->query.authzone = NULL; client->query.authdbset = ISC_FALSE; client->query.isreferral = ISC_FALSE; client->query.dns64_aaaa = NULL; client->query.dns64_sigaaaa = NULL; client->query.dns64_aaaaok = NULL; client->query.dns64_aaaaoklen = 0; query_reset(client, ISC_FALSE); result = query_newdbversion(client, 3); if (result != ISC_R_SUCCESS) { DESTROYLOCK(&client->query.fetchlock); return (result); } result = query_newnamebuf(client); if (result != ISC_R_SUCCESS) query_freefreeversions(client, ISC_TRUE); return (result); } static inline ns_dbversion_t * query_findversion(ns_client_t *client, dns_db_t *db) { ns_dbversion_t *dbversion; /*% * We may already have done a query related to this * database. If so, we must be sure to make subsequent * queries from the same version. */ for (dbversion = ISC_LIST_HEAD(client->query.activeversions); dbversion != NULL; dbversion = ISC_LIST_NEXT(dbversion, link)) { if (dbversion->db == db) break; } if (dbversion == NULL) { /* * This is a new zone for this query. Add it to * the active list. */ dbversion = query_getdbversion(client); if (dbversion == NULL) return (NULL); dns_db_attach(db, &dbversion->db); dns_db_currentversion(db, &dbversion->version); dbversion->acl_checked = ISC_FALSE; dbversion->queryok = ISC_FALSE; ISC_LIST_APPEND(client->query.activeversions, dbversion, link); } return (dbversion); } static inline isc_result_t query_validatezonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, unsigned int options, dns_zone_t *zone, dns_db_t *db, dns_dbversion_t **versionp) { isc_result_t result; dns_acl_t *queryacl, *queryonacl; ns_dbversion_t *dbversion; REQUIRE(zone != NULL); REQUIRE(db != NULL); /* * This limits our searching to the zone where the first name * (the query target) was looked for. This prevents following * CNAMES or DNAMES into other zones and prevents returning * additional data from other zones. */ if (!client->view->additionalfromauth && client->query.authdbset && db != client->query.authdb) return (DNS_R_REFUSED); /* * Non recursive query to a static-stub zone is prohibited; its * zone content is not public data, but a part of local configuration * and should not be disclosed. */ if (dns_zone_gettype(zone) == dns_zone_staticstub && !RECURSIONOK(client)) { return (DNS_R_REFUSED); } /* * If the zone has an ACL, we'll check it, otherwise * we use the view's "allow-query" ACL. Each ACL is only checked * once per query. * * Also, get the database version to use. */ /* * Get the current version of this database. */ dbversion = query_findversion(client, db); if (dbversion == NULL) return (DNS_R_SERVFAIL); if ((options & DNS_GETDB_IGNOREACL) != 0) goto approved; if (dbversion->acl_checked) { if (!dbversion->queryok) return (DNS_R_REFUSED); goto approved; } queryacl = dns_zone_getqueryacl(zone); if (queryacl == NULL) { queryacl = client->view->queryacl; if ((client->query.attributes & NS_QUERYATTR_QUERYOKVALID) != 0) { /* * We've evaluated the view's queryacl already. If * NS_QUERYATTR_QUERYOK is set, then the client is * allowed to make queries, otherwise the query should * be refused. */ dbversion->acl_checked = ISC_TRUE; if ((client->query.attributes & NS_QUERYATTR_QUERYOK) == 0) { dbversion->queryok = ISC_FALSE; return (DNS_R_REFUSED); } dbversion->queryok = ISC_TRUE; goto approved; } } result = ns_client_checkaclsilent(client, NULL, queryacl, ISC_TRUE); if ((options & DNS_GETDB_NOLOG) == 0) { char msg[NS_CLIENT_ACLMSGSIZE("query")]; if (result == ISC_R_SUCCESS) { if (isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(3))) { ns_client_aclmsg("query", name, qtype, client->view->rdclass, msg, sizeof(msg)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_DEBUG(3), "%s approved", msg); } } else { ns_client_aclmsg("query", name, qtype, client->view->rdclass, msg, sizeof(msg)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_INFO, "%s denied", msg); } } if (queryacl == client->view->queryacl) { if (result == ISC_R_SUCCESS) { /* * We were allowed by the default * "allow-query" ACL. Remember this so we * don't have to check again. */ client->query.attributes |= NS_QUERYATTR_QUERYOK; } /* * We've now evaluated the view's query ACL, and * the NS_QUERYATTR_QUERYOK attribute is now valid. */ client->query.attributes |= NS_QUERYATTR_QUERYOKVALID; } /* If and only if we've gotten this far, check allow-query-on too */ if (result == ISC_R_SUCCESS) { queryonacl = dns_zone_getqueryonacl(zone); if (queryonacl == NULL) queryonacl = client->view->queryonacl; result = ns_client_checkaclsilent(client, &client->destaddr, queryonacl, ISC_TRUE); if ((options & DNS_GETDB_NOLOG) == 0 && result != ISC_R_SUCCESS) ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_INFO, "query-on denied"); } dbversion->acl_checked = ISC_TRUE; if (result != ISC_R_SUCCESS) { dbversion->queryok = ISC_FALSE; return (DNS_R_REFUSED); } dbversion->queryok = ISC_TRUE; approved: /* Transfer ownership, if necessary. */ if (versionp != NULL) *versionp = dbversion->version; return (ISC_R_SUCCESS); } static inline isc_result_t query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, unsigned int options, dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp) { isc_result_t result; unsigned int ztoptions; dns_zone_t *zone = NULL; dns_db_t *db = NULL; isc_boolean_t partial = ISC_FALSE; REQUIRE(zonep != NULL && *zonep == NULL); REQUIRE(dbp != NULL && *dbp == NULL); /*% * Find a zone database to answer the query. */ ztoptions = ((options & DNS_GETDB_NOEXACT) != 0) ? DNS_ZTFIND_NOEXACT : 0; result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL, &zone); if (result == DNS_R_PARTIALMATCH) partial = ISC_TRUE; if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) result = dns_zone_getdb(zone, &db); if (result != ISC_R_SUCCESS) goto fail; result = query_validatezonedb(client, name, qtype, options, zone, db, versionp); if (result != ISC_R_SUCCESS) goto fail; /* Transfer ownership. */ *zonep = zone; *dbp = db; if (partial && (options & DNS_GETDB_PARTIAL) != 0) return (DNS_R_PARTIALMATCH); return (ISC_R_SUCCESS); fail: if (zone != NULL) dns_zone_detach(&zone); if (db != NULL) dns_db_detach(&db); return (result); } static void rpz_log_rewrite(ns_client_t *client, isc_boolean_t disabled, dns_rpz_policy_t policy, dns_rpz_type_t type, dns_zone_t *zone, dns_name_t *rpz_qname) { isc_stats_t *zonestats; char qname_buf[DNS_NAME_FORMATSIZE]; char rpz_qname_buf[DNS_NAME_FORMATSIZE]; /* * Count enabled rewrites in the global counter. * Count both enabled and disabled rewrites for each zone. */ if (!disabled && policy != DNS_RPZ_POLICY_PASSTHRU) { isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_rpz_rewrites); } if (zone != NULL) { zonestats = dns_zone_getrequeststats(zone); if (zonestats != NULL) isc_stats_increment(zonestats, dns_nsstatscounter_rpz_rewrites); } if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL)) return; dns_name_format(client->query.qname, qname_buf, sizeof(qname_buf)); dns_name_format(rpz_qname, rpz_qname_buf, sizeof(rpz_qname_buf)); ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY, DNS_RPZ_INFO_LEVEL, "%srpz %s %s rewrite %s via %s", disabled ? "disabled " : "", dns_rpz_type2str(type), dns_rpz_policy2str(policy), qname_buf, rpz_qname_buf); } static void rpz_log_fail(ns_client_t *client, int level, dns_rpz_type_t rpz_type, dns_name_t *name, const char *str, isc_result_t result) { char namebuf1[DNS_NAME_FORMATSIZE]; char namebuf2[DNS_NAME_FORMATSIZE]; if (!isc_log_wouldlog(ns_g_lctx, level)) return; /* * bin/tests/system/rpz/tests.sh looks for "rpz.*failed". */ dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1)); dns_name_format(name, namebuf2, sizeof(namebuf2)); ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS, NS_LOGMODULE_QUERY, level, "rpz %s rewrite %s via %s %sfailed: %s", dns_rpz_type2str(rpz_type), namebuf1, namebuf2, str, isc_result_totext(result)); } /* * Get a policy rewrite zone database. */ static isc_result_t rpz_getdb(ns_client_t *client, dns_rpz_type_t rpz_type, dns_name_t *rpz_qname, dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp) { char namebuf1[DNS_NAME_FORMATSIZE]; char namebuf2[DNS_NAME_FORMATSIZE]; dns_dbversion_t *rpz_version = NULL; isc_result_t result; result = query_getzonedb(client, rpz_qname, dns_rdatatype_any, DNS_GETDB_IGNOREACL, zonep, dbp, &rpz_version); if (result == ISC_R_SUCCESS) { if (isc_log_wouldlog(ns_g_lctx, DNS_RPZ_DEBUG_LEVEL2)) { dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1)); dns_name_format(rpz_qname, namebuf2, sizeof(namebuf2)); ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY, DNS_RPZ_DEBUG_LEVEL2, "try rpz %s rewrite %s via %s", dns_rpz_type2str(rpz_type), namebuf1, namebuf2); } *versionp = rpz_version; return (ISC_R_SUCCESS); } rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, rpz_qname, "query_getzonedb() ", result); return (result); } static inline isc_result_t query_getcachedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, dns_db_t **dbp, unsigned int options) { isc_result_t result; isc_boolean_t check_acl; dns_db_t *db = NULL; REQUIRE(dbp != NULL && *dbp == NULL); /*% * Find a cache database to answer the query. * This may fail with DNS_R_REFUSED if the client * is not allowed to use the cache. */ if (!USECACHE(client)) return (DNS_R_REFUSED); dns_db_attach(client->view->cachedb, &db); if ((client->query.attributes & NS_QUERYATTR_CACHEACLOKVALID) != 0) { /* * We've evaluated the view's cacheacl already. If * NS_QUERYATTR_CACHEACLOK is set, then the client is * allowed to make queries, otherwise the query should * be refused. */ check_acl = ISC_FALSE; if ((client->query.attributes & NS_QUERYATTR_CACHEACLOK) == 0) goto refuse; } else { /* * We haven't evaluated the view's queryacl yet. */ check_acl = ISC_TRUE; } if (check_acl) { isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0); char msg[NS_CLIENT_ACLMSGSIZE("query (cache)")]; result = ns_client_checkaclsilent(client, NULL, client->view->cacheacl, ISC_TRUE); if (result == ISC_R_SUCCESS) { /* * We were allowed by the "allow-query-cache" ACL. * Remember this so we don't have to check again. */ client->query.attributes |= NS_QUERYATTR_CACHEACLOK; if (log && isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(3))) { ns_client_aclmsg("query (cache)", name, qtype, client->view->rdclass, msg, sizeof(msg)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_DEBUG(3), "%s approved", msg); } } else if (log) { ns_client_aclmsg("query (cache)", name, qtype, client->view->rdclass, msg, sizeof(msg)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_INFO, "%s denied", msg); } /* * We've now evaluated the view's query ACL, and * the NS_QUERYATTR_CACHEACLOKVALID attribute is now valid. */ client->query.attributes |= NS_QUERYATTR_CACHEACLOKVALID; if (result != ISC_R_SUCCESS) goto refuse; } /* Approved. */ /* Transfer ownership. */ *dbp = db; return (ISC_R_SUCCESS); refuse: result = DNS_R_REFUSED; if (db != NULL) dns_db_detach(&db); return (result); } static inline isc_result_t query_getdb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, unsigned int options, dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp, isc_boolean_t *is_zonep) { isc_result_t result; isc_result_t tresult; unsigned int namelabels; unsigned int zonelabels; dns_zone_t *zone = NULL; dns_db_t *tdbp; REQUIRE(zonep != NULL && *zonep == NULL); tdbp = NULL; /* Calculate how many labels are in name. */ namelabels = dns_name_countlabels(name); zonelabels = 0; /* Try to find name in bind's standard database. */ result = query_getzonedb(client, name, qtype, options, &zone, dbp, versionp); /* See how many labels are in the zone's name. */ if (result == ISC_R_SUCCESS && zone != NULL) zonelabels = dns_name_countlabels(dns_zone_getorigin(zone)); /* * If # zone labels < # name labels, try to find an even better match * Only try if a DLZ driver is loaded for this view */ if (zonelabels < namelabels && client->view->dlzdatabase != NULL) { tresult = dns_dlzfindzone(client->view, name, zonelabels, &tdbp); /* If we successful, we found a better match. */ if (tresult == ISC_R_SUCCESS) { /* * If the previous search returned a zone, detach it. */ if (zone != NULL) dns_zone_detach(&zone); /* * If the previous search returned a database, * detach it. */ if (*dbp != NULL) dns_db_detach(dbp); /* * If the previous search returned a version, clear it. */ *versionp = NULL; /* * Get our database version. */ dns_db_currentversion(tdbp, versionp); /* * Be sure to return our database. */ *dbp = tdbp; /* * We return a null zone, No stats for DLZ zones. */ zone = NULL; result = tresult; } } /* If successful, Transfer ownership of zone. */ if (result == ISC_R_SUCCESS) { *zonep = zone; /* * If neither attempt above succeeded, return the cache instead */ *is_zonep = ISC_TRUE; } else if (result == ISC_R_NOTFOUND) { result = query_getcachedb(client, name, qtype, dbp, options); *is_zonep = ISC_FALSE; } return (result); } static inline isc_boolean_t query_isduplicate(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type, dns_name_t **mnamep) { dns_section_t section; dns_name_t *mname = NULL; isc_result_t result; CTRACE("query_isduplicate"); for (section = DNS_SECTION_ANSWER; section <= DNS_SECTION_ADDITIONAL; section++) { result = dns_message_findname(client->message, section, name, type, 0, &mname, NULL); if (result == ISC_R_SUCCESS) { /* * We've already got this RRset in the response. */ CTRACE("query_isduplicate: true: done"); return (ISC_TRUE); } else if (result == DNS_R_NXRRSET) { /* * The name exists, but the rdataset does not. */ if (section == DNS_SECTION_ADDITIONAL) break; } else RUNTIME_CHECK(result == DNS_R_NXDOMAIN); mname = NULL; } if (mnamep != NULL) *mnamep = mname; CTRACE("query_isduplicate: false: done"); return (ISC_FALSE); } static isc_result_t query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) { ns_client_t *client = arg; isc_result_t result, eresult; dns_dbnode_t *node; dns_db_t *db; dns_name_t *fname, *mname; dns_rdataset_t *rdataset, *sigrdataset, *trdataset; isc_buffer_t *dbuf; isc_buffer_t b; dns_dbversion_t *version; isc_boolean_t added_something, need_addname; dns_zone_t *zone; dns_rdatatype_t type; REQUIRE(NS_CLIENT_VALID(client)); REQUIRE(qtype != dns_rdatatype_any); if (!WANTDNSSEC(client) && dns_rdatatype_isdnssec(qtype)) return (ISC_R_SUCCESS); CTRACE("query_addadditional"); /* * Initialization. */ eresult = ISC_R_SUCCESS; fname = NULL; rdataset = NULL; sigrdataset = NULL; trdataset = NULL; db = NULL; version = NULL; node = NULL; added_something = ISC_FALSE; need_addname = ISC_FALSE; zone = NULL; /* * We treat type A additional section processing as if it * were "any address type" additional section processing. * To avoid multiple lookups, we do an 'any' database * lookup and iterate over the node. */ if (qtype == dns_rdatatype_a) type = dns_rdatatype_any; else type = qtype; /* * Get some resources. */ dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); rdataset = query_newrdataset(client); if (fname == NULL || rdataset == NULL) goto cleanup; if (WANTDNSSEC(client)) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) goto cleanup; } /* * Look for a zone database that might contain authoritative * additional data. */ result = query_getzonedb(client, name, qtype, DNS_GETDB_NOLOG, &zone, &db, &version); if (result != ISC_R_SUCCESS) goto try_cache; CTRACE("query_addadditional: db_find"); /* * Since we are looking for authoritative data, we do not set * the GLUEOK flag. Glue will be looked for later, but not * necessarily in the same database. */ node = NULL; result = dns_db_find(db, name, version, type, client->query.dboptions, client->now, &node, fname, rdataset, sigrdataset); if (result == ISC_R_SUCCESS) { if (sigrdataset != NULL && !dns_db_issecure(db) && dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); goto found; } 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); version = NULL; dns_db_detach(&db); /* * No authoritative data was found. The cache is our next best bet. */ try_cache: result = query_getcachedb(client, name, qtype, &db, DNS_GETDB_NOLOG); if (result != ISC_R_SUCCESS) /* * Most likely the client isn't allowed to query the cache. */ goto try_glue; /* * Attempt to validate glue. */ if (sigrdataset == NULL) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) goto cleanup; } result = dns_db_find(db, name, version, type, client->query.dboptions | DNS_DBFIND_GLUEOK | DNS_DBFIND_ADDITIONALOK, client->now, &node, fname, rdataset, sigrdataset); if (result == DNS_R_GLUE && validate(client, db, fname, rdataset, sigrdataset)) result = ISC_R_SUCCESS; if (!WANTDNSSEC(client)) query_putrdataset(client, &sigrdataset); if (result == ISC_R_SUCCESS) goto found; 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); dns_db_detach(&db); try_glue: /* * No cached data was found. Glue is our last chance. * RFC1035 sayeth: * * 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. * * This is the "special search". Note that we must search * the zone where the NS record resides, not the zone it * points to, and that we only do the search in the delegation * case (identified by client->query.gluedb being set). */ if (client->query.gluedb == NULL) goto cleanup; /* * Don't poison caches using the bailiwick protection model. */ if (!dns_name_issubdomain(name, dns_db_origin(client->query.gluedb))) goto cleanup; dns_db_attach(client->query.gluedb, &db); result = dns_db_find(db, name, version, type, client->query.dboptions | DNS_DBFIND_GLUEOK, client->now, &node, fname, rdataset, sigrdataset); if (!(result == ISC_R_SUCCESS || result == DNS_R_ZONECUT || result == DNS_R_GLUE)) goto cleanup; found: /* * We have found a potential additional data rdataset, or * at least a node to iterate over. */ query_keepname(client, fname, dbuf); /* * If we have an rdataset, add it to the additional data * section. */ mname = NULL; if (dns_rdataset_isassociated(rdataset) && !query_isduplicate(client, fname, type, &mname)) { if (mname != NULL) { INSIST(mname != fname); query_releasename(client, &fname); fname = mname; } else need_addname = ISC_TRUE; ISC_LIST_APPEND(fname->list, rdataset, link); trdataset = rdataset; rdataset = NULL; added_something = ISC_TRUE; /* * Note: we only add SIGs if we've added the type they cover, * so we do not need to check if the SIG rdataset is already * in the response. */ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { ISC_LIST_APPEND(fname->list, sigrdataset, link); sigrdataset = NULL; } } if (qtype == dns_rdatatype_a) { #ifdef ALLOW_FILTER_AAAA_ON_V4 isc_boolean_t have_a = ISC_FALSE; #endif /* * We now go looking for A and AAAA records, along with * their signatures. * * XXXRTH This code could be more efficient. */ if (rdataset != NULL) { if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); } else { rdataset = query_newrdataset(client); if (rdataset == NULL) goto addname; } if (sigrdataset != NULL) { if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); } else if (WANTDNSSEC(client)) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) goto addname; } if (query_isduplicate(client, fname, dns_rdatatype_a, NULL)) goto aaaa_lookup; result = dns_db_findrdataset(db, node, version, dns_rdatatype_a, 0, client->now, rdataset, sigrdataset); if (result == DNS_R_NCACHENXDOMAIN) goto addname; if (result == DNS_R_NCACHENXRRSET) { dns_rdataset_disassociate(rdataset); if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); } if (result == ISC_R_SUCCESS) { mname = NULL; #ifdef ALLOW_FILTER_AAAA_ON_V4 have_a = ISC_TRUE; #endif if (!query_isduplicate(client, fname, dns_rdatatype_a, &mname)) { if (mname != fname) { if (mname != NULL) { query_releasename(client, &fname); fname = mname; } else need_addname = ISC_TRUE; } ISC_LIST_APPEND(fname->list, rdataset, link); added_something = ISC_TRUE; if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { ISC_LIST_APPEND(fname->list, sigrdataset, link); sigrdataset = query_newrdataset(client); } rdataset = query_newrdataset(client); if (rdataset == NULL) goto addname; if (WANTDNSSEC(client) && sigrdataset == NULL) goto addname; } else { dns_rdataset_disassociate(rdataset); if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); } } aaaa_lookup: if (query_isduplicate(client, fname, dns_rdatatype_aaaa, NULL)) goto addname; result = dns_db_findrdataset(db, node, version, dns_rdatatype_aaaa, 0, client->now, rdataset, sigrdataset); if (result == DNS_R_NCACHENXDOMAIN) goto addname; if (result == DNS_R_NCACHENXRRSET) { dns_rdataset_disassociate(rdataset); if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); } if (result == ISC_R_SUCCESS) { mname = NULL; /* * There's an A; check whether we're filtering AAAA */ #ifdef ALLOW_FILTER_AAAA_ON_V4 if (have_a && (client->filter_aaaa == dns_v4_aaaa_break_dnssec || (client->filter_aaaa == dns_v4_aaaa_filter && (!WANTDNSSEC(client) || sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset))))) goto addname; #endif if (!query_isduplicate(client, fname, dns_rdatatype_aaaa, &mname)) { if (mname != fname) { if (mname != NULL) { query_releasename(client, &fname); fname = mname; } else need_addname = ISC_TRUE; } ISC_LIST_APPEND(fname->list, rdataset, link); added_something = ISC_TRUE; if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { ISC_LIST_APPEND(fname->list, sigrdataset, link); sigrdataset = NULL; } rdataset = NULL; } } } addname: CTRACE("query_addadditional: addname"); /* * If we haven't added anything, then we're done. */ if (!added_something) goto cleanup; /* * We may have added our rdatasets to an existing name, if so, then * need_addname will be ISC_FALSE. Whether we used an existing name * or a new one, we must set fname to NULL to prevent cleanup. */ if (need_addname) dns_message_addname(client->message, fname, DNS_SECTION_ADDITIONAL); fname = NULL; /* * In a few cases, we want to add additional data for additional * data. It's simpler to just deal with special cases here than * to try to create a general purpose mechanism and allow the * rdata implementations to do it themselves. * * This involves recursion, but the depth is limited. The * most complex case is adding a SRV rdataset, which involves * recursing to add address records, which in turn can cause * recursion to add KEYs. */ if (type == dns_rdatatype_srv && trdataset != NULL) { /* * If we're adding SRV records to the additional data * section, it's helpful if we add the SRV additional data * as well. */ eresult = dns_rdataset_additionaldata(trdataset, query_addadditional, client); } cleanup: CTRACE("query_addadditional: cleanup"); query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); if (node != NULL) dns_db_detachnode(db, &node); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); CTRACE("query_addadditional: done"); return (eresult); } static inline void query_discardcache(ns_client_t *client, dns_rdataset_t *rdataset_base, dns_rdatasetadditional_t additionaltype, dns_rdatatype_t type, dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp, dns_dbnode_t **nodep, dns_name_t *fname) { dns_rdataset_t *rdataset; while ((rdataset = ISC_LIST_HEAD(fname->list)) != NULL) { ISC_LIST_UNLINK(fname->list, rdataset, link); query_putrdataset(client, &rdataset); } if (*versionp != NULL) dns_db_closeversion(*dbp, versionp, ISC_FALSE); if (*nodep != NULL) dns_db_detachnode(*dbp, nodep); if (*dbp != NULL) dns_db_detach(dbp); if (*zonep != NULL) dns_zone_detach(zonep); (void)dns_rdataset_putadditional(client->view->acache, rdataset_base, additionaltype, type); } static inline isc_result_t query_iscachevalid(dns_zone_t *zone, dns_db_t *db, dns_db_t *db0, dns_dbversion_t *version) { isc_result_t result = ISC_R_SUCCESS; dns_dbversion_t *version_current = NULL; dns_db_t *db_current = db0; if (db_current == NULL) { result = dns_zone_getdb(zone, &db_current); if (result != ISC_R_SUCCESS) return (result); } dns_db_currentversion(db_current, &version_current); if (db_current != db || version_current != version) { result = ISC_R_FAILURE; goto cleanup; } cleanup: dns_db_closeversion(db_current, &version_current, ISC_FALSE); if (db0 == NULL && db_current != NULL) dns_db_detach(&db_current); return (result); } static isc_result_t query_addadditional2(void *arg, dns_name_t *name, dns_rdatatype_t qtype) { client_additionalctx_t *additionalctx = arg; dns_rdataset_t *rdataset_base; ns_client_t *client; isc_result_t result, eresult; dns_dbnode_t *node, *cnode; dns_db_t *db, *cdb; dns_name_t *fname, *mname0, cfname; dns_rdataset_t *rdataset, *sigrdataset; dns_rdataset_t *crdataset, *crdataset_next; isc_buffer_t *dbuf; isc_buffer_t b; dns_dbversion_t *version, *cversion; isc_boolean_t added_something, need_addname, needadditionalcache; isc_boolean_t need_sigrrset; dns_zone_t *zone; dns_rdatatype_t type; dns_rdatasetadditional_t additionaltype; /* * If we don't have an additional cache call query_addadditional. */ client = additionalctx->client; REQUIRE(NS_CLIENT_VALID(client)); if (qtype != dns_rdatatype_a || client->view->acache == NULL) { /* * This function is optimized for "address" types. For other * types, use a generic routine. * XXX: ideally, this function should be generic enough. */ return (query_addadditional(additionalctx->client, name, qtype)); } /* * Initialization. */ rdataset_base = additionalctx->rdataset; eresult = ISC_R_SUCCESS; fname = NULL; rdataset = NULL; sigrdataset = NULL; db = NULL; cdb = NULL; version = NULL; cversion = NULL; node = NULL; cnode = NULL; added_something = ISC_FALSE; need_addname = ISC_FALSE; zone = NULL; needadditionalcache = ISC_FALSE; POST(needadditionalcache); additionaltype = dns_rdatasetadditional_fromauth; dns_name_init(&cfname, NULL); CTRACE("query_addadditional2"); /* * We treat type A additional section processing as if it * were "any address type" additional section processing. * To avoid multiple lookups, we do an 'any' database * lookup and iterate over the node. * XXXJT: this approach can cause a suboptimal result when the cache * DB only has partial address types and the glue DB has remaining * ones. */ type = dns_rdatatype_any; /* * Get some resources. */ dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); if (fname == NULL) goto cleanup; dns_name_setbuffer(&cfname, &b); /* share the buffer */ /* Check additional cache */ result = dns_rdataset_getadditional(rdataset_base, additionaltype, type, client->view->acache, &zone, &cdb, &cversion, &cnode, &cfname, client->message, client->now); if (result != ISC_R_SUCCESS) goto findauthdb; if (zone == NULL) { CTRACE("query_addadditional2: auth zone not found"); goto try_cache; } /* Is the cached DB up-to-date? */ result = query_iscachevalid(zone, cdb, NULL, cversion); if (result != ISC_R_SUCCESS) { CTRACE("query_addadditional2: old auth additional cache"); query_discardcache(client, rdataset_base, additionaltype, type, &zone, &cdb, &cversion, &cnode, &cfname); goto findauthdb; } if (cnode == NULL) { /* * We have a negative cache. We don't have to check the zone * ACL, since the result (not using this zone) would be same * regardless of the result. */ CTRACE("query_addadditional2: negative auth additional cache"); dns_db_closeversion(cdb, &cversion, ISC_FALSE); dns_db_detach(&cdb); dns_zone_detach(&zone); goto try_cache; } result = query_validatezonedb(client, name, qtype, DNS_GETDB_NOLOG, zone, cdb, NULL); if (result != ISC_R_SUCCESS) { query_discardcache(client, rdataset_base, additionaltype, type, &zone, &cdb, &cversion, &cnode, &cfname); goto try_cache; } /* We've got an active cache. */ CTRACE("query_addadditional2: auth additional cache"); dns_db_closeversion(cdb, &cversion, ISC_FALSE); db = cdb; node = cnode; dns_name_clone(&cfname, fname); query_keepname(client, fname, dbuf); goto foundcache; /* * Look for a zone database that might contain authoritative * additional data. */ findauthdb: result = query_getzonedb(client, name, qtype, DNS_GETDB_NOLOG, &zone, &db, &version); if (result != ISC_R_SUCCESS) { /* Cache the negative result */ (void)dns_rdataset_setadditional(rdataset_base, additionaltype, type, client->view->acache, NULL, NULL, NULL, NULL, NULL); goto try_cache; } CTRACE("query_addadditional2: db_find"); /* * Since we are looking for authoritative data, we do not set * the GLUEOK flag. Glue will be looked for later, but not * necessarily in the same database. */ node = NULL; result = dns_db_find(db, name, version, type, client->query.dboptions, client->now, &node, fname, NULL, NULL); if (result == ISC_R_SUCCESS) goto found; /* Cache the negative result */ (void)dns_rdataset_setadditional(rdataset_base, additionaltype, type, client->view->acache, zone, db, version, NULL, fname); if (node != NULL) dns_db_detachnode(db, &node); version = NULL; dns_db_detach(&db); /* * No authoritative data was found. The cache is our next best bet. */ try_cache: additionaltype = dns_rdatasetadditional_fromcache; result = query_getcachedb(client, name, qtype, &db, DNS_GETDB_NOLOG); if (result != ISC_R_SUCCESS) /* * Most likely the client isn't allowed to query the cache. */ goto try_glue; result = dns_db_find(db, name, version, type, client->query.dboptions | DNS_DBFIND_GLUEOK | DNS_DBFIND_ADDITIONALOK, client->now, &node, fname, NULL, NULL); if (result == ISC_R_SUCCESS) goto found; if (node != NULL) dns_db_detachnode(db, &node); dns_db_detach(&db); try_glue: /* * No cached data was found. Glue is our last chance. * RFC1035 sayeth: * * 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. * * This is the "special search". Note that we must search * the zone where the NS record resides, not the zone it * points to, and that we only do the search in the delegation * case (identified by client->query.gluedb being set). */ if (client->query.gluedb == NULL) goto cleanup; /* * Don't poison caches using the bailiwick protection model. */ if (!dns_name_issubdomain(name, dns_db_origin(client->query.gluedb))) goto cleanup; /* Check additional cache */ additionaltype = dns_rdatasetadditional_fromglue; result = dns_rdataset_getadditional(rdataset_base, additionaltype, type, client->view->acache, NULL, &cdb, &cversion, &cnode, &cfname, client->message, client->now); if (result != ISC_R_SUCCESS) goto findglue; result = query_iscachevalid(zone, cdb, client->query.gluedb, cversion); if (result != ISC_R_SUCCESS) { CTRACE("query_addadditional2: old glue additional cache"); query_discardcache(client, rdataset_base, additionaltype, type, &zone, &cdb, &cversion, &cnode, &cfname); goto findglue; } if (cnode == NULL) { /* We have a negative cache. */ CTRACE("query_addadditional2: negative glue additional cache"); dns_db_closeversion(cdb, &cversion, ISC_FALSE); dns_db_detach(&cdb); goto cleanup; } /* Cache hit. */ CTRACE("query_addadditional2: glue additional cache"); dns_db_closeversion(cdb, &cversion, ISC_FALSE); db = cdb; node = cnode; dns_name_clone(&cfname, fname); query_keepname(client, fname, dbuf); goto foundcache; findglue: dns_db_attach(client->query.gluedb, &db); result = dns_db_find(db, name, version, type, client->query.dboptions | DNS_DBFIND_GLUEOK, client->now, &node, fname, NULL, NULL); if (!(result == ISC_R_SUCCESS || result == DNS_R_ZONECUT || result == DNS_R_GLUE)) { /* cache the negative result */ (void)dns_rdataset_setadditional(rdataset_base, additionaltype, type, client->view->acache, NULL, db, version, NULL, fname); goto cleanup; } found: /* * We have found a DB node to iterate over from a DB. * We are going to look for address RRsets (i.e., A and AAAA) in the DB * node we've just found. We'll then store the complete information * in the additional data cache. */ dns_name_clone(fname, &cfname); query_keepname(client, fname, dbuf); needadditionalcache = ISC_TRUE; rdataset = query_newrdataset(client); if (rdataset == NULL) goto cleanup; sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) goto cleanup; if (additionaltype == dns_rdatasetadditional_fromcache && query_isduplicate(client, fname, dns_rdatatype_a, NULL)) goto aaaa_lookup; /* * Find A RRset with sig RRset. Even if we don't find a sig RRset * for a client using DNSSEC, we'll continue the process to make a * complete list to be cached. However, we need to cancel the * caching when something unexpected happens, in order to avoid * caching incomplete information. */ result = dns_db_findrdataset(db, node, version, dns_rdatatype_a, 0, client->now, rdataset, sigrdataset); /* * If we can't promote glue/pending from the cache to secure * then drop it. */ if (result == ISC_R_SUCCESS && additionaltype == dns_rdatasetadditional_fromcache && (DNS_TRUST_PENDING(rdataset->trust) || DNS_TRUST_GLUE(rdataset->trust)) && !validate(client, db, fname, rdataset, sigrdataset)) { dns_rdataset_disassociate(rdataset); if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); result = ISC_R_NOTFOUND; } if (result == DNS_R_NCACHENXDOMAIN) goto setcache; if (result == DNS_R_NCACHENXRRSET) { dns_rdataset_disassociate(rdataset); if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); } if (result == ISC_R_SUCCESS) { /* Remember the result as a cache */ ISC_LIST_APPEND(cfname.list, rdataset, link); if (dns_rdataset_isassociated(sigrdataset)) { ISC_LIST_APPEND(cfname.list, sigrdataset, link); sigrdataset = query_newrdataset(client); } rdataset = query_newrdataset(client); if (sigrdataset == NULL || rdataset == NULL) { /* do not cache incomplete information */ goto foundcache; } } aaaa_lookup: if (additionaltype == dns_rdatasetadditional_fromcache && query_isduplicate(client, fname, dns_rdatatype_aaaa, NULL)) goto foundcache; /* Find AAAA RRset with sig RRset */ result = dns_db_findrdataset(db, node, version, dns_rdatatype_aaaa, 0, client->now, rdataset, sigrdataset); /* * If we can't promote glue/pending from the cache to secure * then drop it. */ if (result == ISC_R_SUCCESS && additionaltype == dns_rdatasetadditional_fromcache && (DNS_TRUST_PENDING(rdataset->trust) || DNS_TRUST_GLUE(rdataset->trust)) && !validate(client, db, fname, rdataset, sigrdataset)) { dns_rdataset_disassociate(rdataset); if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); result = ISC_R_NOTFOUND; } if (result == ISC_R_SUCCESS) { ISC_LIST_APPEND(cfname.list, rdataset, link); rdataset = NULL; if (dns_rdataset_isassociated(sigrdataset)) { ISC_LIST_APPEND(cfname.list, sigrdataset, link); sigrdataset = NULL; } } setcache: /* * Set the new result in the cache if required. We do not support * caching additional data from a cache DB. */ if (needadditionalcache == ISC_TRUE && (additionaltype == dns_rdatasetadditional_fromauth || additionaltype == dns_rdatasetadditional_fromglue)) { (void)dns_rdataset_setadditional(rdataset_base, additionaltype, type, client->view->acache, zone, db, version, node, &cfname); } foundcache: need_sigrrset = ISC_FALSE; mname0 = NULL; for (crdataset = ISC_LIST_HEAD(cfname.list); crdataset != NULL; crdataset = crdataset_next) { dns_name_t *mname; crdataset_next = ISC_LIST_NEXT(crdataset, link); mname = NULL; if (crdataset->type == dns_rdatatype_a || crdataset->type == dns_rdatatype_aaaa) { if (!query_isduplicate(client, fname, crdataset->type, &mname)) { if (mname != fname) { if (mname != NULL) { /* * A different type of this name is * already stored in the additional * section. We'll reuse the name. * Note that this should happen at most * once. Otherwise, fname->link could * leak below. */ INSIST(mname0 == NULL); query_releasename(client, &fname); fname = mname; mname0 = mname; } else need_addname = ISC_TRUE; } ISC_LIST_UNLINK(cfname.list, crdataset, link); ISC_LIST_APPEND(fname->list, crdataset, link); added_something = ISC_TRUE; need_sigrrset = ISC_TRUE; } else need_sigrrset = ISC_FALSE; } else if (crdataset->type == dns_rdatatype_rrsig && need_sigrrset && WANTDNSSEC(client)) { ISC_LIST_UNLINK(cfname.list, crdataset, link); ISC_LIST_APPEND(fname->list, crdataset, link); added_something = ISC_TRUE; /* just in case */ need_sigrrset = ISC_FALSE; } } CTRACE("query_addadditional2: addname"); /* * If we haven't added anything, then we're done. */ if (!added_something) goto cleanup; /* * We may have added our rdatasets to an existing name, if so, then * need_addname will be ISC_FALSE. Whether we used an existing name * or a new one, we must set fname to NULL to prevent cleanup. */ if (need_addname) dns_message_addname(client->message, fname, DNS_SECTION_ADDITIONAL); fname = NULL; cleanup: CTRACE("query_addadditional2: cleanup"); if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); while ((crdataset = ISC_LIST_HEAD(cfname.list)) != NULL) { ISC_LIST_UNLINK(cfname.list, crdataset, link); query_putrdataset(client, &crdataset); } if (fname != NULL) query_releasename(client, &fname); if (node != NULL) dns_db_detachnode(db, &node); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); CTRACE("query_addadditional2: done"); return (eresult); } static inline void query_addrdataset(ns_client_t *client, dns_name_t *fname, dns_rdataset_t *rdataset) { client_additionalctx_t additionalctx; /* * Add 'rdataset' and any pertinent additional data to * 'fname', a name in the response message for 'client'. */ CTRACE("query_addrdataset"); ISC_LIST_APPEND(fname->list, rdataset, link); if (client->view->order != NULL) rdataset->attributes |= dns_order_find(client->view->order, fname, rdataset->type, rdataset->rdclass); rdataset->attributes |= DNS_RDATASETATTR_LOADORDER; if (NOADDITIONAL(client)) return; /* * Add additional data. * * We don't care if dns_rdataset_additionaldata() fails. */ additionalctx.client = client; additionalctx.rdataset = rdataset; (void)dns_rdataset_additionaldata(rdataset, query_addadditional2, &additionalctx); CTRACE("query_addrdataset: done"); } static isc_result_t query_dns64(ns_client_t *client, dns_name_t **namep, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, isc_buffer_t *dbuf, dns_section_t section) { dns_name_t *name, *mname; dns_rdata_t *dns64_rdata; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdatalist_t *dns64_rdatalist; dns_rdataset_t *dns64_rdataset; dns_rdataset_t *mrdataset; isc_buffer_t *buffer; isc_region_t r; isc_result_t result; dns_view_t *view = client->view; isc_netaddr_t netaddr; dns_dns64_t *dns64; unsigned int flags = 0; /*% * To the current response for 'client', add the answer RRset * '*rdatasetp' and an optional signature set '*sigrdatasetp', with * owner name '*namep', to section 'section', unless they are * already there. Also add any pertinent additional data. * * If 'dbuf' is not NULL, then '*namep' is the name whose data is * stored in 'dbuf'. In this case, query_addrrset() guarantees that * when it returns the name will either have been kept or released. */ CTRACE("query_dns64"); name = *namep; mname = NULL; mrdataset = NULL; buffer = NULL; dns64_rdata = NULL; dns64_rdataset = NULL; dns64_rdatalist = NULL; result = dns_message_findname(client->message, section, name, dns_rdatatype_aaaa, rdataset->covers, &mname, &mrdataset); if (result == ISC_R_SUCCESS) { /* * We've already got an RRset of the given name and type. * There's nothing else to do; */ CTRACE("query_dns64: dns_message_findname succeeded: done"); if (dbuf != NULL) query_releasename(client, namep); return (ISC_R_SUCCESS); } else if (result == DNS_R_NXDOMAIN) { /* * The name doesn't exist. */ if (dbuf != NULL) query_keepname(client, name, dbuf); dns_message_addname(client->message, name, section); *namep = NULL; mname = name; } else { RUNTIME_CHECK(result == DNS_R_NXRRSET); if (dbuf != NULL) query_releasename(client, namep); } if (rdataset->trust != dns_trust_secure && (section == DNS_SECTION_ANSWER || section == DNS_SECTION_AUTHORITY)) client->query.attributes &= ~NS_QUERYATTR_SECURE; isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr); result = isc_buffer_allocate(client->mctx, &buffer, view->dns64cnt * 16 * dns_rdataset_count(rdataset)); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_message_gettemprdataset(client->message, &dns64_rdataset); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_message_gettemprdatalist(client->message, &dns64_rdatalist); if (result != ISC_R_SUCCESS) goto cleanup; dns_rdataset_init(dns64_rdataset); dns_rdatalist_init(dns64_rdatalist); dns64_rdatalist->rdclass = dns_rdataclass_in; dns64_rdatalist->type = dns_rdatatype_aaaa; if (client->query.dns64_ttl != ISC_UINT32_MAX) dns64_rdatalist->ttl = ISC_MIN(rdataset->ttl, client->query.dns64_ttl); else dns64_rdatalist->ttl = ISC_MIN(rdataset->ttl, 600); if (RECURSIONOK(client)) flags |= DNS_DNS64_RECURSIVE; /* * We use the signatures from the A lookup to set DNS_DNS64_DNSSEC * as this provides a easy way to see if the answer was signed. */ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) flags |= DNS_DNS64_DNSSEC; for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { for (dns64 = ISC_LIST_HEAD(client->view->dns64); dns64 != NULL; dns64 = dns_dns64_next(dns64)) { dns_rdataset_current(rdataset, &rdata); isc__buffer_availableregion(buffer, &r); INSIST(r.length >= 16); result = dns_dns64_aaaafroma(dns64, &netaddr, client->signer, &ns_g_server->aclenv, flags, rdata.data, r.base); if (result != ISC_R_SUCCESS) { dns_rdata_reset(&rdata); continue; } isc_buffer_add(buffer, 16); isc_buffer_remainingregion(buffer, &r); isc_buffer_forward(buffer, 16); result = dns_message_gettemprdata(client->message, &dns64_rdata); if (result != ISC_R_SUCCESS) goto cleanup; dns_rdata_init(dns64_rdata); dns_rdata_fromregion(dns64_rdata, dns_rdataclass_in, dns_rdatatype_aaaa, &r); ISC_LIST_APPEND(dns64_rdatalist->rdata, dns64_rdata, link); dns64_rdata = NULL; dns_rdata_reset(&rdata); } } if (result != ISC_R_NOMORE) goto cleanup; if (ISC_LIST_EMPTY(dns64_rdatalist->rdata)) goto cleanup; result = dns_rdatalist_tordataset(dns64_rdatalist, dns64_rdataset); if (result != ISC_R_SUCCESS) goto cleanup; client->query.attributes |= NS_QUERYATTR_NOADDITIONAL; dns64_rdataset->trust = rdataset->trust; query_addrdataset(client, mname, dns64_rdataset); dns64_rdataset = NULL; dns64_rdatalist = NULL; dns_message_takebuffer(client->message, &buffer); result = ISC_R_SUCCESS; cleanup: if (buffer != NULL) isc_buffer_free(&buffer); if (dns64_rdata != NULL) dns_message_puttemprdata(client->message, &dns64_rdata); if (dns64_rdataset != NULL) dns_message_puttemprdataset(client->message, &dns64_rdataset); if (dns64_rdatalist != NULL) { for (dns64_rdata = ISC_LIST_HEAD(dns64_rdatalist->rdata); dns64_rdata != NULL; dns64_rdata = ISC_LIST_HEAD(dns64_rdatalist->rdata)) { ISC_LIST_UNLINK(dns64_rdatalist->rdata, dns64_rdata, link); dns_message_puttemprdata(client->message, &dns64_rdata); } dns_message_puttemprdatalist(client->message, &dns64_rdatalist); } CTRACE("query_dns64: done"); return (result); } static void query_filter64(ns_client_t *client, dns_name_t **namep, dns_rdataset_t *rdataset, isc_buffer_t *dbuf, dns_section_t section) { dns_name_t *name, *mname; dns_rdata_t *myrdata; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdatalist_t *myrdatalist; dns_rdataset_t *myrdataset; isc_buffer_t *buffer; isc_region_t r; isc_result_t result; unsigned int i; CTRACE("query_filter64"); INSIST(client->query.dns64_aaaaok != NULL); INSIST(client->query.dns64_aaaaoklen == dns_rdataset_count(rdataset)); name = *namep; mname = NULL; buffer = NULL; myrdata = NULL; myrdataset = NULL; myrdatalist = NULL; result = dns_message_findname(client->message, section, name, dns_rdatatype_aaaa, rdataset->covers, &mname, &myrdataset); if (result == ISC_R_SUCCESS) { /* * We've already got an RRset of the given name and type. * There's nothing else to do; */ CTRACE("query_filter64: dns_message_findname succeeded: done"); if (dbuf != NULL) query_releasename(client, namep); return; } else if (result == DNS_R_NXDOMAIN) { mname = name; *namep = NULL; } else { RUNTIME_CHECK(result == DNS_R_NXRRSET); if (dbuf != NULL) query_releasename(client, namep); dbuf = NULL; } if (rdataset->trust != dns_trust_secure && (section == DNS_SECTION_ANSWER || section == DNS_SECTION_AUTHORITY)) client->query.attributes &= ~NS_QUERYATTR_SECURE; result = isc_buffer_allocate(client->mctx, &buffer, 16 * dns_rdataset_count(rdataset)); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_message_gettemprdataset(client->message, &myrdataset); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_message_gettemprdatalist(client->message, &myrdatalist); if (result != ISC_R_SUCCESS) goto cleanup; dns_rdataset_init(myrdataset); dns_rdatalist_init(myrdatalist); myrdatalist->rdclass = dns_rdataclass_in; myrdatalist->type = dns_rdatatype_aaaa; myrdatalist->ttl = rdataset->ttl; i = 0; for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { if (!client->query.dns64_aaaaok[i++]) continue; dns_rdataset_current(rdataset, &rdata); INSIST(rdata.length == 16); isc_buffer_putmem(buffer, rdata.data, rdata.length); isc_buffer_remainingregion(buffer, &r); isc_buffer_forward(buffer, rdata.length); result = dns_message_gettemprdata(client->message, &myrdata); if (result != ISC_R_SUCCESS) goto cleanup; dns_rdata_init(myrdata); dns_rdata_fromregion(myrdata, dns_rdataclass_in, dns_rdatatype_aaaa, &r); ISC_LIST_APPEND(myrdatalist->rdata, myrdata, link); myrdata = NULL; dns_rdata_reset(&rdata); } if (result != ISC_R_NOMORE) goto cleanup; result = dns_rdatalist_tordataset(myrdatalist, myrdataset); if (result != ISC_R_SUCCESS) goto cleanup; client->query.attributes |= NS_QUERYATTR_NOADDITIONAL; if (mname == name) { if (dbuf != NULL) query_keepname(client, name, dbuf); dns_message_addname(client->message, name, section); dbuf = NULL; } myrdataset->trust = rdataset->trust; query_addrdataset(client, mname, myrdataset); myrdataset = NULL; myrdatalist = NULL; dns_message_takebuffer(client->message, &buffer); cleanup: if (buffer != NULL) isc_buffer_free(&buffer); if (myrdata != NULL) dns_message_puttemprdata(client->message, &myrdata); if (myrdataset != NULL) dns_message_puttemprdataset(client->message, &myrdataset); if (myrdatalist != NULL) { for (myrdata = ISC_LIST_HEAD(myrdatalist->rdata); myrdata != NULL; myrdata = ISC_LIST_HEAD(myrdatalist->rdata)) { ISC_LIST_UNLINK(myrdatalist->rdata, myrdata, link); dns_message_puttemprdata(client->message, &myrdata); } dns_message_puttemprdatalist(client->message, &myrdatalist); } if (dbuf != NULL) query_releasename(client, &name); CTRACE("query_filter64: done"); } static void query_addrrset(ns_client_t *client, dns_name_t **namep, dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp, isc_buffer_t *dbuf, dns_section_t section) { dns_name_t *name, *mname; dns_rdataset_t *rdataset, *mrdataset, *sigrdataset; isc_result_t result; /*% * To the current response for 'client', add the answer RRset * '*rdatasetp' and an optional signature set '*sigrdatasetp', with * owner name '*namep', to section 'section', unless they are * already there. Also add any pertinent additional data. * * If 'dbuf' is not NULL, then '*namep' is the name whose data is * stored in 'dbuf'. In this case, query_addrrset() guarantees that * when it returns the name will either have been kept or released. */ CTRACE("query_addrrset"); name = *namep; rdataset = *rdatasetp; if (sigrdatasetp != NULL) sigrdataset = *sigrdatasetp; else sigrdataset = NULL; mname = NULL; mrdataset = NULL; result = dns_message_findname(client->message, section, name, rdataset->type, rdataset->covers, &mname, &mrdataset); if (result == ISC_R_SUCCESS) { /* * We've already got an RRset of the given name and type. * There's nothing else to do; */ CTRACE("query_addrrset: dns_message_findname succeeded: done"); if (dbuf != NULL) query_releasename(client, namep); return; } else if (result == DNS_R_NXDOMAIN) { /* * The name doesn't exist. */ if (dbuf != NULL) query_keepname(client, name, dbuf); dns_message_addname(client->message, name, section); *namep = NULL; mname = name; } else { RUNTIME_CHECK(result == DNS_R_NXRRSET); if (dbuf != NULL) query_releasename(client, namep); } if (rdataset->trust != dns_trust_secure && (section == DNS_SECTION_ANSWER || section == DNS_SECTION_AUTHORITY)) client->query.attributes &= ~NS_QUERYATTR_SECURE; /* * Note: we only add SIGs if we've added the type they cover, so * we do not need to check if the SIG rdataset is already in the * response. */ query_addrdataset(client, mname, rdataset); *rdatasetp = NULL; if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { /* * We have a signature. Add it to the response. */ ISC_LIST_APPEND(mname->list, sigrdataset, link); *sigrdatasetp = NULL; } CTRACE("query_addrrset: done"); } static inline isc_result_t query_addsoa(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version, unsigned int override_ttl, isc_boolean_t isassociated) { dns_name_t *name; dns_dbnode_t *node; isc_result_t result, eresult; dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; dns_rdataset_t **sigrdatasetp = NULL; CTRACE("query_addsoa"); /* * Initialization. */ eresult = ISC_R_SUCCESS; name = NULL; rdataset = NULL; node = NULL; /* * Don't add the SOA record for test which set "-T nosoa". */ if (ns_g_nosoa && (!WANTDNSSEC(client) || !isassociated)) return (ISC_R_SUCCESS); /* * Get resources and make 'name' be the database origin. */ result = dns_message_gettempname(client->message, &name); if (result != ISC_R_SUCCESS) return (result); dns_name_init(name, NULL); dns_name_clone(dns_db_origin(db), name); rdataset = query_newrdataset(client); if (rdataset == NULL) { eresult = DNS_R_SERVFAIL; goto cleanup; } if (WANTDNSSEC(client) && dns_db_issecure(db)) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) { eresult = DNS_R_SERVFAIL; goto cleanup; } } /* * Find the SOA. */ result = dns_db_getoriginnode(db, &node); if (result == ISC_R_SUCCESS) { result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa, 0, client->now, rdataset, sigrdataset); } else { dns_fixedname_t foundname; dns_name_t *fname; dns_fixedname_init(&foundname); fname = dns_fixedname_name(&foundname); result = dns_db_find(db, name, version, dns_rdatatype_soa, client->query.dboptions, 0, &node, fname, rdataset, sigrdataset); } if (result != ISC_R_SUCCESS) { /* * This is bad. We tried to get the SOA RR at the zone top * and it didn't work! */ eresult = DNS_R_SERVFAIL; } else { /* * Extract the SOA MINIMUM. */ dns_rdata_soa_t soa; dns_rdata_t rdata = DNS_RDATA_INIT; result = dns_rdataset_first(rdataset); RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &soa, NULL); if (result != ISC_R_SUCCESS) goto cleanup; if (override_ttl != ISC_UINT32_MAX && override_ttl < rdataset->ttl) { rdataset->ttl = override_ttl; if (sigrdataset != NULL) sigrdataset->ttl = override_ttl; } /* * Add the SOA and its SIG to the response, with the * TTLs adjusted per RFC2308 section 3. */ if (rdataset->ttl > soa.minimum) rdataset->ttl = soa.minimum; if (sigrdataset != NULL && sigrdataset->ttl > soa.minimum) sigrdataset->ttl = soa.minimum; if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; query_addrrset(client, &name, &rdataset, sigrdatasetp, NULL, DNS_SECTION_AUTHORITY); } cleanup: query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (name != NULL) query_releasename(client, &name); if (node != NULL) dns_db_detachnode(db, &node); return (eresult); } static inline isc_result_t query_addns(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version) { dns_name_t *name, *fname; dns_dbnode_t *node; isc_result_t result, eresult; dns_fixedname_t foundname; dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; dns_rdataset_t **sigrdatasetp = NULL; CTRACE("query_addns"); /* * Initialization. */ eresult = ISC_R_SUCCESS; name = NULL; rdataset = NULL; node = NULL; dns_fixedname_init(&foundname); fname = dns_fixedname_name(&foundname); /* * Get resources and make 'name' be the database origin. */ result = dns_message_gettempname(client->message, &name); if (result != ISC_R_SUCCESS) { CTRACE("query_addns: dns_message_gettempname failed: done"); return (result); } dns_name_init(name, NULL); dns_name_clone(dns_db_origin(db), name); rdataset = query_newrdataset(client); if (rdataset == NULL) { CTRACE("query_addns: query_newrdataset failed"); eresult = DNS_R_SERVFAIL; goto cleanup; } if (WANTDNSSEC(client) && dns_db_issecure(db)) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) { CTRACE("query_addns: query_newrdataset failed"); eresult = DNS_R_SERVFAIL; goto cleanup; } } /* * Find the NS rdataset. */ result = dns_db_getoriginnode(db, &node); if (result == ISC_R_SUCCESS) { result = dns_db_findrdataset(db, node, version, dns_rdatatype_ns, 0, client->now, rdataset, sigrdataset); } else { CTRACE("query_addns: calling dns_db_find"); result = dns_db_find(db, name, NULL, dns_rdatatype_ns, client->query.dboptions, 0, &node, fname, rdataset, sigrdataset); CTRACE("query_addns: dns_db_find complete"); } if (result != ISC_R_SUCCESS) { CTRACE("query_addns: " "dns_db_findrdataset or dns_db_find failed"); /* * This is bad. We tried to get the NS rdataset at the zone * top and it didn't work! */ eresult = DNS_R_SERVFAIL; } else { if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; query_addrrset(client, &name, &rdataset, sigrdatasetp, NULL, DNS_SECTION_AUTHORITY); } cleanup: CTRACE("query_addns: cleanup"); query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (name != NULL) query_releasename(client, &name); if (node != NULL) dns_db_detachnode(db, &node); CTRACE("query_addns: done"); return (eresult); } static isc_result_t query_add_cname(ns_client_t *client, dns_name_t *qname, dns_name_t *tname, dns_trust_t trust, dns_ttl_t ttl) { dns_rdataset_t *rdataset; dns_rdatalist_t *rdatalist; dns_rdata_t *rdata; isc_region_t r; dns_name_t *aname; isc_result_t result; /* * We assume the name data referred to by tname won't go away. */ aname = NULL; result = dns_message_gettempname(client->message, &aname); if (result != ISC_R_SUCCESS) return (result); result = dns_name_dup(qname, client->mctx, aname); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &aname); return (result); } rdatalist = NULL; result = dns_message_gettemprdatalist(client->message, &rdatalist); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &aname); return (result); } rdata = NULL; result = dns_message_gettemprdata(client->message, &rdata); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &aname); dns_message_puttemprdatalist(client->message, &rdatalist); return (result); } rdataset = NULL; result = dns_message_gettemprdataset(client->message, &rdataset); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &aname); dns_message_puttemprdatalist(client->message, &rdatalist); dns_message_puttemprdata(client->message, &rdata); return (result); } dns_rdataset_init(rdataset); rdatalist->type = dns_rdatatype_cname; rdatalist->covers = 0; rdatalist->rdclass = client->message->rdclass; rdatalist->ttl = ttl; dns_name_toregion(tname, &r); rdata->data = r.base; rdata->length = r.length; rdata->rdclass = client->message->rdclass; rdata->type = dns_rdatatype_cname; ISC_LIST_INIT(rdatalist->rdata); ISC_LIST_APPEND(rdatalist->rdata, rdata, link); RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == ISC_R_SUCCESS); rdataset->trust = trust; query_addrrset(client, &aname, &rdataset, NULL, NULL, DNS_SECTION_ANSWER); if (rdataset != NULL) { if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); dns_message_puttemprdataset(client->message, &rdataset); } if (aname != NULL) dns_message_puttempname(client->message, &aname); return (ISC_R_SUCCESS); } /* * Mark the RRsets as secure. Update the cache (db) to reflect the * change in trust level. */ static void mark_secure(ns_client_t *client, dns_db_t *db, dns_name_t *name, dns_rdata_rrsig_t *rrsig, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { isc_result_t result; dns_dbnode_t *node = NULL; isc_stdtime_t now; rdataset->trust = dns_trust_secure; sigrdataset->trust = dns_trust_secure; /* * Save the updated secure state. Ignore failures. */ result = dns_db_findnode(db, name, ISC_TRUE, &node); if (result != ISC_R_SUCCESS) return; isc_stdtime_get(&now); dns_rdataset_trimttl(rdataset, sigrdataset, rrsig, now, client->view->acceptexpired); (void)dns_db_addrdataset(db, node, NULL, client->now, rdataset, 0, NULL); (void)dns_db_addrdataset(db, node, NULL, client->now, sigrdataset, 0, NULL); dns_db_detachnode(db, &node); } /* * Find the secure key that corresponds to rrsig. * Note: 'keyrdataset' maintains state between successive calls, * there may be multiple keys with the same keyid. * Return ISC_FALSE if we have exhausted all the possible keys. */ static isc_boolean_t get_key(ns_client_t *client, dns_db_t *db, dns_rdata_rrsig_t *rrsig, dns_rdataset_t *keyrdataset, dst_key_t **keyp) { isc_result_t result; dns_dbnode_t *node = NULL; isc_boolean_t secure = ISC_FALSE; if (!dns_rdataset_isassociated(keyrdataset)) { result = dns_db_findnode(db, &rrsig->signer, ISC_FALSE, &node); if (result != ISC_R_SUCCESS) return (ISC_FALSE); result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey, 0, client->now, keyrdataset, NULL); dns_db_detachnode(db, &node); if (result != ISC_R_SUCCESS) return (ISC_FALSE); if (keyrdataset->trust != dns_trust_secure) return (ISC_FALSE); result = dns_rdataset_first(keyrdataset); } else result = dns_rdataset_next(keyrdataset); for ( ; result == ISC_R_SUCCESS; result = dns_rdataset_next(keyrdataset)) { dns_rdata_t rdata = DNS_RDATA_INIT; isc_buffer_t b; dns_rdataset_current(keyrdataset, &rdata); isc_buffer_init(&b, rdata.data, rdata.length); isc_buffer_add(&b, rdata.length); result = dst_key_fromdns(&rrsig->signer, rdata.rdclass, &b, client->mctx, keyp); if (result != ISC_R_SUCCESS) continue; if (rrsig->algorithm == (dns_secalg_t)dst_key_alg(*keyp) && rrsig->keyid == (dns_keytag_t)dst_key_id(*keyp) && dst_key_iszonekey(*keyp)) { secure = ISC_TRUE; break; } dst_key_free(keyp); } return (secure); } static isc_boolean_t verify(dst_key_t *key, dns_name_t *name, dns_rdataset_t *rdataset, dns_rdata_t *rdata, isc_mem_t *mctx, isc_boolean_t acceptexpired) { isc_result_t result; dns_fixedname_t fixed; isc_boolean_t ignore = ISC_FALSE; dns_fixedname_init(&fixed); again: result = dns_dnssec_verify2(name, rdataset, key, ignore, mctx, rdata, NULL); if (result == DNS_R_SIGEXPIRED && acceptexpired) { ignore = ISC_TRUE; goto again; } if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) return (ISC_TRUE); return (ISC_FALSE); } /* * Validate the rdataset if possible with available records. */ static isc_boolean_t validate(ns_client_t *client, dns_db_t *db, dns_name_t *name, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_rrsig_t rrsig; dst_key_t *key = NULL; dns_rdataset_t keyrdataset; if (sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset)) return (ISC_FALSE); for (result = dns_rdataset_first(sigrdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(sigrdataset)) { dns_rdata_reset(&rdata); dns_rdataset_current(sigrdataset, &rdata); result = dns_rdata_tostruct(&rdata, &rrsig, NULL); if (result != ISC_R_SUCCESS) return (ISC_FALSE); if (!dns_resolver_algorithm_supported(client->view->resolver, name, rrsig.algorithm)) continue; if (!dns_name_issubdomain(name, &rrsig.signer)) continue; dns_rdataset_init(&keyrdataset); do { if (!get_key(client, db, &rrsig, &keyrdataset, &key)) break; if (verify(key, name, rdataset, &rdata, client->mctx, client->view->acceptexpired)) { dst_key_free(&key); dns_rdataset_disassociate(&keyrdataset); mark_secure(client, db, name, &rrsig, rdataset, sigrdataset); return (ISC_TRUE); } dst_key_free(&key); } while (1); if (dns_rdataset_isassociated(&keyrdataset)) dns_rdataset_disassociate(&keyrdataset); } return (ISC_FALSE); } static void query_addbestns(ns_client_t *client) { dns_db_t *db, *zdb; dns_dbnode_t *node; dns_name_t *fname, *zfname; dns_rdataset_t *rdataset, *sigrdataset, *zrdataset, *zsigrdataset; isc_boolean_t is_zone, use_zone; isc_buffer_t *dbuf; isc_result_t result; dns_dbversion_t *version; dns_zone_t *zone; isc_buffer_t b; CTRACE("query_addbestns"); fname = NULL; zfname = NULL; rdataset = NULL; zrdataset = NULL; sigrdataset = NULL; zsigrdataset = NULL; node = NULL; db = NULL; zdb = NULL; version = NULL; zone = NULL; is_zone = ISC_FALSE; use_zone = ISC_FALSE; /* * Find the right database. */ result = query_getdb(client, client->query.qname, dns_rdatatype_ns, 0, &zone, &db, &version, &is_zone); if (result != ISC_R_SUCCESS) goto cleanup; db_find: /* * We'll need some resources... */ dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); rdataset = query_newrdataset(client); if (fname == NULL || rdataset == NULL) goto cleanup; /* * Get the RRSIGs if the client requested them or if we may * need to validate answers from the cache. */ if (WANTDNSSEC(client) || !is_zone) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) goto cleanup; } /* * Now look for the zonecut. */ if (is_zone) { result = dns_db_find(db, client->query.qname, version, dns_rdatatype_ns, client->query.dboptions, client->now, &node, fname, rdataset, sigrdataset); if (result != DNS_R_DELEGATION) goto cleanup; if (USECACHE(client)) { query_keepname(client, fname, dbuf); zdb = db; zfname = fname; fname = NULL; zrdataset = rdataset; rdataset = NULL; zsigrdataset = sigrdataset; sigrdataset = NULL; dns_db_detachnode(db, &node); version = NULL; db = NULL; dns_db_attach(client->view->cachedb, &db); is_zone = ISC_FALSE; goto db_find; } } else { result = dns_db_findzonecut(db, client->query.qname, client->query.dboptions, client->now, &node, fname, rdataset, sigrdataset); if (result == ISC_R_SUCCESS) { if (zfname != NULL && !dns_name_issubdomain(fname, zfname)) { /* * We found a zonecut in the cache, but our * zone delegation is better. */ use_zone = ISC_TRUE; } } else if (result == ISC_R_NOTFOUND && zfname != NULL) { /* * We didn't find anything in the cache, but we * have a zone delegation, so use it. */ use_zone = ISC_TRUE; } else goto cleanup; } if (use_zone) { query_releasename(client, &fname); fname = zfname; zfname = NULL; /* * We've already done query_keepname() on * zfname, so we must set dbuf to NULL to * prevent query_addrrset() from trying to * call query_keepname() again. */ dbuf = NULL; query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); rdataset = zrdataset; zrdataset = NULL; sigrdataset = zsigrdataset; zsigrdataset = NULL; } /* * Attempt to validate RRsets that are pending or that are glue. */ if ((DNS_TRUST_PENDING(rdataset->trust) || (sigrdataset != NULL && DNS_TRUST_PENDING(sigrdataset->trust))) && !validate(client, db, fname, rdataset, sigrdataset) && !PENDINGOK(client->query.dboptions)) goto cleanup; if ((DNS_TRUST_GLUE(rdataset->trust) || (sigrdataset != NULL && DNS_TRUST_GLUE(sigrdataset->trust))) && !validate(client, db, fname, rdataset, sigrdataset) && SECURE(client) && WANTDNSSEC(client)) goto cleanup; /* * If the answer is secure only add NS records if they are secure * when the client may be looking for AD in the response. */ if (SECURE(client) && (WANTDNSSEC(client) || WANTAD(client)) && ((rdataset->trust != dns_trust_secure) || (sigrdataset != NULL && sigrdataset->trust != dns_trust_secure))) goto cleanup; /* * If the client doesn't want DNSSEC we can discard the sigrdataset * now. */ if (!WANTDNSSEC(client)) query_putrdataset(client, &sigrdataset); query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); cleanup: if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); if (node != NULL) dns_db_detachnode(db, &node); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); if (zdb != NULL) { query_putrdataset(client, &zrdataset); if (zsigrdataset != NULL) query_putrdataset(client, &zsigrdataset); if (zfname != NULL) query_releasename(client, &zfname); dns_db_detach(&zdb); } } static void fixrdataset(ns_client_t *client, dns_rdataset_t **rdataset) { if (*rdataset == NULL) *rdataset = query_newrdataset(client); else if (dns_rdataset_isassociated(*rdataset)) dns_rdataset_disassociate(*rdataset); } static void fixfname(ns_client_t *client, dns_name_t **fname, isc_buffer_t **dbuf, isc_buffer_t *nbuf) { if (*fname == NULL) { *dbuf = query_getnamebuf(client); if (*dbuf == NULL) return; *fname = query_newname(client, *dbuf, nbuf); } } static void query_addds(ns_client_t *client, dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, dns_name_t *name) { dns_fixedname_t fixed; dns_name_t *fname = NULL; dns_name_t *rname; dns_rdataset_t *rdataset, *sigrdataset; isc_buffer_t *dbuf, b; isc_result_t result; unsigned int count; CTRACE("query_addds"); rname = NULL; rdataset = NULL; sigrdataset = NULL; /* * We'll need some resources... */ rdataset = query_newrdataset(client); sigrdataset = query_newrdataset(client); if (rdataset == NULL || sigrdataset == NULL) goto cleanup; /* * Look for the DS record, which may or may not be present. */ result = dns_db_findrdataset(db, node, version, dns_rdatatype_ds, 0, client->now, rdataset, sigrdataset); /* * If we didn't find it, look for an NSEC. */ if (result == ISC_R_NOTFOUND) result = dns_db_findrdataset(db, node, version, dns_rdatatype_nsec, 0, client->now, rdataset, sigrdataset); if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) goto addnsec3; if (!dns_rdataset_isassociated(rdataset) || !dns_rdataset_isassociated(sigrdataset)) goto addnsec3; /* * We've already added the NS record, so if the name's not there, * we have other problems. Use this name rather than calling * query_addrrset(). */ result = dns_message_firstname(client->message, DNS_SECTION_AUTHORITY); if (result != ISC_R_SUCCESS) goto cleanup; rname = NULL; dns_message_currentname(client->message, DNS_SECTION_AUTHORITY, &rname); result = dns_message_findtype(rname, dns_rdatatype_ns, 0, NULL); if (result != ISC_R_SUCCESS) goto cleanup; ISC_LIST_APPEND(rname->list, rdataset, link); ISC_LIST_APPEND(rname->list, sigrdataset, link); rdataset = NULL; sigrdataset = NULL; return; addnsec3: if (!dns_db_iszone(db)) goto cleanup; /* * Add the NSEC3 which proves the DS does not exist. */ dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); dns_fixedname_init(&fixed); if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); query_findclosestnsec3(name, db, version, client, rdataset, sigrdataset, fname, ISC_TRUE, dns_fixedname_name(&fixed)); if (!dns_rdataset_isassociated(rdataset)) goto cleanup; query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); /* * Did we find the closest provable encloser instead? * If so add the nearest to the closest provable encloser. */ if (!dns_name_equal(name, dns_fixedname_name(&fixed))) { count = dns_name_countlabels(dns_fixedname_name(&fixed)) + 1; dns_name_getlabelsequence(name, dns_name_countlabels(name) - count, count, dns_fixedname_name(&fixed)); fixfname(client, &fname, &dbuf, &b); fixrdataset(client, &rdataset); fixrdataset(client, &sigrdataset); if (fname == NULL || rdataset == NULL || sigrdataset == NULL) goto cleanup; query_findclosestnsec3(dns_fixedname_name(&fixed), db, version, client, rdataset, sigrdataset, fname, ISC_FALSE, NULL); if (!dns_rdataset_isassociated(rdataset)) goto cleanup; query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); } cleanup: if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); } static void query_addwildcardproof(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, isc_boolean_t ispositive, isc_boolean_t nodata) { isc_buffer_t *dbuf, b; dns_name_t *fname; dns_rdataset_t *rdataset, *sigrdataset; dns_fixedname_t wfixed; dns_name_t *wname; dns_dbnode_t *node; unsigned int options; unsigned int olabels, nlabels, labels; isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_nsec_t nsec; isc_boolean_t have_wname; int order; dns_fixedname_t cfixed; dns_name_t *cname; CTRACE("query_addwildcardproof"); fname = NULL; rdataset = NULL; sigrdataset = NULL; node = NULL; /* * Get the NOQNAME proof then if !ispositive * get the NOWILDCARD proof. * * DNS_DBFIND_NOWILD finds the NSEC records that covers the * name ignoring any wildcard. From the owner and next names * of this record you can compute which wildcard (if it exists) * will match by finding the longest common suffix of the * owner name and next names with the qname and prefixing that * with the wildcard label. * * e.g. * Given: * example SOA * example NSEC b.example * b.example A * b.example NSEC a.d.example * a.d.example A * a.d.example NSEC g.f.example * g.f.example A * g.f.example NSEC z.i.example * z.i.example A * z.i.example NSEC example * * QNAME: * a.example -> example NSEC b.example * owner common example * next common example * wild *.example * d.b.example -> b.example NSEC a.d.example * owner common b.example * next common example * wild *.b.example * a.f.example -> a.d.example NSEC g.f.example * owner common example * next common f.example * wild *.f.example * j.example -> z.i.example NSEC example * owner common example * next common example * wild *.example */ options = client->query.dboptions | DNS_DBFIND_NOWILD; dns_fixedname_init(&wfixed); wname = dns_fixedname_name(&wfixed); again: have_wname = ISC_FALSE; /* * We'll need some resources... */ dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); rdataset = query_newrdataset(client); sigrdataset = query_newrdataset(client); if (fname == NULL || rdataset == NULL || sigrdataset == NULL) goto cleanup; result = dns_db_find(db, name, version, dns_rdatatype_nsec, options, 0, &node, fname, rdataset, sigrdataset); if (node != NULL) dns_db_detachnode(db, &node); if (!dns_rdataset_isassociated(rdataset)) { /* * No NSEC proof available, return NSEC3 proofs instead. */ dns_fixedname_init(&cfixed); cname = dns_fixedname_name(&cfixed); /* * Find the closest encloser. */ dns_name_copy(name, cname, NULL); while (result == DNS_R_NXDOMAIN) { labels = dns_name_countlabels(cname) - 1; /* * Sanity check. */ if (labels == 0U) goto cleanup; dns_name_split(cname, labels, NULL, cname); result = dns_db_find(db, cname, version, dns_rdatatype_nsec, options, 0, NULL, fname, NULL, NULL); } /* * Add closest (provable) encloser NSEC3. */ query_findclosestnsec3(cname, db, NULL, client, rdataset, sigrdataset, fname, ISC_TRUE, cname); if (!dns_rdataset_isassociated(rdataset)) goto cleanup; if (!ispositive) query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); /* * Replace resources which were consumed by query_addrrset. */ if (fname == NULL) { dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); } if (rdataset == NULL) rdataset = query_newrdataset(client); else if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); if (sigrdataset == NULL) sigrdataset = query_newrdataset(client); else if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); if (fname == NULL || rdataset == NULL || sigrdataset == NULL) goto cleanup; /* * Add no qname proof. */ labels = dns_name_countlabels(cname) + 1; if (dns_name_countlabels(name) == labels) dns_name_copy(name, wname, NULL); else dns_name_split(name, labels, NULL, wname); query_findclosestnsec3(wname, db, NULL, client, rdataset, sigrdataset, fname, ISC_FALSE, NULL); if (!dns_rdataset_isassociated(rdataset)) goto cleanup; query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); if (ispositive) goto cleanup; /* * Replace resources which were consumed by query_addrrset. */ if (fname == NULL) { dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); } if (rdataset == NULL) rdataset = query_newrdataset(client); else if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); if (sigrdataset == NULL) sigrdataset = query_newrdataset(client); else if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); if (fname == NULL || rdataset == NULL || sigrdataset == NULL) goto cleanup; /* * Add the no wildcard proof. */ result = dns_name_concatenate(dns_wildcardname, cname, wname, NULL); if (result != ISC_R_SUCCESS) goto cleanup; query_findclosestnsec3(wname, db, NULL, client, rdataset, sigrdataset, fname, nodata, NULL); if (!dns_rdataset_isassociated(rdataset)) goto cleanup; query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); goto cleanup; } else if (result == DNS_R_NXDOMAIN) { if (!ispositive) result = dns_rdataset_first(rdataset); if (result == ISC_R_SUCCESS) { dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &nsec, NULL); } if (result == ISC_R_SUCCESS) { (void)dns_name_fullcompare(name, fname, &order, &olabels); (void)dns_name_fullcompare(name, &nsec.next, &order, &nlabels); /* * Check for a pathological condition created when * serving some malformed signed zones and bail out. */ if (dns_name_countlabels(name) == nlabels) goto cleanup; if (olabels > nlabels) dns_name_split(name, olabels, NULL, wname); else dns_name_split(name, nlabels, NULL, wname); result = dns_name_concatenate(dns_wildcardname, wname, wname, NULL); if (result == ISC_R_SUCCESS) have_wname = ISC_TRUE; dns_rdata_freestruct(&nsec); } query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); } if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); if (have_wname) { ispositive = ISC_TRUE; /* prevent loop */ if (!dns_name_equal(name, wname)) { name = wname; goto again; } } cleanup: if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); } static void query_addnxrrsetnsec(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version, dns_name_t **namep, dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp) { dns_name_t *name; dns_rdataset_t *sigrdataset; dns_rdata_t sigrdata; dns_rdata_rrsig_t sig; unsigned int labels; isc_buffer_t *dbuf, b; dns_name_t *fname; isc_result_t result; name = *namep; if ((name->attributes & DNS_NAMEATTR_WILDCARD) == 0) { query_addrrset(client, namep, rdatasetp, sigrdatasetp, NULL, DNS_SECTION_AUTHORITY); return; } if (sigrdatasetp == NULL) return; sigrdataset = *sigrdatasetp; if (sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset)) return; result = dns_rdataset_first(sigrdataset); if (result != ISC_R_SUCCESS) return; dns_rdata_init(&sigrdata); dns_rdataset_current(sigrdataset, &sigrdata); result = dns_rdata_tostruct(&sigrdata, &sig, NULL); if (result != ISC_R_SUCCESS) return; labels = dns_name_countlabels(name); if ((unsigned int)sig.labels + 1 >= labels) return; /* XXX */ query_addwildcardproof(client, db, version, client->query.qname, ISC_TRUE, ISC_FALSE); /* * We'll need some resources... */ dbuf = query_getnamebuf(client); if (dbuf == NULL) return; fname = query_newname(client, dbuf, &b); if (fname == NULL) return; dns_name_split(name, sig.labels + 1, NULL, fname); /* This will succeed, since we've stripped labels. */ RUNTIME_CHECK(dns_name_concatenate(dns_wildcardname, fname, fname, NULL) == ISC_R_SUCCESS); query_addrrset(client, &fname, rdatasetp, sigrdatasetp, dbuf, DNS_SECTION_AUTHORITY); } static void query_resume(isc_task_t *task, isc_event_t *event) { dns_fetchevent_t *devent = (dns_fetchevent_t *)event; dns_fetch_t *fetch; ns_client_t *client; isc_boolean_t fetch_canceled, client_shuttingdown; isc_result_t result; isc_logcategory_t *logcategory = NS_LOGCATEGORY_QUERY_EERRORS; int errorloglevel; /* * Resume a query after recursion. */ UNUSED(task); REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); client = devent->ev_arg; REQUIRE(NS_CLIENT_VALID(client)); REQUIRE(task == client->task); REQUIRE(RECURSING(client)); LOCK(&client->query.fetchlock); if (client->query.fetch != NULL) { /* * This is the fetch we've been waiting for. */ INSIST(devent->fetch == client->query.fetch); client->query.fetch = NULL; fetch_canceled = ISC_FALSE; /* * Update client->now. */ isc_stdtime_get(&client->now); } else { /* * This is a fetch completion event for a canceled fetch. * Clean up and don't resume the find. */ fetch_canceled = ISC_TRUE; } UNLOCK(&client->query.fetchlock); INSIST(client->query.fetch == NULL); client->query.attributes &= ~NS_QUERYATTR_RECURSING; fetch = devent->fetch; devent->fetch = NULL; /* * If this client is shutting down, or this transaction * has timed out, do not resume the find. */ client_shuttingdown = ns_client_shuttingdown(client); if (fetch_canceled || client_shuttingdown) { if (devent->node != NULL) dns_db_detachnode(devent->db, &devent->node); if (devent->db != NULL) dns_db_detach(&devent->db); query_putrdataset(client, &devent->rdataset); if (devent->sigrdataset != NULL) query_putrdataset(client, &devent->sigrdataset); isc_event_free(&event); if (fetch_canceled) query_error(client, DNS_R_SERVFAIL, __LINE__); else query_next(client, ISC_R_CANCELED); /* * This may destroy the client. */ ns_client_detach(&client); } else { result = query_find(client, devent, 0); if (result != ISC_R_SUCCESS) { if (result == DNS_R_SERVFAIL) errorloglevel = ISC_LOG_DEBUG(2); else errorloglevel = ISC_LOG_DEBUG(4); if (isc_log_wouldlog(ns_g_lctx, errorloglevel)) { dns_resolver_logfetch(fetch, ns_g_lctx, logcategory, NS_LOGMODULE_QUERY, errorloglevel, ISC_FALSE); } } } dns_resolver_destroyfetch(&fetch); } static isc_result_t query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, dns_name_t *qdomain, dns_rdataset_t *nameservers, isc_boolean_t resuming) { isc_result_t result; dns_rdataset_t *rdataset, *sigrdataset; isc_sockaddr_t *peeraddr; if (!resuming) inc_stats(client, dns_nsstatscounter_recursion); /* * We are about to recurse, which means that this client will * be unavailable for serving new requests for an indeterminate * amount of time. If this client is currently responsible * for handling incoming queries, set up a new client * object to handle them while we are waiting for a * response. There is no need to replace TCP clients * because those have already been replaced when the * connection was accepted (if allowed by the TCP quota). */ if (client->recursionquota == NULL) { result = isc_quota_attach(&ns_g_server->recursionquota, &client->recursionquota); if (result == ISC_R_SOFTQUOTA) { static isc_stdtime_t last = 0; isc_stdtime_t now; isc_stdtime_get(&now); if (now != last) { last = now; ns_client_log(client, NS_LOGCATEGORY_CLIENT, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "recursive-clients soft limit " "exceeded (%d/%d/%d), " "aborting oldest query", client->recursionquota->used, client->recursionquota->soft, client->recursionquota->max); } ns_client_killoldestquery(client); result = ISC_R_SUCCESS; } else if (result == ISC_R_QUOTA) { static isc_stdtime_t last = 0; isc_stdtime_t now; isc_stdtime_get(&now); if (now != last) { last = now; ns_client_log(client, NS_LOGCATEGORY_CLIENT, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "no more recursive clients " "(%d/%d/%d): %s", ns_g_server->recursionquota.used, ns_g_server->recursionquota.soft, ns_g_server->recursionquota.max, isc_result_totext(result)); } ns_client_killoldestquery(client); } if (result == ISC_R_SUCCESS && !client->mortal && (client->attributes & NS_CLIENTATTR_TCP) == 0) { result = ns_client_replace(client); if (result != ISC_R_SUCCESS) { ns_client_log(client, NS_LOGCATEGORY_CLIENT, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "ns_client_replace() failed: %s", isc_result_totext(result)); isc_quota_detach(&client->recursionquota); } } if (result != ISC_R_SUCCESS) return (result); ns_client_recursing(client); } /* * Invoke the resolver. */ REQUIRE(nameservers == NULL || nameservers->type == dns_rdatatype_ns); REQUIRE(client->query.fetch == NULL); rdataset = query_newrdataset(client); if (rdataset == NULL) return (ISC_R_NOMEMORY); if (WANTDNSSEC(client)) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) { query_putrdataset(client, &rdataset); return (ISC_R_NOMEMORY); } } else sigrdataset = NULL; if (client->query.timerset == ISC_FALSE) ns_client_settimeout(client, 60); if ((client->attributes & NS_CLIENTATTR_TCP) == 0) peeraddr = &client->peeraddr; else peeraddr = NULL; - result = dns_resolver_createfetch2(client->view->resolver, + result = dns_resolver_createfetch3(client->view->resolver, qname, qtype, qdomain, nameservers, NULL, peeraddr, client->message->id, - client->query.fetchoptions, - client->task, - query_resume, client, + client->query.fetchoptions, 0, NULL, + client->task, query_resume, client, rdataset, sigrdataset, &client->query.fetch); if (result == ISC_R_SUCCESS) { /* * Record that we're waiting for an event. A client which * is shutting down will not be destroyed until all the * events have been received. */ } else { query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); } return (result); } static inline void rpz_clean(dns_zone_t **zonep, dns_db_t **dbp, dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp) { if (nodep != NULL && *nodep != NULL) { REQUIRE(dbp != NULL && *dbp != NULL); dns_db_detachnode(*dbp, nodep); } if (dbp != NULL && *dbp != NULL) dns_db_detach(dbp); if (zonep != NULL && *zonep != NULL) dns_zone_detach(zonep); if (rdatasetp != NULL && *rdatasetp != NULL && dns_rdataset_isassociated(*rdatasetp)) dns_rdataset_disassociate(*rdatasetp); } static void rpz_match_clear(dns_rpz_st_t *st) { rpz_clean(&st->m.zone, &st->m.db, &st->m.node, &st->m.rdataset); st->m.version = NULL; } static inline isc_result_t rpz_ready(ns_client_t *client, dns_zone_t **zonep, dns_db_t **dbp, dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp) { REQUIRE(rdatasetp != NULL); rpz_clean(zonep, dbp, nodep, rdatasetp); if (*rdatasetp == NULL) { *rdatasetp = query_newrdataset(client); if (*rdatasetp == NULL) return (DNS_R_SERVFAIL); } return (ISC_R_SUCCESS); } static void rpz_st_clear(ns_client_t *client) { dns_rpz_st_t *st = client->query.rpz_st; if (st->m.rdataset != NULL) query_putrdataset(client, &st->m.rdataset); rpz_match_clear(st); rpz_clean(NULL, &st->r.db, NULL, NULL); if (st->r.ns_rdataset != NULL) query_putrdataset(client, &st->r.ns_rdataset); if (st->r.r_rdataset != NULL) query_putrdataset(client, &st->r.r_rdataset); rpz_clean(&st->q.zone, &st->q.db, &st->q.node, NULL); if (st->q.rdataset != NULL) query_putrdataset(client, &st->q.rdataset); if (st->q.sigrdataset != NULL) query_putrdataset(client, &st->q.sigrdataset); st->state = 0; st->m.type = DNS_RPZ_TYPE_BAD; st->m.policy = DNS_RPZ_POLICY_MISS; } /* * Get NS, A, or AAAA rrset for response policy zone checks. */ static isc_result_t rpz_rrset_find(ns_client_t *client, dns_rpz_type_t rpz_type, dns_name_t *name, dns_rdatatype_t type, dns_db_t **dbp, dns_dbversion_t *version, dns_rdataset_t **rdatasetp, isc_boolean_t resuming) { dns_rpz_st_t *st; isc_boolean_t is_zone; dns_dbnode_t *node; dns_fixedname_t fixed; dns_name_t *found; isc_result_t result; st = client->query.rpz_st; if ((st->state & DNS_RPZ_RECURSING) != 0) { INSIST(st->r.r_type == type); INSIST(dns_name_equal(name, st->r_name)); INSIST(*rdatasetp == NULL || !dns_rdataset_isassociated(*rdatasetp)); st->state &= ~DNS_RPZ_RECURSING; *dbp = st->r.db; st->r.db = NULL; if (*rdatasetp != NULL) query_putrdataset(client, rdatasetp); *rdatasetp = st->r.r_rdataset; st->r.r_rdataset = NULL; result = st->r.r_result; if (result == DNS_R_DELEGATION) { rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, name, "rpz_rrset_find(1) ", result); st->m.policy = DNS_RPZ_POLICY_ERROR; result = DNS_R_SERVFAIL; } return (result); } result = rpz_ready(client, NULL, NULL, NULL, rdatasetp); if (result != ISC_R_SUCCESS) { st->m.policy = DNS_RPZ_POLICY_ERROR; return (result); } if (*dbp != NULL) { is_zone = ISC_FALSE; } else { dns_zone_t *zone; version = NULL; zone = NULL; result = query_getdb(client, name, type, 0, &zone, dbp, &version, &is_zone); if (result != ISC_R_SUCCESS) { rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, name, "rpz_rrset_find(2) ", result); st->m.policy = DNS_RPZ_POLICY_ERROR; if (zone != NULL) dns_zone_detach(&zone); return (result); } if (zone != NULL) dns_zone_detach(&zone); } node = NULL; dns_fixedname_init(&fixed); found = dns_fixedname_name(&fixed); result = dns_db_find(*dbp, name, version, type, DNS_DBFIND_GLUEOK, client->now, &node, found, *rdatasetp, NULL); if (result == DNS_R_DELEGATION && is_zone && USECACHE(client)) { /* * Try the cache if we're authoritative for an * ancestor but not the domain itself. */ rpz_clean(NULL, dbp, &node, rdatasetp); version = NULL; dns_db_attach(client->view->cachedb, dbp); result = dns_db_find(*dbp, name, version, dns_rdatatype_ns, 0, client->now, &node, found, *rdatasetp, NULL); } rpz_clean(NULL, dbp, &node, NULL); if (result == DNS_R_DELEGATION) { rpz_clean(NULL, NULL, NULL, rdatasetp); /* * Recurse for NS rrset or A or AAAA rrset for an NS. * Do not recurse for addresses for the query name. */ if (rpz_type == DNS_RPZ_TYPE_IP) { result = DNS_R_NXRRSET; } else { dns_name_copy(name, st->r_name, NULL); result = query_recurse(client, type, st->r_name, NULL, NULL, resuming); if (result == ISC_R_SUCCESS) { st->state |= DNS_RPZ_RECURSING; result = DNS_R_DELEGATION; } } } return (result); } /* * Check the IP address in an A or AAAA rdataset against * the IP or NSIP response policy rules of a view. */ static isc_result_t rpz_rewrite_ip(ns_client_t *client, dns_rdataset_t *rdataset, dns_rpz_type_t rpz_type) { dns_rpz_st_t *st; dns_dbversion_t *version; dns_zone_t *zone; dns_db_t *db; dns_rpz_zone_t *rpz; isc_result_t result; st = client->query.rpz_st; if (st->m.rdataset == NULL) { st->m.rdataset = query_newrdataset(client); if (st->m.rdataset == NULL) return (DNS_R_SERVFAIL); } zone = NULL; db = NULL; for (rpz = ISC_LIST_HEAD(client->view->rpz_zones); rpz != NULL; rpz = ISC_LIST_NEXT(rpz, link)) { if (!RECURSIONOK(client) && rpz->recursive_only) continue; /* * Do not check policy zones that cannot replace a policy * already known to match. */ if (st->m.policy != DNS_RPZ_POLICY_MISS) { if (st->m.rpz->num < rpz->num) break; if (st->m.rpz->num == rpz->num && st->m.type < rpz_type) continue; } /* * Find the database for this policy zone to get its radix tree. */ version = NULL; result = rpz_getdb(client, rpz_type, &rpz->origin, &zone, &db, &version); if (result != ISC_R_SUCCESS) { rpz_clean(&zone, &db, NULL, NULL); continue; } /* * Look for a better (e.g. longer prefix) hit for an IP address * in this rdataset in this radix tree than than the previous * hit, if any. Note the domain name and quality of the * best hit. */ dns_db_rpz_findips(rpz, rpz_type, zone, db, version, rdataset, st, client->query.rpz_st->qname); rpz_clean(&zone, &db, NULL, NULL); } return (ISC_R_SUCCESS); } /* * Look for an A or AAAA rdataset * and check for IP or NSIP rewrite policy rules. */ static isc_result_t rpz_rewrite_rrset(ns_client_t *client, dns_rpz_type_t rpz_type, dns_rdatatype_t type, dns_name_t *name, dns_db_t **dbp, dns_dbversion_t *version, dns_rdataset_t **rdatasetp, isc_boolean_t resuming) { isc_result_t result; result = rpz_rrset_find(client, rpz_type, name, type, dbp, version, rdatasetp, resuming); switch (result) { case ISC_R_SUCCESS: case DNS_R_GLUE: case DNS_R_ZONECUT: result = rpz_rewrite_ip(client, *rdatasetp, rpz_type); break; case DNS_R_EMPTYNAME: case DNS_R_EMPTYWILD: case DNS_R_NXDOMAIN: case DNS_R_NCACHENXDOMAIN: case DNS_R_NXRRSET: case DNS_R_NCACHENXRRSET: case ISC_R_NOTFOUND: result = ISC_R_SUCCESS; break; case DNS_R_DELEGATION: case DNS_R_DUPLICATE: case DNS_R_DROP: break; case DNS_R_CNAME: case DNS_R_DNAME: rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, rpz_type, name, "NS address rewrite rrset ", result); result = ISC_R_SUCCESS; break; default: if (client->query.rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) { client->query.rpz_st->m.policy = DNS_RPZ_POLICY_ERROR; rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, name, "NS address rewrite rrset ", result); } break; } return (result); } /* * Look for both A and AAAA rdatasets * and check for IP or NSIP rewrite policy rules. * Look only for addresses that will be in the ANSWER section * when checking for IP rules. */ static isc_result_t rpz_rewrite_rrsets(ns_client_t *client, dns_rpz_type_t rpz_type, dns_name_t *name, dns_rdatatype_t type, dns_rdataset_t **rdatasetp, isc_boolean_t resuming) { dns_rpz_st_t *st; dns_dbversion_t *version; dns_db_t *ipdb; isc_result_t result; st = client->query.rpz_st; version = NULL; ipdb = NULL; if ((st->state & DNS_RPZ_DONE_IPv4) == 0 && ((rpz_type == DNS_RPZ_TYPE_NSIP) ? (st->state & DNS_RPZ_HAVE_NSIPv4) : (st->state & DNS_RPZ_HAVE_IP)) != 0 && (type == dns_rdatatype_any || type == dns_rdatatype_a)) { result = rpz_rewrite_rrset(client, rpz_type, dns_rdatatype_a, name, &ipdb, version, rdatasetp, resuming); if (result == ISC_R_SUCCESS) st->state |= DNS_RPZ_DONE_IPv4; } else { result = ISC_R_SUCCESS; } if (result == ISC_R_SUCCESS && ((rpz_type == DNS_RPZ_TYPE_NSIP) ? (st->state & DNS_RPZ_HAVE_NSIPv6) : (st->state & DNS_RPZ_HAVE_IP)) != 0 && (type == dns_rdatatype_any || type == dns_rdatatype_aaaa)) { result = rpz_rewrite_rrset(client, rpz_type, dns_rdatatype_aaaa, name, &ipdb, version, rdatasetp, resuming); } if (ipdb != NULL) dns_db_detach(&ipdb); return (result); } /* * Get the rrset from a response policy zone. */ static isc_result_t rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef, dns_name_t *sname, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp, dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp, dns_rpz_policy_t *policyp) { dns_rpz_policy_t policy; dns_fixedname_t fixed; dns_name_t *found; isc_result_t result; REQUIRE(nodep != NULL); result = rpz_ready(client, zonep, dbp, nodep, rdatasetp); if (result != ISC_R_SUCCESS) { *policyp = DNS_RPZ_POLICY_ERROR; return (result); } /* * Try to get either a CNAME or the type of record demanded by the * request from the policy zone. */ *versionp = NULL; result = rpz_getdb(client, rpz_type, qnamef, zonep, dbp, versionp); if (result != ISC_R_SUCCESS) { *policyp = DNS_RPZ_POLICY_MISS; return (DNS_R_NXDOMAIN); } dns_fixedname_init(&fixed); found = dns_fixedname_name(&fixed); result = dns_db_find(*dbp, qnamef, *versionp, dns_rdatatype_any, 0, client->now, nodep, found, *rdatasetp, NULL); if (result == ISC_R_SUCCESS) { dns_rdatasetiter_t *rdsiter; rdsiter = NULL; result = dns_db_allrdatasets(*dbp, *nodep, *versionp, 0, &rdsiter); if (result != ISC_R_SUCCESS) { dns_db_detachnode(*dbp, nodep); rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef, "allrdatasets() ", result); *policyp = DNS_RPZ_POLICY_ERROR; return (DNS_R_SERVFAIL); } for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; result = dns_rdatasetiter_next(rdsiter)) { dns_rdatasetiter_current(rdsiter, *rdatasetp); if ((*rdatasetp)->type == dns_rdatatype_cname || (*rdatasetp)->type == qtype) break; dns_rdataset_disassociate(*rdatasetp); } dns_rdatasetiter_destroy(&rdsiter); if (result != ISC_R_SUCCESS) { if (result != ISC_R_NOMORE) { rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef, "rdatasetiter ", result); *policyp = DNS_RPZ_POLICY_ERROR; return (DNS_R_SERVFAIL); } /* * Ask again to get the right DNS_R_DNAME/NXRRSET/... * result if there is neither a CNAME nor target type. */ if (dns_rdataset_isassociated(*rdatasetp)) dns_rdataset_disassociate(*rdatasetp); dns_db_detachnode(*dbp, nodep); if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig) result = DNS_R_NXRRSET; else result = dns_db_find(*dbp, qnamef, *versionp, qtype, 0, client->now, nodep, found, *rdatasetp, NULL); } } switch (result) { case ISC_R_SUCCESS: if ((*rdatasetp)->type != dns_rdatatype_cname) { policy = DNS_RPZ_POLICY_RECORD; } else { policy = dns_rpz_decode_cname(rpz, *rdatasetp, sname); if ((policy == DNS_RPZ_POLICY_RECORD || policy == DNS_RPZ_POLICY_WILDCNAME) && qtype != dns_rdatatype_cname && qtype != dns_rdatatype_any) result = DNS_R_CNAME; } break; case DNS_R_NXRRSET: policy = DNS_RPZ_POLICY_NODATA; break; case DNS_R_DNAME: /* * DNAME policy RRs have very few if any uses that are not * better served with simple wildcards. Making the work would * require complications to get the number of labels matched * in the name or the found name to the main DNS_R_DNAME case * in query_find(). */ dns_rdataset_disassociate(*rdatasetp); dns_db_detachnode(*dbp, nodep); /* * Fall through to treat it as a miss. */ case DNS_R_NXDOMAIN: case DNS_R_EMPTYNAME: /* * If we don't get a qname hit, * see if it is worth looking for other types. */ (void)dns_db_rpz_enabled(*dbp, client->query.rpz_st); dns_db_detach(dbp); dns_zone_detach(zonep); result = DNS_R_NXDOMAIN; policy = DNS_RPZ_POLICY_MISS; break; default: dns_db_detach(dbp); dns_zone_detach(zonep); rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef, "", result); return (DNS_R_SERVFAIL); } *policyp = policy; return (result); } /* * Build and look for a QNAME or NSDNAME owner name in a response policy zone. */ static isc_result_t rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, dns_rpz_type_t rpz_type, dns_rdataset_t **rdatasetp) { dns_rpz_st_t *st; dns_rpz_zone_t *rpz; dns_fixedname_t prefixf, rpz_qnamef; dns_name_t *prefix, *suffix, *rpz_qname; dns_zone_t *zone; dns_db_t *db; dns_dbversion_t *version; dns_dbnode_t *node; dns_rpz_policy_t policy; unsigned int labels; isc_result_t result; st = client->query.rpz_st; zone = NULL; db = NULL; node = NULL; for (rpz = ISC_LIST_HEAD(client->view->rpz_zones); rpz != NULL; rpz = ISC_LIST_NEXT(rpz, link)) { if (!RECURSIONOK(client) && rpz->recursive_only) continue; /* * Do not check policy zones that cannot replace a policy * already known to match. */ if (st->m.policy != DNS_RPZ_POLICY_MISS) { if (st->m.rpz->num < rpz->num) break; if (st->m.rpz->num == rpz->num && st->m.type < rpz_type) continue; } /* * Construct the policy's owner name. */ dns_fixedname_init(&prefixf); prefix = dns_fixedname_name(&prefixf); dns_name_split(qname, 1, prefix, NULL); if (rpz_type == DNS_RPZ_TYPE_NSDNAME) suffix = &rpz->nsdname; else suffix = &rpz->origin; dns_fixedname_init(&rpz_qnamef); rpz_qname = dns_fixedname_name(&rpz_qnamef); for (;;) { result = dns_name_concatenate(prefix, suffix, rpz_qname, NULL); if (result == ISC_R_SUCCESS) break; INSIST(result == DNS_R_NAMETOOLONG); /* * Trim the name until it is not too long. */ labels = dns_name_countlabels(prefix); if (labels < 2) { rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, suffix, "concatentate() ", result); return (ISC_R_SUCCESS); } if (labels+1 == dns_name_countlabels(qname)) { rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, rpz_type, suffix, "concatentate() ", result); } dns_name_split(prefix, labels - 1, NULL, prefix); } /* * See if the policy record exists and get its policy. */ result = rpz_find(client, qtype, rpz_qname, qname, rpz, rpz_type, &zone, &db, &version, &node, rdatasetp, &policy); switch (result) { case DNS_R_NXDOMAIN: break; case DNS_R_SERVFAIL: rpz_clean(&zone, &db, &node, rdatasetp); st->m.policy = DNS_RPZ_POLICY_ERROR; return (DNS_R_SERVFAIL); default: /* * We are dealing with names here. * With more than one applicable policy, prefer * the earliest configured policy, * QNAME over IP over NSDNAME over NSIP, * and the smallest name. * Because of the testing above, * we known st->m.rpz->num >= rpz->num and either * st->m.rpz->num > rpz->num or st->m.type >= rpz_type */ if (st->m.policy != DNS_RPZ_POLICY_MISS && rpz->num == st->m.rpz->num && (st->m.type < rpz_type || (st->m.type == rpz_type && 0 >= dns_name_compare(rpz_qname, st->qname)))) continue; #if 0 /* * This code would block a customer reported information * leak of rpz rules by rewriting requests in the * rpz-ip, rpz-nsip, rpz-nsdname,and rpz-passthru TLDs. * Without this code, a bad guy could request * 24.0.3.2.10.rpz-ip. to find the policy rule for * 10.2.3.0/14. It is an insignificant leak and this * code is not worth its cost, because the bad guy * could publish "evil.com A 10.2.3.4" and request * evil.com to get the same information. * Keep code with "#if 0" in case customer demand * is irresistible. * * We have the less frequent case of a triggered * policy. Check that we have not trigger on one * of the pretend RPZ TLDs. * This test would make it impossible to rewrite * names in TLDs that start with "rpz-" should * ICANN ever allow such TLDs. */ labels = dns_name_countlabels(qname); if (labels >= 2) { dns_label_t label; dns_name_getlabel(qname, labels-2, &label); if (label.length >= sizeof(DNS_RPZ_PREFIX)-1 && strncasecmp((const char *)label.base+1, DNS_RPZ_PREFIX, sizeof(DNS_RPZ_PREFIX)-1) == 0) continue; } #endif /* * Merely log DNS_RPZ_POLICY_DISABLED hits. */ if (rpz->policy == DNS_RPZ_POLICY_DISABLED) { rpz_log_rewrite(client, ISC_TRUE, policy, rpz_type, zone, rpz_qname); continue; } rpz_match_clear(st); st->m.rpz = rpz; st->m.type = rpz_type; st->m.prefix = 0; st->m.policy = policy; st->m.result = result; dns_name_copy(rpz_qname, st->qname, NULL); if (*rdatasetp != NULL && dns_rdataset_isassociated(*rdatasetp)) { dns_rdataset_t *trdataset; trdataset = st->m.rdataset; st->m.rdataset = *rdatasetp; *rdatasetp = trdataset; st->m.ttl = ISC_MIN(st->m.rdataset->ttl, rpz->max_policy_ttl); } else { st->m.ttl = ISC_MIN(DNS_RPZ_TTL_DEFAULT, rpz->max_policy_ttl); } st->m.node = node; node = NULL; st->m.db = db; db = NULL; st->m.version = version; st->m.zone = zone; zone = NULL; } } rpz_clean(&zone, &db, &node, rdatasetp); return (ISC_R_SUCCESS); } static void rpz_rewrite_ns_skip(ns_client_t *client, dns_name_t *nsname, isc_result_t result, int level, const char *str) { dns_rpz_st_t *st; st = client->query.rpz_st; if (str != NULL) rpz_log_fail(client, level, DNS_RPZ_TYPE_NSIP, nsname, str, result); if (st->r.ns_rdataset != NULL && dns_rdataset_isassociated(st->r.ns_rdataset)) dns_rdataset_disassociate(st->r.ns_rdataset); st->r.label--; } /* * Look for response policy zone QNAME, NSIP, and NSDNAME rewriting. */ static isc_result_t rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, isc_boolean_t resuming) { dns_rpz_st_t *st; dns_rdataset_t *rdataset; dns_fixedname_t nsnamef; dns_name_t *nsname; isc_boolean_t ck_ip; isc_result_t result; st = client->query.rpz_st; if (st == NULL) { st = isc_mem_get(client->mctx, sizeof(*st)); if (st == NULL) return (ISC_R_NOMEMORY); st->state = 0; memset(&st->m, 0, sizeof(st->m)); st->m.type = DNS_RPZ_TYPE_BAD; st->m.policy = DNS_RPZ_POLICY_MISS; memset(&st->r, 0, sizeof(st->r)); memset(&st->q, 0, sizeof(st->q)); dns_fixedname_init(&st->_qnamef); dns_fixedname_init(&st->_r_namef); dns_fixedname_init(&st->_fnamef); st->qname = dns_fixedname_name(&st->_qnamef); st->r_name = dns_fixedname_name(&st->_r_namef); st->fname = dns_fixedname_name(&st->_fnamef); client->query.rpz_st = st; } /* * There is nothing to rewrite if the main query failed. */ switch (qresult) { case ISC_R_SUCCESS: case DNS_R_GLUE: case DNS_R_ZONECUT: ck_ip = ISC_TRUE; break; case DNS_R_EMPTYNAME: case DNS_R_NXRRSET: case DNS_R_NXDOMAIN: case DNS_R_EMPTYWILD: case DNS_R_NCACHENXDOMAIN: case DNS_R_NCACHENXRRSET: case DNS_R_CNAME: case DNS_R_DNAME: ck_ip = ISC_FALSE; break; case DNS_R_DELEGATION: case ISC_R_NOTFOUND: return (ISC_R_SUCCESS); case ISC_R_FAILURE: case ISC_R_TIMEDOUT: case DNS_R_BROKENCHAIN: rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, DNS_RPZ_TYPE_QNAME, client->query.qname, "stop on qresult in rpz_rewrite() ", qresult); return (ISC_R_SUCCESS); default: rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, DNS_RPZ_TYPE_QNAME, client->query.qname, "stop on unrecognized qresult in rpz_rewrite() ", qresult); return (ISC_R_SUCCESS); } rdataset = NULL; if ((st->state & DNS_RPZ_DONE_QNAME) == 0) { /* * Check rules for the query name if this is the first time * for the current qname, i.e. we've not been recursing. * There is a first time for each name in a CNAME chain. */ result = rpz_rewrite_name(client, qtype, client->query.qname, DNS_RPZ_TYPE_QNAME, &rdataset); if (result != ISC_R_SUCCESS) goto cleanup; st->r.label = dns_name_countlabels(client->query.qname); st->state &= ~(DNS_RPZ_DONE_QNAME_IP | DNS_RPZ_DONE_IPv4); st->state |= DNS_RPZ_DONE_QNAME; } /* * Check known IP addresses for the query name. * Any recursion required for the query has already happened. * Do not check addresses that will not be in the ANSWER section. */ if ((st->state & DNS_RPZ_DONE_QNAME_IP) == 0 && (st->state & DNS_RPZ_HAVE_IP) != 0 && ck_ip) { result = rpz_rewrite_rrsets(client, DNS_RPZ_TYPE_IP, client->query.qname, qtype, &rdataset, resuming); if (result != ISC_R_SUCCESS) goto cleanup; st->state &= ~DNS_RPZ_DONE_IPv4; st->state |= DNS_RPZ_DONE_QNAME_IP; } /* * Stop looking for rules if there are none of the other kinds. */ if ((st->state & (DNS_RPZ_HAVE_NSIPv4 | DNS_RPZ_HAVE_NSIPv6 | DNS_RPZ_HAVE_NSDNAME)) == 0) { result = ISC_R_SUCCESS; goto cleanup; } dns_fixedname_init(&nsnamef); dns_name_clone(client->query.qname, dns_fixedname_name(&nsnamef)); while (st->r.label > client->view->rpz_min_ns_labels) { /* * Get NS rrset for each domain in the current qname. */ if (st->r.label == dns_name_countlabels(client->query.qname)) { nsname = client->query.qname; } else { nsname = dns_fixedname_name(&nsnamef); dns_name_split(client->query.qname, st->r.label, NULL, nsname); } if (st->r.ns_rdataset == NULL || !dns_rdataset_isassociated(st->r.ns_rdataset)) { dns_db_t *db = NULL; result = rpz_rrset_find(client, DNS_RPZ_TYPE_NSDNAME, nsname, dns_rdatatype_ns, &db, NULL, &st->r.ns_rdataset, resuming); if (db != NULL) dns_db_detach(&db); if (st->m.policy == DNS_RPZ_POLICY_ERROR) goto cleanup; switch (result) { case ISC_R_SUCCESS: result = dns_rdataset_first(st->r.ns_rdataset); if (result != ISC_R_SUCCESS) goto cleanup; st->state &= ~(DNS_RPZ_DONE_NSDNAME | DNS_RPZ_DONE_IPv4); break; case DNS_R_DELEGATION: goto cleanup; case DNS_R_EMPTYNAME: case DNS_R_NXRRSET: case DNS_R_EMPTYWILD: case DNS_R_NXDOMAIN: case DNS_R_NCACHENXDOMAIN: case DNS_R_NCACHENXRRSET: case ISC_R_NOTFOUND: case DNS_R_CNAME: case DNS_R_DNAME: rpz_rewrite_ns_skip(client, nsname, result, 0, NULL); continue; case ISC_R_TIMEDOUT: case DNS_R_BROKENCHAIN: case ISC_R_FAILURE: rpz_rewrite_ns_skip(client, nsname, result, DNS_RPZ_DEBUG_LEVEL3, "NS db_find() "); continue; default: rpz_rewrite_ns_skip(client, nsname, result, DNS_RPZ_INFO_LEVEL, "unrecognized NS db_find() "); continue; } } /* * Check all NS names. */ do { dns_rdata_ns_t ns; dns_rdata_t nsrdata = DNS_RDATA_INIT; dns_rdataset_current(st->r.ns_rdataset, &nsrdata); result = dns_rdata_tostruct(&nsrdata, &ns, NULL); dns_rdata_reset(&nsrdata); if (result != ISC_R_SUCCESS) { rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, DNS_RPZ_TYPE_NSIP, nsname, "rdata_tostruct() ", result); st->m.policy = DNS_RPZ_POLICY_ERROR; goto cleanup; } /* * Do nothing about "NS ." */ if (dns_name_equal(&ns.name, dns_rootname)) { dns_rdata_freestruct(&ns); result = dns_rdataset_next(st->r.ns_rdataset); continue; } /* * Check this NS name if we did not handle it * during a previous recursion. */ if ((st->state & DNS_RPZ_DONE_NSDNAME) == 0 && (st->state & DNS_RPZ_HAVE_NSDNAME) != 0) { result = rpz_rewrite_name(client, qtype, &ns.name, DNS_RPZ_TYPE_NSDNAME, &rdataset); if (result != ISC_R_SUCCESS) { dns_rdata_freestruct(&ns); goto cleanup; } st->state |= DNS_RPZ_DONE_NSDNAME; } /* * Check all IP addresses for this NS name. */ result = rpz_rewrite_rrsets(client, DNS_RPZ_TYPE_NSIP, &ns.name, dns_rdatatype_any, &rdataset, resuming); dns_rdata_freestruct(&ns); if (result != ISC_R_SUCCESS) goto cleanup; st->state &= ~(DNS_RPZ_DONE_NSDNAME | DNS_RPZ_DONE_IPv4); result = dns_rdataset_next(st->r.ns_rdataset); } while (result == ISC_R_SUCCESS); dns_rdataset_disassociate(st->r.ns_rdataset); st->r.label--; } /* * Use the best, if any, hit. */ result = ISC_R_SUCCESS; cleanup: if (st->m.policy != DNS_RPZ_POLICY_MISS && st->m.policy != DNS_RPZ_POLICY_ERROR && st->m.rpz->policy != DNS_RPZ_POLICY_GIVEN) st->m.policy = st->m.rpz->policy; if (st->m.policy == DNS_RPZ_POLICY_MISS || st->m.policy == DNS_RPZ_POLICY_PASSTHRU || st->m.policy == DNS_RPZ_POLICY_ERROR) { if (st->m.policy == DNS_RPZ_POLICY_PASSTHRU && result != DNS_R_DELEGATION) rpz_log_rewrite(client, ISC_FALSE, st->m.policy, st->m.type, st->m.zone, st->qname); rpz_match_clear(st); } if (st->m.policy == DNS_RPZ_POLICY_ERROR) { st->m.type = DNS_RPZ_TYPE_BAD; result = DNS_R_SERVFAIL; } query_putrdataset(client, &rdataset); if ((st->state & DNS_RPZ_RECURSING) == 0) rpz_clean(NULL, &st->r.db, NULL, &st->r.ns_rdataset); return (result); } /* * See if response policy zone rewriting is allowed by a lack of interest * by the client in DNSSEC or a lack of signatures. */ static isc_boolean_t rpz_ck_dnssec(ns_client_t *client, isc_result_t result, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { dns_fixedname_t fixed; dns_name_t *found; dns_rdataset_t trdataset; dns_rdatatype_t type; if (client->view->rpz_break_dnssec) return (ISC_TRUE); /* * sigrdataset == NULL if and only !WANTDNSSEC(client) */ if (sigrdataset == NULL) return (ISC_TRUE); if (dns_rdataset_isassociated(sigrdataset)) return (ISC_FALSE); /* * We are happy to rewrite nothing. */ if (rdataset == NULL || !dns_rdataset_isassociated(rdataset)) return (ISC_TRUE); /* * Do not rewrite if there is any sign of signatures. */ if (rdataset->type == dns_rdatatype_nsec || rdataset->type == dns_rdatatype_nsec3 || rdataset->type == dns_rdatatype_rrsig) return (ISC_FALSE); /* * Look for a signature in a negative cache rdataset. */ if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) == 0) return (ISC_TRUE); dns_fixedname_init(&fixed); found = dns_fixedname_name(&fixed); dns_rdataset_init(&trdataset); for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { dns_ncache_current(rdataset, found, &trdataset); type = trdataset.type; dns_rdataset_disassociate(&trdataset); if (type == dns_rdatatype_nsec || type == dns_rdatatype_nsec3 || type == dns_rdatatype_rrsig) return (ISC_FALSE); } return (ISC_TRUE); } /* * Add a CNAME to the query response, including translating foo.evil.com and * *.evil.com CNAME *.example.com * to * foo.evil.com CNAME foo.evil.com.example.com */ static isc_result_t rpz_add_cname(ns_client_t *client, dns_rpz_st_t *st, dns_name_t *cname, dns_name_t *fname, isc_buffer_t *dbuf) { dns_fixedname_t prefix, suffix; unsigned int labels; isc_result_t result; labels = dns_name_countlabels(cname); if (labels > 2 && dns_name_iswildcard(cname)) { dns_fixedname_init(&prefix); dns_name_split(client->query.qname, 1, dns_fixedname_name(&prefix), NULL); dns_fixedname_init(&suffix); dns_name_split(cname, labels-1, NULL, dns_fixedname_name(&suffix)); result = dns_name_concatenate(dns_fixedname_name(&prefix), dns_fixedname_name(&suffix), fname, NULL); if (result == DNS_R_NAMETOOLONG) client->message->rcode = dns_rcode_yxdomain; } else { result = dns_name_copy(cname, fname, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); } if (result != ISC_R_SUCCESS) return (result); query_keepname(client, fname, dbuf); result = query_add_cname(client, client->query.qname, fname, dns_trust_authanswer, st->m.ttl); if (result != ISC_R_SUCCESS) return (result); rpz_log_rewrite(client, ISC_FALSE, st->m.policy, st->m.type, st->m.zone, st->qname); ns_client_qnamereplace(client, fname); /* * Turn off DNSSEC because the results of a * response policy zone cannot verify. */ client->attributes &= ~(NS_CLIENTATTR_WANTDNSSEC | DNS_MESSAGEFLAG_AD); return (ISC_R_SUCCESS); } #define MAX_RESTARTS 16 #define QUERY_ERROR(r) \ do { \ eresult = r; \ want_restart = ISC_FALSE; \ line = __LINE__; \ } while (0) #define RECURSE_ERROR(r) \ do { \ if ((r) == DNS_R_DUPLICATE || (r) == DNS_R_DROP) \ QUERY_ERROR(r); \ else \ QUERY_ERROR(DNS_R_SERVFAIL); \ } while (0) /* * Extract a network address from the RDATA of an A or AAAA * record. * * Returns: * ISC_R_SUCCESS * ISC_R_NOTIMPLEMENTED The rdata is not a known address type. */ static isc_result_t rdata_tonetaddr(const dns_rdata_t *rdata, isc_netaddr_t *netaddr) { struct in_addr ina; struct in6_addr in6a; switch (rdata->type) { case dns_rdatatype_a: INSIST(rdata->length == 4); memmove(&ina.s_addr, rdata->data, 4); isc_netaddr_fromin(netaddr, &ina); return (ISC_R_SUCCESS); case dns_rdatatype_aaaa: INSIST(rdata->length == 16); memmove(in6a.s6_addr, rdata->data, 16); isc_netaddr_fromin6(netaddr, &in6a); return (ISC_R_SUCCESS); default: return (ISC_R_NOTIMPLEMENTED); } } /* * Find the sort order of 'rdata' in the topology-like * ACL forming the second element in a 2-element top-level * sortlist statement. */ static int query_sortlist_order_2element(const dns_rdata_t *rdata, const void *arg) { isc_netaddr_t netaddr; if (rdata_tonetaddr(rdata, &netaddr) != ISC_R_SUCCESS) return (INT_MAX); return (ns_sortlist_addrorder2(&netaddr, arg)); } /* * Find the sort order of 'rdata' in the matching element * of a 1-element top-level sortlist statement. */ static int query_sortlist_order_1element(const dns_rdata_t *rdata, const void *arg) { isc_netaddr_t netaddr; if (rdata_tonetaddr(rdata, &netaddr) != ISC_R_SUCCESS) return (INT_MAX); return (ns_sortlist_addrorder1(&netaddr, arg)); } /* * Find the sortlist statement that applies to 'client' and set up * the sortlist info in in client->message appropriately. */ static void setup_query_sortlist(ns_client_t *client) { isc_netaddr_t netaddr; dns_rdatasetorderfunc_t order = NULL; const void *order_arg = NULL; isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr); switch (ns_sortlist_setup(client->view->sortlist, &netaddr, &order_arg)) { case NS_SORTLISTTYPE_1ELEMENT: order = query_sortlist_order_1element; break; case NS_SORTLISTTYPE_2ELEMENT: order = query_sortlist_order_2element; break; case NS_SORTLISTTYPE_NONE: order = NULL; break; default: INSIST(0); break; } dns_message_setsortorder(client->message, order, order_arg); } static void query_addnoqnameproof(ns_client_t *client, dns_rdataset_t *rdataset) { isc_buffer_t *dbuf, b; dns_name_t *fname; dns_rdataset_t *neg, *negsig; isc_result_t result = ISC_R_NOMEMORY; CTRACE("query_addnoqnameproof"); fname = NULL; neg = NULL; negsig = NULL; dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); neg = query_newrdataset(client); negsig = query_newrdataset(client); if (fname == NULL || neg == NULL || negsig == NULL) goto cleanup; result = dns_rdataset_getnoqname(rdataset, fname, neg, negsig); RUNTIME_CHECK(result == ISC_R_SUCCESS); query_addrrset(client, &fname, &neg, &negsig, dbuf, DNS_SECTION_AUTHORITY); if ((rdataset->attributes & DNS_RDATASETATTR_CLOSEST) == 0) goto cleanup; if (fname == NULL) { dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); } if (neg == NULL) neg = query_newrdataset(client); else if (dns_rdataset_isassociated(neg)) dns_rdataset_disassociate(neg); if (negsig == NULL) negsig = query_newrdataset(client); else if (dns_rdataset_isassociated(negsig)) dns_rdataset_disassociate(negsig); if (fname == NULL || neg == NULL || negsig == NULL) goto cleanup; result = dns_rdataset_getclosest(rdataset, fname, neg, negsig); RUNTIME_CHECK(result == ISC_R_SUCCESS); query_addrrset(client, &fname, &neg, &negsig, dbuf, DNS_SECTION_AUTHORITY); cleanup: if (neg != NULL) query_putrdataset(client, &neg); if (negsig != NULL) query_putrdataset(client, &negsig); if (fname != NULL) query_releasename(client, &fname); } static inline void answer_in_glue(ns_client_t *client, dns_rdatatype_t qtype) { dns_name_t *name; dns_message_t *msg; dns_section_t section = DNS_SECTION_ADDITIONAL; dns_rdataset_t *rdataset = NULL; msg = client->message; for (name = ISC_LIST_HEAD(msg->sections[section]); name != NULL; name = ISC_LIST_NEXT(name, link)) if (dns_name_equal(name, client->query.qname)) { for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) if (rdataset->type == qtype) break; break; } if (rdataset != NULL) { ISC_LIST_UNLINK(msg->sections[section], name, link); ISC_LIST_PREPEND(msg->sections[section], name, link); ISC_LIST_UNLINK(name->list, rdataset, link); ISC_LIST_PREPEND(name->list, rdataset, link); rdataset->attributes |= DNS_RDATASETATTR_REQUIREDGLUE; } } #define NS_NAME_INIT(A,B) \ { \ DNS_NAME_MAGIC, \ A, sizeof(A), sizeof(B), \ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, \ B, NULL, { (void *)-1, (void *)-1}, \ {NULL, NULL} \ } 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 rfc1918names[] = { NS_NAME_INIT(inaddr10, inaddr10_offsets), NS_NAME_INIT(inaddr16172, inaddr172_offsets), NS_NAME_INIT(inaddr17172, inaddr172_offsets), NS_NAME_INIT(inaddr18172, inaddr172_offsets), NS_NAME_INIT(inaddr19172, inaddr172_offsets), NS_NAME_INIT(inaddr20172, inaddr172_offsets), NS_NAME_INIT(inaddr21172, inaddr172_offsets), NS_NAME_INIT(inaddr22172, inaddr172_offsets), NS_NAME_INIT(inaddr23172, inaddr172_offsets), NS_NAME_INIT(inaddr24172, inaddr172_offsets), NS_NAME_INIT(inaddr25172, inaddr172_offsets), NS_NAME_INIT(inaddr26172, inaddr172_offsets), NS_NAME_INIT(inaddr27172, inaddr172_offsets), NS_NAME_INIT(inaddr28172, inaddr172_offsets), NS_NAME_INIT(inaddr29172, inaddr172_offsets), NS_NAME_INIT(inaddr30172, inaddr172_offsets), NS_NAME_INIT(inaddr31172, inaddr172_offsets), NS_NAME_INIT(inaddr168192, inaddr192_offsets) }; static unsigned char prisoner_data[] = "\010prisoner\004iana\003org"; static unsigned char hostmaster_data[] = "\012hostmaster\014root-servers\003org"; static unsigned char prisoner_offsets[] = { 0, 9, 14, 18 }; static unsigned char hostmaster_offsets[] = { 0, 11, 24, 28 }; static dns_name_t prisoner = NS_NAME_INIT(prisoner_data, prisoner_offsets); static dns_name_t hostmaster = NS_NAME_INIT(hostmaster_data, hostmaster_offsets); static void warn_rfc1918(ns_client_t *client, dns_name_t *fname, dns_rdataset_t *rdataset) { unsigned int i; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_soa_t soa; dns_rdataset_t found; isc_result_t result; for (i = 0; i < (sizeof(rfc1918names)/sizeof(*rfc1918names)); i++) { if (dns_name_issubdomain(fname, &rfc1918names[i])) { dns_rdataset_init(&found); result = dns_ncache_getrdataset(rdataset, &rfc1918names[i], dns_rdatatype_soa, &found); if (result != ISC_R_SUCCESS) return; result = dns_rdataset_first(&found); RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_rdataset_current(&found, &rdata); result = dns_rdata_tostruct(&rdata, &soa, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); if (dns_name_equal(&soa.origin, &prisoner) && dns_name_equal(&soa.contact, &hostmaster)) { char buf[DNS_NAME_FORMATSIZE]; dns_name_format(fname, buf, sizeof(buf)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "RFC 1918 response from " "Internet for %s", buf); } dns_rdataset_disassociate(&found); return; } } } static void query_findclosestnsec3(dns_name_t *qname, dns_db_t *db, dns_dbversion_t *version, ns_client_t *client, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, dns_name_t *fname, isc_boolean_t exact, dns_name_t *found) { unsigned char salt[256]; size_t salt_length; isc_uint16_t iterations; isc_result_t result; unsigned int dboptions; dns_fixedname_t fixed; dns_hash_t hash; dns_name_t name; unsigned int skip = 0, labels; dns_rdata_nsec3_t nsec3; dns_rdata_t rdata = DNS_RDATA_INIT; isc_boolean_t optout; salt_length = sizeof(salt); result = dns_db_getnsec3parameters(db, version, &hash, NULL, &iterations, salt, &salt_length); if (result != ISC_R_SUCCESS) return; dns_name_init(&name, NULL); dns_name_clone(qname, &name); labels = dns_name_countlabels(&name); /* * Map unknown algorithm to known value. */ if (hash == DNS_NSEC3_UNKNOWNALG) hash = 1; again: dns_fixedname_init(&fixed); result = dns_nsec3_hashname(&fixed, NULL, NULL, &name, dns_db_origin(db), hash, iterations, salt, salt_length); if (result != ISC_R_SUCCESS) return; dboptions = client->query.dboptions | DNS_DBFIND_FORCENSEC3; result = dns_db_find(db, dns_fixedname_name(&fixed), version, dns_rdatatype_nsec3, dboptions, client->now, NULL, fname, rdataset, sigrdataset); if (result == DNS_R_NXDOMAIN) { if (!dns_rdataset_isassociated(rdataset)) { return; } result = dns_rdataset_first(rdataset); INSIST(result == ISC_R_SUCCESS); dns_rdataset_current(rdataset, &rdata); dns_rdata_tostruct(&rdata, &nsec3, NULL); dns_rdata_reset(&rdata); optout = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0); if (found != NULL && optout && dns_name_issubdomain(&name, dns_db_origin(db))) { dns_rdataset_disassociate(rdataset); if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); skip++; dns_name_getlabelsequence(qname, skip, labels - skip, &name); ns_client_log(client, DNS_LOGCATEGORY_DNSSEC, NS_LOGMODULE_QUERY, ISC_LOG_DEBUG(3), "looking for closest provable encloser"); goto again; } if (exact) ns_client_log(client, DNS_LOGCATEGORY_DNSSEC, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "expected a exact match NSEC3, got " "a covering record"); } else if (result != ISC_R_SUCCESS) { return; } else if (!exact) ns_client_log(client, DNS_LOGCATEGORY_DNSSEC, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "expected covering NSEC3, got an exact match"); if (found == qname) { if (skip != 0U) dns_name_getlabelsequence(qname, skip, labels - skip, found); } else if (found != NULL) dns_name_copy(&name, found, NULL); return; } #ifdef ALLOW_FILTER_AAAA_ON_V4 static isc_boolean_t is_v4_client(ns_client_t *client) { if (isc_sockaddr_pf(&client->peeraddr) == AF_INET) return (ISC_TRUE); if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr)) return (ISC_TRUE); return (ISC_FALSE); } #endif static isc_uint32_t dns64_ttl(dns_db_t *db, dns_dbversion_t *version) { dns_dbnode_t *node = NULL; dns_rdata_soa_t soa; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_t rdataset; isc_result_t result; isc_uint32_t ttl = ISC_UINT32_MAX; dns_rdataset_init(&rdataset); result = dns_db_getoriginnode(db, &node); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa, 0, 0, &rdataset, NULL); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_rdataset_first(&rdataset); if (result != ISC_R_SUCCESS) goto cleanup; dns_rdataset_current(&rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &soa, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); ttl = ISC_MIN(rdataset.ttl, soa.minimum); cleanup: if (dns_rdataset_isassociated(&rdataset)) dns_rdataset_disassociate(&rdataset); if (node != NULL) dns_db_detachnode(db, &node); return (ttl); } static isc_boolean_t dns64_aaaaok(ns_client_t *client, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { isc_netaddr_t netaddr; dns_dns64_t *dns64 = ISC_LIST_HEAD(client->view->dns64); unsigned int flags = 0; unsigned int i, count; isc_boolean_t *aaaaok; INSIST(client->query.dns64_aaaaok == NULL); INSIST(client->query.dns64_aaaaoklen == 0); INSIST(client->query.dns64_aaaa == NULL); INSIST(client->query.dns64_sigaaaa == NULL); if (dns64 == NULL) return (ISC_TRUE); if (RECURSIONOK(client)) flags |= DNS_DNS64_RECURSIVE; if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) flags |= DNS_DNS64_DNSSEC; count = dns_rdataset_count(rdataset); aaaaok = isc_mem_get(client->mctx, sizeof(isc_boolean_t) * count); isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr); if (dns_dns64_aaaaok(dns64, &netaddr, client->signer, &ns_g_server->aclenv, flags, rdataset, aaaaok, count)) { for (i = 0; i < count; i++) { if (aaaaok != NULL && !aaaaok[i]) { client->query.dns64_aaaaok = aaaaok; client->query.dns64_aaaaoklen = count; break; } } if (i == count && aaaaok != NULL) isc_mem_put(client->mctx, aaaaok, sizeof(isc_boolean_t) * count); return (ISC_TRUE); } if (aaaaok != NULL) isc_mem_put(client->mctx, aaaaok, sizeof(isc_boolean_t) * count); return (ISC_FALSE); } /* * Do the bulk of query processing for the current query of 'client'. * If 'event' is non-NULL, we are returning from recursion and 'qtype' * is ignored. Otherwise, 'qtype' is the query type. */ static isc_result_t query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) { dns_db_t *db, *zdb; dns_dbnode_t *node; dns_rdatatype_t type; dns_name_t *fname, *zfname, *tname, *prefix; dns_rdataset_t *rdataset, *trdataset; dns_rdataset_t *sigrdataset, *zrdataset, *zsigrdataset; dns_rdataset_t **sigrdatasetp; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdatasetiter_t *rdsiter; isc_boolean_t want_restart, authoritative, is_zone, need_wildcardproof; isc_boolean_t is_staticstub_zone; unsigned int n, nlabels; dns_namereln_t namereln; int order; isc_buffer_t *dbuf; isc_buffer_t b; isc_result_t result, eresult; dns_fixedname_t fixed; dns_fixedname_t wildcardname; dns_dbversion_t *version, *zversion; dns_zone_t *zone; dns_rdata_cname_t cname; dns_rdata_dname_t dname; unsigned int options; isc_boolean_t empty_wild; dns_rdataset_t *noqname; dns_rpz_st_t *rpz_st; isc_boolean_t resuming; int line = -1; isc_boolean_t dns64_exclude, dns64; CTRACE("query_find"); /* * One-time initialization. * * It's especially important to initialize anything that the cleanup * code might cleanup. */ eresult = ISC_R_SUCCESS; fname = NULL; zfname = NULL; rdataset = NULL; zrdataset = NULL; sigrdataset = NULL; zsigrdataset = NULL; zversion = NULL; node = NULL; db = NULL; zdb = NULL; version = NULL; zone = NULL; need_wildcardproof = ISC_FALSE; empty_wild = ISC_FALSE; dns64_exclude = dns64 = ISC_FALSE; options = 0; resuming = ISC_FALSE; is_zone = ISC_FALSE; is_staticstub_zone = ISC_FALSE; if (event != NULL) { /* * We're returning from recursion. Restore the query context * and resume. */ want_restart = ISC_FALSE; rpz_st = client->query.rpz_st; if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) != 0) { is_zone = rpz_st->q.is_zone; authoritative = rpz_st->q.authoritative; zone = rpz_st->q.zone; rpz_st->q.zone = NULL; node = rpz_st->q.node; rpz_st->q.node = NULL; db = rpz_st->q.db; rpz_st->q.db = NULL; rdataset = rpz_st->q.rdataset; rpz_st->q.rdataset = NULL; sigrdataset = rpz_st->q.sigrdataset; rpz_st->q.sigrdataset = NULL; qtype = rpz_st->q.qtype; rpz_st->r.db = event->db; if (event->node != NULL) dns_db_detachnode(event->db, &event->node); rpz_st->r.r_type = event->qtype; rpz_st->r.r_rdataset = event->rdataset; query_putrdataset(client, &event->sigrdataset); } else { authoritative = ISC_FALSE; qtype = event->qtype; db = event->db; node = event->node; rdataset = event->rdataset; sigrdataset = event->sigrdataset; } if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig) type = dns_rdatatype_any; else type = qtype; if (DNS64(client)) { client->query.attributes &= ~NS_QUERYATTR_DNS64; dns64 = ISC_TRUE; } if (DNS64EXCLUDE(client)) { client->query.attributes &= ~NS_QUERYATTR_DNS64EXCLUDE; dns64_exclude = ISC_TRUE; } /* * We'll need some resources... */ dbuf = query_getnamebuf(client); if (dbuf == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } fname = query_newname(client, dbuf, &b); if (fname == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) != 0) { tname = rpz_st->fname; } else { tname = dns_fixedname_name(&event->foundname); } result = dns_name_copy(tname, fname, NULL); if (result != ISC_R_SUCCESS) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) != 0) { rpz_st->r.r_result = event->result; result = rpz_st->q.result; isc_event_free(ISC_EVENT_PTR(&event)); } else { result = event->result; } resuming = ISC_TRUE; goto resume; } /* * Not returning from recursion. */ /* * If it's a SIG query, we'll iterate the node. */ if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig) type = dns_rdatatype_any; else type = qtype; restart: CTRACE("query_find: restart"); want_restart = ISC_FALSE; authoritative = ISC_FALSE; version = NULL; need_wildcardproof = ISC_FALSE; if (client->view->checknames && !dns_rdata_checkowner(client->query.qname, client->message->rdclass, qtype, ISC_FALSE)) { char namebuf[DNS_NAME_FORMATSIZE]; char typename[DNS_RDATATYPE_FORMATSIZE]; char classname[DNS_RDATACLASS_FORMATSIZE]; dns_name_format(client->query.qname, namebuf, sizeof(namebuf)); dns_rdatatype_format(qtype, typename, sizeof(typename)); dns_rdataclass_format(client->message->rdclass, classname, sizeof(classname)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_ERROR, "check-names failure %s/%s/%s", namebuf, typename, classname); QUERY_ERROR(DNS_R_REFUSED); goto cleanup; } /* * First we must find the right database. */ options &= DNS_GETDB_NOLOG; /* Preserve DNS_GETDB_NOLOG. */ if (dns_rdatatype_atparent(qtype) && !dns_name_equal(client->query.qname, dns_rootname)) options |= DNS_GETDB_NOEXACT; result = query_getdb(client, client->query.qname, qtype, options, &zone, &db, &version, &is_zone); if ((result != ISC_R_SUCCESS || !is_zone) && !RECURSIONOK(client) && (options & DNS_GETDB_NOEXACT) != 0 && qtype == dns_rdatatype_ds) { /* * Look to see if we are authoritative for the * child zone if the query type is DS. */ dns_db_t *tdb = NULL; dns_zone_t *tzone = NULL; dns_dbversion_t *tversion = NULL; isc_result_t tresult; tresult = query_getzonedb(client, client->query.qname, qtype, DNS_GETDB_PARTIAL, &tzone, &tdb, &tversion); if (tresult == ISC_R_SUCCESS) { options &= ~DNS_GETDB_NOEXACT; query_putrdataset(client, &rdataset); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); version = tversion; db = tdb; zone = tzone; is_zone = ISC_TRUE; result = ISC_R_SUCCESS; } else { if (tdb != NULL) dns_db_detach(&tdb); if (tzone != NULL) dns_zone_detach(&tzone); } } if (result != ISC_R_SUCCESS) { if (result == DNS_R_REFUSED) { if (WANTRECURSION(client)) { inc_stats(client, dns_nsstatscounter_recurserej); } else inc_stats(client, dns_nsstatscounter_authrej); if (!PARTIALANSWER(client)) QUERY_ERROR(DNS_R_REFUSED); } else QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } is_staticstub_zone = ISC_FALSE; if (is_zone) { authoritative = ISC_TRUE; if (zone != NULL && dns_zone_gettype(zone) == dns_zone_staticstub) is_staticstub_zone = ISC_TRUE; } if (event == NULL && client->query.restarts == 0) { if (is_zone) { if (zone != NULL) { /* * if is_zone = true, zone = NULL then this is * a DLZ zone. Don't attempt to attach zone. */ dns_zone_attach(zone, &client->query.authzone); } dns_db_attach(db, &client->query.authdb); } client->query.authdbset = ISC_TRUE; } db_find: CTRACE("query_find: db_find"); /* * We'll need some resources... */ dbuf = query_getnamebuf(client); if (dbuf == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } fname = query_newname(client, dbuf, &b); rdataset = query_newrdataset(client); if (fname == NULL || rdataset == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } if (WANTDNSSEC(client) && (!is_zone || dns_db_issecure(db))) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } } /* * Now look for an answer in the database. */ result = dns_db_find(db, client->query.qname, version, type, client->query.dboptions, client->now, &node, fname, rdataset, sigrdataset); resume: CTRACE("query_find: resume"); if (!ISC_LIST_EMPTY(client->view->rpz_zones) && (RECURSIONOK(client) || !client->view->rpz_recursive_only) && rpz_ck_dnssec(client, result, rdataset, sigrdataset) && !RECURSING(client) && (client->query.rpz_st == NULL || (client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0) && !dns_name_equal(client->query.qname, dns_rootname)) { isc_result_t rresult; rresult = rpz_rewrite(client, qtype, result, resuming); rpz_st = client->query.rpz_st; switch (rresult) { case ISC_R_SUCCESS: break; case DNS_R_DELEGATION: /* * recursing for NS names or addresses, * so save the main query state */ rpz_st->q.qtype = qtype; rpz_st->q.is_zone = is_zone; rpz_st->q.authoritative = authoritative; rpz_st->q.zone = zone; zone = NULL; rpz_st->q.db = db; db = NULL; rpz_st->q.node = node; node = NULL; rpz_st->q.rdataset = rdataset; rdataset = NULL; rpz_st->q.sigrdataset = sigrdataset; sigrdataset = NULL; dns_name_copy(fname, rpz_st->fname, NULL); rpz_st->q.result = result; client->query.attributes |= NS_QUERYATTR_RECURSING; goto cleanup; default: RECURSE_ERROR(rresult); goto cleanup; } if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS) rpz_st->state |= DNS_RPZ_REWRITTEN; if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS && rpz_st->m.policy != DNS_RPZ_POLICY_PASSTHRU && rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) { if (rpz_st->m.type == DNS_RPZ_TYPE_QNAME) { result = dns_name_copy(client->query.qname, fname, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); } rpz_clean(&zone, &db, &node, NULL); if (rpz_st->m.rdataset != NULL) { query_putrdataset(client, &rdataset); rdataset = rpz_st->m.rdataset; rpz_st->m.rdataset = NULL; } else if (rdataset != NULL && dns_rdataset_isassociated(rdataset)) { dns_rdataset_disassociate(rdataset); } node = rpz_st->m.node; rpz_st->m.node = NULL; db = rpz_st->m.db; rpz_st->m.db = NULL; version = rpz_st->m.version; rpz_st->m.version = NULL; zone = rpz_st->m.zone; rpz_st->m.zone = NULL; switch (rpz_st->m.policy) { case DNS_RPZ_POLICY_NXDOMAIN: result = DNS_R_NXDOMAIN; break; case DNS_RPZ_POLICY_NODATA: result = DNS_R_NXRRSET; break; case DNS_RPZ_POLICY_RECORD: result = rpz_st->m.result; if (qtype == dns_rdatatype_any && result != DNS_R_CNAME) { /* * We will add all of the rdatasets of * the node by iterating, setting the * TTL then. */ if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); } else { /* * We will add this rdataset. */ rdataset->ttl = ISC_MIN(rdataset->ttl, rpz_st->m.ttl); } break; case DNS_RPZ_POLICY_WILDCNAME: result = dns_rdataset_first(rdataset); RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &cname, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_rdata_reset(&rdata); result = rpz_add_cname(client, rpz_st, &cname.cname, fname, dbuf); if (result != ISC_R_SUCCESS) goto cleanup; fname = NULL; want_restart = ISC_TRUE; goto cleanup; case DNS_RPZ_POLICY_CNAME: /* * Add overridding CNAME from a named.conf * response-policy statement */ result = rpz_add_cname(client, rpz_st, &rpz_st->m.rpz->cname, fname, dbuf); if (result != ISC_R_SUCCESS) goto cleanup; fname = NULL; want_restart = ISC_TRUE; goto cleanup; default: INSIST(0); } /* * Turn off DNSSEC because the results of a * response policy zone cannot verify. */ client->attributes &= ~(NS_CLIENTATTR_WANTDNSSEC | DNS_MESSAGEFLAG_AD); query_putrdataset(client, &sigrdataset); rpz_st->q.is_zone = is_zone; is_zone = ISC_TRUE; rpz_log_rewrite(client, ISC_FALSE, rpz_st->m.policy, rpz_st->m.type, zone, rpz_st->qname); } } switch (result) { case ISC_R_SUCCESS: /* * This case is handled in the main line below. */ break; case DNS_R_GLUE: case DNS_R_ZONECUT: /* * These cases are handled in the main line below. */ INSIST(is_zone); authoritative = ISC_FALSE; break; case ISC_R_NOTFOUND: /* * The cache doesn't even have the root NS. Get them from * the hints DB. */ INSIST(!is_zone); if (db != NULL) dns_db_detach(&db); if (client->view->hints == NULL) { /* We have no hints. */ result = ISC_R_FAILURE; } else { dns_db_attach(client->view->hints, &db); result = dns_db_find(db, dns_rootname, NULL, dns_rdatatype_ns, 0, client->now, &node, fname, rdataset, sigrdataset); } if (result != ISC_R_SUCCESS) { /* * Nonsensical root hints may require cleanup. */ 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); /* * We don't have any root server hints, but * we may have working forwarders, so try to * recurse anyway. */ if (RECURSIONOK(client)) { result = query_recurse(client, qtype, client->query.qname, NULL, NULL, resuming); if (result == ISC_R_SUCCESS) { client->query.attributes |= NS_QUERYATTR_RECURSING; if (dns64) client->query.attributes |= NS_QUERYATTR_DNS64; if (dns64_exclude) client->query.attributes |= NS_QUERYATTR_DNS64EXCLUDE; } else RECURSE_ERROR(result); goto cleanup; } else { /* Unable to give root server referral. */ QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } } /* * XXXRTH We should trigger root server priming here. */ /* FALLTHROUGH */ case DNS_R_DELEGATION: authoritative = ISC_FALSE; if (is_zone) { /* * Look to see if we are authoritative for the * child zone if the query type is DS. */ if (!RECURSIONOK(client) && (options & DNS_GETDB_NOEXACT) != 0 && qtype == dns_rdatatype_ds) { dns_db_t *tdb = NULL; dns_zone_t *tzone = NULL; dns_dbversion_t *tversion = NULL; result = query_getzonedb(client, client->query.qname, qtype, DNS_GETDB_PARTIAL, &tzone, &tdb, &tversion); if (result == ISC_R_SUCCESS) { options &= ~DNS_GETDB_NOEXACT; query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); if (node != NULL) dns_db_detachnode(db, &node); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); version = tversion; db = tdb; zone = tzone; authoritative = ISC_TRUE; goto db_find; } if (tdb != NULL) dns_db_detach(&tdb); if (tzone != NULL) dns_zone_detach(&tzone); } /* * We're authoritative for an ancestor of QNAME. */ if (!USECACHE(client) || !RECURSIONOK(client)) { dns_fixedname_t fixed; dns_fixedname_init(&fixed); dns_name_copy(fname, dns_fixedname_name(&fixed), NULL); /* * If we don't have a cache, this is the best * answer. * * If the client is making a nonrecursive * query we always give out the authoritative * delegation. This way even if we get * junk in our cache, we won't fail in our * role as the delegating authority if another * nameserver asks us about a delegated * subzone. * * We enable the retrieval of glue for this * database by setting client->query.gluedb. */ client->query.gluedb = db; client->query.isreferral = ISC_TRUE; /* * We must ensure NOADDITIONAL is off, * because the generation of * additional data is required in * delegations. */ client->query.attributes &= ~NS_QUERYATTR_NOADDITIONAL; if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, DNS_SECTION_AUTHORITY); client->query.gluedb = NULL; if (WANTDNSSEC(client)) query_addds(client, db, node, version, dns_fixedname_name(&fixed)); } else { /* * We might have a better answer or delegation * in the cache. We'll remember the current * values of fname, rdataset, and sigrdataset. * We'll then go looking for QNAME in the * cache. If we find something better, we'll * use it instead. */ query_keepname(client, fname, dbuf); zdb = db; zfname = fname; fname = NULL; zrdataset = rdataset; rdataset = NULL; zsigrdataset = sigrdataset; sigrdataset = NULL; dns_db_detachnode(db, &node); zversion = version; version = NULL; db = NULL; dns_db_attach(client->view->cachedb, &db); is_zone = ISC_FALSE; goto db_find; } } else { if (zfname != NULL && (!dns_name_issubdomain(fname, zfname) || (is_staticstub_zone && dns_name_equal(fname, zfname)))) { /* * In the following cases use "authoritative" * data instead of the cache delegation: * 1. We've already got a delegation from * authoritative data, and it is better * than what we found in the cache. * 2. The query name matches the origin name * of a static-stub zone. This needs to be * considered for the case where the NS of * the static-stub zone and the cached NS * are different. We still need to contact * the nameservers configured in the * static-stub zone. */ query_releasename(client, &fname); fname = zfname; zfname = NULL; /* * We've already done query_keepname() on * zfname, so we must set dbuf to NULL to * prevent query_addrrset() from trying to * call query_keepname() again. */ dbuf = NULL; query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); rdataset = zrdataset; zrdataset = NULL; sigrdataset = zsigrdataset; zsigrdataset = NULL; version = zversion; zversion = NULL; /* * We don't clean up zdb here because we * may still need it. It will get cleaned * up by the main cleanup code. */ } if (RECURSIONOK(client)) { /* * Recurse! */ if (dns_rdatatype_atparent(type)) result = query_recurse(client, qtype, client->query.qname, NULL, NULL, resuming); else if (dns64) result = query_recurse(client, dns_rdatatype_a, client->query.qname, NULL, NULL, resuming); else result = query_recurse(client, qtype, client->query.qname, fname, rdataset, resuming); if (result == ISC_R_SUCCESS) { client->query.attributes |= NS_QUERYATTR_RECURSING; if (dns64) client->query.attributes |= NS_QUERYATTR_DNS64; if (dns64_exclude) client->query.attributes |= NS_QUERYATTR_DNS64EXCLUDE; } else if (result == DNS_R_DUPLICATE || result == DNS_R_DROP) QUERY_ERROR(result); else RECURSE_ERROR(result); } else { dns_fixedname_t fixed; dns_fixedname_init(&fixed); dns_name_copy(fname, dns_fixedname_name(&fixed), NULL); /* * This is the best answer. */ client->query.attributes |= NS_QUERYATTR_CACHEGLUEOK; client->query.gluedb = zdb; client->query.isreferral = ISC_TRUE; /* * We must ensure NOADDITIONAL is off, * because the generation of * additional data is required in * delegations. */ client->query.attributes &= ~NS_QUERYATTR_NOADDITIONAL; if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, DNS_SECTION_AUTHORITY); client->query.gluedb = NULL; client->query.attributes &= ~NS_QUERYATTR_CACHEGLUEOK; if (WANTDNSSEC(client)) query_addds(client, db, node, version, dns_fixedname_name(&fixed)); } } goto cleanup; case DNS_R_EMPTYNAME: case DNS_R_NXRRSET: iszone_nxrrset: INSIST(is_zone); #ifdef dns64_bis_return_excluded_addresses if (dns64) #else if (dns64 && !dns64_exclude) #endif { /* * Restore the answers from the previous AAAA lookup. */ if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); rdataset = client->query.dns64_aaaa; sigrdataset = client->query.dns64_sigaaaa; client->query.dns64_aaaa = NULL; client->query.dns64_sigaaaa = NULL; if (fname == NULL) { dbuf = query_getnamebuf(client); if (dbuf == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } fname = query_newname(client, dbuf, &b); if (fname == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } } dns_name_copy(client->query.qname, fname, NULL); dns64 = ISC_FALSE; #ifdef dns64_bis_return_excluded_addresses /* * Resume the diverted processing of the AAAA response? */ if (dns64_excluded) break; #endif } else if (result == DNS_R_NXRRSET && !ISC_LIST_EMPTY(client->view->dns64) && client->message->rdclass == dns_rdataclass_in && qtype == dns_rdatatype_aaaa) { /* * Look to see if there are A records for this * name. */ INSIST(client->query.dns64_aaaa == NULL); INSIST(client->query.dns64_sigaaaa == NULL); client->query.dns64_aaaa = rdataset; client->query.dns64_sigaaaa = sigrdataset; client->query.dns64_ttl = dns64_ttl(db, version); query_releasename(client, &fname); dns_db_detachnode(db, &node); rdataset = NULL; sigrdataset = NULL; type = qtype = dns_rdatatype_a; rpz_st = client->query.rpz_st; if (rpz_st != NULL) { /* * Arrange for RPZ rewriting of any A records. */ if ((rpz_st->state & DNS_RPZ_REWRITTEN) != 0) is_zone = rpz_st->q.is_zone; rpz_st_clear(client); } dns64 = ISC_TRUE; goto db_find; } /* * Look for a NSEC3 record if we don't have a NSEC record. */ nxrrset_rrsig: if (!dns_rdataset_isassociated(rdataset) && WANTDNSSEC(client)) { if ((fname->attributes & DNS_NAMEATTR_WILDCARD) == 0) { dns_name_t *found; dns_name_t *qname; dns_fixedname_init(&fixed); found = dns_fixedname_name(&fixed); qname = client->query.qname; query_findclosestnsec3(qname, db, version, client, rdataset, sigrdataset, fname, ISC_TRUE, found); /* * Did we find the closest provable encloser * instead? If so add the nearest to the * closest provable encloser. */ if (dns_rdataset_isassociated(rdataset) && !dns_name_equal(qname, found) && !(ns_g_nonearest && qtype != dns_rdatatype_ds)) { unsigned int count; unsigned int skip; /* * Add the closest provable encloser. */ query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); count = dns_name_countlabels(found) + 1; skip = dns_name_countlabels(qname) - count; dns_name_getlabelsequence(qname, skip, count, found); fixfname(client, &fname, &dbuf, &b); fixrdataset(client, &rdataset); fixrdataset(client, &sigrdataset); if (fname == NULL || rdataset == NULL || sigrdataset == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } /* * 'nearest' doesn't exist so * 'exist' is set to ISC_FALSE. */ query_findclosestnsec3(found, db, version, client, rdataset, sigrdataset, fname, ISC_FALSE, NULL); } } else { query_releasename(client, &fname); query_addwildcardproof(client, db, version, client->query.qname, ISC_FALSE, ISC_TRUE); } } if (dns_rdataset_isassociated(rdataset)) { /* * If we've got a NSEC record, we need to save the * name now because we're going call query_addsoa() * below, and it needs to use the name buffer. */ query_keepname(client, fname, dbuf); } else if (fname != NULL) { /* * We're not going to use fname, and need to release * our hold on the name buffer so query_addsoa() * may use it. */ query_releasename(client, &fname); } /* * Add SOA. */ result = query_addsoa(client, db, version, ISC_UINT32_MAX, dns_rdataset_isassociated(rdataset)); if (result != ISC_R_SUCCESS) { QUERY_ERROR(result); goto cleanup; } /* * Add NSEC record if we found one. */ if (WANTDNSSEC(client)) { if (dns_rdataset_isassociated(rdataset)) query_addnxrrsetnsec(client, db, version, &fname, &rdataset, &sigrdataset); } goto cleanup; case DNS_R_EMPTYWILD: empty_wild = ISC_TRUE; /* FALLTHROUGH */ case DNS_R_NXDOMAIN: INSIST(is_zone); if (dns_rdataset_isassociated(rdataset)) { /* * If we've got a NSEC record, we need to save the * name now because we're going call query_addsoa() * below, and it needs to use the name buffer. */ query_keepname(client, fname, dbuf); } else if (fname != NULL) { /* * We're not going to use fname, and need to release * our hold on the name buffer so query_addsoa() * may use it. */ query_releasename(client, &fname); } /* * Add SOA. If the query was for a SOA record force the * ttl to zero so that it is possible for clients to find * the containing zone of an arbitrary name with a stub * resolver and not have it cached. */ if (qtype == dns_rdatatype_soa && zone != NULL && dns_zone_getzeronosoattl(zone)) result = query_addsoa(client, db, version, 0, dns_rdataset_isassociated(rdataset)); else result = query_addsoa(client, db, version, ISC_UINT32_MAX, dns_rdataset_isassociated(rdataset)); if (result != ISC_R_SUCCESS) { QUERY_ERROR(result); goto cleanup; } if (WANTDNSSEC(client)) { /* * Add NSEC record if we found one. */ if (dns_rdataset_isassociated(rdataset)) query_addrrset(client, &fname, &rdataset, &sigrdataset, NULL, DNS_SECTION_AUTHORITY); query_addwildcardproof(client, db, version, client->query.qname, ISC_FALSE, ISC_FALSE); } /* * Set message rcode. */ if (empty_wild) client->message->rcode = dns_rcode_noerror; else client->message->rcode = dns_rcode_nxdomain; goto cleanup; case DNS_R_NCACHENXDOMAIN: case DNS_R_NCACHENXRRSET: ncache_nxrrset: INSIST(!is_zone); authoritative = ISC_FALSE; /* * Set message rcode, if required. */ if (result == DNS_R_NCACHENXDOMAIN) client->message->rcode = dns_rcode_nxdomain; /* * Look for RFC 1918 leakage from Internet. */ if (result == DNS_R_NCACHENXDOMAIN && qtype == dns_rdatatype_ptr && client->message->rdclass == dns_rdataclass_in && dns_name_countlabels(fname) == 7) warn_rfc1918(client, fname, rdataset); #ifdef dns64_bis_return_excluded_addresses if (dns64) #else if (dns64 && !dns64_exclude) #endif { /* * Restore the answers from the previous AAAA lookup. */ if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); rdataset = client->query.dns64_aaaa; sigrdataset = client->query.dns64_sigaaaa; client->query.dns64_aaaa = NULL; client->query.dns64_sigaaaa = NULL; if (fname == NULL) { dbuf = query_getnamebuf(client); if (dbuf == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } fname = query_newname(client, dbuf, &b); if (fname == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } } dns_name_copy(client->query.qname, fname, NULL); dns64 = ISC_FALSE; #ifdef dns64_bis_return_excluded_addresses if (dns64_excluded) break; #endif } else if (result == DNS_R_NCACHENXRRSET && !ISC_LIST_EMPTY(client->view->dns64) && client->message->rdclass == dns_rdataclass_in && qtype == dns_rdatatype_aaaa) { /* * Look to see if there are A records for this * name. */ INSIST(client->query.dns64_aaaa == NULL); INSIST(client->query.dns64_sigaaaa == NULL); client->query.dns64_aaaa = rdataset; client->query.dns64_sigaaaa = sigrdataset; /* * If the ttl is zero we need to workout if we have just * decremented to zero or if there was no negative cache * ttl in the answer. */ if (rdataset->ttl != 0) client->query.dns64_ttl = rdataset->ttl; else if (dns_rdataset_first(rdataset) == ISC_R_SUCCESS) client->query.dns64_ttl = 0; query_releasename(client, &fname); dns_db_detachnode(db, &node); rdataset = NULL; sigrdataset = NULL; fname = NULL; type = qtype = dns_rdatatype_a; rpz_st = client->query.rpz_st; if (rpz_st != NULL) { /* * Arrange for RPZ rewriting of any A records. */ if ((rpz_st->state & DNS_RPZ_REWRITTEN) != 0) is_zone = rpz_st->q.is_zone; rpz_st_clear(client); } dns64 = ISC_TRUE; goto db_find; } /* * We don't call query_addrrset() because we don't need any * of its extra features (and things would probably break!). */ query_keepname(client, fname, dbuf); dns_message_addname(client->message, fname, DNS_SECTION_AUTHORITY); ISC_LIST_APPEND(fname->list, rdataset, link); fname = NULL; rdataset = NULL; goto cleanup; case DNS_R_CNAME: /* * Keep a copy of the rdataset. We have to do this because * query_addrrset may clear 'rdataset' (to prevent the * cleanup code from cleaning it up). */ trdataset = rdataset; /* * Add the CNAME to the answer section. */ if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; if (WANTDNSSEC(client) && (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0) { dns_fixedname_init(&wildcardname); dns_name_copy(fname, dns_fixedname_name(&wildcardname), NULL); need_wildcardproof = ISC_TRUE; } if (NOQNAME(rdataset) && WANTDNSSEC(client)) noqname = rdataset; else noqname = NULL; query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, DNS_SECTION_ANSWER); if (noqname != NULL) query_addnoqnameproof(client, noqname); /* * We set the PARTIALANSWER attribute so that if anything goes * wrong later on, we'll return what we've got so far. */ client->query.attributes |= NS_QUERYATTR_PARTIALANSWER; /* * Reset qname to be the target name of the CNAME and restart * the query. */ tname = NULL; result = dns_message_gettempname(client->message, &tname); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_rdataset_first(trdataset); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &tname); goto cleanup; } dns_rdataset_current(trdataset, &rdata); result = dns_rdata_tostruct(&rdata, &cname, NULL); dns_rdata_reset(&rdata); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &tname); goto cleanup; } dns_name_init(tname, NULL); result = dns_name_dup(&cname.cname, client->mctx, tname); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &tname); dns_rdata_freestruct(&cname); goto cleanup; } dns_rdata_freestruct(&cname); ns_client_qnamereplace(client, tname); want_restart = ISC_TRUE; if (!WANTRECURSION(client)) options |= DNS_GETDB_NOLOG; goto addauth; case DNS_R_DNAME: /* * Compare the current qname to the found name. We need * to know how many labels and bits are in common because * we're going to have to split qname later on. */ namereln = dns_name_fullcompare(client->query.qname, fname, &order, &nlabels); INSIST(namereln == dns_namereln_subdomain); /* * Keep a copy of the rdataset. We have to do this because * query_addrrset may clear 'rdataset' (to prevent the * cleanup code from cleaning it up). */ trdataset = rdataset; /* * Add the DNAME to the answer section. */ if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; if (WANTDNSSEC(client) && (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0) { dns_fixedname_init(&wildcardname); dns_name_copy(fname, dns_fixedname_name(&wildcardname), NULL); need_wildcardproof = ISC_TRUE; } query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, DNS_SECTION_ANSWER); /* * We set the PARTIALANSWER attribute so that if anything goes * wrong later on, we'll return what we've got so far. */ client->query.attributes |= NS_QUERYATTR_PARTIALANSWER; /* * Get the target name of the DNAME. */ tname = NULL; result = dns_message_gettempname(client->message, &tname); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_rdataset_first(trdataset); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &tname); goto cleanup; } dns_rdataset_current(trdataset, &rdata); result = dns_rdata_tostruct(&rdata, &dname, NULL); dns_rdata_reset(&rdata); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &tname); goto cleanup; } dns_name_clone(&dname.dname, tname); dns_rdata_freestruct(&dname); /* * Construct the new qname consisting of * . */ dns_fixedname_init(&fixed); prefix = dns_fixedname_name(&fixed); dns_name_split(client->query.qname, nlabels, prefix, NULL); INSIST(fname == NULL); dbuf = query_getnamebuf(client); if (dbuf == NULL) { dns_message_puttempname(client->message, &tname); goto cleanup; } fname = query_newname(client, dbuf, &b); if (fname == NULL) { dns_message_puttempname(client->message, &tname); goto cleanup; } result = dns_name_concatenate(prefix, tname, fname, NULL); dns_message_puttempname(client->message, &tname); /* * RFC2672, section 4.1, subsection 3c says * we should return YXDOMAIN if the constructed * name would be too long. */ if (result == DNS_R_NAMETOOLONG) client->message->rcode = dns_rcode_yxdomain; if (result != ISC_R_SUCCESS) goto cleanup; query_keepname(client, fname, dbuf); /* * Synthesize a CNAME consisting of * CNAME * with * * Synthesize a CNAME so old old clients that don't understand * DNAME can chain. * * We do not try to synthesize a signature because we hope * that security aware servers will understand DNAME. Also, * even if we had an online key, making a signature * on-the-fly is costly, and not really legitimate anyway * since the synthesized CNAME is NOT in the zone. */ result = query_add_cname(client, client->query.qname, fname, trdataset->trust, trdataset->ttl); if (result != ISC_R_SUCCESS) goto cleanup; /* * Switch to the new qname and restart. */ ns_client_qnamereplace(client, fname); fname = NULL; want_restart = ISC_TRUE; if (!WANTRECURSION(client)) options |= DNS_GETDB_NOLOG; goto addauth; default: /* * Something has gone wrong. */ QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } if (WANTDNSSEC(client) && (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0) { dns_fixedname_init(&wildcardname); dns_name_copy(fname, dns_fixedname_name(&wildcardname), NULL); need_wildcardproof = ISC_TRUE; } #ifdef ALLOW_FILTER_AAAA_ON_V4 if (client->view->v4_aaaa != dns_v4_aaaa_ok && is_v4_client(client) && ns_client_checkaclsilent(client, NULL, client->view->v4_aaaa_acl, ISC_TRUE) == ISC_R_SUCCESS) client->filter_aaaa = client->view->v4_aaaa; else client->filter_aaaa = dns_v4_aaaa_ok; #endif if (type == dns_rdatatype_any) { #ifdef ALLOW_FILTER_AAAA_ON_V4 isc_boolean_t have_aaaa, have_a, have_sig; /* * The filter-aaaa-on-v4 option should * suppress AAAAs for IPv4 clients if there is an A. * If we are not authoritative, assume there is a A * even in if it is not in our cache. This assumption could * be wrong but it is a good bet. */ have_aaaa = ISC_FALSE; have_a = !authoritative; have_sig = ISC_FALSE; #endif /* * XXXRTH Need to handle zonecuts with special case * code. */ n = 0; rdsiter = NULL; result = dns_db_allrdatasets(db, node, version, 0, &rdsiter); if (result != ISC_R_SUCCESS) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } /* * Calling query_addrrset() with a non-NULL dbuf is going * to either keep or release the name. We don't want it to * release fname, since we may have to call query_addrrset() * more than once. That means we have to call query_keepname() * now, and pass a NULL dbuf to query_addrrset(). * * If we do a query_addrrset() below, we must set fname to * NULL before leaving this block, otherwise we might try to * cleanup fname even though we're using it! */ query_keepname(client, fname, dbuf); tname = fname; result = dns_rdatasetiter_first(rdsiter); while (result == ISC_R_SUCCESS) { dns_rdatasetiter_current(rdsiter, rdataset); #ifdef ALLOW_FILTER_AAAA_ON_V4 /* * Notice the presence of A and AAAAs so * that AAAAs can be hidden from IPv4 clients. */ if (client->filter_aaaa != dns_v4_aaaa_ok) { if (rdataset->type == dns_rdatatype_aaaa) have_aaaa = ISC_TRUE; else if (rdataset->type == dns_rdatatype_a) have_a = ISC_TRUE; } #endif if (is_zone && qtype == dns_rdatatype_any && !dns_db_issecure(db) && dns_rdatatype_isdnssec(rdataset->type)) { /* * The zone is transitioning from insecure * to secure. Hide the dnssec records from * ANY queries. */ dns_rdataset_disassociate(rdataset); } else if ((qtype == dns_rdatatype_any || rdataset->type == qtype) && rdataset->type != 0) { #ifdef ALLOW_FILTER_AAAA_ON_V4 if (dns_rdatatype_isdnssec(rdataset->type)) have_sig = ISC_TRUE; #endif if (NOQNAME(rdataset) && WANTDNSSEC(client)) noqname = rdataset; else noqname = NULL; rpz_st = client->query.rpz_st; if (rpz_st != NULL) rdataset->ttl = ISC_MIN(rdataset->ttl, rpz_st->m.ttl); query_addrrset(client, fname != NULL ? &fname : &tname, &rdataset, NULL, NULL, DNS_SECTION_ANSWER); if (noqname != NULL) query_addnoqnameproof(client, noqname); n++; INSIST(tname != NULL); /* * rdataset is non-NULL only in certain * pathological cases involving DNAMEs. */ if (rdataset != NULL) query_putrdataset(client, &rdataset); rdataset = query_newrdataset(client); if (rdataset == NULL) break; } else { /* * We're not interested in this rdataset. */ dns_rdataset_disassociate(rdataset); } result = dns_rdatasetiter_next(rdsiter); } #ifdef ALLOW_FILTER_AAAA_ON_V4 /* * Filter AAAAs if there is an A and there is no signature * or we are supposed to break DNSSEC. */ if (client->filter_aaaa == dns_v4_aaaa_break_dnssec) client->attributes |= NS_CLIENTATTR_FILTER_AAAA; else if (client->filter_aaaa != dns_v4_aaaa_ok && have_aaaa && have_a && (!have_sig || !WANTDNSSEC(client))) client->attributes |= NS_CLIENTATTR_FILTER_AAAA; #endif if (fname != NULL) dns_message_puttempname(client->message, &fname); if (n == 0) { /* * No matching rdatasets found in cache. If we were * searching for RRSIG/SIG, that's probably okay; * otherwise this is an error condition. */ if ((qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig) && result == ISC_R_NOMORE) { if (!is_zone) { authoritative = ISC_FALSE; dns_rdatasetiter_destroy(&rdsiter); client->attributes &= ~NS_CLIENTATTR_RA; goto addauth; } if (qtype == dns_rdatatype_rrsig && dns_db_issecure(db)) { char namebuf[DNS_NAME_FORMATSIZE]; dns_name_format(client->query.qname, namebuf, sizeof(namebuf)); ns_client_log(client, DNS_LOGCATEGORY_DNSSEC, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "missing signature " "for %s", namebuf); } dns_rdatasetiter_destroy(&rdsiter); fname = query_newname(client, dbuf, &b); goto nxrrset_rrsig; } else result = DNS_R_SERVFAIL; } dns_rdatasetiter_destroy(&rdsiter); if (result != ISC_R_NOMORE) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } } else { /* * This is the "normal" case -- an ordinary question to which * we know the answer. */ #ifdef ALLOW_FILTER_AAAA_ON_V4 /* * Optionally hide AAAAs from IPv4 clients if there is an A. * We add the AAAAs now, but might refuse to render them later * after DNSSEC is figured out. * This could be more efficient, but the whole idea is * so fundamentally wrong, unavoidably inaccurate, and * unneeded that it is best to keep it as short as possible. */ if (client->filter_aaaa == dns_v4_aaaa_break_dnssec || (client->filter_aaaa == dns_v4_aaaa_filter && (!WANTDNSSEC(client) || sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset)))) { if (qtype == dns_rdatatype_aaaa) { trdataset = query_newrdataset(client); result = dns_db_findrdataset(db, node, version, dns_rdatatype_a, 0, client->now, trdataset, NULL); if (dns_rdataset_isassociated(trdataset)) dns_rdataset_disassociate(trdataset); query_putrdataset(client, &trdataset); /* * We have an AAAA but the A is not in our cache. * Assume any result other than DNS_R_DELEGATION * or ISC_R_NOTFOUND means there is no A and * so AAAAs are ok. * Assume there is no A if we can't recurse * for this client, although that could be * the wrong answer. What else can we do? * Besides, that we have the AAAA and are using * this mechanism suggests that we care more * about As than AAAAs and would have cached * the A if it existed. */ if (result == ISC_R_SUCCESS) { client->attributes |= NS_CLIENTATTR_FILTER_AAAA; } else if (authoritative || !RECURSIONOK(client) || (result != DNS_R_DELEGATION && result != ISC_R_NOTFOUND)) { client->attributes &= ~NS_CLIENTATTR_FILTER_AAAA; } else { /* * This is an ugly kludge to recurse * for the A and discard the result. * * Continue to add the AAAA now. * We'll make a note to not render it * if the recursion for the A succeeds. */ result = query_recurse(client, dns_rdatatype_a, client->query.qname, NULL, NULL, resuming); if (result == ISC_R_SUCCESS) { client->attributes |= NS_CLIENTATTR_FILTER_AAAA_RC; client->query.attributes |= NS_QUERYATTR_RECURSING; } } } else if (qtype == dns_rdatatype_a && (client->attributes & NS_CLIENTATTR_FILTER_AAAA_RC) != 0) { client->attributes &= ~NS_CLIENTATTR_FILTER_AAAA_RC; client->attributes |= NS_CLIENTATTR_FILTER_AAAA; dns_rdataset_disassociate(rdataset); if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); goto cleanup; } } #endif /* * Check to see if the AAAA RRset has non-excluded addresses * in it. If not look for a A RRset. */ INSIST(client->query.dns64_aaaaok == NULL); if (qtype == dns_rdatatype_aaaa && !dns64_exclude && !ISC_LIST_EMPTY(client->view->dns64) && client->message->rdclass == dns_rdataclass_in && !dns64_aaaaok(client, rdataset, sigrdataset)) { /* * Look to see if there are A records for this * name. */ client->query.dns64_aaaa = rdataset; client->query.dns64_sigaaaa = sigrdataset; client->query.dns64_ttl = rdataset->ttl; query_releasename(client, &fname); dns_db_detachnode(db, &node); rdataset = NULL; sigrdataset = NULL; type = qtype = dns_rdatatype_a; rpz_st = client->query.rpz_st; if (rpz_st != NULL) { /* * Arrange for RPZ rewriting of any A records. */ if ((rpz_st->state & DNS_RPZ_REWRITTEN) != 0) is_zone = rpz_st->q.is_zone; rpz_st_clear(client); } dns64_exclude = dns64 = ISC_TRUE; goto db_find; } if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; if (NOQNAME(rdataset) && WANTDNSSEC(client)) noqname = rdataset; else noqname = NULL; /* * BIND 8 priming queries need the additional section. */ if (is_zone && qtype == dns_rdatatype_ns && dns_name_equal(client->query.qname, dns_rootname)) client->query.attributes &= ~NS_QUERYATTR_NOADDITIONAL; if (dns64) { qtype = type = dns_rdatatype_aaaa; result = query_dns64(client, &fname, rdataset, sigrdataset, dbuf, DNS_SECTION_ANSWER); dns_rdataset_disassociate(rdataset); dns_message_puttemprdataset(client->message, &rdataset); if (result == ISC_R_NOMORE) { #ifndef dns64_bis_return_excluded_addresses if (dns64_exclude) { if (!is_zone) goto cleanup; /* * Add a fake SOA record. */ (void)query_addsoa(client, db, version, 600, ISC_FALSE); goto cleanup; } #endif if (is_zone) goto iszone_nxrrset; else goto ncache_nxrrset; } else if (result != ISC_R_SUCCESS) { eresult = result; goto cleanup; } } else if (client->query.dns64_aaaaok != NULL) { query_filter64(client, &fname, rdataset, dbuf, DNS_SECTION_ANSWER); query_putrdataset(client, &rdataset); } else query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, DNS_SECTION_ANSWER); if (noqname != NULL) query_addnoqnameproof(client, noqname); /* * We shouldn't ever fail to add 'rdataset' * because it's already in the answer. */ INSIST(rdataset == NULL); } addauth: CTRACE("query_find: addauth"); /* * Add NS records to the authority section (if we haven't already * added them to the answer section). */ if (!want_restart && !NOAUTHORITY(client)) { if (is_zone) { if (!((qtype == dns_rdatatype_ns || qtype == dns_rdatatype_any) && dns_name_equal(client->query.qname, dns_db_origin(db)))) (void)query_addns(client, db, version); } else if (qtype != dns_rdatatype_ns) { if (fname != NULL) query_releasename(client, &fname); query_addbestns(client); } } /* * Add NSEC records to the authority section if they're needed for * DNSSEC wildcard proofs. */ if (need_wildcardproof && dns_db_issecure(db)) query_addwildcardproof(client, db, version, dns_fixedname_name(&wildcardname), ISC_TRUE, ISC_FALSE); cleanup: CTRACE("query_find: cleanup"); /* * General cleanup. */ rpz_st = client->query.rpz_st; if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) == 0) { rpz_match_clear(rpz_st); rpz_st->state &= ~DNS_RPZ_DONE_QNAME; } if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); if (node != NULL) dns_db_detachnode(db, &node); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); if (zdb != NULL) { query_putrdataset(client, &zrdataset); if (zsigrdataset != NULL) query_putrdataset(client, &zsigrdataset); if (zfname != NULL) query_releasename(client, &zfname); dns_db_detach(&zdb); } if (event != NULL) isc_event_free(ISC_EVENT_PTR(&event)); /* * AA bit. */ if (client->query.restarts == 0 && !authoritative) { /* * We're not authoritative, so we must ensure the AA bit * isn't set. */ client->message->flags &= ~DNS_MESSAGEFLAG_AA; } /* * Restart the query? */ if (want_restart && client->query.restarts < MAX_RESTARTS) { client->query.restarts++; goto restart; } if (eresult != ISC_R_SUCCESS && (!PARTIALANSWER(client) || WANTRECURSION(client))) { if (eresult == DNS_R_DUPLICATE || eresult == DNS_R_DROP) { /* * This was a duplicate query that we are * recursing on. Don't send a response now. * The original query will still cause a response. */ query_next(client, eresult); } else { /* * If we don't have any answer to give the client, * or if the client requested recursion and thus wanted * the complete answer, send an error response. */ INSIST(line >= 0); query_error(client, eresult, line); } ns_client_detach(&client); } else if (!RECURSING(client)) { /* * We are done. Set up sortlist data for the message * rendering code, make a final tweak to the AA bit if the * auth-nxdomain config option says so, then render and * send the response. */ setup_query_sortlist(client); /* * If this is a referral and the answer to the question * is in the glue sort it to the start of the additional * section. */ if (ISC_LIST_EMPTY(client->message->sections[DNS_SECTION_ANSWER]) && client->message->rcode == dns_rcode_noerror && (qtype == dns_rdatatype_a || qtype == dns_rdatatype_aaaa)) answer_in_glue(client, qtype); if (client->message->rcode == dns_rcode_nxdomain && client->view->auth_nxdomain == ISC_TRUE) client->message->flags |= DNS_MESSAGEFLAG_AA; /* * If the response is somehow unexpected for the client and this * is a result of recursion, return an error to the caller * to indicate it may need to be logged. */ if (resuming && (ISC_LIST_EMPTY(client->message->sections[DNS_SECTION_ANSWER]) || client->message->rcode != dns_rcode_noerror)) eresult = ISC_R_FAILURE; query_send(client); ns_client_detach(&client); } CTRACE("query_find: done"); return (eresult); } static inline void log_query(ns_client_t *client, unsigned int flags, unsigned int extflags) { char namebuf[DNS_NAME_FORMATSIZE]; char typename[DNS_RDATATYPE_FORMATSIZE]; char classname[DNS_RDATACLASS_FORMATSIZE]; char onbuf[ISC_NETADDR_FORMATSIZE]; dns_rdataset_t *rdataset; int level = ISC_LOG_INFO; if (! isc_log_wouldlog(ns_g_lctx, level)) return; rdataset = ISC_LIST_HEAD(client->query.qname->list); INSIST(rdataset != NULL); dns_name_format(client->query.qname, namebuf, sizeof(namebuf)); dns_rdataclass_format(rdataset->rdclass, classname, sizeof(classname)); dns_rdatatype_format(rdataset->type, typename, sizeof(typename)); isc_netaddr_format(&client->destaddr, onbuf, sizeof(onbuf)); ns_client_log(client, NS_LOGCATEGORY_QUERIES, NS_LOGMODULE_QUERY, level, "query: %s %s %s %s%s%s%s%s%s (%s)", namebuf, classname, typename, WANTRECURSION(client) ? "+" : "-", (client->signer != NULL) ? "S": "", (client->opt != NULL) ? "E" : "", ((client->attributes & NS_CLIENTATTR_TCP) != 0) ? "T" : "", ((extflags & DNS_MESSAGEEXTFLAG_DO) != 0) ? "D" : "", ((flags & DNS_MESSAGEFLAG_CD) != 0) ? "C" : "", onbuf); } static inline void log_queryerror(ns_client_t *client, isc_result_t result, int line, int level) { char namebuf[DNS_NAME_FORMATSIZE]; char typename[DNS_RDATATYPE_FORMATSIZE]; char classname[DNS_RDATACLASS_FORMATSIZE]; const char *namep, *typep, *classp, *sep1, *sep2; dns_rdataset_t *rdataset; if (!isc_log_wouldlog(ns_g_lctx, level)) return; namep = typep = classp = sep1 = sep2 = ""; /* * Query errors can happen for various reasons. In some cases we cannot * even assume the query contains a valid question section, so we should * expect exceptional cases. */ if (client->query.origqname != NULL) { dns_name_format(client->query.origqname, namebuf, sizeof(namebuf)); namep = namebuf; sep1 = " for "; rdataset = ISC_LIST_HEAD(client->query.origqname->list); if (rdataset != NULL) { dns_rdataclass_format(rdataset->rdclass, classname, sizeof(classname)); classp = classname; dns_rdatatype_format(rdataset->type, typename, sizeof(typename)); typep = typename; sep2 = "/"; } } ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS, NS_LOGMODULE_QUERY, level, "query failed (%s)%s%s%s%s%s%s at %s:%d", isc_result_totext(result), sep1, namep, sep2, classp, sep2, typep, __FILE__, line); } void ns_query_start(ns_client_t *client) { isc_result_t result; dns_message_t *message = client->message; dns_rdataset_t *rdataset; ns_client_t *qclient; dns_rdatatype_t qtype; unsigned int saved_extflags = client->extflags; unsigned int saved_flags = client->message->flags; CTRACE("ns_query_start"); /* * Test only. */ if (ns_g_clienttest && (client->attributes & NS_CLIENTATTR_TCP) == 0) RUNTIME_CHECK(ns_client_replace(client) == ISC_R_SUCCESS); /* * Ensure that appropriate cleanups occur. */ client->next = query_next_callback; /* * Behave as if we don't support DNSSEC if not enabled. */ if (!client->view->enablednssec) { message->flags &= ~DNS_MESSAGEFLAG_CD; client->extflags &= ~DNS_MESSAGEEXTFLAG_DO; if (client->opt != NULL) client->opt->ttl &= ~DNS_MESSAGEEXTFLAG_DO; } if ((message->flags & DNS_MESSAGEFLAG_RD) != 0) client->query.attributes |= NS_QUERYATTR_WANTRECURSION; if ((client->extflags & DNS_MESSAGEEXTFLAG_DO) != 0) client->attributes |= NS_CLIENTATTR_WANTDNSSEC; if (client->view->minimalresponses) client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY | NS_QUERYATTR_NOADDITIONAL); if ((client->view->cachedb == NULL) || (!client->view->additionalfromcache)) { /* * We don't have a cache. Turn off cache support and * recursion. */ client->query.attributes &= ~(NS_QUERYATTR_RECURSIONOK|NS_QUERYATTR_CACHEOK); } else if ((client->attributes & NS_CLIENTATTR_RA) == 0 || (message->flags & DNS_MESSAGEFLAG_RD) == 0) { /* * If the client isn't allowed to recurse (due to * "recursion no", the allow-recursion ACL, or the * lack of a resolver in this view), or if it * doesn't want recursion, turn recursion off. */ client->query.attributes &= ~NS_QUERYATTR_RECURSIONOK; } /* * Get the question name. */ result = dns_message_firstname(message, DNS_SECTION_QUESTION); if (result != ISC_R_SUCCESS) { query_error(client, result, __LINE__); return; } dns_message_currentname(message, DNS_SECTION_QUESTION, &client->query.qname); client->query.origqname = client->query.qname; result = dns_message_nextname(message, DNS_SECTION_QUESTION); if (result != ISC_R_NOMORE) { if (result == ISC_R_SUCCESS) { /* * There's more than one QNAME in the question * section. */ query_error(client, DNS_R_FORMERR, __LINE__); } else query_error(client, result, __LINE__); return; } if (ns_g_server->log_queries) log_query(client, saved_flags, saved_extflags); /* * Check for multiple question queries, since edns1 is dead. */ if (message->counts[DNS_SECTION_QUESTION] > 1) { query_error(client, DNS_R_FORMERR, __LINE__); return; } /* * Check for meta-queries like IXFR and AXFR. */ rdataset = ISC_LIST_HEAD(client->query.qname->list); INSIST(rdataset != NULL); qtype = rdataset->type; dns_rdatatypestats_increment(ns_g_server->rcvquerystats, qtype); if (dns_rdatatype_ismeta(qtype)) { switch (qtype) { case dns_rdatatype_any: break; /* Let query_find handle it. */ case dns_rdatatype_ixfr: case dns_rdatatype_axfr: ns_xfr_start(client, rdataset->type); return; case dns_rdatatype_maila: case dns_rdatatype_mailb: query_error(client, DNS_R_NOTIMP, __LINE__); return; case dns_rdatatype_tkey: result = dns_tkey_processquery(client->message, ns_g_server->tkeyctx, client->view->dynamickeys); if (result == ISC_R_SUCCESS) query_send(client); else query_error(client, result, __LINE__); return; default: /* TSIG, etc. */ query_error(client, DNS_R_FORMERR, __LINE__); return; } } /* * Turn on minimal response for DNSKEY and DS queries. */ if (qtype == dns_rdatatype_dnskey || qtype == dns_rdatatype_ds) client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY | NS_QUERYATTR_NOADDITIONAL); /* * Turn on minimal responses for EDNS/UDP bufsize 512 queries. */ if (client->opt != NULL && client->udpsize <= 512U && (client->attributes & NS_CLIENTATTR_TCP) == 0) client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY | NS_QUERYATTR_NOADDITIONAL); /* * If the client has requested that DNSSEC checking be disabled, * allow lookups to return pending data and instruct the resolver * to return data before validation has completed. * * We don't need to set DNS_DBFIND_PENDINGOK when validation is * disabled as there will be no pending data. */ if (message->flags & DNS_MESSAGEFLAG_CD || qtype == dns_rdatatype_rrsig) { client->query.dboptions |= DNS_DBFIND_PENDINGOK; client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE; } else if (!client->view->enablevalidation) client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE; /* * Allow glue NS records to be added to the authority section * if the answer is secure. */ if (message->flags & DNS_MESSAGEFLAG_CD) client->query.attributes &= ~NS_QUERYATTR_SECURE; /* * Set NS_CLIENTATTR_WANTDNSSEC if the client has set AD in the query. * This allows AD to be returned on queries without DO set. */ if ((message->flags & DNS_MESSAGEFLAG_AD) != 0) client->attributes |= NS_CLIENTATTR_WANTAD; /* * This is an ordinary query. */ result = dns_message_reply(message, ISC_TRUE); if (result != ISC_R_SUCCESS) { query_next(client, result); return; } /* * Assume authoritative response until it is known to be * otherwise. * * If "-T noaa" has been set on the command line don't set * AA on authoritative answers. */ if (!ns_g_noaa) message->flags |= DNS_MESSAGEFLAG_AA; /* * Set AD. We must clear it if we add non-validated data to a * response. */ if (WANTDNSSEC(client) || WANTAD(client)) message->flags |= DNS_MESSAGEFLAG_AD; qclient = NULL; ns_client_attach(client, &qclient); (void)query_find(qclient, NULL, qtype); } Index: stable/8/contrib/bind9/bin/named/server.c =================================================================== --- stable/8/contrib/bind9/bin/named/server.c (revision 275668) +++ stable/8/contrib/bind9/bin/named/server.c (revision 275669) @@ -1,8105 +1,8115 @@ /* * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: server.c,v 1.599.8.19 2012/02/22 00:33:32 each Exp $ */ /*! \file */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBSCF #include #include #endif #ifndef PATH_MAX #define PATH_MAX 1024 #endif /*% * Check an operation for failure. Assumes that the function * using it has a 'result' variable and a 'cleanup' label. */ #define CHECK(op) \ do { result = (op); \ if (result != ISC_R_SUCCESS) goto cleanup; \ } while (0) #define CHECKM(op, msg) \ do { result = (op); \ if (result != ISC_R_SUCCESS) { \ isc_log_write(ns_g_lctx, \ NS_LOGCATEGORY_GENERAL, \ NS_LOGMODULE_SERVER, \ ISC_LOG_ERROR, \ "%s: %s", msg, \ isc_result_totext(result)); \ goto cleanup; \ } \ } while (0) \ #define CHECKMF(op, msg, file) \ do { result = (op); \ if (result != ISC_R_SUCCESS) { \ isc_log_write(ns_g_lctx, \ NS_LOGCATEGORY_GENERAL, \ NS_LOGMODULE_SERVER, \ ISC_LOG_ERROR, \ "%s '%s': %s", msg, file, \ isc_result_totext(result)); \ goto cleanup; \ } \ } while (0) \ #define CHECKFATAL(op, msg) \ do { result = (op); \ if (result != ISC_R_SUCCESS) \ fatal(msg, result); \ } while (0) \ /*% * Maximum ADB size for views that share a cache. Use this limit to suppress * the total of memory footprint, which should be the main reason for sharing * a cache. Only effective when a finite max-cache-size is specified. * This is currently defined to be 8MB. */ #define MAX_ADB_SIZE_FOR_CACHESHARE 8388608U struct ns_dispatch { isc_sockaddr_t addr; unsigned int dispatchgen; dns_dispatch_t *dispatch; ISC_LINK(struct ns_dispatch) link; }; struct ns_cache { dns_cache_t *cache; dns_view_t *primaryview; isc_boolean_t needflush; isc_boolean_t adbsizeadjusted; ISC_LINK(ns_cache_t) link; }; struct dumpcontext { isc_mem_t *mctx; isc_boolean_t dumpcache; isc_boolean_t dumpzones; FILE *fp; ISC_LIST(struct viewlistentry) viewlist; struct viewlistentry *view; struct zonelistentry *zone; dns_dumpctx_t *mdctx; dns_db_t *db; dns_db_t *cache; isc_task_t *task; dns_dbversion_t *version; }; struct viewlistentry { dns_view_t *view; ISC_LINK(struct viewlistentry) link; ISC_LIST(struct zonelistentry) zonelist; }; struct zonelistentry { dns_zone_t *zone; ISC_LINK(struct zonelistentry) link; }; /*% * Configuration context to retain for each view that allows * new zones to be added at runtime. */ struct cfg_context { isc_mem_t * mctx; cfg_parser_t * parser; cfg_obj_t * config; cfg_parser_t * nzparser; cfg_obj_t * nzconfig; cfg_aclconfctx_t * actx; }; /* * These zones should not leak onto the Internet. */ static const struct { const char *zone; isc_boolean_t rfc1918; } empty_zones[] = { /* RFC 1918 */ { "10.IN-ADDR.ARPA", ISC_TRUE }, { "16.172.IN-ADDR.ARPA", ISC_TRUE }, { "17.172.IN-ADDR.ARPA", ISC_TRUE }, { "18.172.IN-ADDR.ARPA", ISC_TRUE }, { "19.172.IN-ADDR.ARPA", ISC_TRUE }, { "20.172.IN-ADDR.ARPA", ISC_TRUE }, { "21.172.IN-ADDR.ARPA", ISC_TRUE }, { "22.172.IN-ADDR.ARPA", ISC_TRUE }, { "23.172.IN-ADDR.ARPA", ISC_TRUE }, { "24.172.IN-ADDR.ARPA", ISC_TRUE }, { "25.172.IN-ADDR.ARPA", ISC_TRUE }, { "26.172.IN-ADDR.ARPA", ISC_TRUE }, { "27.172.IN-ADDR.ARPA", ISC_TRUE }, { "28.172.IN-ADDR.ARPA", ISC_TRUE }, { "29.172.IN-ADDR.ARPA", ISC_TRUE }, { "30.172.IN-ADDR.ARPA", ISC_TRUE }, { "31.172.IN-ADDR.ARPA", ISC_TRUE }, { "168.192.IN-ADDR.ARPA", ISC_TRUE }, /* RFC 6598 */ { "64.100.IN-ADDR.ARPA", ISC_FALSE }, { "65.100.IN-ADDR.ARPA", ISC_FALSE }, { "66.100.IN-ADDR.ARPA", ISC_FALSE }, { "67.100.IN-ADDR.ARPA", ISC_FALSE }, { "68.100.IN-ADDR.ARPA", ISC_FALSE }, { "69.100.IN-ADDR.ARPA", ISC_FALSE }, { "70.100.IN-ADDR.ARPA", ISC_FALSE }, { "71.100.IN-ADDR.ARPA", ISC_FALSE }, { "72.100.IN-ADDR.ARPA", ISC_FALSE }, { "73.100.IN-ADDR.ARPA", ISC_FALSE }, { "74.100.IN-ADDR.ARPA", ISC_FALSE }, { "75.100.IN-ADDR.ARPA", ISC_FALSE }, { "76.100.IN-ADDR.ARPA", ISC_FALSE }, { "77.100.IN-ADDR.ARPA", ISC_FALSE }, { "78.100.IN-ADDR.ARPA", ISC_FALSE }, { "79.100.IN-ADDR.ARPA", ISC_FALSE }, { "80.100.IN-ADDR.ARPA", ISC_FALSE }, { "81.100.IN-ADDR.ARPA", ISC_FALSE }, { "82.100.IN-ADDR.ARPA", ISC_FALSE }, { "83.100.IN-ADDR.ARPA", ISC_FALSE }, { "84.100.IN-ADDR.ARPA", ISC_FALSE }, { "85.100.IN-ADDR.ARPA", ISC_FALSE }, { "86.100.IN-ADDR.ARPA", ISC_FALSE }, { "87.100.IN-ADDR.ARPA", ISC_FALSE }, { "88.100.IN-ADDR.ARPA", ISC_FALSE }, { "89.100.IN-ADDR.ARPA", ISC_FALSE }, { "90.100.IN-ADDR.ARPA", ISC_FALSE }, { "91.100.IN-ADDR.ARPA", ISC_FALSE }, { "92.100.IN-ADDR.ARPA", ISC_FALSE }, { "93.100.IN-ADDR.ARPA", ISC_FALSE }, { "94.100.IN-ADDR.ARPA", ISC_FALSE }, { "95.100.IN-ADDR.ARPA", ISC_FALSE }, { "96.100.IN-ADDR.ARPA", ISC_FALSE }, { "97.100.IN-ADDR.ARPA", ISC_FALSE }, { "98.100.IN-ADDR.ARPA", ISC_FALSE }, { "99.100.IN-ADDR.ARPA", ISC_FALSE }, { "100.100.IN-ADDR.ARPA", ISC_FALSE }, { "101.100.IN-ADDR.ARPA", ISC_FALSE }, { "102.100.IN-ADDR.ARPA", ISC_FALSE }, { "103.100.IN-ADDR.ARPA", ISC_FALSE }, { "104.100.IN-ADDR.ARPA", ISC_FALSE }, { "105.100.IN-ADDR.ARPA", ISC_FALSE }, { "106.100.IN-ADDR.ARPA", ISC_FALSE }, { "107.100.IN-ADDR.ARPA", ISC_FALSE }, { "108.100.IN-ADDR.ARPA", ISC_FALSE }, { "109.100.IN-ADDR.ARPA", ISC_FALSE }, { "110.100.IN-ADDR.ARPA", ISC_FALSE }, { "111.100.IN-ADDR.ARPA", ISC_FALSE }, { "112.100.IN-ADDR.ARPA", ISC_FALSE }, { "113.100.IN-ADDR.ARPA", ISC_FALSE }, { "114.100.IN-ADDR.ARPA", ISC_FALSE }, { "115.100.IN-ADDR.ARPA", ISC_FALSE }, { "116.100.IN-ADDR.ARPA", ISC_FALSE }, { "117.100.IN-ADDR.ARPA", ISC_FALSE }, { "118.100.IN-ADDR.ARPA", ISC_FALSE }, { "119.100.IN-ADDR.ARPA", ISC_FALSE }, { "120.100.IN-ADDR.ARPA", ISC_FALSE }, { "121.100.IN-ADDR.ARPA", ISC_FALSE }, { "122.100.IN-ADDR.ARPA", ISC_FALSE }, { "123.100.IN-ADDR.ARPA", ISC_FALSE }, { "124.100.IN-ADDR.ARPA", ISC_FALSE }, { "125.100.IN-ADDR.ARPA", ISC_FALSE }, { "126.100.IN-ADDR.ARPA", ISC_FALSE }, { "127.100.IN-ADDR.ARPA", ISC_FALSE }, /* RFC 5735 and RFC 5737 */ { "0.IN-ADDR.ARPA", ISC_FALSE }, /* THIS NETWORK */ { "127.IN-ADDR.ARPA", ISC_FALSE }, /* LOOPBACK */ { "254.169.IN-ADDR.ARPA", ISC_FALSE }, /* LINK LOCAL */ { "2.0.192.IN-ADDR.ARPA", ISC_FALSE }, /* TEST NET */ { "100.51.198.IN-ADDR.ARPA", ISC_FALSE }, /* TEST NET 2 */ { "113.0.203.IN-ADDR.ARPA", ISC_FALSE }, /* TEST NET 3 */ { "255.255.255.255.IN-ADDR.ARPA", ISC_FALSE }, /* BROADCAST */ /* Local IPv6 Unicast Addresses */ { "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA", ISC_FALSE }, { "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA", ISC_FALSE }, /* LOCALLY ASSIGNED LOCAL ADDRESS SCOPE */ { "D.F.IP6.ARPA", ISC_FALSE }, { "8.E.F.IP6.ARPA", ISC_FALSE }, /* LINK LOCAL */ { "9.E.F.IP6.ARPA", ISC_FALSE }, /* LINK LOCAL */ { "A.E.F.IP6.ARPA", ISC_FALSE }, /* LINK LOCAL */ { "B.E.F.IP6.ARPA", ISC_FALSE }, /* LINK LOCAL */ /* Example Prefix, RFC 3849. */ { "8.B.D.0.1.0.0.2.IP6.ARPA", ISC_FALSE }, { NULL, ISC_FALSE } }; ISC_PLATFORM_NORETURN_PRE static void fatal(const char *msg, isc_result_t result) ISC_PLATFORM_NORETURN_POST; static void ns_server_reload(isc_task_t *task, isc_event_t *event); static isc_result_t ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, ns_listenelt_t **target); static isc_result_t ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, ns_listenlist_t **target); static isc_result_t configure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin, const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype); static isc_result_t configure_alternates(const cfg_obj_t *config, dns_view_t *view, const cfg_obj_t *alternates); static isc_result_t configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view, cfg_aclconfctx_t *aclconf, isc_boolean_t added); static isc_result_t add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx); static void end_reserved_dispatches(ns_server_t *server, isc_boolean_t all); static void newzone_cfgctx_destroy(void **cfgp); static isc_result_t putstr(isc_buffer_t *b, const char *str); isc_result_t add_comment(FILE *fp, const char *viewname); /*% * Configure a single view ACL at '*aclp'. Get its configuration from * 'vconfig' (for per-view configuration) and maybe from 'config' */ static isc_result_t configure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config, const char *aclname, const char *acltuplename, cfg_aclconfctx_t *actx, isc_mem_t *mctx, dns_acl_t **aclp) { isc_result_t result; const cfg_obj_t *maps[3]; const cfg_obj_t *aclobj = NULL; int i = 0; if (*aclp != NULL) dns_acl_detach(aclp); if (vconfig != NULL) maps[i++] = cfg_tuple_get(vconfig, "options"); if (config != NULL) { const cfg_obj_t *options = NULL; (void)cfg_map_get(config, "options", &options); if (options != NULL) maps[i++] = options; } maps[i] = NULL; (void)ns_config_get(maps, aclname, &aclobj); if (aclobj == NULL) /* * No value available. *aclp == NULL. */ return (ISC_R_SUCCESS); if (acltuplename != NULL) { /* * If the ACL is given in an optional tuple, retrieve it. * The parser should have ensured that a valid object be * returned. */ aclobj = cfg_tuple_get(aclobj, acltuplename); } result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx, actx, mctx, 0, aclp); return (result); } /*% * Configure a sortlist at '*aclp'. Essentially the same as * configure_view_acl() except it calls cfg_acl_fromconfig with a * nest_level value of 2. */ static isc_result_t configure_view_sortlist(const cfg_obj_t *vconfig, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, dns_acl_t **aclp) { isc_result_t result; const cfg_obj_t *maps[3]; const cfg_obj_t *aclobj = NULL; int i = 0; if (*aclp != NULL) dns_acl_detach(aclp); if (vconfig != NULL) maps[i++] = cfg_tuple_get(vconfig, "options"); if (config != NULL) { const cfg_obj_t *options = NULL; (void)cfg_map_get(config, "options", &options); if (options != NULL) maps[i++] = options; } maps[i] = NULL; (void)ns_config_get(maps, "sortlist", &aclobj); if (aclobj == NULL) return (ISC_R_SUCCESS); /* * Use a nest level of 3 for the "top level" of the sortlist; * this means each entry in the top three levels will be stored * as lists of separate, nested ACLs, rather than merged together * into IP tables as is usually done with ACLs. */ result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx, actx, mctx, 3, aclp); return (result); } static isc_result_t configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config, const char *confname, const char *conftuplename, isc_mem_t *mctx, dns_rbt_t **rbtp) { isc_result_t result; const cfg_obj_t *maps[3]; const cfg_obj_t *obj = NULL; const cfg_listelt_t *element; int i = 0; dns_fixedname_t fixed; dns_name_t *name; isc_buffer_t b; const char *str; const cfg_obj_t *nameobj; if (*rbtp != NULL) dns_rbt_destroy(rbtp); if (vconfig != NULL) maps[i++] = cfg_tuple_get(vconfig, "options"); if (config != NULL) { const cfg_obj_t *options = NULL; (void)cfg_map_get(config, "options", &options); if (options != NULL) maps[i++] = options; } maps[i] = NULL; (void)ns_config_get(maps, confname, &obj); if (obj == NULL) /* * No value available. *rbtp == NULL. */ return (ISC_R_SUCCESS); if (conftuplename != NULL) { obj = cfg_tuple_get(obj, conftuplename); if (cfg_obj_isvoid(obj)) return (ISC_R_SUCCESS); } result = dns_rbt_create(mctx, NULL, NULL, rbtp); if (result != ISC_R_SUCCESS) return (result); dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); for (element = cfg_list_first(obj); element != NULL; element = cfg_list_next(element)) { nameobj = cfg_listelt_value(element); str = cfg_obj_asstring(nameobj); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); /* * We don't need the node data, but need to set dummy data to * avoid a partial match with an empty node. For example, if * we have foo.example.com and bar.example.com, we'd get a match * for baz.example.com, which is not the expected result. * We simply use (void *)1 as the dummy data. */ result = dns_rbt_addname(*rbtp, name, (void *)1); if (result != ISC_R_SUCCESS) { cfg_obj_log(nameobj, ns_g_lctx, ISC_LOG_ERROR, "failed to add %s for %s: %s", str, confname, isc_result_totext(result)); goto cleanup; } } return (result); cleanup: dns_rbt_destroy(rbtp); return (result); } static isc_result_t dstkey_fromconfig(const cfg_obj_t *vconfig, const cfg_obj_t *key, isc_boolean_t managed, dst_key_t **target, isc_mem_t *mctx) { dns_rdataclass_t viewclass; dns_rdata_dnskey_t keystruct; isc_uint32_t flags, proto, alg; const char *keystr, *keynamestr; unsigned char keydata[4096]; isc_buffer_t keydatabuf; unsigned char rrdata[4096]; isc_buffer_t rrdatabuf; isc_region_t r; dns_fixedname_t fkeyname; dns_name_t *keyname; isc_buffer_t namebuf; isc_result_t result; dst_key_t *dstkey = NULL; INSIST(target != NULL && *target == NULL); flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags")); proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol")); alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm")); keyname = dns_fixedname_name(&fkeyname); keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); if (managed) { const char *initmethod; initmethod = cfg_obj_asstring(cfg_tuple_get(key, "init")); if (strcasecmp(initmethod, "initial-key") != 0) { cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR, "managed key '%s': " "invalid initialization method '%s'", keynamestr, initmethod); result = ISC_R_FAILURE; goto cleanup; } } if (vconfig == NULL) viewclass = dns_rdataclass_in; else { const cfg_obj_t *classobj = cfg_tuple_get(vconfig, "class"); CHECK(ns_config_getclass(classobj, dns_rdataclass_in, &viewclass)); } keystruct.common.rdclass = viewclass; keystruct.common.rdtype = dns_rdatatype_dnskey; /* * The key data in keystruct is not dynamically allocated. */ keystruct.mctx = NULL; ISC_LINK_INIT(&keystruct.common, link); if (flags > 0xffff) CHECKM(ISC_R_RANGE, "key flags"); if (proto > 0xff) CHECKM(ISC_R_RANGE, "key protocol"); if (alg > 0xff) CHECKM(ISC_R_RANGE, "key algorithm"); keystruct.flags = (isc_uint16_t)flags; keystruct.protocol = (isc_uint8_t)proto; keystruct.algorithm = (isc_uint8_t)alg; isc_buffer_init(&keydatabuf, keydata, sizeof(keydata)); isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); keystr = cfg_obj_asstring(cfg_tuple_get(key, "key")); CHECK(isc_base64_decodestring(keystr, &keydatabuf)); isc_buffer_usedregion(&keydatabuf, &r); keystruct.datalen = r.length; keystruct.data = r.base; if ((keystruct.algorithm == DST_ALG_RSASHA1 || keystruct.algorithm == DST_ALG_RSAMD5) && r.length > 1 && r.base[0] == 1 && r.base[1] == 3) cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING, "%s key '%s' has a weak exponent", managed ? "managed" : "trusted", keynamestr); CHECK(dns_rdata_fromstruct(NULL, keystruct.common.rdclass, keystruct.common.rdtype, &keystruct, &rrdatabuf)); dns_fixedname_init(&fkeyname); isc_buffer_constinit(&namebuf, keynamestr, strlen(keynamestr)); isc_buffer_add(&namebuf, strlen(keynamestr)); CHECK(dns_name_fromtext(keyname, &namebuf, dns_rootname, 0, NULL)); CHECK(dst_key_fromdns(keyname, viewclass, &rrdatabuf, mctx, &dstkey)); *target = dstkey; return (ISC_R_SUCCESS); cleanup: if (result == DST_R_NOCRYPTO) { cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR, "ignoring %s key for '%s': no crypto support", managed ? "managed" : "trusted", keynamestr); } else if (result == DST_R_UNSUPPORTEDALG) { cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING, "skipping %s key for '%s': %s", managed ? "managed" : "trusted", keynamestr, isc_result_totext(result)); } else { cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR, "configuring %s key for '%s': %s", managed ? "managed" : "trusted", keynamestr, isc_result_totext(result)); result = ISC_R_FAILURE; } if (dstkey != NULL) dst_key_free(&dstkey); return (result); } static isc_result_t load_view_keys(const cfg_obj_t *keys, const cfg_obj_t *vconfig, dns_view_t *view, isc_boolean_t managed, dns_name_t *keyname, isc_mem_t *mctx) { const cfg_listelt_t *elt, *elt2; const cfg_obj_t *key, *keylist; dst_key_t *dstkey = NULL; isc_result_t result; dns_keytable_t *secroots = NULL; CHECK(dns_view_getsecroots(view, &secroots)); for (elt = cfg_list_first(keys); elt != NULL; elt = cfg_list_next(elt)) { keylist = cfg_listelt_value(elt); for (elt2 = cfg_list_first(keylist); elt2 != NULL; elt2 = cfg_list_next(elt2)) { key = cfg_listelt_value(elt2); result = dstkey_fromconfig(vconfig, key, managed, &dstkey, mctx); if (result == DST_R_UNSUPPORTEDALG) { result = ISC_R_SUCCESS; continue; } if (result != ISC_R_SUCCESS) goto cleanup; /* * If keyname was specified, we only add that key. */ if (keyname != NULL && !dns_name_equal(keyname, dst_key_name(dstkey))) { dst_key_free(&dstkey); continue; } CHECK(dns_keytable_add(secroots, managed, &dstkey)); } } cleanup: if (dstkey != NULL) dst_key_free(&dstkey); if (secroots != NULL) dns_keytable_detach(&secroots); if (result == DST_R_NOCRYPTO) result = ISC_R_SUCCESS; return (result); } /*% * Configure DNSSEC keys for a view. * * The per-view configuration values and the server-global defaults are read * from 'vconfig' and 'config'. */ static isc_result_t configure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig, const cfg_obj_t *config, const cfg_obj_t *bindkeys, isc_boolean_t auto_dlv, isc_boolean_t auto_root, isc_mem_t *mctx) { isc_result_t result = ISC_R_SUCCESS; const cfg_obj_t *view_keys = NULL; const cfg_obj_t *global_keys = NULL; const cfg_obj_t *view_managed_keys = NULL; const cfg_obj_t *global_managed_keys = NULL; const cfg_obj_t *maps[4]; const cfg_obj_t *voptions = NULL; const cfg_obj_t *options = NULL; const cfg_obj_t *obj = NULL; const char *directory; int i = 0; /* We don't need trust anchors for the _bind view */ if (strcmp(view->name, "_bind") == 0 && view->rdclass == dns_rdataclass_chaos) { return (ISC_R_SUCCESS); } if (vconfig != NULL) { voptions = cfg_tuple_get(vconfig, "options"); if (voptions != NULL) { (void) cfg_map_get(voptions, "trusted-keys", &view_keys); (void) cfg_map_get(voptions, "managed-keys", &view_managed_keys); maps[i++] = voptions; } } if (config != NULL) { (void)cfg_map_get(config, "trusted-keys", &global_keys); (void)cfg_map_get(config, "managed-keys", &global_managed_keys); (void)cfg_map_get(config, "options", &options); if (options != NULL) { maps[i++] = options; } } maps[i++] = ns_g_defaults; maps[i] = NULL; result = dns_view_initsecroots(view, mctx); if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "couldn't create keytable"); return (ISC_R_UNEXPECTED); } if (auto_dlv && view->rdclass == dns_rdataclass_in) { const cfg_obj_t *builtin_keys = NULL; const cfg_obj_t *builtin_managed_keys = NULL; isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "using built-in DLV key for view %s", view->name); /* * If bind.keys exists, it overrides the managed-keys * clause hard-coded in ns_g_config. */ if (bindkeys != NULL) { (void)cfg_map_get(bindkeys, "trusted-keys", &builtin_keys); (void)cfg_map_get(bindkeys, "managed-keys", &builtin_managed_keys); } else { (void)cfg_map_get(ns_g_config, "trusted-keys", &builtin_keys); (void)cfg_map_get(ns_g_config, "managed-keys", &builtin_managed_keys); } if (builtin_keys != NULL) CHECK(load_view_keys(builtin_keys, vconfig, view, ISC_FALSE, view->dlv, mctx)); if (builtin_managed_keys != NULL) CHECK(load_view_keys(builtin_managed_keys, vconfig, view, ISC_TRUE, view->dlv, mctx)); } if (auto_root && view->rdclass == dns_rdataclass_in) { const cfg_obj_t *builtin_keys = NULL; const cfg_obj_t *builtin_managed_keys = NULL; isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "using built-in root key for view %s", view->name); /* * If bind.keys exists, it overrides the managed-keys * clause hard-coded in ns_g_config. */ if (bindkeys != NULL) { (void)cfg_map_get(bindkeys, "trusted-keys", &builtin_keys); (void)cfg_map_get(bindkeys, "managed-keys", &builtin_managed_keys); } else { (void)cfg_map_get(ns_g_config, "trusted-keys", &builtin_keys); (void)cfg_map_get(ns_g_config, "managed-keys", &builtin_managed_keys); } if (builtin_keys != NULL) CHECK(load_view_keys(builtin_keys, vconfig, view, ISC_FALSE, dns_rootname, mctx)); if (builtin_managed_keys != NULL) CHECK(load_view_keys(builtin_managed_keys, vconfig, view, ISC_TRUE, dns_rootname, mctx)); } CHECK(load_view_keys(view_keys, vconfig, view, ISC_FALSE, NULL, mctx)); CHECK(load_view_keys(view_managed_keys, vconfig, view, ISC_TRUE, NULL, mctx)); if (view->rdclass == dns_rdataclass_in) { CHECK(load_view_keys(global_keys, vconfig, view, ISC_FALSE, NULL, mctx)); CHECK(load_view_keys(global_managed_keys, vconfig, view, ISC_TRUE, NULL, mctx)); } /* * Add key zone for managed-keys. */ obj = NULL; (void)ns_config_get(maps, "managed-keys-directory", &obj); directory = (obj != NULL ? cfg_obj_asstring(obj) : NULL); if (directory != NULL) result = isc_file_isdirectory(directory); if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "invalid managed-keys-directory %s: %s", directory, isc_result_totext(result)); goto cleanup; } CHECK(add_keydata_zone(view, directory, ns_g_mctx)); cleanup: return (result); } static isc_result_t mustbesecure(const cfg_obj_t *mbs, dns_resolver_t *resolver) { const cfg_listelt_t *element; const cfg_obj_t *obj; const char *str; dns_fixedname_t fixed; dns_name_t *name; isc_boolean_t value; isc_result_t result; isc_buffer_t b; dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); for (element = cfg_list_first(mbs); element != NULL; element = cfg_list_next(element)) { obj = cfg_listelt_value(element); str = cfg_obj_asstring(cfg_tuple_get(obj, "name")); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); value = cfg_obj_asboolean(cfg_tuple_get(obj, "value")); CHECK(dns_resolver_setmustbesecure(resolver, name, value)); } result = ISC_R_SUCCESS; cleanup: return (result); } /*% * Get a dispatch appropriate for the resolver of a given view. */ static isc_result_t get_view_querysource_dispatch(const cfg_obj_t **maps, int af, dns_dispatch_t **dispatchp, isc_boolean_t is_firstview) { isc_result_t result = ISC_R_FAILURE; dns_dispatch_t *disp; isc_sockaddr_t sa; unsigned int attrs, attrmask; const cfg_obj_t *obj = NULL; unsigned int maxdispatchbuffers; switch (af) { case AF_INET: result = ns_config_get(maps, "query-source", &obj); INSIST(result == ISC_R_SUCCESS); break; case AF_INET6: result = ns_config_get(maps, "query-source-v6", &obj); INSIST(result == ISC_R_SUCCESS); break; default: INSIST(0); } sa = *(cfg_obj_assockaddr(obj)); INSIST(isc_sockaddr_pf(&sa) == af); /* * If we don't support this address family, we're done! */ switch (af) { case AF_INET: result = isc_net_probeipv4(); break; case AF_INET6: result = isc_net_probeipv6(); break; default: INSIST(0); } if (result != ISC_R_SUCCESS) return (ISC_R_SUCCESS); /* * Try to find a dispatcher that we can share. */ attrs = 0; attrs |= DNS_DISPATCHATTR_UDP; switch (af) { case AF_INET: attrs |= DNS_DISPATCHATTR_IPV4; break; case AF_INET6: attrs |= DNS_DISPATCHATTR_IPV6; break; } if (isc_sockaddr_getport(&sa) == 0) { attrs |= DNS_DISPATCHATTR_EXCLUSIVE; maxdispatchbuffers = 4096; } else { INSIST(obj != NULL); if (is_firstview) { cfg_obj_log(obj, ns_g_lctx, ISC_LOG_INFO, "using specific query-source port " "suppresses port randomization and can be " "insecure."); } maxdispatchbuffers = 1000; } attrmask = 0; attrmask |= DNS_DISPATCHATTR_UDP; attrmask |= DNS_DISPATCHATTR_TCP; attrmask |= DNS_DISPATCHATTR_IPV4; attrmask |= DNS_DISPATCHATTR_IPV6; disp = NULL; result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr, ns_g_taskmgr, &sa, 4096, maxdispatchbuffers, 32768, 16411, 16433, attrs, attrmask, &disp); if (result != ISC_R_SUCCESS) { isc_sockaddr_t any; char buf[ISC_SOCKADDR_FORMATSIZE]; switch (af) { case AF_INET: isc_sockaddr_any(&any); break; case AF_INET6: isc_sockaddr_any6(&any); break; } if (isc_sockaddr_equal(&sa, &any)) return (ISC_R_SUCCESS); isc_sockaddr_format(&sa, buf, sizeof(buf)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "could not get query source dispatcher (%s)", buf); return (result); } *dispatchp = disp; return (ISC_R_SUCCESS); } static isc_result_t configure_order(dns_order_t *order, const cfg_obj_t *ent) { dns_rdataclass_t rdclass; dns_rdatatype_t rdtype; const cfg_obj_t *obj; dns_fixedname_t fixed; unsigned int mode = 0; const char *str; isc_buffer_t b; isc_result_t result; isc_boolean_t addroot; result = ns_config_getclass(cfg_tuple_get(ent, "class"), dns_rdataclass_any, &rdclass); if (result != ISC_R_SUCCESS) return (result); result = ns_config_gettype(cfg_tuple_get(ent, "type"), dns_rdatatype_any, &rdtype); if (result != ISC_R_SUCCESS) return (result); obj = cfg_tuple_get(ent, "name"); if (cfg_obj_isstring(obj)) str = cfg_obj_asstring(obj); else str = "*"; addroot = ISC_TF(strcmp(str, "*") == 0); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); dns_fixedname_init(&fixed); result = dns_name_fromtext(dns_fixedname_name(&fixed), &b, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) return (result); obj = cfg_tuple_get(ent, "ordering"); INSIST(cfg_obj_isstring(obj)); str = cfg_obj_asstring(obj); if (!strcasecmp(str, "fixed")) mode = DNS_RDATASETATTR_FIXEDORDER; else if (!strcasecmp(str, "random")) mode = DNS_RDATASETATTR_RANDOMIZE; else if (!strcasecmp(str, "cyclic")) mode = 0; else INSIST(0); /* * "*" should match everything including the root (BIND 8 compat). * As dns_name_matcheswildcard(".", "*.") returns FALSE add a * explicit entry for "." when the name is "*". */ if (addroot) { result = dns_order_add(order, dns_rootname, rdtype, rdclass, mode); if (result != ISC_R_SUCCESS) return (result); } return (dns_order_add(order, dns_fixedname_name(&fixed), rdtype, rdclass, mode)); } static isc_result_t configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) { isc_netaddr_t na; dns_peer_t *peer; const cfg_obj_t *obj; const char *str; isc_result_t result; unsigned int prefixlen; cfg_obj_asnetprefix(cfg_map_getname(cpeer), &na, &prefixlen); peer = NULL; result = dns_peer_newprefix(mctx, &na, prefixlen, &peer); if (result != ISC_R_SUCCESS) return (result); obj = NULL; (void)cfg_map_get(cpeer, "bogus", &obj); if (obj != NULL) CHECK(dns_peer_setbogus(peer, cfg_obj_asboolean(obj))); obj = NULL; (void)cfg_map_get(cpeer, "provide-ixfr", &obj); if (obj != NULL) CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj))); obj = NULL; (void)cfg_map_get(cpeer, "request-ixfr", &obj); if (obj != NULL) CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj))); obj = NULL; (void)cfg_map_get(cpeer, "request-nsid", &obj); if (obj != NULL) CHECK(dns_peer_setrequestnsid(peer, cfg_obj_asboolean(obj))); obj = NULL; (void)cfg_map_get(cpeer, "edns", &obj); if (obj != NULL) CHECK(dns_peer_setsupportedns(peer, cfg_obj_asboolean(obj))); obj = NULL; (void)cfg_map_get(cpeer, "edns-udp-size", &obj); if (obj != NULL) { isc_uint32_t udpsize = cfg_obj_asuint32(obj); if (udpsize < 512) udpsize = 512; if (udpsize > 4096) udpsize = 4096; CHECK(dns_peer_setudpsize(peer, (isc_uint16_t)udpsize)); } obj = NULL; (void)cfg_map_get(cpeer, "max-udp-size", &obj); if (obj != NULL) { isc_uint32_t udpsize = cfg_obj_asuint32(obj); if (udpsize < 512) udpsize = 512; if (udpsize > 4096) udpsize = 4096; CHECK(dns_peer_setmaxudp(peer, (isc_uint16_t)udpsize)); } obj = NULL; (void)cfg_map_get(cpeer, "transfers", &obj); if (obj != NULL) CHECK(dns_peer_settransfers(peer, cfg_obj_asuint32(obj))); obj = NULL; (void)cfg_map_get(cpeer, "transfer-format", &obj); if (obj != NULL) { str = cfg_obj_asstring(obj); if (strcasecmp(str, "many-answers") == 0) CHECK(dns_peer_settransferformat(peer, dns_many_answers)); else if (strcasecmp(str, "one-answer") == 0) CHECK(dns_peer_settransferformat(peer, dns_one_answer)); else INSIST(0); } obj = NULL; (void)cfg_map_get(cpeer, "keys", &obj); if (obj != NULL) { result = dns_peer_setkeybycharp(peer, cfg_obj_asstring(obj)); if (result != ISC_R_SUCCESS) goto cleanup; } obj = NULL; if (na.family == AF_INET) (void)cfg_map_get(cpeer, "transfer-source", &obj); else (void)cfg_map_get(cpeer, "transfer-source-v6", &obj); if (obj != NULL) { result = dns_peer_settransfersource(peer, cfg_obj_assockaddr(obj)); if (result != ISC_R_SUCCESS) goto cleanup; ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj)); } obj = NULL; if (na.family == AF_INET) (void)cfg_map_get(cpeer, "notify-source", &obj); else (void)cfg_map_get(cpeer, "notify-source-v6", &obj); if (obj != NULL) { result = dns_peer_setnotifysource(peer, cfg_obj_assockaddr(obj)); if (result != ISC_R_SUCCESS) goto cleanup; ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj)); } obj = NULL; if (na.family == AF_INET) (void)cfg_map_get(cpeer, "query-source", &obj); else (void)cfg_map_get(cpeer, "query-source-v6", &obj); if (obj != NULL) { result = dns_peer_setquerysource(peer, cfg_obj_assockaddr(obj)); if (result != ISC_R_SUCCESS) goto cleanup; ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj)); } *peerp = peer; return (ISC_R_SUCCESS); cleanup: dns_peer_detach(&peer); return (result); } static isc_result_t disable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) { isc_result_t result; const cfg_obj_t *algorithms; const cfg_listelt_t *element; const char *str; dns_fixedname_t fixed; dns_name_t *name; isc_buffer_t b; dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); str = cfg_obj_asstring(cfg_tuple_get(disabled, "name")); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); algorithms = cfg_tuple_get(disabled, "algorithms"); for (element = cfg_list_first(algorithms); element != NULL; element = cfg_list_next(element)) { isc_textregion_t r; dns_secalg_t alg; DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base); r.length = strlen(r.base); result = dns_secalg_fromtext(&alg, &r); if (result != ISC_R_SUCCESS) { isc_uint8_t ui; result = isc_parse_uint8(&ui, r.base, 10); alg = ui; } if (result != ISC_R_SUCCESS) { cfg_obj_log(cfg_listelt_value(element), ns_g_lctx, ISC_LOG_ERROR, "invalid algorithm"); CHECK(result); } CHECK(dns_resolver_disable_algorithm(resolver, name, alg)); } cleanup: return (result); } static isc_boolean_t on_disable_list(const cfg_obj_t *disablelist, dns_name_t *zonename) { const cfg_listelt_t *element; dns_fixedname_t fixed; dns_name_t *name; isc_result_t result; const cfg_obj_t *value; const char *str; isc_buffer_t b; dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); for (element = cfg_list_first(disablelist); element != NULL; element = cfg_list_next(element)) { value = cfg_listelt_value(element); str = cfg_obj_asstring(value); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); if (dns_name_equal(name, zonename)) return (ISC_TRUE); } return (ISC_FALSE); } static isc_result_t check_dbtype(dns_zone_t *zone, unsigned int dbtypec, const char **dbargv, isc_mem_t *mctx) { char **argv = NULL; unsigned int i; isc_result_t result = ISC_R_SUCCESS; CHECK(dns_zone_getdbtype(zone, &argv, mctx)); /* * Check that all the arguments match. */ for (i = 0; i < dbtypec; i++) if (argv[i] == NULL || strcmp(argv[i], dbargv[i]) != 0) { CHECK(ISC_R_FAILURE); break; } /* * Check that there are not extra arguments. */ if (i == dbtypec && argv[i] != NULL) result = ISC_R_FAILURE; cleanup: isc_mem_free(mctx, argv); return (result); } static isc_result_t setquerystats(dns_zone_t *zone, isc_mem_t *mctx, isc_boolean_t on) { isc_result_t result; isc_stats_t *zoneqrystats; zoneqrystats = NULL; if (on) { result = isc_stats_create(mctx, &zoneqrystats, dns_nsstatscounter_max); if (result != ISC_R_SUCCESS) return (result); } dns_zone_setrequeststats(zone, zoneqrystats); if (zoneqrystats != NULL) isc_stats_detach(&zoneqrystats); return (ISC_R_SUCCESS); } static ns_cache_t * cachelist_find(ns_cachelist_t *cachelist, const char *cachename) { ns_cache_t *nsc; for (nsc = ISC_LIST_HEAD(*cachelist); nsc != NULL; nsc = ISC_LIST_NEXT(nsc, link)) { if (strcmp(dns_cache_getname(nsc->cache), cachename) == 0) return (nsc); } return (NULL); } static isc_boolean_t cache_reusable(dns_view_t *originview, dns_view_t *view, isc_boolean_t new_zero_no_soattl) { if (originview->checknames != view->checknames || dns_resolver_getzeronosoattl(originview->resolver) != new_zero_no_soattl || originview->acceptexpired != view->acceptexpired || originview->enablevalidation != view->enablevalidation || originview->maxcachettl != view->maxcachettl || originview->maxncachettl != view->maxncachettl) { return (ISC_FALSE); } return (ISC_TRUE); } static isc_boolean_t cache_sharable(dns_view_t *originview, dns_view_t *view, isc_boolean_t new_zero_no_soattl, unsigned int new_cleaning_interval, isc_uint32_t new_max_cache_size) { /* * If the cache cannot even reused for the same view, it cannot be * shared with other views. */ if (!cache_reusable(originview, view, new_zero_no_soattl)) return (ISC_FALSE); /* * Check other cache related parameters that must be consistent among * the sharing views. */ if (dns_cache_getcleaninginterval(originview->cache) != new_cleaning_interval || dns_cache_getcachesize(originview->cache) != new_max_cache_size) { return (ISC_FALSE); } return (ISC_TRUE); } /* * Callback from DLZ configure when the driver sets up a writeable zone */ static isc_result_t dlzconfigure_callback(dns_view_t *view, dns_zone_t *zone) { dns_name_t *origin = dns_zone_getorigin(zone); dns_rdataclass_t zclass = view->rdclass; isc_result_t result; result = dns_zonemgr_managezone(ns_g_server->zonemgr, zone); if (result != ISC_R_SUCCESS) return result; dns_zone_setstats(zone, ns_g_server->zonestats); return ns_zone_configure_writeable_dlz(view->dlzdatabase, zone, zclass, origin); } static isc_result_t dns64_reverse(dns_view_t *view, isc_mem_t *mctx, isc_netaddr_t *na, unsigned int prefixlen, const char *server, const char *contact) { char *cp; char reverse[48+sizeof("ip6.arpa.")]; const char *dns64_dbtype[4] = { "_dns64", "dns64", ".", "." }; const char *sep = ": view "; const char *viewname = view->name; const unsigned char *s6; dns_fixedname_t fixed; dns_name_t *name; dns_zone_t *zone = NULL; int dns64_dbtypec = 4; isc_buffer_t b; isc_result_t result; REQUIRE(prefixlen == 32 || prefixlen == 40 || prefixlen == 48 || prefixlen == 56 || prefixlen == 64 || prefixlen == 96); if (!strcmp(viewname, "_default")) { sep = ""; viewname = ""; } /* * Construct the reverse name of the zone. */ cp = reverse; s6 = na->type.in6.s6_addr; while (prefixlen > 0) { prefixlen -= 8; sprintf(cp, "%x.%x.", s6[prefixlen/8] & 0xf, (s6[prefixlen/8] >> 4) & 0xf); cp += 4; } strcat(cp, "ip6.arpa."); /* * Create the actual zone. */ if (server != NULL) dns64_dbtype[2] = server; if (contact != NULL) dns64_dbtype[3] = contact; dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); isc_buffer_constinit(&b, reverse, strlen(reverse)); isc_buffer_add(&b, strlen(reverse)); CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); CHECK(dns_zone_create(&zone, mctx)); CHECK(dns_zone_setorigin(zone, name)); dns_zone_setview(zone, view); CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone)); dns_zone_setclass(zone, view->rdclass); dns_zone_settype(zone, dns_zone_master); dns_zone_setstats(zone, ns_g_server->zonestats); CHECK(dns_zone_setdbtype(zone, dns64_dbtypec, dns64_dbtype)); if (view->queryacl != NULL) dns_zone_setqueryacl(zone, view->queryacl); if (view->queryonacl != NULL) dns_zone_setqueryonacl(zone, view->queryonacl); dns_zone_setdialup(zone, dns_dialuptype_no); dns_zone_setnotifytype(zone, dns_notifytype_no); dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE); CHECK(setquerystats(zone, mctx, ISC_FALSE)); /* XXXMPA */ CHECK(dns_view_addzone(view, zone)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dns64 reverse zone%s%s: %s", sep, viewname, reverse); cleanup: if (zone != NULL) dns_zone_detach(&zone); return (result); } static isc_result_t configure_rpz_name(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name, const char *str, const char *msg) { isc_result_t result; result = dns_name_fromstring(name, str, DNS_NAME_DOWNCASE, view->mctx); if (result != ISC_R_SUCCESS) cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "invalid %s '%s'", msg, str); return (result); } static isc_result_t configure_rpz_name2(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name, const char *str, const dns_name_t *origin) { isc_result_t result; result = dns_name_fromstring2(name, str, origin, DNS_NAME_DOWNCASE, view->mctx); if (result != ISC_R_SUCCESS) cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "invalid zone '%s'", str); return (result); } static isc_result_t configure_rpz(dns_view_t *view, const cfg_listelt_t *element, isc_boolean_t recursive_only_def, dns_ttl_t ttl_def) { const cfg_obj_t *rpz_obj, *obj; const char *str; dns_rpz_zone_t *old, *new; isc_result_t result; rpz_obj = cfg_listelt_value(element); new = isc_mem_get(view->mctx, sizeof(*new)); if (new == NULL) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "no memory for response policy zones"); return (ISC_R_NOMEMORY); } memset(new, 0, sizeof(*new)); dns_name_init(&new->origin, NULL); dns_name_init(&new->nsdname, NULL); dns_name_init(&new->passthru, NULL); dns_name_init(&new->cname, NULL); ISC_LIST_INITANDAPPEND(view->rpz_zones, new, link); obj = cfg_tuple_get(rpz_obj, "recursive-only"); if (cfg_obj_isvoid(obj)) { new->recursive_only = recursive_only_def; } else { new->recursive_only = cfg_obj_asboolean(obj); } if (!new->recursive_only) view->rpz_recursive_only = ISC_FALSE; obj = cfg_tuple_get(rpz_obj, "max-policy-ttl"); if (cfg_obj_isuint32(obj)) { new->max_policy_ttl = cfg_obj_asuint32(obj); } else { new->max_policy_ttl = ttl_def; } str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "zone name")); result = configure_rpz_name(view, rpz_obj, &new->origin, str, "zone"); if (result != ISC_R_SUCCESS) return (result); if (dns_name_equal(&new->origin, dns_rootname)) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "invalid zone name '%s'", str); return (DNS_R_EMPTYLABEL); } for (old = ISC_LIST_HEAD(view->rpz_zones); old != new; old = ISC_LIST_NEXT(old, link)) { ++new->num; if (dns_name_equal(&old->origin, &new->origin)) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "duplicate '%s'", str); result = DNS_R_DUPLICATE; return (result); } } result = configure_rpz_name2(view, rpz_obj, &new->nsdname, DNS_RPZ_NSDNAME_ZONE, &new->origin); if (result != ISC_R_SUCCESS) return (result); result = configure_rpz_name(view, rpz_obj, &new->passthru, DNS_RPZ_PASSTHRU_ZONE, "zone"); if (result != ISC_R_SUCCESS) return (result); obj = cfg_tuple_get(rpz_obj, "policy"); if (cfg_obj_isvoid(obj)) { new->policy = DNS_RPZ_POLICY_GIVEN; } else { str = cfg_obj_asstring(cfg_tuple_get(obj, "policy name")); new->policy = dns_rpz_str2policy(str); INSIST(new->policy != DNS_RPZ_POLICY_ERROR); if (new->policy == DNS_RPZ_POLICY_CNAME) { str = cfg_obj_asstring(cfg_tuple_get(obj, "cname")); result = configure_rpz_name(view, rpz_obj, &new->cname, str, "cname"); if (result != ISC_R_SUCCESS) return (result); } } return (ISC_R_SUCCESS); } static isc_result_t add_soa(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, dns_name_t *origin, dns_name_t *contact) { dns_dbnode_t *node = NULL; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdatalist_t rdatalist; dns_rdataset_t rdataset; isc_result_t result; unsigned char buf[DNS_SOA_BUFFERSIZE]; dns_rdataset_init(&rdataset); dns_rdatalist_init(&rdatalist); CHECK(dns_soa_buildrdata(origin, contact, dns_db_class(db), 0, 28800, 7200, 604800, 86400, buf, &rdata)); rdatalist.type = rdata.type; rdatalist.covers = 0; rdatalist.rdclass = rdata.rdclass; rdatalist.ttl = 86400; ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset)); CHECK(dns_db_findnode(db, name, ISC_TRUE, &node)); CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL)); cleanup: if (node != NULL) dns_db_detachnode(db, &node); return (result); } static isc_result_t add_ns(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, dns_name_t *nsname) { dns_dbnode_t *node = NULL; dns_rdata_ns_t ns; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdatalist_t rdatalist; dns_rdataset_t rdataset; isc_result_t result; isc_buffer_t b; unsigned char buf[DNS_NAME_MAXWIRE]; isc_buffer_init(&b, buf, sizeof(buf)); dns_rdataset_init(&rdataset); dns_rdatalist_init(&rdatalist); ns.common.rdtype = dns_rdatatype_ns; ns.common.rdclass = dns_db_class(db); ns.mctx = NULL; dns_name_init(&ns.name, NULL); dns_name_clone(nsname, &ns.name); CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db), dns_rdatatype_ns, &ns, &b)); rdatalist.type = rdata.type; rdatalist.covers = 0; rdatalist.rdclass = rdata.rdclass; rdatalist.ttl = 86400; ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset)); CHECK(dns_db_findnode(db, name, ISC_TRUE, &node)); CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL)); cleanup: if (node != NULL) dns_db_detachnode(db, &node); return (result); } static isc_result_t create_empty_zone(dns_zone_t *zone, dns_name_t *name, dns_view_t *view, const cfg_obj_t *zonelist, const char **empty_dbtype, int empty_dbtypec, isc_boolean_t zonestats_on) { char namebuf[DNS_NAME_FORMATSIZE]; const cfg_listelt_t *element; const cfg_obj_t *obj; const cfg_obj_t *zconfig; const cfg_obj_t *zoptions; const char *rbt_dbtype[4] = { "rbt" }; const char *sep = ": view "; const char *str; const char *viewname = view->name; dns_db_t *db = NULL; dns_dbversion_t *version = NULL; dns_fixedname_t cfixed; dns_fixedname_t fixed; dns_fixedname_t nsfixed; dns_name_t *contact; dns_name_t *ns; dns_name_t *zname; dns_zone_t *myzone = NULL; int rbt_dbtypec = 1; isc_result_t result; dns_namereln_t namereln; int order; unsigned int nlabels; dns_fixedname_init(&fixed); zname = dns_fixedname_name(&fixed); dns_fixedname_init(&nsfixed); ns = dns_fixedname_name(&nsfixed); dns_fixedname_init(&cfixed); contact = dns_fixedname_name(&cfixed); /* * Look for forward "zones" beneath this empty zone and if so * create a custom db for the empty zone. */ for (element = cfg_list_first(zonelist); element != NULL; element = cfg_list_next(element)) { zconfig = cfg_listelt_value(element); str = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); CHECK(dns_name_fromstring(zname, str, 0, NULL)); namereln = dns_name_fullcompare(zname, name, &order, &nlabels); if (namereln != dns_namereln_subdomain) continue; zoptions = cfg_tuple_get(zconfig, "options"); obj = NULL; (void)cfg_map_get(zoptions, "type", &obj); INSIST(obj != NULL); if (strcasecmp(cfg_obj_asstring(obj), "forward") == 0) { obj = NULL; (void)cfg_map_get(zoptions, "forward", &obj); if (obj == NULL) continue; if (strcasecmp(cfg_obj_asstring(obj), "only") != 0) continue; } if (db == NULL) { CHECK(dns_db_create(view->mctx, "rbt", name, dns_dbtype_zone, view->rdclass, 0, NULL, &db)); CHECK(dns_db_newversion(db, &version)); if (strcmp(empty_dbtype[2], "@") == 0) dns_name_clone(name, ns); else CHECK(dns_name_fromstring(ns, empty_dbtype[2], 0, NULL)); CHECK(dns_name_fromstring(contact, empty_dbtype[3], 0, NULL)); CHECK(add_soa(db, version, name, ns, contact)); CHECK(add_ns(db, version, name, ns)); } CHECK(add_ns(db, version, zname, dns_rootname)); } /* * Is the existing zone the ok to use? */ if (zone != NULL) { unsigned int typec; const char **dbargv; if (db != NULL) { typec = rbt_dbtypec; dbargv = rbt_dbtype; } else { typec = empty_dbtypec; dbargv = empty_dbtype; } result = check_dbtype(zone, typec, dbargv, view->mctx); if (result != ISC_R_SUCCESS) zone = NULL; if (zone != NULL && dns_zone_gettype(zone) != dns_zone_master) zone = NULL; if (zone != NULL && dns_zone_getfile(zone) != NULL) zone = NULL; } if (zone == NULL) { CHECK(dns_zone_create(&myzone, view->mctx)); zone = myzone; CHECK(dns_zone_setorigin(zone, name)); CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone)); if (db == NULL) CHECK(dns_zone_setdbtype(zone, empty_dbtypec, empty_dbtype)); dns_zone_setclass(zone, view->rdclass); dns_zone_settype(zone, dns_zone_master); dns_zone_setstats(zone, ns_g_server->zonestats); } dns_zone_setoption(zone, ~DNS_ZONEOPT_NOCHECKNS, ISC_FALSE); dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE); dns_zone_setnotifytype(zone, dns_notifytype_no); dns_zone_setdialup(zone, dns_dialuptype_no); if (view->queryacl) dns_zone_setqueryacl(zone, view->queryacl); else dns_zone_clearqueryacl(zone); if (view->queryonacl) dns_zone_setqueryonacl(zone, view->queryonacl); else dns_zone_clearqueryonacl(zone); dns_zone_clearupdateacl(zone); dns_zone_clearxfracl(zone); CHECK(setquerystats(zone, view->mctx, zonestats_on)); if (db != NULL) { dns_db_closeversion(db, &version, ISC_TRUE); CHECK(dns_zone_replacedb(zone, db, ISC_FALSE)); } dns_zone_setview(zone, view); CHECK(dns_view_addzone(view, zone)); if (!strcmp(viewname, "_default")) { sep = ""; viewname = ""; } dns_name_format(name, namebuf, sizeof(namebuf)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "automatic empty zone%s%s: %s", sep, viewname, namebuf); cleanup: if (myzone != NULL) dns_zone_detach(&myzone); if (version != NULL) dns_db_closeversion(db, &version, ISC_FALSE); if (db != NULL) dns_db_detach(&db); return (result); } /* * Configure 'view' according to 'vconfig', taking defaults from 'config' * where values are missing in 'vconfig'. * * When configuring the default view, 'vconfig' will be NULL and the * global defaults in 'config' used exclusively. */ static isc_result_t configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, ns_cachelist_t *cachelist, const cfg_obj_t *bindkeys, isc_mem_t *mctx, cfg_aclconfctx_t *actx, isc_boolean_t need_hints) { const cfg_obj_t *maps[4]; const cfg_obj_t *cfgmaps[3]; const cfg_obj_t *optionmaps[3]; const cfg_obj_t *options = NULL; const cfg_obj_t *voptions = NULL; const cfg_obj_t *forwardtype; const cfg_obj_t *forwarders; const cfg_obj_t *alternates; const cfg_obj_t *zonelist; const cfg_obj_t *dlz; unsigned int dlzargc; char **dlzargv; const cfg_obj_t *disabled; const cfg_obj_t *obj; const cfg_listelt_t *element; in_port_t port; dns_cache_t *cache = NULL; isc_result_t result; isc_uint32_t max_adb_size; unsigned int cleaning_interval; isc_uint32_t max_cache_size; isc_uint32_t max_acache_size; isc_uint32_t lame_ttl; dns_tsig_keyring_t *ring = NULL; dns_view_t *pview = NULL; /* Production view */ isc_mem_t *cmctx = NULL, *hmctx = NULL; dns_dispatch_t *dispatch4 = NULL; dns_dispatch_t *dispatch6 = NULL; isc_boolean_t reused_cache = ISC_FALSE; isc_boolean_t shared_cache = ISC_FALSE; int i = 0, j = 0, k = 0; const char *str; const char *cachename = NULL; dns_order_t *order = NULL; isc_uint32_t udpsize; unsigned int resopts = 0; dns_zone_t *zone = NULL; isc_uint32_t max_clients_per_query; const char *sep = ": view "; const char *viewname = view->name; isc_boolean_t rfc1918; isc_boolean_t empty_zones_enable; const cfg_obj_t *disablelist = NULL; isc_stats_t *resstats = NULL; dns_stats_t *resquerystats = NULL; isc_boolean_t auto_dlv = ISC_FALSE; isc_boolean_t auto_root = ISC_FALSE; ns_cache_t *nsc; isc_boolean_t zero_no_soattl; dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL; unsigned int query_timeout; struct cfg_context *nzctx; dns_rpz_zone_t *rpz; REQUIRE(DNS_VIEW_VALID(view)); if (config != NULL) (void)cfg_map_get(config, "options", &options); /* * maps: view options, options, defaults * cfgmaps: view options, config * optionmaps: view options, options */ if (vconfig != NULL) { voptions = cfg_tuple_get(vconfig, "options"); maps[i++] = voptions; optionmaps[j++] = voptions; cfgmaps[k++] = voptions; } if (options != NULL) { maps[i++] = options; optionmaps[j++] = options; } maps[i++] = ns_g_defaults; maps[i] = NULL; optionmaps[j] = NULL; if (config != NULL) cfgmaps[k++] = config; cfgmaps[k] = NULL; if (!strcmp(viewname, "_default")) { sep = ""; viewname = ""; } /* * Set the view's port number for outgoing queries. */ CHECKM(ns_config_getport(config, &port), "port"); dns_view_setdstport(view, port); /* * Create additional cache for this view and zones under the view * if explicitly enabled. * XXX950 default to on. */ obj = NULL; (void)ns_config_get(maps, "acache-enable", &obj); if (obj != NULL && cfg_obj_asboolean(obj)) { cmctx = NULL; CHECK(isc_mem_create(0, 0, &cmctx)); CHECK(dns_acache_create(&view->acache, cmctx, ns_g_taskmgr, ns_g_timermgr)); isc_mem_setname(cmctx, "acache", NULL); isc_mem_detach(&cmctx); } if (view->acache != NULL) { obj = NULL; result = ns_config_get(maps, "acache-cleaning-interval", &obj); INSIST(result == ISC_R_SUCCESS); dns_acache_setcleaninginterval(view->acache, cfg_obj_asuint32(obj) * 60); obj = NULL; result = ns_config_get(maps, "max-acache-size", &obj); INSIST(result == ISC_R_SUCCESS); if (cfg_obj_isstring(obj)) { str = cfg_obj_asstring(obj); INSIST(strcasecmp(str, "unlimited") == 0); max_acache_size = ISC_UINT32_MAX; } else { isc_resourcevalue_t value; value = cfg_obj_asuint64(obj); if (value > ISC_UINT32_MAX) { cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, "'max-acache-size " "%" ISC_PRINT_QUADFORMAT "d' is too large", value); result = ISC_R_RANGE; goto cleanup; } max_acache_size = (isc_uint32_t)value; } dns_acache_setcachesize(view->acache, max_acache_size); } CHECK(configure_view_acl(vconfig, config, "allow-query", NULL, actx, ns_g_mctx, &view->queryacl)); if (view->queryacl == NULL) { CHECK(configure_view_acl(NULL, ns_g_config, "allow-query", NULL, actx, ns_g_mctx, &view->queryacl)); } /* * Make the list of response policy zone names for a view that * is used for real lookups and so cares about hints. */ obj = NULL; if (view->rdclass == dns_rdataclass_in && need_hints && ns_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) { const cfg_obj_t *rpz_obj; isc_boolean_t recursive_only_def; dns_ttl_t ttl_def; rpz_obj = cfg_tuple_get(obj, "recursive-only"); if (!cfg_obj_isvoid(rpz_obj) && !cfg_obj_asboolean(rpz_obj)) recursive_only_def = ISC_FALSE; else recursive_only_def = ISC_TRUE; rpz_obj = cfg_tuple_get(obj, "break-dnssec"); if (!cfg_obj_isvoid(rpz_obj) && cfg_obj_asboolean(rpz_obj)) view->rpz_break_dnssec = ISC_TRUE; else view->rpz_break_dnssec = ISC_FALSE; rpz_obj = cfg_tuple_get(obj, "max-policy-ttl"); if (cfg_obj_isuint32(rpz_obj)) ttl_def = cfg_obj_asuint32(rpz_obj); else ttl_def = DNS_RPZ_MAX_TTL_DEFAULT; rpz_obj = cfg_tuple_get(obj, "min-ns-dots"); if (cfg_obj_isuint32(rpz_obj)) view->rpz_min_ns_labels = cfg_obj_asuint32(rpz_obj) + 1; else view->rpz_min_ns_labels = 2; element = cfg_list_first(cfg_tuple_get(obj, "zone list")); while (element != NULL) { result = configure_rpz(view, element, recursive_only_def, ttl_def); if (result != ISC_R_SUCCESS) goto cleanup; element = cfg_list_next(element); } } /* * Configure the zones. */ zonelist = NULL; if (voptions != NULL) (void)cfg_map_get(voptions, "zone", &zonelist); else (void)cfg_map_get(config, "zone", &zonelist); /* * Load zone configuration */ for (element = cfg_list_first(zonelist); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *zconfig = cfg_listelt_value(element); CHECK(configure_zone(config, zconfig, vconfig, mctx, view, actx, ISC_FALSE)); } for (rpz = ISC_LIST_HEAD(view->rpz_zones); rpz != NULL; rpz = ISC_LIST_NEXT(rpz, link)) { if (!rpz->defined) { char namebuf[DNS_NAME_FORMATSIZE]; dns_name_format(&rpz->origin, namebuf, sizeof(namebuf)); cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "'%s' is not a master or slave zone", namebuf); result = ISC_R_NOTFOUND; goto cleanup; } } /* * If we're allowing added zones, then load zone configuration * from the newzone file for zones that were added during previous * runs. */ nzctx = view->new_zone_config; if (nzctx != NULL && nzctx->nzconfig != NULL) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "loading additional zones for view '%s'", view->name); zonelist = NULL; cfg_map_get(nzctx->nzconfig, "zone", &zonelist); for (element = cfg_list_first(zonelist); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *zconfig = cfg_listelt_value(element); CHECK(configure_zone(config, zconfig, vconfig, mctx, view, actx, ISC_TRUE)); } } /* * Create Dynamically Loadable Zone driver. */ dlz = NULL; if (voptions != NULL) (void)cfg_map_get(voptions, "dlz", &dlz); else (void)cfg_map_get(config, "dlz", &dlz); obj = NULL; if (dlz != NULL) { (void)cfg_map_get(cfg_tuple_get(dlz, "options"), "database", &obj); if (obj != NULL) { char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj)); if (s == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } result = dns_dlzstrtoargv(mctx, s, &dlzargc, &dlzargv); if (result != ISC_R_SUCCESS) { isc_mem_free(mctx, s); goto cleanup; } obj = cfg_tuple_get(dlz, "name"); result = dns_dlzcreate(mctx, cfg_obj_asstring(obj), dlzargv[0], dlzargc, dlzargv, &view->dlzdatabase); isc_mem_free(mctx, s); isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv)); if (result != ISC_R_SUCCESS) goto cleanup; /* * If the dlz backend supports configuration, * then call its configure method now. */ result = dns_dlzconfigure(view, dlzconfigure_callback); if (result != ISC_R_SUCCESS) goto cleanup; } } /* * Obtain configuration parameters that affect the decision of whether * we can reuse/share an existing cache. */ obj = NULL; result = ns_config_get(maps, "cleaning-interval", &obj); INSIST(result == ISC_R_SUCCESS); cleaning_interval = cfg_obj_asuint32(obj) * 60; obj = NULL; result = ns_config_get(maps, "max-cache-size", &obj); INSIST(result == ISC_R_SUCCESS); if (cfg_obj_isstring(obj)) { str = cfg_obj_asstring(obj); INSIST(strcasecmp(str, "unlimited") == 0); max_cache_size = ISC_UINT32_MAX; } else { isc_resourcevalue_t value; value = cfg_obj_asuint64(obj); if (value > ISC_UINT32_MAX) { cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, "'max-cache-size " "%" ISC_PRINT_QUADFORMAT "d' is too large", value); result = ISC_R_RANGE; goto cleanup; } max_cache_size = (isc_uint32_t)value; } /* Check-names. */ obj = NULL; result = ns_checknames_get(maps, "response", &obj); INSIST(result == ISC_R_SUCCESS); str = cfg_obj_asstring(obj); if (strcasecmp(str, "fail") == 0) { resopts |= DNS_RESOLVER_CHECKNAMES | DNS_RESOLVER_CHECKNAMESFAIL; view->checknames = ISC_TRUE; } else if (strcasecmp(str, "warn") == 0) { resopts |= DNS_RESOLVER_CHECKNAMES; view->checknames = ISC_FALSE; } else if (strcasecmp(str, "ignore") == 0) { view->checknames = ISC_FALSE; } else INSIST(0); obj = NULL; result = ns_config_get(maps, "zero-no-soa-ttl-cache", &obj); INSIST(result == ISC_R_SUCCESS); zero_no_soattl = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "dns64", &obj); if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") && strcmp(view->name, "_meta")) { const cfg_listelt_t *element; isc_netaddr_t na, suffix, *sp; unsigned int prefixlen; const char *server, *contact; const cfg_obj_t *myobj; myobj = NULL; result = ns_config_get(maps, "dns64-server", &myobj); if (result == ISC_R_SUCCESS) server = cfg_obj_asstring(myobj); else server = NULL; myobj = NULL; result = ns_config_get(maps, "dns64-contact", &myobj); if (result == ISC_R_SUCCESS) contact = cfg_obj_asstring(myobj); else contact = NULL; for (element = cfg_list_first(obj); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *map = cfg_listelt_value(element); dns_dns64_t *dns64 = NULL; unsigned int dns64options = 0; cfg_obj_asnetprefix(cfg_map_getname(map), &na, &prefixlen); obj = NULL; (void)cfg_map_get(map, "suffix", &obj); if (obj != NULL) { sp = &suffix; isc_netaddr_fromsockaddr(sp, cfg_obj_assockaddr(obj)); } else sp = NULL; clients = mapped = excluded = NULL; obj = NULL; (void)cfg_map_get(map, "clients", &obj); if (obj != NULL) { result = cfg_acl_fromconfig(obj, config, ns_g_lctx, actx, mctx, 0, &clients); if (result != ISC_R_SUCCESS) goto cleanup; } obj = NULL; (void)cfg_map_get(map, "mapped", &obj); if (obj != NULL) { result = cfg_acl_fromconfig(obj, config, ns_g_lctx, actx, mctx, 0, &mapped); if (result != ISC_R_SUCCESS) goto cleanup; } obj = NULL; (void)cfg_map_get(map, "exclude", &obj); if (obj != NULL) { result = cfg_acl_fromconfig(obj, config, ns_g_lctx, actx, mctx, 0, &excluded); if (result != ISC_R_SUCCESS) goto cleanup; } obj = NULL; (void)cfg_map_get(map, "recursive-only", &obj); if (obj != NULL && cfg_obj_asboolean(obj)) dns64options |= DNS_DNS64_RECURSIVE_ONLY; obj = NULL; (void)cfg_map_get(map, "break-dnssec", &obj); if (obj != NULL && cfg_obj_asboolean(obj)) dns64options |= DNS_DNS64_BREAK_DNSSEC; result = dns_dns64_create(mctx, &na, prefixlen, sp, clients, mapped, excluded, dns64options, &dns64); if (result != ISC_R_SUCCESS) goto cleanup; dns_dns64_append(&view->dns64, dns64); view->dns64cnt++; result = dns64_reverse(view, mctx, &na, prefixlen, server, contact); if (result != ISC_R_SUCCESS) goto cleanup; if (clients != NULL) dns_acl_detach(&clients); if (mapped != NULL) dns_acl_detach(&mapped); if (excluded != NULL) dns_acl_detach(&excluded); } } obj = NULL; result = ns_config_get(maps, "dnssec-accept-expired", &obj); INSIST(result == ISC_R_SUCCESS); view->acceptexpired = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "dnssec-validation", &obj); INSIST(result == ISC_R_SUCCESS); if (cfg_obj_isboolean(obj)) { view->enablevalidation = cfg_obj_asboolean(obj); } else { /* If dnssec-validation is not boolean, it must be "auto" */ view->enablevalidation = ISC_TRUE; auto_root = ISC_TRUE; } obj = NULL; result = ns_config_get(maps, "max-cache-ttl", &obj); INSIST(result == ISC_R_SUCCESS); view->maxcachettl = cfg_obj_asuint32(obj); obj = NULL; result = ns_config_get(maps, "max-ncache-ttl", &obj); INSIST(result == ISC_R_SUCCESS); view->maxncachettl = cfg_obj_asuint32(obj); if (view->maxncachettl > 7 * 24 * 3600) view->maxncachettl = 7 * 24 * 3600; /* * Configure the view's cache. * * First, check to see if there are any attach-cache options. If yes, * attempt to lookup an existing cache at attach it to the view. If * there is not one, then try to reuse an existing cache if possible; * otherwise create a new cache. * * Note that the ADB is not preserved or shared in either case. * * When a matching view is found, the associated statistics are also * retrieved and reused. * * XXX Determining when it is safe to reuse or share a cache is tricky. * When the view's configuration changes, the cached data may become * invalid because it reflects our old view of the world. We check * some of the configuration parameters that could invalidate the cache * or otherwise make it unsharable, but there are other configuration * options that should be checked. For example, if a view uses a * forwarder, changes in the forwarder configuration may invalidate * the cache. At the moment, it's the administrator's responsibility to * ensure these configuration options don't invalidate reusing/sharing. */ obj = NULL; result = ns_config_get(maps, "attach-cache", &obj); if (result == ISC_R_SUCCESS) cachename = cfg_obj_asstring(obj); else cachename = view->name; cache = NULL; nsc = cachelist_find(cachelist, cachename); if (nsc != NULL) { if (!cache_sharable(nsc->primaryview, view, zero_no_soattl, cleaning_interval, max_cache_size)) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "views %s and %s can't share the cache " "due to configuration parameter mismatch", nsc->primaryview->name, view->name); result = ISC_R_FAILURE; goto cleanup; } dns_cache_attach(nsc->cache, &cache); shared_cache = ISC_TRUE; } else { if (strcmp(cachename, view->name) == 0) { result = dns_viewlist_find(&ns_g_server->viewlist, cachename, view->rdclass, &pview); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) goto cleanup; if (pview != NULL) { if (!cache_reusable(pview, view, zero_no_soattl)) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), "cache cannot be reused " "for view %s due to " "configuration parameter " "mismatch", view->name); } else { INSIST(pview->cache != NULL); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(3), "reusing existing cache"); reused_cache = ISC_TRUE; dns_cache_attach(pview->cache, &cache); } dns_view_getresstats(pview, &resstats); dns_view_getresquerystats(pview, &resquerystats); dns_view_detach(&pview); } } if (cache == NULL) { /* * Create a cache with the desired name. This normally * equals the view name, but may also be a forward * reference to a view that share the cache with this * view but is not yet configured. If it is not the * view name but not a forward reference either, then it * is simply a named cache that is not shared. * * We use two separate memory contexts for the * cache, for the main cache memory and the heap * memory. */ CHECK(isc_mem_create(0, 0, &cmctx)); isc_mem_setname(cmctx, "cache", NULL); CHECK(isc_mem_create(0, 0, &hmctx)); isc_mem_setname(hmctx, "cache_heap", NULL); CHECK(dns_cache_create3(cmctx, hmctx, ns_g_taskmgr, ns_g_timermgr, view->rdclass, cachename, "rbt", 0, NULL, &cache)); isc_mem_detach(&cmctx); isc_mem_detach(&hmctx); } nsc = isc_mem_get(mctx, sizeof(*nsc)); if (nsc == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } nsc->cache = NULL; dns_cache_attach(cache, &nsc->cache); nsc->primaryview = view; nsc->needflush = ISC_FALSE; nsc->adbsizeadjusted = ISC_FALSE; ISC_LINK_INIT(nsc, link); ISC_LIST_APPEND(*cachelist, nsc, link); } dns_view_setcache2(view, cache, shared_cache); /* * cache-file cannot be inherited if views are present, but this * should be caught by the configuration checking stage. */ obj = NULL; result = ns_config_get(maps, "cache-file", &obj); if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) { CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj))); if (!reused_cache && !shared_cache) CHECK(dns_cache_load(cache)); } dns_cache_setcleaninginterval(cache, cleaning_interval); dns_cache_setcachesize(cache, max_cache_size); dns_cache_detach(&cache); /* * Resolver. * * XXXRTH Hardwired number of tasks. */ CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4, ISC_TF(ISC_LIST_PREV(view, link) == NULL))); CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6, ISC_TF(ISC_LIST_PREV(view, link) == NULL))); if (dispatch4 == NULL && dispatch6 == NULL) { UNEXPECTED_ERROR(__FILE__, __LINE__, "unable to obtain neither an IPv4 nor" " an IPv6 dispatch"); result = ISC_R_UNEXPECTED; goto cleanup; } CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31, ns_g_socketmgr, ns_g_timermgr, resopts, ns_g_dispatchmgr, dispatch4, dispatch6)); if (resstats == NULL) { CHECK(isc_stats_create(mctx, &resstats, dns_resstatscounter_max)); } dns_view_setresstats(view, resstats); if (resquerystats == NULL) CHECK(dns_rdatatypestats_create(mctx, &resquerystats)); dns_view_setresquerystats(view, resquerystats); /* * Set the ADB cache size to 1/8th of the max-cache-size or * MAX_ADB_SIZE_FOR_CACHESHARE when the cache is shared. */ max_adb_size = 0; if (max_cache_size != 0U) { max_adb_size = max_cache_size / 8; if (max_adb_size == 0U) max_adb_size = 1; /* Force minimum. */ if (view != nsc->primaryview && max_adb_size > MAX_ADB_SIZE_FOR_CACHESHARE) { max_adb_size = MAX_ADB_SIZE_FOR_CACHESHARE; if (!nsc->adbsizeadjusted) { dns_adb_setadbsize(nsc->primaryview->adb, MAX_ADB_SIZE_FOR_CACHESHARE); nsc->adbsizeadjusted = ISC_TRUE; } } } dns_adb_setadbsize(view->adb, max_adb_size); /* * Set resolver's lame-ttl. */ obj = NULL; result = ns_config_get(maps, "lame-ttl", &obj); INSIST(result == ISC_R_SUCCESS); lame_ttl = cfg_obj_asuint32(obj); if (lame_ttl > 1800) lame_ttl = 1800; dns_resolver_setlamettl(view->resolver, lame_ttl); /* * Set the resolver's query timeout. */ obj = NULL; result = ns_config_get(maps, "resolver-query-timeout", &obj); INSIST(result == ISC_R_SUCCESS); query_timeout = cfg_obj_asuint32(obj); dns_resolver_settimeout(view->resolver, query_timeout); /* Specify whether to use 0-TTL for negative response for SOA query */ dns_resolver_setzeronosoattl(view->resolver, zero_no_soattl); /* * Set the resolver's EDNS UDP size. */ obj = NULL; result = ns_config_get(maps, "edns-udp-size", &obj); INSIST(result == ISC_R_SUCCESS); udpsize = cfg_obj_asuint32(obj); if (udpsize < 512) udpsize = 512; if (udpsize > 4096) udpsize = 4096; dns_resolver_setudpsize(view->resolver, (isc_uint16_t)udpsize); /* * Set the maximum UDP response size. */ obj = NULL; result = ns_config_get(maps, "max-udp-size", &obj); INSIST(result == ISC_R_SUCCESS); udpsize = cfg_obj_asuint32(obj); if (udpsize < 512) udpsize = 512; if (udpsize > 4096) udpsize = 4096; view->maxudp = udpsize; /* * Set supported DNSSEC algorithms. */ dns_resolver_reset_algorithms(view->resolver); disabled = NULL; (void)ns_config_get(maps, "disable-algorithms", &disabled); if (disabled != NULL) { for (element = cfg_list_first(disabled); element != NULL; element = cfg_list_next(element)) CHECK(disable_algorithms(cfg_listelt_value(element), view->resolver)); } /* * A global or view "forwarders" option, if present, * creates an entry for "." in the forwarding table. */ forwardtype = NULL; forwarders = NULL; (void)ns_config_get(maps, "forward", &forwardtype); (void)ns_config_get(maps, "forwarders", &forwarders); if (forwarders != NULL) CHECK(configure_forward(config, view, dns_rootname, forwarders, forwardtype)); /* * Dual Stack Servers. */ alternates = NULL; (void)ns_config_get(maps, "dual-stack-servers", &alternates); if (alternates != NULL) CHECK(configure_alternates(config, view, alternates)); /* * We have default hints for class IN if we need them. */ if (view->rdclass == dns_rdataclass_in && view->hints == NULL) dns_view_sethints(view, ns_g_server->in_roothints); /* * If we still have no hints, this is a non-IN view with no * "hints zone" configured. Issue a warning, except if this * is a root server. Root servers never need to consult * their hints, so it's no point requiring users to configure * them. */ if (view->hints == NULL) { dns_zone_t *rootzone = NULL; (void)dns_view_findzone(view, dns_rootname, &rootzone); if (rootzone != NULL) { dns_zone_detach(&rootzone); need_hints = ISC_FALSE; } if (need_hints) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "no root hints for view '%s'", view->name); } /* * Configure the view's TSIG keys. */ CHECK(ns_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring)); if (ns_g_server->sessionkey != NULL) { CHECK(dns_tsigkeyring_add(ring, ns_g_server->session_keyname, ns_g_server->sessionkey)); } dns_view_setkeyring(view, ring); dns_tsigkeyring_detach(&ring); /* * See if we can re-use a dynamic key ring. */ result = dns_viewlist_find(&ns_g_server->viewlist, view->name, view->rdclass, &pview); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) goto cleanup; if (pview != NULL) { dns_view_getdynamickeyring(pview, &ring); if (ring != NULL) dns_view_setdynamickeyring(view, ring); dns_tsigkeyring_detach(&ring); dns_view_detach(&pview); } else dns_view_restorekeyring(view); /* * Configure the view's peer list. */ { const cfg_obj_t *peers = NULL; const cfg_listelt_t *element; dns_peerlist_t *newpeers = NULL; (void)ns_config_get(cfgmaps, "server", &peers); CHECK(dns_peerlist_new(mctx, &newpeers)); for (element = cfg_list_first(peers); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *cpeer = cfg_listelt_value(element); dns_peer_t *peer; CHECK(configure_peer(cpeer, mctx, &peer)); dns_peerlist_addpeer(newpeers, peer); dns_peer_detach(&peer); } dns_peerlist_detach(&view->peers); view->peers = newpeers; /* Transfer ownership. */ } /* * Configure the views rrset-order. */ { const cfg_obj_t *rrsetorder = NULL; const cfg_listelt_t *element; (void)ns_config_get(maps, "rrset-order", &rrsetorder); CHECK(dns_order_create(mctx, &order)); for (element = cfg_list_first(rrsetorder); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *ent = cfg_listelt_value(element); CHECK(configure_order(order, ent)); } if (view->order != NULL) dns_order_detach(&view->order); dns_order_attach(order, &view->order); dns_order_detach(&order); } /* * Copy the aclenv object. */ dns_aclenv_copy(&view->aclenv, &ns_g_server->aclenv); /* * Configure the "match-clients" and "match-destinations" ACL. */ CHECK(configure_view_acl(vconfig, config, "match-clients", NULL, actx, ns_g_mctx, &view->matchclients)); CHECK(configure_view_acl(vconfig, config, "match-destinations", NULL, actx, ns_g_mctx, &view->matchdestinations)); /* * Configure the "match-recursive-only" option. */ obj = NULL; (void)ns_config_get(maps, "match-recursive-only", &obj); if (obj != NULL && cfg_obj_asboolean(obj)) view->matchrecursiveonly = ISC_TRUE; else view->matchrecursiveonly = ISC_FALSE; /* * Configure other configurable data. */ obj = NULL; result = ns_config_get(maps, "recursion", &obj); INSIST(result == ISC_R_SUCCESS); view->recursion = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "auth-nxdomain", &obj); INSIST(result == ISC_R_SUCCESS); view->auth_nxdomain = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "minimal-responses", &obj); INSIST(result == ISC_R_SUCCESS); view->minimalresponses = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "transfer-format", &obj); INSIST(result == ISC_R_SUCCESS); str = cfg_obj_asstring(obj); if (strcasecmp(str, "many-answers") == 0) view->transfer_format = dns_many_answers; else if (strcasecmp(str, "one-answer") == 0) view->transfer_format = dns_one_answer; else INSIST(0); /* * Set sources where additional data and CNAME/DNAME * targets for authoritative answers may be found. */ obj = NULL; result = ns_config_get(maps, "additional-from-auth", &obj); INSIST(result == ISC_R_SUCCESS); view->additionalfromauth = cfg_obj_asboolean(obj); if (view->recursion && ! view->additionalfromauth) { cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING, "'additional-from-auth no' is only supported " "with 'recursion no'"); view->additionalfromauth = ISC_TRUE; } obj = NULL; result = ns_config_get(maps, "additional-from-cache", &obj); INSIST(result == ISC_R_SUCCESS); view->additionalfromcache = cfg_obj_asboolean(obj); if (view->recursion && ! view->additionalfromcache) { cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING, "'additional-from-cache no' is only supported " "with 'recursion no'"); view->additionalfromcache = ISC_TRUE; } /* * Set "allow-query-cache", "allow-query-cache-on", * "allow-recursion", and "allow-recursion-on" acls if * configured in named.conf. */ CHECK(configure_view_acl(vconfig, config, "allow-query-cache", NULL, actx, ns_g_mctx, &view->cacheacl)); CHECK(configure_view_acl(vconfig, config, "allow-query-cache-on", NULL, actx, ns_g_mctx, &view->cacheonacl)); if (view->cacheonacl == NULL) CHECK(configure_view_acl(NULL, ns_g_config, "allow-query-cache-on", NULL, actx, ns_g_mctx, &view->cacheonacl)); if (strcmp(view->name, "_bind") != 0) { CHECK(configure_view_acl(vconfig, config, "allow-recursion", NULL, actx, ns_g_mctx, &view->recursionacl)); CHECK(configure_view_acl(vconfig, config, "allow-recursion-on", NULL, actx, ns_g_mctx, &view->recursiononacl)); } /* * "allow-query-cache" inherits from "allow-recursion" if set, * otherwise from "allow-query" if set. * "allow-recursion" inherits from "allow-query-cache" if set, * otherwise from "allow-query" if set. */ if (view->cacheacl == NULL && view->recursionacl != NULL) dns_acl_attach(view->recursionacl, &view->cacheacl); /* * XXXEACH: This call to configure_view_acl() is redundant. We * are leaving it as it is because we are making a minimal change * for a patch release. In the future this should be changed to * dns_acl_attach(view->queryacl, &view->cacheacl). */ if (view->cacheacl == NULL && view->recursion) CHECK(configure_view_acl(vconfig, config, "allow-query", NULL, actx, ns_g_mctx, &view->cacheacl)); if (view->recursion && view->recursionacl == NULL && view->cacheacl != NULL) dns_acl_attach(view->cacheacl, &view->recursionacl); /* * Set default "allow-recursion", "allow-recursion-on" and * "allow-query-cache" acls. */ if (view->recursionacl == NULL && view->recursion) CHECK(configure_view_acl(NULL, ns_g_config, "allow-recursion", NULL, actx, ns_g_mctx, &view->recursionacl)); if (view->recursiononacl == NULL && view->recursion) CHECK(configure_view_acl(NULL, ns_g_config, "allow-recursion-on", NULL, actx, ns_g_mctx, &view->recursiononacl)); if (view->cacheacl == NULL) { if (view->recursion) CHECK(configure_view_acl(NULL, ns_g_config, "allow-query-cache", NULL, actx, ns_g_mctx, &view->cacheacl)); else CHECK(dns_acl_none(mctx, &view->cacheacl)); } /* * Filter setting on addresses in the answer section. */ CHECK(configure_view_acl(vconfig, config, "deny-answer-addresses", "acl", actx, ns_g_mctx, &view->denyansweracl)); CHECK(configure_view_nametable(vconfig, config, "deny-answer-addresses", "except-from", ns_g_mctx, &view->answeracl_exclude)); /* * Filter setting on names (CNAME/DNAME targets) in the answer section. */ CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases", "name", ns_g_mctx, &view->denyanswernames)); CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases", "except-from", ns_g_mctx, &view->answernames_exclude)); /* * Configure sortlist, if set */ CHECK(configure_view_sortlist(vconfig, config, actx, ns_g_mctx, &view->sortlist)); /* * Configure default allow-transfer, allow-notify, allow-update * and allow-update-forwarding ACLs, if set, so they can be * inherited by zones. */ if (view->notifyacl == NULL) CHECK(configure_view_acl(NULL, ns_g_config, "allow-notify", NULL, actx, ns_g_mctx, &view->notifyacl)); if (view->transferacl == NULL) CHECK(configure_view_acl(NULL, ns_g_config, "allow-transfer", NULL, actx, ns_g_mctx, &view->transferacl)); if (view->updateacl == NULL) CHECK(configure_view_acl(NULL, ns_g_config, "allow-update", NULL, actx, ns_g_mctx, &view->updateacl)); if (view->upfwdacl == NULL) CHECK(configure_view_acl(NULL, ns_g_config, "allow-update-forwarding", NULL, actx, ns_g_mctx, &view->upfwdacl)); obj = NULL; result = ns_config_get(maps, "request-ixfr", &obj); INSIST(result == ISC_R_SUCCESS); view->requestixfr = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "provide-ixfr", &obj); INSIST(result == ISC_R_SUCCESS); view->provideixfr = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "request-nsid", &obj); INSIST(result == ISC_R_SUCCESS); view->requestnsid = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "max-clients-per-query", &obj); INSIST(result == ISC_R_SUCCESS); max_clients_per_query = cfg_obj_asuint32(obj); obj = NULL; result = ns_config_get(maps, "clients-per-query", &obj); INSIST(result == ISC_R_SUCCESS); dns_resolver_setclientsperquery(view->resolver, cfg_obj_asuint32(obj), max_clients_per_query); + obj = NULL; + result = ns_config_get(maps, "max-recursion-depth", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_resolver_setmaxdepth(view->resolver, cfg_obj_asuint32(obj)); + + obj = NULL; + result = ns_config_get(maps, "max-recursion-queries", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_resolver_setmaxqueries(view->resolver, cfg_obj_asuint32(obj)); + #ifdef ALLOW_FILTER_AAAA_ON_V4 obj = NULL; result = ns_config_get(maps, "filter-aaaa-on-v4", &obj); INSIST(result == ISC_R_SUCCESS); if (cfg_obj_isboolean(obj)) { if (cfg_obj_asboolean(obj)) view->v4_aaaa = dns_v4_aaaa_filter; else view->v4_aaaa = dns_v4_aaaa_ok; } else { const char *v4_aaaastr = cfg_obj_asstring(obj); if (strcasecmp(v4_aaaastr, "break-dnssec") == 0) view->v4_aaaa = dns_v4_aaaa_break_dnssec; else INSIST(0); } CHECK(configure_view_acl(vconfig, config, "filter-aaaa", NULL, actx, ns_g_mctx, &view->v4_aaaa_acl)); #endif obj = NULL; result = ns_config_get(maps, "dnssec-enable", &obj); INSIST(result == ISC_R_SUCCESS); view->enablednssec = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(optionmaps, "dnssec-lookaside", &obj); if (result == ISC_R_SUCCESS) { /* If set to "auto", use the version from the defaults */ const cfg_obj_t *dlvobj; const char *dom; dlvobj = cfg_listelt_value(cfg_list_first(obj)); dom = cfg_obj_asstring(cfg_tuple_get(dlvobj, "domain")); if (cfg_obj_isvoid(cfg_tuple_get(dlvobj, "trust-anchor"))) { /* If "no", skip; if "auto", use global default */ if (!strcasecmp(dom, "no")) result = ISC_R_NOTFOUND; else if (!strcasecmp(dom, "auto")) { auto_dlv = ISC_TRUE; obj = NULL; result = cfg_map_get(ns_g_defaults, "dnssec-lookaside", &obj); } } } if (result == ISC_R_SUCCESS) { for (element = cfg_list_first(obj); element != NULL; element = cfg_list_next(element)) { const char *str; isc_buffer_t b; dns_name_t *dlv; obj = cfg_listelt_value(element); str = cfg_obj_asstring(cfg_tuple_get(obj, "trust-anchor")); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); dlv = dns_fixedname_name(&view->dlv_fixed); CHECK(dns_name_fromtext(dlv, &b, dns_rootname, DNS_NAME_DOWNCASE, NULL)); view->dlv = dns_fixedname_name(&view->dlv_fixed); } } else view->dlv = NULL; /* * For now, there is only one kind of trusted keys, the * "security roots". */ CHECK(configure_view_dnsseckeys(view, vconfig, config, bindkeys, auto_dlv, auto_root, mctx)); dns_resolver_resetmustbesecure(view->resolver); obj = NULL; result = ns_config_get(maps, "dnssec-must-be-secure", &obj); if (result == ISC_R_SUCCESS) CHECK(mustbesecure(obj, view->resolver)); obj = NULL; result = ns_config_get(maps, "preferred-glue", &obj); if (result == ISC_R_SUCCESS) { str = cfg_obj_asstring(obj); if (strcasecmp(str, "a") == 0) view->preferred_glue = dns_rdatatype_a; else if (strcasecmp(str, "aaaa") == 0) view->preferred_glue = dns_rdatatype_aaaa; else view->preferred_glue = 0; } else view->preferred_glue = 0; obj = NULL; result = ns_config_get(maps, "root-delegation-only", &obj); if (result == ISC_R_SUCCESS) { dns_view_setrootdelonly(view, ISC_TRUE); if (!cfg_obj_isvoid(obj)) { dns_fixedname_t fixed; dns_name_t *name; isc_buffer_t b; const char *str; const cfg_obj_t *exclude; dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); for (element = cfg_list_first(obj); element != NULL; element = cfg_list_next(element)) { exclude = cfg_listelt_value(element); str = cfg_obj_asstring(exclude); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); CHECK(dns_view_excludedelegationonly(view, name)); } } } else dns_view_setrootdelonly(view, ISC_FALSE); /* * Setup automatic empty zones. If recursion is off then * they are disabled by default. */ obj = NULL; (void)ns_config_get(maps, "empty-zones-enable", &obj); (void)ns_config_get(maps, "disable-empty-zone", &disablelist); if (obj == NULL && disablelist == NULL && view->rdclass == dns_rdataclass_in) { rfc1918 = ISC_FALSE; empty_zones_enable = view->recursion; } else if (view->rdclass == dns_rdataclass_in) { rfc1918 = ISC_TRUE; if (obj != NULL) empty_zones_enable = cfg_obj_asboolean(obj); else empty_zones_enable = view->recursion; } else { rfc1918 = ISC_FALSE; empty_zones_enable = ISC_FALSE; } if (empty_zones_enable && !lwresd_g_useresolvconf) { const char *empty; int empty_zone = 0; dns_fixedname_t fixed; dns_name_t *name; isc_buffer_t buffer; const char *str; char server[DNS_NAME_FORMATSIZE + 1]; char contact[DNS_NAME_FORMATSIZE + 1]; isc_boolean_t logit; const char *empty_dbtype[4] = { "_builtin", "empty", NULL, NULL }; int empty_dbtypec = 4; isc_boolean_t zonestats_on; dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); obj = NULL; result = ns_config_get(maps, "empty-server", &obj); if (result == ISC_R_SUCCESS) { str = cfg_obj_asstring(obj); isc_buffer_constinit(&buffer, str, strlen(str)); isc_buffer_add(&buffer, strlen(str)); CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL)); isc_buffer_init(&buffer, server, sizeof(server) - 1); CHECK(dns_name_totext(name, ISC_FALSE, &buffer)); server[isc_buffer_usedlength(&buffer)] = 0; empty_dbtype[2] = server; } else empty_dbtype[2] = "@"; obj = NULL; result = ns_config_get(maps, "empty-contact", &obj); if (result == ISC_R_SUCCESS) { str = cfg_obj_asstring(obj); isc_buffer_constinit(&buffer, str, strlen(str)); isc_buffer_add(&buffer, strlen(str)); CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL)); isc_buffer_init(&buffer, contact, sizeof(contact) - 1); CHECK(dns_name_totext(name, ISC_FALSE, &buffer)); contact[isc_buffer_usedlength(&buffer)] = 0; empty_dbtype[3] = contact; } else empty_dbtype[3] = "."; obj = NULL; result = ns_config_get(maps, "zone-statistics", &obj); INSIST(result == ISC_R_SUCCESS); zonestats_on = cfg_obj_asboolean(obj); logit = ISC_TRUE; for (empty = empty_zones[empty_zone].zone; empty != NULL; empty = empty_zones[++empty_zone].zone) { dns_forwarders_t *forwarders = NULL; dns_view_t *pview = NULL; isc_buffer_constinit(&buffer, empty, strlen(empty)); isc_buffer_add(&buffer, strlen(empty)); /* * Look for zone on drop list. */ CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL)); if (disablelist != NULL && on_disable_list(disablelist, name)) continue; /* * This zone already exists. */ (void)dns_view_findzone(view, name, &zone); if (zone != NULL) { dns_zone_detach(&zone); continue; } /* * If we would forward this name don't add a * empty zone for it. */ result = dns_fwdtable_find(view->fwdtable, name, &forwarders); if (result == ISC_R_SUCCESS && forwarders->fwdpolicy == dns_fwdpolicy_only) continue; if (!rfc1918 && empty_zones[empty_zone].rfc1918) { if (logit) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "Warning%s%s: " "'empty-zones-enable/" "disable-empty-zone' " "not set: disabling " "RFC 1918 empty zones", sep, viewname); logit = ISC_FALSE; } continue; } /* * See if we can re-use a existing zone. */ result = dns_viewlist_find(&ns_g_server->viewlist, view->name, view->rdclass, &pview); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) goto cleanup; if (pview != NULL) { (void)dns_view_findzone(pview, name, &zone); dns_view_detach(&pview); } CHECK(create_empty_zone(zone, name, view, zonelist, empty_dbtype, empty_dbtypec, zonestats_on)); if (zone != NULL) dns_zone_detach(&zone); } } result = ISC_R_SUCCESS; cleanup: if (clients != NULL) dns_acl_detach(&clients); if (mapped != NULL) dns_acl_detach(&mapped); if (excluded != NULL) dns_acl_detach(&excluded); if (ring != NULL) dns_tsigkeyring_detach(&ring); if (zone != NULL) dns_zone_detach(&zone); if (dispatch4 != NULL) dns_dispatch_detach(&dispatch4); if (dispatch6 != NULL) dns_dispatch_detach(&dispatch6); if (resstats != NULL) isc_stats_detach(&resstats); if (resquerystats != NULL) dns_stats_detach(&resquerystats); if (order != NULL) dns_order_detach(&order); if (cmctx != NULL) isc_mem_detach(&cmctx); if (hmctx != NULL) isc_mem_detach(&hmctx); if (cache != NULL) dns_cache_detach(&cache); return (result); } static isc_result_t configure_hints(dns_view_t *view, const char *filename) { isc_result_t result; dns_db_t *db; db = NULL; result = dns_rootns_create(view->mctx, view->rdclass, filename, &db); if (result == ISC_R_SUCCESS) { dns_view_sethints(view, db); dns_db_detach(&db); } return (result); } static isc_result_t configure_alternates(const cfg_obj_t *config, dns_view_t *view, const cfg_obj_t *alternates) { const cfg_obj_t *portobj; const cfg_obj_t *addresses; const cfg_listelt_t *element; isc_result_t result = ISC_R_SUCCESS; in_port_t port; /* * Determine which port to send requests to. */ if (ns_g_lwresdonly && ns_g_port != 0) port = ns_g_port; else CHECKM(ns_config_getport(config, &port), "port"); if (alternates != NULL) { portobj = cfg_tuple_get(alternates, "port"); if (cfg_obj_isuint32(portobj)) { isc_uint32_t val = cfg_obj_asuint32(portobj); if (val > ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port '%u' out of range", val); return (ISC_R_RANGE); } port = (in_port_t) val; } } addresses = NULL; if (alternates != NULL) addresses = cfg_tuple_get(alternates, "addresses"); for (element = cfg_list_first(addresses); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *alternate = cfg_listelt_value(element); isc_sockaddr_t sa; if (!cfg_obj_issockaddr(alternate)) { dns_fixedname_t fixed; dns_name_t *name; const char *str = cfg_obj_asstring(cfg_tuple_get( alternate, "name")); isc_buffer_t buffer; in_port_t myport = port; isc_buffer_constinit(&buffer, str, strlen(str)); isc_buffer_add(&buffer, strlen(str)); dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL)); portobj = cfg_tuple_get(alternate, "port"); if (cfg_obj_isuint32(portobj)) { isc_uint32_t val = cfg_obj_asuint32(portobj); if (val > ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port '%u' out of range", val); return (ISC_R_RANGE); } myport = (in_port_t) val; } CHECK(dns_resolver_addalternate(view->resolver, NULL, name, myport)); continue; } sa = *cfg_obj_assockaddr(alternate); if (isc_sockaddr_getport(&sa) == 0) isc_sockaddr_setport(&sa, port); CHECK(dns_resolver_addalternate(view->resolver, &sa, NULL, 0)); } cleanup: return (result); } static isc_result_t configure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin, const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype) { const cfg_obj_t *portobj; const cfg_obj_t *faddresses; const cfg_listelt_t *element; dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none; isc_sockaddrlist_t addresses; isc_sockaddr_t *sa; isc_result_t result; in_port_t port; ISC_LIST_INIT(addresses); /* * Determine which port to send forwarded requests to. */ if (ns_g_lwresdonly && ns_g_port != 0) port = ns_g_port; else CHECKM(ns_config_getport(config, &port), "port"); if (forwarders != NULL) { portobj = cfg_tuple_get(forwarders, "port"); if (cfg_obj_isuint32(portobj)) { isc_uint32_t val = cfg_obj_asuint32(portobj); if (val > ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port '%u' out of range", val); return (ISC_R_RANGE); } port = (in_port_t) val; } } faddresses = NULL; if (forwarders != NULL) faddresses = cfg_tuple_get(forwarders, "addresses"); for (element = cfg_list_first(faddresses); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *forwarder = cfg_listelt_value(element); sa = isc_mem_get(view->mctx, sizeof(isc_sockaddr_t)); if (sa == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } *sa = *cfg_obj_assockaddr(forwarder); if (isc_sockaddr_getport(sa) == 0) isc_sockaddr_setport(sa, port); ISC_LINK_INIT(sa, link); ISC_LIST_APPEND(addresses, sa, link); } if (ISC_LIST_EMPTY(addresses)) { if (forwardtype != NULL) cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING, "no forwarders seen; disabling " "forwarding"); fwdpolicy = dns_fwdpolicy_none; } else { if (forwardtype == NULL) fwdpolicy = dns_fwdpolicy_first; else { const char *forwardstr = cfg_obj_asstring(forwardtype); if (strcasecmp(forwardstr, "first") == 0) fwdpolicy = dns_fwdpolicy_first; else if (strcasecmp(forwardstr, "only") == 0) fwdpolicy = dns_fwdpolicy_only; else INSIST(0); } } result = dns_fwdtable_add(view->fwdtable, origin, &addresses, fwdpolicy); if (result != ISC_R_SUCCESS) { char namebuf[DNS_NAME_FORMATSIZE]; dns_name_format(origin, namebuf, sizeof(namebuf)); cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING, "could not set up forwarding for domain '%s': %s", namebuf, isc_result_totext(result)); goto cleanup; } result = ISC_R_SUCCESS; cleanup: while (!ISC_LIST_EMPTY(addresses)) { sa = ISC_LIST_HEAD(addresses); ISC_LIST_UNLINK(addresses, sa, link); isc_mem_put(view->mctx, sa, sizeof(isc_sockaddr_t)); } return (result); } static isc_result_t get_viewinfo(const cfg_obj_t *vconfig, const char **namep, dns_rdataclass_t *classp) { isc_result_t result = ISC_R_SUCCESS; const char *viewname; dns_rdataclass_t viewclass; REQUIRE(namep != NULL && *namep == NULL); REQUIRE(classp != NULL); if (vconfig != NULL) { const cfg_obj_t *classobj = NULL; viewname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name")); classobj = cfg_tuple_get(vconfig, "class"); result = ns_config_getclass(classobj, dns_rdataclass_in, &viewclass); } else { viewname = "_default"; viewclass = dns_rdataclass_in; } *namep = viewname; *classp = viewclass; return (result); } /* * Find a view based on its configuration info and attach to it. * * If 'vconfig' is NULL, attach to the default view. */ static isc_result_t find_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist, dns_view_t **viewp) { isc_result_t result; const char *viewname = NULL; dns_rdataclass_t viewclass; dns_view_t *view = NULL; result = get_viewinfo(vconfig, &viewname, &viewclass); if (result != ISC_R_SUCCESS) return (result); result = dns_viewlist_find(viewlist, viewname, viewclass, &view); if (result != ISC_R_SUCCESS) return (result); *viewp = view; return (ISC_R_SUCCESS); } /* * Create a new view and add it to the list. * * If 'vconfig' is NULL, create the default view. * * The view created is attached to '*viewp'. */ static isc_result_t create_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist, dns_view_t **viewp) { isc_result_t result; const char *viewname = NULL; dns_rdataclass_t viewclass; dns_view_t *view = NULL; result = get_viewinfo(vconfig, &viewname, &viewclass); if (result != ISC_R_SUCCESS) return (result); result = dns_viewlist_find(viewlist, viewname, viewclass, &view); if (result == ISC_R_SUCCESS) return (ISC_R_EXISTS); if (result != ISC_R_NOTFOUND) return (result); INSIST(view == NULL); result = dns_view_create(ns_g_mctx, viewclass, viewname, &view); if (result != ISC_R_SUCCESS) return (result); ISC_LIST_APPEND(*viewlist, view, link); dns_view_attach(view, viewp); return (ISC_R_SUCCESS); } /* * Configure or reconfigure a zone. */ static isc_result_t configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view, cfg_aclconfctx_t *aclconf, isc_boolean_t added) { dns_view_t *pview = NULL; /* Production view */ dns_zone_t *zone = NULL; /* New or reused zone */ dns_zone_t *dupzone = NULL; const cfg_obj_t *options = NULL; const cfg_obj_t *zoptions = NULL; const cfg_obj_t *typeobj = NULL; const cfg_obj_t *forwarders = NULL; const cfg_obj_t *forwardtype = NULL; const cfg_obj_t *only = NULL; isc_result_t result; isc_result_t tresult; isc_buffer_t buffer; dns_fixedname_t fixorigin; dns_name_t *origin; const char *zname; dns_rdataclass_t zclass; const char *ztypestr; isc_boolean_t is_rpz; dns_rpz_zone_t *rpz; options = NULL; (void)cfg_map_get(config, "options", &options); zoptions = cfg_tuple_get(zconfig, "options"); /* * Get the zone origin as a dns_name_t. */ zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); isc_buffer_constinit(&buffer, zname, strlen(zname)); isc_buffer_add(&buffer, strlen(zname)); dns_fixedname_init(&fixorigin); CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin), &buffer, dns_rootname, 0, NULL)); origin = dns_fixedname_name(&fixorigin); CHECK(ns_config_getclass(cfg_tuple_get(zconfig, "class"), view->rdclass, &zclass)); if (zclass != view->rdclass) { const char *vname = NULL; if (vconfig != NULL) vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name")); else vname = ""; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "zone '%s': wrong class for view '%s'", zname, vname); result = ISC_R_FAILURE; goto cleanup; } (void)cfg_map_get(zoptions, "type", &typeobj); if (typeobj == NULL) { cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR, "zone '%s' 'type' not specified", zname); return (ISC_R_FAILURE); } ztypestr = cfg_obj_asstring(typeobj); /* * "hints zones" aren't zones. If we've got one, * configure it and return. */ if (strcasecmp(ztypestr, "hint") == 0) { const cfg_obj_t *fileobj = NULL; if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "zone '%s': 'file' not specified", zname); result = ISC_R_FAILURE; goto cleanup; } if (dns_name_equal(origin, dns_rootname)) { const char *hintsfile = cfg_obj_asstring(fileobj); result = configure_hints(view, hintsfile); if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "could not configure root hints " "from '%s': %s", hintsfile, isc_result_totext(result)); goto cleanup; } /* * Hint zones may also refer to delegation only points. */ only = NULL; tresult = cfg_map_get(zoptions, "delegation-only", &only); if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only)) CHECK(dns_view_adddelegationonly(view, origin)); } else { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "ignoring non-root hint zone '%s'", zname); result = ISC_R_SUCCESS; } /* Skip ordinary zone processing. */ goto cleanup; } /* * "forward zones" aren't zones either. Translate this syntax into * the appropriate selective forwarding configuration and return. */ if (strcasecmp(ztypestr, "forward") == 0) { forwardtype = NULL; forwarders = NULL; (void)cfg_map_get(zoptions, "forward", &forwardtype); (void)cfg_map_get(zoptions, "forwarders", &forwarders); result = configure_forward(config, view, origin, forwarders, forwardtype); goto cleanup; } /* * "delegation-only zones" aren't zones either. */ if (strcasecmp(ztypestr, "delegation-only") == 0) { result = dns_view_adddelegationonly(view, origin); goto cleanup; } /* * Check for duplicates in the new zone table. */ result = dns_view_findzone(view, origin, &dupzone); if (result == ISC_R_SUCCESS) { /* * We already have this zone! */ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR, "zone '%s' already exists", zname); dns_zone_detach(&dupzone); result = ISC_R_EXISTS; goto cleanup; } INSIST(dupzone == NULL); /* * Note whether this is a response policy zone. */ is_rpz = ISC_FALSE; for (rpz = ISC_LIST_HEAD(view->rpz_zones); rpz != NULL; rpz = ISC_LIST_NEXT(rpz, link)) { if (dns_name_equal(&rpz->origin, origin)) { is_rpz = ISC_TRUE; rpz->defined = ISC_TRUE; break; } } /* * See if we can reuse an existing zone. This is * only possible if all of these are true: * - The zone's view exists * - A zone with the right name exists in the view * - The zone is compatible with the config * options (e.g., an existing master zone cannot * be reused if the options specify a slave zone) * - The zone was and is or was not and is not a policy zone */ result = dns_viewlist_find(&ns_g_server->viewlist, view->name, view->rdclass, &pview); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) goto cleanup; if (pview != NULL) result = dns_view_findzone(pview, origin, &zone); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) goto cleanup; if (zone != NULL && !ns_zone_reusable(zone, zconfig)) dns_zone_detach(&zone); if (zone != NULL && is_rpz != dns_zone_get_rpz(zone)) dns_zone_detach(&zone); if (zone != NULL) { /* * We found a reusable zone. Make it use the * new view. */ dns_zone_setview(zone, view); if (view->acache != NULL) dns_zone_setacache(zone, view->acache); } else { /* * We cannot reuse an existing zone, we have * to create a new one. */ CHECK(dns_zone_create(&zone, mctx)); CHECK(dns_zone_setorigin(zone, origin)); dns_zone_setview(zone, view); if (view->acache != NULL) dns_zone_setacache(zone, view->acache); CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone)); dns_zone_setstats(zone, ns_g_server->zonestats); } if (is_rpz) { result = dns_zone_rpz_enable(zone); if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "zone '%s': incompatible" " masterfile-format or database" " for a response policy zone", zname); goto cleanup; } } /* * If the zone contains a 'forwarders' statement, configure * selective forwarding. */ forwarders = NULL; if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS) { forwardtype = NULL; (void)cfg_map_get(zoptions, "forward", &forwardtype); CHECK(configure_forward(config, view, origin, forwarders, forwardtype)); } /* * Stub and forward zones may also refer to delegation only points. */ only = NULL; if (cfg_map_get(zoptions, "delegation-only", &only) == ISC_R_SUCCESS) { if (cfg_obj_asboolean(only)) CHECK(dns_view_adddelegationonly(view, origin)); } /* * Mark whether the zone was originally added at runtime or not */ dns_zone_setadded(zone, added); /* * Configure the zone. */ CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone)); /* * Add the zone to its view in the new view list. */ CHECK(dns_view_addzone(view, zone)); /* * Ensure that zone keys are reloaded on reconfig */ if ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0) dns_zone_rekey(zone, ISC_FALSE); cleanup: if (zone != NULL) dns_zone_detach(&zone); if (pview != NULL) dns_view_detach(&pview); return (result); } /* * Configure built-in zone for storing managed-key data. */ #define KEYZONE "managed-keys.bind" #define MKEYS ".mkeys" static isc_result_t add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) { isc_result_t result; dns_view_t *pview = NULL; dns_zone_t *zone = NULL; dns_acl_t *none = NULL; char filename[PATH_MAX]; char buffer[ISC_SHA256_DIGESTSTRINGLENGTH + sizeof(MKEYS)]; int n; REQUIRE(view != NULL); /* See if we can re-use an existing keydata zone. */ result = dns_viewlist_find(&ns_g_server->viewlist, view->name, view->rdclass, &pview); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) return (result); if (pview != NULL && pview->managed_keys != NULL) { dns_zone_attach(pview->managed_keys, &view->managed_keys); dns_zone_setview(pview->managed_keys, view); dns_view_detach(&pview); dns_zone_synckeyzone(view->managed_keys); return (ISC_R_SUCCESS); } /* No existing keydata zone was found; create one */ CHECK(dns_zone_create(&zone, mctx)); CHECK(dns_zone_setorigin(zone, dns_rootname)); isc_sha256_data((void *)view->name, strlen(view->name), buffer); strcat(buffer, MKEYS); n = snprintf(filename, sizeof(filename), "%s%s%s", directory ? directory : "", directory ? "/" : "", strcmp(view->name, "_default") == 0 ? KEYZONE : buffer); if (n < 0 || (size_t)n >= sizeof(filename)) { result = (n < 0) ? ISC_R_FAILURE : ISC_R_NOSPACE; goto cleanup; } CHECK(dns_zone_setfile(zone, filename)); dns_zone_setview(zone, view); dns_zone_settype(zone, dns_zone_key); dns_zone_setclass(zone, view->rdclass); CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone)); if (view->acache != NULL) dns_zone_setacache(zone, view->acache); CHECK(dns_acl_none(mctx, &none)); dns_zone_setqueryacl(zone, none); dns_zone_setqueryonacl(zone, none); dns_acl_detach(&none); dns_zone_setdialup(zone, dns_dialuptype_no); dns_zone_setnotifytype(zone, dns_notifytype_no); dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE); dns_zone_setjournalsize(zone, 0); dns_zone_setstats(zone, ns_g_server->zonestats); CHECK(setquerystats(zone, mctx, ISC_FALSE)); if (view->managed_keys != NULL) dns_zone_detach(&view->managed_keys); dns_zone_attach(zone, &view->managed_keys); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "set up managed keys zone for view %s, file '%s'", view->name, filename); cleanup: if (zone != NULL) dns_zone_detach(&zone); if (none != NULL) dns_acl_detach(&none); return (result); } /* * Configure a single server quota. */ static void configure_server_quota(const cfg_obj_t **maps, const char *name, isc_quota_t *quota) { const cfg_obj_t *obj = NULL; isc_result_t result; result = ns_config_get(maps, name, &obj); INSIST(result == ISC_R_SUCCESS); isc_quota_max(quota, cfg_obj_asuint32(obj)); } /* * This function is called as soon as the 'directory' statement has been * parsed. This can be extended to support other options if necessary. */ static isc_result_t directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) { isc_result_t result; const char *directory; REQUIRE(strcasecmp("directory", clausename) == 0); UNUSED(arg); UNUSED(clausename); /* * Change directory. */ directory = cfg_obj_asstring(obj); if (! isc_file_ischdiridempotent(directory)) cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING, "option 'directory' contains relative path '%s'", directory); result = isc_dir_chdir(directory); if (result != ISC_R_SUCCESS) { cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, "change directory to '%s' failed: %s", directory, isc_result_totext(result)); return (result); } return (ISC_R_SUCCESS); } static void scan_interfaces(ns_server_t *server, isc_boolean_t verbose) { isc_boolean_t match_mapped = server->aclenv.match_mapped; ns_interfacemgr_scan(server->interfacemgr, verbose); /* * Update the "localhost" and "localnets" ACLs to match the * current set of network interfaces. */ dns_aclenv_copy(&server->aclenv, ns_interfacemgr_getaclenv(server->interfacemgr)); server->aclenv.match_mapped = match_mapped; } static isc_result_t add_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr, isc_boolean_t wcardport_ok) { ns_listenelt_t *lelt = NULL; dns_acl_t *src_acl = NULL; isc_result_t result; isc_sockaddr_t any_sa6; isc_netaddr_t netaddr; REQUIRE(isc_sockaddr_pf(addr) == AF_INET6); isc_sockaddr_any6(&any_sa6); if (!isc_sockaddr_equal(&any_sa6, addr) && (wcardport_ok || isc_sockaddr_getport(addr) != 0)) { isc_netaddr_fromin6(&netaddr, &addr->type.sin6.sin6_addr); result = dns_acl_create(mctx, 0, &src_acl); if (result != ISC_R_SUCCESS) return (result); result = dns_iptable_addprefix(src_acl->iptable, &netaddr, 128, ISC_TRUE); if (result != ISC_R_SUCCESS) goto clean; result = ns_listenelt_create(mctx, isc_sockaddr_getport(addr), src_acl, &lelt); if (result != ISC_R_SUCCESS) goto clean; ISC_LIST_APPEND(list->elts, lelt, link); } return (ISC_R_SUCCESS); clean: INSIST(lelt == NULL); dns_acl_detach(&src_acl); return (result); } /* * Make a list of xxx-source addresses and call ns_interfacemgr_adjust() * to update the listening interfaces accordingly. * We currently only consider IPv6, because this only affects IPv6 wildcard * sockets. */ static void adjust_interfaces(ns_server_t *server, isc_mem_t *mctx) { isc_result_t result; ns_listenlist_t *list = NULL; dns_view_t *view; dns_zone_t *zone, *next; isc_sockaddr_t addr, *addrp; result = ns_listenlist_create(mctx, &list); if (result != ISC_R_SUCCESS) return; for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { dns_dispatch_t *dispatch6; dispatch6 = dns_resolver_dispatchv6(view->resolver); if (dispatch6 == NULL) continue; result = dns_dispatch_getlocaladdress(dispatch6, &addr); if (result != ISC_R_SUCCESS) goto fail; /* * We always add non-wildcard address regardless of whether * the port is 'any' (the fourth arg is TRUE): if the port is * specific, we need to add it since it may conflict with a * listening interface; if it's zero, we'll dynamically open * query ports, and some of them may override an existing * wildcard IPv6 port. */ result = add_listenelt(mctx, list, &addr, ISC_TRUE); if (result != ISC_R_SUCCESS) goto fail; } zone = NULL; for (result = dns_zone_first(server->zonemgr, &zone); result == ISC_R_SUCCESS; next = NULL, result = dns_zone_next(zone, &next), zone = next) { dns_view_t *zoneview; /* * At this point the zone list may contain a stale zone * just removed from the configuration. To see the validity, * check if the corresponding view is in our current view list. * There may also be old zones that are still in the process * of shutting down and have detached from their old view * (zoneview == NULL). */ zoneview = dns_zone_getview(zone); if (zoneview == NULL) continue; for (view = ISC_LIST_HEAD(server->viewlist); view != NULL && view != zoneview; view = ISC_LIST_NEXT(view, link)) ; if (view == NULL) continue; addrp = dns_zone_getnotifysrc6(zone); result = add_listenelt(mctx, list, addrp, ISC_FALSE); if (result != ISC_R_SUCCESS) goto fail; addrp = dns_zone_getxfrsource6(zone); result = add_listenelt(mctx, list, addrp, ISC_FALSE); if (result != ISC_R_SUCCESS) goto fail; } ns_interfacemgr_adjust(server->interfacemgr, list, ISC_TRUE); clean: ns_listenlist_detach(&list); return; fail: /* * Even when we failed the procedure, most of other interfaces * should work correctly. We therefore just warn it. */ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "could not adjust the listen-on list; " "some interfaces may not work"); goto clean; } /* * This event callback is invoked to do periodic network * interface scanning. */ static void interface_timer_tick(isc_task_t *task, isc_event_t *event) { isc_result_t result; ns_server_t *server = (ns_server_t *) event->ev_arg; INSIST(task == server->task); UNUSED(task); isc_event_free(&event); /* * XXX should scan interfaces unlocked and get exclusive access * only to replace ACLs. */ result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); scan_interfaces(server, ISC_FALSE); isc_task_endexclusive(server->task); } static void heartbeat_timer_tick(isc_task_t *task, isc_event_t *event) { ns_server_t *server = (ns_server_t *) event->ev_arg; dns_view_t *view; UNUSED(task); isc_event_free(&event); view = ISC_LIST_HEAD(server->viewlist); while (view != NULL) { dns_view_dialup(view); view = ISC_LIST_NEXT(view, link); } } static void pps_timer_tick(isc_task_t *task, isc_event_t *event) { static unsigned int oldrequests = 0; unsigned int requests = ns_client_requests; UNUSED(task); isc_event_free(&event); /* * Don't worry about wrapping as the overflow result will be right. */ dns_pps = (requests - oldrequests) / 1200; oldrequests = requests; } /* * Replace the current value of '*field', a dynamically allocated * string or NULL, with a dynamically allocated copy of the * null-terminated string pointed to by 'value', or NULL. */ static isc_result_t setstring(ns_server_t *server, char **field, const char *value) { char *copy; if (value != NULL) { copy = isc_mem_strdup(server->mctx, value); if (copy == NULL) return (ISC_R_NOMEMORY); } else { copy = NULL; } if (*field != NULL) isc_mem_free(server->mctx, *field); *field = copy; return (ISC_R_SUCCESS); } /* * Replace the current value of '*field', a dynamically allocated * string or NULL, with another dynamically allocated string * or NULL if whether 'obj' is a string or void value, respectively. */ static isc_result_t setoptstring(ns_server_t *server, char **field, const cfg_obj_t *obj) { if (cfg_obj_isvoid(obj)) return (setstring(server, field, NULL)); else return (setstring(server, field, cfg_obj_asstring(obj))); } static void set_limit(const cfg_obj_t **maps, const char *configname, const char *description, isc_resource_t resourceid, isc_resourcevalue_t defaultvalue) { const cfg_obj_t *obj = NULL; const char *resource; isc_resourcevalue_t value; isc_result_t result; if (ns_config_get(maps, configname, &obj) != ISC_R_SUCCESS) return; if (cfg_obj_isstring(obj)) { resource = cfg_obj_asstring(obj); if (strcasecmp(resource, "unlimited") == 0) value = ISC_RESOURCE_UNLIMITED; else { INSIST(strcasecmp(resource, "default") == 0); value = defaultvalue; } } else value = cfg_obj_asuint64(obj); result = isc_resource_setlimit(resourceid, value); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, result == ISC_R_SUCCESS ? ISC_LOG_DEBUG(3) : ISC_LOG_WARNING, "set maximum %s to %" ISC_PRINT_QUADFORMAT "u: %s", description, value, isc_result_totext(result)); } #define SETLIMIT(cfgvar, resource, description) \ set_limit(maps, cfgvar, description, isc_resource_ ## resource, \ ns_g_init ## resource) static void set_limits(const cfg_obj_t **maps) { SETLIMIT("stacksize", stacksize, "stack size"); SETLIMIT("datasize", datasize, "data size"); SETLIMIT("coresize", coresize, "core size"); SETLIMIT("files", openfiles, "open files"); } static void portset_fromconf(isc_portset_t *portset, const cfg_obj_t *ports, isc_boolean_t positive) { const cfg_listelt_t *element; for (element = cfg_list_first(ports); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *obj = cfg_listelt_value(element); if (cfg_obj_isuint32(obj)) { in_port_t port = (in_port_t)cfg_obj_asuint32(obj); if (positive) isc_portset_add(portset, port); else isc_portset_remove(portset, port); } else { const cfg_obj_t *obj_loport, *obj_hiport; in_port_t loport, hiport; obj_loport = cfg_tuple_get(obj, "loport"); loport = (in_port_t)cfg_obj_asuint32(obj_loport); obj_hiport = cfg_tuple_get(obj, "hiport"); hiport = (in_port_t)cfg_obj_asuint32(obj_hiport); if (positive) isc_portset_addrange(portset, loport, hiport); else { isc_portset_removerange(portset, loport, hiport); } } } } static isc_result_t removed(dns_zone_t *zone, void *uap) { const char *type; if (dns_zone_getview(zone) != uap) return (ISC_R_SUCCESS); switch (dns_zone_gettype(zone)) { case dns_zone_master: type = "master"; break; case dns_zone_slave: type = "slave"; break; case dns_zone_stub: type = "stub"; break; default: type = "other"; break; } dns_zone_log(zone, ISC_LOG_INFO, "(%s) removed", type); return (ISC_R_SUCCESS); } static void cleanup_session_key(ns_server_t *server, isc_mem_t *mctx) { if (server->session_keyfile != NULL) { isc_file_remove(server->session_keyfile); isc_mem_free(mctx, server->session_keyfile); server->session_keyfile = NULL; } if (server->session_keyname != NULL) { if (dns_name_dynamic(server->session_keyname)) dns_name_free(server->session_keyname, mctx); isc_mem_put(mctx, server->session_keyname, sizeof(dns_name_t)); server->session_keyname = NULL; } if (server->sessionkey != NULL) dns_tsigkey_detach(&server->sessionkey); server->session_keyalg = DST_ALG_UNKNOWN; server->session_keybits = 0; } static isc_result_t generate_session_key(const char *filename, const char *keynamestr, dns_name_t *keyname, const char *algstr, dns_name_t *algname, unsigned int algtype, isc_uint16_t bits, isc_mem_t *mctx, dns_tsigkey_t **tsigkeyp) { isc_result_t result = ISC_R_SUCCESS; dst_key_t *key = NULL; isc_buffer_t key_txtbuffer; isc_buffer_t key_rawbuffer; char key_txtsecret[256]; char key_rawsecret[64]; isc_region_t key_rawregion; isc_stdtime_t now; dns_tsigkey_t *tsigkey = NULL; FILE *fp = NULL; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "generating session key for dynamic DNS"); /* generate key */ result = dst_key_generate(keyname, algtype, bits, 1, 0, DNS_KEYPROTO_ANY, dns_rdataclass_in, mctx, &key); if (result != ISC_R_SUCCESS) return (result); /* * Dump the key to the buffer for later use. Should be done before * we transfer the ownership of key to tsigkey. */ isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret)); CHECK(dst_key_tobuffer(key, &key_rawbuffer)); isc_buffer_usedregion(&key_rawbuffer, &key_rawregion); isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret)); CHECK(isc_base64_totext(&key_rawregion, -1, "", &key_txtbuffer)); /* Store the key in tsigkey. */ isc_stdtime_get(&now); CHECK(dns_tsigkey_createfromkey(dst_key_name(key), algname, key, ISC_FALSE, NULL, now, now, mctx, NULL, &tsigkey)); /* Dump the key to the key file. */ fp = ns_os_openfile(filename, S_IRUSR|S_IWUSR, ISC_TRUE); if (fp == NULL) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "could not create %s", filename); result = ISC_R_NOPERM; goto cleanup; } fprintf(fp, "key \"%s\" {\n" "\talgorithm %s;\n" "\tsecret \"%.*s\";\n};\n", keynamestr, algstr, (int) isc_buffer_usedlength(&key_txtbuffer), (char*) isc_buffer_base(&key_txtbuffer)); RUNTIME_CHECK(isc_stdio_flush(fp) == ISC_R_SUCCESS); RUNTIME_CHECK(isc_stdio_close(fp) == ISC_R_SUCCESS); dst_key_free(&key); *tsigkeyp = tsigkey; return (ISC_R_SUCCESS); cleanup: isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "failed to generate session key " "for dynamic DNS: %s", isc_result_totext(result)); if (tsigkey != NULL) dns_tsigkey_detach(&tsigkey); if (key != NULL) dst_key_free(&key); return (result); } static isc_result_t configure_session_key(const cfg_obj_t **maps, ns_server_t *server, isc_mem_t *mctx) { const char *keyfile, *keynamestr, *algstr; unsigned int algtype; dns_fixedname_t fname; dns_name_t *keyname, *algname; isc_buffer_t buffer; isc_uint16_t bits; const cfg_obj_t *obj; isc_boolean_t need_deleteold = ISC_FALSE; isc_boolean_t need_createnew = ISC_FALSE; isc_result_t result; obj = NULL; result = ns_config_get(maps, "session-keyfile", &obj); if (result == ISC_R_SUCCESS) { if (cfg_obj_isvoid(obj)) keyfile = NULL; /* disable it */ else keyfile = cfg_obj_asstring(obj); } else keyfile = ns_g_defaultsessionkeyfile; obj = NULL; result = ns_config_get(maps, "session-keyname", &obj); INSIST(result == ISC_R_SUCCESS); keynamestr = cfg_obj_asstring(obj); dns_fixedname_init(&fname); isc_buffer_constinit(&buffer, keynamestr, strlen(keynamestr)); isc_buffer_add(&buffer, strlen(keynamestr)); keyname = dns_fixedname_name(&fname); result = dns_name_fromtext(keyname, &buffer, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) return (result); obj = NULL; result = ns_config_get(maps, "session-keyalg", &obj); INSIST(result == ISC_R_SUCCESS); algstr = cfg_obj_asstring(obj); algname = NULL; result = ns_config_getkeyalgorithm2(algstr, &algname, &algtype, &bits); if (result != ISC_R_SUCCESS) { const char *s = " (keeping current key)"; cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, "session-keyalg: " "unsupported or unknown algorithm '%s'%s", algstr, server->session_keyfile != NULL ? s : ""); return (result); } /* See if we need to (re)generate a new key. */ if (keyfile == NULL) { if (server->session_keyfile != NULL) need_deleteold = ISC_TRUE; } else if (server->session_keyfile == NULL) need_createnew = ISC_TRUE; else if (strcmp(keyfile, server->session_keyfile) != 0 || !dns_name_equal(server->session_keyname, keyname) || server->session_keyalg != algtype || server->session_keybits != bits) { need_deleteold = ISC_TRUE; need_createnew = ISC_TRUE; } if (need_deleteold) { INSIST(server->session_keyfile != NULL); INSIST(server->session_keyname != NULL); INSIST(server->sessionkey != NULL); cleanup_session_key(server, mctx); } if (need_createnew) { INSIST(server->sessionkey == NULL); INSIST(server->session_keyfile == NULL); INSIST(server->session_keyname == NULL); INSIST(server->session_keyalg == DST_ALG_UNKNOWN); INSIST(server->session_keybits == 0); server->session_keyname = isc_mem_get(mctx, sizeof(dns_name_t)); if (server->session_keyname == NULL) goto cleanup; dns_name_init(server->session_keyname, NULL); CHECK(dns_name_dup(keyname, mctx, server->session_keyname)); server->session_keyfile = isc_mem_strdup(mctx, keyfile); if (server->session_keyfile == NULL) goto cleanup; server->session_keyalg = algtype; server->session_keybits = bits; CHECK(generate_session_key(keyfile, keynamestr, keyname, algstr, algname, algtype, bits, mctx, &server->sessionkey)); } return (result); cleanup: cleanup_session_key(server, mctx); return (result); } static isc_result_t setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, cfg_parser_t *parser, cfg_aclconfctx_t *actx) { isc_result_t result = ISC_R_SUCCESS; isc_boolean_t allow = ISC_FALSE; struct cfg_context *nzcfg = NULL; cfg_parser_t *nzparser = NULL; cfg_obj_t *nzconfig = NULL; const cfg_obj_t *maps[4]; const cfg_obj_t *options = NULL, *voptions = NULL; const cfg_obj_t *nz = NULL; int i = 0; REQUIRE (config != NULL); if (vconfig != NULL) voptions = cfg_tuple_get(vconfig, "options"); if (voptions != NULL) maps[i++] = voptions; result = cfg_map_get(config, "options", &options); if (result == ISC_R_SUCCESS) maps[i++] = options; maps[i++] = ns_g_defaults; maps[i] = NULL; result = ns_config_get(maps, "allow-new-zones", &nz); if (result == ISC_R_SUCCESS) allow = cfg_obj_asboolean(nz); if (!allow) { dns_view_setnewzones(view, ISC_FALSE, NULL, NULL); return (ISC_R_SUCCESS); } nzcfg = isc_mem_get(view->mctx, sizeof(*nzcfg)); if (nzcfg == NULL) { dns_view_setnewzones(view, ISC_FALSE, NULL, NULL); return (ISC_R_NOMEMORY); } dns_view_setnewzones(view, allow, nzcfg, newzone_cfgctx_destroy); memset(nzcfg, 0, sizeof(*nzcfg)); isc_mem_attach(view->mctx, &nzcfg->mctx); cfg_obj_attach(config, &nzcfg->config); cfg_parser_attach(parser, &nzcfg->parser); cfg_aclconfctx_attach(actx, &nzcfg->actx); /* * Attempt to create a parser and parse the newzones * file. If successful, preserve both; otherwise leave * them NULL. */ result = cfg_parser_create(view->mctx, ns_g_lctx, &nzparser); if (result == ISC_R_SUCCESS) result = cfg_parse_file(nzparser, view->new_zone_file, &cfg_type_newzones, &nzconfig); if (result == ISC_R_SUCCESS) { cfg_parser_attach(nzparser, &nzcfg->nzparser); cfg_obj_attach(nzconfig, &nzcfg->nzconfig); } if (nzparser != NULL) { if (nzconfig != NULL) cfg_obj_destroy(nzparser, &nzconfig); cfg_parser_destroy(&nzparser); } return (ISC_R_SUCCESS); } static int count_zones(const cfg_obj_t *conf) { const cfg_obj_t *zonelist = NULL; const cfg_listelt_t *element; int n = 0; REQUIRE(conf != NULL); cfg_map_get(conf, "zone", &zonelist); for (element = cfg_list_first(zonelist); element != NULL; element = cfg_list_next(element)) n++; return (n); } static isc_result_t load_configuration(const char *filename, ns_server_t *server, isc_boolean_t first_time) { cfg_obj_t *config = NULL, *bindkeys = NULL; cfg_parser_t *conf_parser = NULL, *bindkeys_parser = NULL; const cfg_listelt_t *element; const cfg_obj_t *builtin_views; const cfg_obj_t *maps[3]; const cfg_obj_t *obj; const cfg_obj_t *options; const cfg_obj_t *usev4ports, *avoidv4ports, *usev6ports, *avoidv6ports; const cfg_obj_t *views; dns_view_t *view = NULL; dns_view_t *view_next; dns_viewlist_t tmpviewlist; dns_viewlist_t viewlist, builtin_viewlist; in_port_t listen_port, udpport_low, udpport_high; int i; int num_zones = 0; isc_boolean_t exclusive = ISC_FALSE; isc_interval_t interval; isc_logconfig_t *logc = NULL; isc_portset_t *v4portset = NULL; isc_portset_t *v6portset = NULL; isc_resourcevalue_t nfiles; isc_result_t result; isc_uint32_t heartbeat_interval; isc_uint32_t interface_interval; isc_uint32_t reserved; isc_uint32_t udpsize; ns_cache_t *nsc; ns_cachelist_t cachelist, tmpcachelist; struct cfg_context *nzctx; unsigned int maxsocks; ISC_LIST_INIT(viewlist); ISC_LIST_INIT(builtin_viewlist); ISC_LIST_INIT(cachelist); /* Create the ACL configuration context */ if (ns_g_aclconfctx != NULL) cfg_aclconfctx_detach(&ns_g_aclconfctx); CHECK(cfg_aclconfctx_create(ns_g_mctx, &ns_g_aclconfctx)); /* * Parse the global default pseudo-config file. */ if (first_time) { CHECK(ns_config_parsedefaults(ns_g_parser, &ns_g_config)); RUNTIME_CHECK(cfg_map_get(ns_g_config, "options", &ns_g_defaults) == ISC_R_SUCCESS); } /* * Parse the configuration file using the new config code. */ result = ISC_R_FAILURE; config = NULL; /* * Unless this is lwresd with the -C option, parse the config file. */ if (!(ns_g_lwresdonly && lwresd_g_useresolvconf)) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "loading configuration from '%s'", filename); CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser)); cfg_parser_setcallback(conf_parser, directory_callback, NULL); result = cfg_parse_file(conf_parser, filename, &cfg_type_namedconf, &config); } /* * If this is lwresd with the -C option, or lwresd with no -C or -c * option where the above parsing failed, parse resolv.conf. */ if (ns_g_lwresdonly && (lwresd_g_useresolvconf || (!ns_g_conffileset && result == ISC_R_FILENOTFOUND))) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "loading configuration from '%s'", lwresd_g_resolvconffile); if (conf_parser != NULL) cfg_parser_destroy(&conf_parser); CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser)); result = ns_lwresd_parseeresolvconf(ns_g_mctx, conf_parser, &config); } CHECK(result); /* * Check the validity of the configuration. */ CHECK(bind9_check_namedconf(config, ns_g_lctx, ns_g_mctx)); /* * Fill in the maps array, used for resolving defaults. */ i = 0; options = NULL; result = cfg_map_get(config, "options", &options); if (result == ISC_R_SUCCESS) maps[i++] = options; maps[i++] = ns_g_defaults; maps[i] = NULL; /* * If bind.keys exists, load it. If "dnssec-lookaside auto" * is turned on, the keys found there will be used as default * trust anchors. */ obj = NULL; result = ns_config_get(maps, "bindkeys-file", &obj); INSIST(result == ISC_R_SUCCESS); CHECKM(setstring(server, &server->bindkeysfile, cfg_obj_asstring(obj)), "strdup"); if (access(server->bindkeysfile, R_OK) == 0) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "reading built-in trusted " "keys from file '%s'", server->bindkeysfile); CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &bindkeys_parser)); result = cfg_parse_file(bindkeys_parser, server->bindkeysfile, &cfg_type_bindkeys, &bindkeys); CHECK(result); } /* Ensure exclusive access to configuration data. */ if (!exclusive) { result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); exclusive = ISC_TRUE; } /* * Set process limits, which (usually) needs to be done as root. */ set_limits(maps); /* * Check if max number of open sockets that the system allows is * sufficiently large. Failing this condition is not necessarily fatal, * but may cause subsequent runtime failures for a busy recursive * server. */ result = isc_socketmgr_getmaxsockets(ns_g_socketmgr, &maxsocks); if (result != ISC_R_SUCCESS) maxsocks = 0; result = isc_resource_getcurlimit(isc_resource_openfiles, &nfiles); if (result == ISC_R_SUCCESS && (isc_resourcevalue_t)maxsocks > nfiles) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "max open files (%" ISC_PRINT_QUADFORMAT "u)" " is smaller than max sockets (%u)", nfiles, maxsocks); } /* * Set the number of socket reserved for TCP, stdio etc. */ obj = NULL; result = ns_config_get(maps, "reserved-sockets", &obj); INSIST(result == ISC_R_SUCCESS); reserved = cfg_obj_asuint32(obj); if (maxsocks != 0) { if (maxsocks < 128U) /* Prevent underflow. */ reserved = 0; else if (reserved > maxsocks - 128U) /* Minimum UDP space. */ reserved = maxsocks - 128; } /* Minimum TCP/stdio space. */ if (reserved < 128U) reserved = 128; if (reserved + 128U > maxsocks && maxsocks != 0) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "less than 128 UDP sockets available after " "applying 'reserved-sockets' and 'maxsockets'"); } isc__socketmgr_setreserved(ns_g_socketmgr, reserved); /* * Configure various server options. */ configure_server_quota(maps, "transfers-out", &server->xfroutquota); configure_server_quota(maps, "tcp-clients", &server->tcpquota); configure_server_quota(maps, "recursive-clients", &server->recursionquota); if (server->recursionquota.max > 1000) isc_quota_soft(&server->recursionquota, server->recursionquota.max - 100); else isc_quota_soft(&server->recursionquota, 0); CHECK(configure_view_acl(NULL, config, "blackhole", NULL, ns_g_aclconfctx, ns_g_mctx, &server->blackholeacl)); if (server->blackholeacl != NULL) dns_dispatchmgr_setblackhole(ns_g_dispatchmgr, server->blackholeacl); obj = NULL; result = ns_config_get(maps, "match-mapped-addresses", &obj); INSIST(result == ISC_R_SUCCESS); server->aclenv.match_mapped = cfg_obj_asboolean(obj); CHECKM(ns_statschannels_configure(ns_g_server, config, ns_g_aclconfctx), "configuring statistics server(s)"); /* * Configure sets of UDP query source ports. */ CHECKM(isc_portset_create(ns_g_mctx, &v4portset), "creating UDP port set"); CHECKM(isc_portset_create(ns_g_mctx, &v6portset), "creating UDP port set"); usev4ports = NULL; usev6ports = NULL; avoidv4ports = NULL; avoidv6ports = NULL; (void)ns_config_get(maps, "use-v4-udp-ports", &usev4ports); if (usev4ports != NULL) portset_fromconf(v4portset, usev4ports, ISC_TRUE); else { CHECKM(isc_net_getudpportrange(AF_INET, &udpport_low, &udpport_high), "get the default UDP/IPv4 port range"); if (udpport_low == udpport_high) isc_portset_add(v4portset, udpport_low); else { isc_portset_addrange(v4portset, udpport_low, udpport_high); } isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "using default UDP/IPv4 port range: [%d, %d]", udpport_low, udpport_high); } (void)ns_config_get(maps, "avoid-v4-udp-ports", &avoidv4ports); if (avoidv4ports != NULL) portset_fromconf(v4portset, avoidv4ports, ISC_FALSE); (void)ns_config_get(maps, "use-v6-udp-ports", &usev6ports); if (usev6ports != NULL) portset_fromconf(v6portset, usev6ports, ISC_TRUE); else { CHECKM(isc_net_getudpportrange(AF_INET6, &udpport_low, &udpport_high), "get the default UDP/IPv6 port range"); if (udpport_low == udpport_high) isc_portset_add(v6portset, udpport_low); else { isc_portset_addrange(v6portset, udpport_low, udpport_high); } isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "using default UDP/IPv6 port range: [%d, %d]", udpport_low, udpport_high); } (void)ns_config_get(maps, "avoid-v6-udp-ports", &avoidv6ports); if (avoidv6ports != NULL) portset_fromconf(v6portset, avoidv6ports, ISC_FALSE); dns_dispatchmgr_setavailports(ns_g_dispatchmgr, v4portset, v6portset); /* * Set the EDNS UDP size when we don't match a view. */ obj = NULL; result = ns_config_get(maps, "edns-udp-size", &obj); INSIST(result == ISC_R_SUCCESS); udpsize = cfg_obj_asuint32(obj); if (udpsize < 512) udpsize = 512; if (udpsize > 4096) udpsize = 4096; ns_g_udpsize = (isc_uint16_t)udpsize; /* * Configure the zone manager. */ obj = NULL; result = ns_config_get(maps, "transfers-in", &obj); INSIST(result == ISC_R_SUCCESS); dns_zonemgr_settransfersin(server->zonemgr, cfg_obj_asuint32(obj)); obj = NULL; result = ns_config_get(maps, "transfers-per-ns", &obj); INSIST(result == ISC_R_SUCCESS); dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj)); obj = NULL; result = ns_config_get(maps, "serial-query-rate", &obj); INSIST(result == ISC_R_SUCCESS); dns_zonemgr_setserialqueryrate(server->zonemgr, cfg_obj_asuint32(obj)); /* * Determine which port to use for listening for incoming connections. */ if (ns_g_port != 0) listen_port = ns_g_port; else CHECKM(ns_config_getport(config, &listen_port), "port"); /* * Find the listen queue depth. */ obj = NULL; result = ns_config_get(maps, "tcp-listen-queue", &obj); INSIST(result == ISC_R_SUCCESS); ns_g_listen = cfg_obj_asuint32(obj); if ((ns_g_listen > 0) && (ns_g_listen < 10)) ns_g_listen = 10; /* * Configure the interface manager according to the "listen-on" * statement. */ { const cfg_obj_t *clistenon = NULL; ns_listenlist_t *listenon = NULL; clistenon = NULL; /* * Even though listen-on is present in the default * configuration, we can't use it here, since it isn't * used if we're in lwresd mode. This way is easier. */ if (options != NULL) (void)cfg_map_get(options, "listen-on", &clistenon); if (clistenon != NULL) { /* check return code? */ (void)ns_listenlist_fromconfig(clistenon, config, ns_g_aclconfctx, ns_g_mctx, &listenon); } else if (!ns_g_lwresdonly) { /* * Not specified, use default. */ CHECK(ns_listenlist_default(ns_g_mctx, listen_port, ISC_TRUE, &listenon)); } if (listenon != NULL) { ns_interfacemgr_setlistenon4(server->interfacemgr, listenon); ns_listenlist_detach(&listenon); } } /* * Ditto for IPv6. */ { const cfg_obj_t *clistenon = NULL; ns_listenlist_t *listenon = NULL; if (options != NULL) (void)cfg_map_get(options, "listen-on-v6", &clistenon); if (clistenon != NULL) { /* check return code? */ (void)ns_listenlist_fromconfig(clistenon, config, ns_g_aclconfctx, ns_g_mctx, &listenon); } else if (!ns_g_lwresdonly) { isc_boolean_t enable; /* * Not specified, use default. */ enable = ISC_TF(isc_net_probeipv4() != ISC_R_SUCCESS); CHECK(ns_listenlist_default(ns_g_mctx, listen_port, enable, &listenon)); } if (listenon != NULL) { ns_interfacemgr_setlistenon6(server->interfacemgr, listenon); ns_listenlist_detach(&listenon); } } /* * Rescan the interface list to pick up changes in the * listen-on option. It's important that we do this before we try * to configure the query source, since the dispatcher we use might * be shared with an interface. */ scan_interfaces(server, ISC_TRUE); /* * Arrange for further interface scanning to occur periodically * as specified by the "interface-interval" option. */ obj = NULL; result = ns_config_get(maps, "interface-interval", &obj); INSIST(result == ISC_R_SUCCESS); interface_interval = cfg_obj_asuint32(obj) * 60; if (interface_interval == 0) { CHECK(isc_timer_reset(server->interface_timer, isc_timertype_inactive, NULL, NULL, ISC_TRUE)); } else if (server->interface_interval != interface_interval) { isc_interval_set(&interval, interface_interval, 0); CHECK(isc_timer_reset(server->interface_timer, isc_timertype_ticker, NULL, &interval, ISC_FALSE)); } server->interface_interval = interface_interval; /* * Configure the dialup heartbeat timer. */ obj = NULL; result = ns_config_get(maps, "heartbeat-interval", &obj); INSIST(result == ISC_R_SUCCESS); heartbeat_interval = cfg_obj_asuint32(obj) * 60; if (heartbeat_interval == 0) { CHECK(isc_timer_reset(server->heartbeat_timer, isc_timertype_inactive, NULL, NULL, ISC_TRUE)); } else if (server->heartbeat_interval != heartbeat_interval) { isc_interval_set(&interval, heartbeat_interval, 0); CHECK(isc_timer_reset(server->heartbeat_timer, isc_timertype_ticker, NULL, &interval, ISC_FALSE)); } server->heartbeat_interval = heartbeat_interval; isc_interval_set(&interval, 1200, 0); CHECK(isc_timer_reset(server->pps_timer, isc_timertype_ticker, NULL, &interval, ISC_FALSE)); /* * Write the PID file. */ obj = NULL; if (ns_config_get(maps, "pid-file", &obj) == ISC_R_SUCCESS) if (cfg_obj_isvoid(obj)) ns_os_writepidfile(NULL, first_time); else ns_os_writepidfile(cfg_obj_asstring(obj), first_time); else if (ns_g_lwresdonly) ns_os_writepidfile(lwresd_g_defaultpidfile, first_time); else ns_os_writepidfile(ns_g_defaultpidfile, first_time); /* * Configure the server-wide session key. This must be done before * configure views because zone configuration may need to know * session-keyname. * * Failure of session key generation isn't fatal at this time; if it * turns out that a session key is really needed but doesn't exist, * we'll treat it as a fatal error then. */ (void)configure_session_key(maps, server, ns_g_mctx); views = NULL; (void)cfg_map_get(config, "view", &views); /* * Create the views and count all the configured zones in * order to correctly size the zone manager's task table. * (We only count zones for configured views; the built-in * "bind" view can be ignored as it only adds a negligible * number of zones.) * * If we're allowing new zones, we need to be able to find the * new zone file and count those as well. So we setup the new * zone configuration context, but otherwise view configuration * waits until after the zone manager's task list has been sized. */ for (element = cfg_list_first(views); element != NULL; element = cfg_list_next(element)) { cfg_obj_t *vconfig = cfg_listelt_value(element); const cfg_obj_t *voptions = cfg_tuple_get(vconfig, "options"); view = NULL; CHECK(create_view(vconfig, &viewlist, &view)); INSIST(view != NULL); num_zones += count_zones(voptions); CHECK(setup_newzones(view, config, vconfig, conf_parser, ns_g_aclconfctx)); nzctx = view->new_zone_config; if (nzctx != NULL && nzctx->nzconfig != NULL) num_zones += count_zones(nzctx->nzconfig); dns_view_detach(&view); } /* * If there were no explicit views then we do the default * view here. */ if (views == NULL) { CHECK(create_view(NULL, &viewlist, &view)); INSIST(view != NULL); num_zones = count_zones(config); CHECK(setup_newzones(view, config, NULL, conf_parser, ns_g_aclconfctx)); nzctx = view->new_zone_config; if (nzctx != NULL && nzctx->nzconfig != NULL) num_zones += count_zones(nzctx->nzconfig); dns_view_detach(&view); } /* * Zones have been counted; set the zone manager task pool size. */ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "sizing zone task pool based on %d zones", num_zones); CHECK(dns_zonemgr_setsize(ns_g_server->zonemgr, num_zones)); /* * Configure and freeze all explicit views. Explicit * views that have zones were already created at parsing * time, but views with no zones must be created here. */ for (element = cfg_list_first(views); element != NULL; element = cfg_list_next(element)) { cfg_obj_t *vconfig = cfg_listelt_value(element); view = NULL; CHECK(find_view(vconfig, &viewlist, &view)); CHECK(configure_view(view, config, vconfig, &cachelist, bindkeys, ns_g_mctx, ns_g_aclconfctx, ISC_TRUE)); dns_view_freeze(view); dns_view_detach(&view); } /* * Make sure we have a default view if and only if there * were no explicit views. */ if (views == NULL) { view = NULL; CHECK(find_view(NULL, &viewlist, &view)); CHECK(configure_view(view, config, NULL, &cachelist, bindkeys, ns_g_mctx, ns_g_aclconfctx, ISC_TRUE)); dns_view_freeze(view); dns_view_detach(&view); } /* * Create (or recreate) the built-in views. */ builtin_views = NULL; RUNTIME_CHECK(cfg_map_get(ns_g_config, "view", &builtin_views) == ISC_R_SUCCESS); for (element = cfg_list_first(builtin_views); element != NULL; element = cfg_list_next(element)) { cfg_obj_t *vconfig = cfg_listelt_value(element); CHECK(create_view(vconfig, &builtin_viewlist, &view)); CHECK(configure_view(view, config, vconfig, &cachelist, bindkeys, ns_g_mctx, ns_g_aclconfctx, ISC_FALSE)); dns_view_freeze(view); dns_view_detach(&view); view = NULL; } /* Now combine the two viewlists into one */ ISC_LIST_APPENDLIST(viewlist, builtin_viewlist, link); /* Swap our new view list with the production one. */ tmpviewlist = server->viewlist; server->viewlist = viewlist; viewlist = tmpviewlist; /* Make the view list available to each of the views */ view = ISC_LIST_HEAD(server->viewlist); while (view != NULL) { view->viewlist = &server->viewlist; view = ISC_LIST_NEXT(view, link); } /* Swap our new cache list with the production one. */ tmpcachelist = server->cachelist; server->cachelist = cachelist; cachelist = tmpcachelist; /* Load the TKEY information from the configuration. */ if (options != NULL) { dns_tkeyctx_t *t = NULL; CHECKM(ns_tkeyctx_fromconfig(options, ns_g_mctx, ns_g_entropy, &t), "configuring TKEY"); if (server->tkeyctx != NULL) dns_tkeyctx_destroy(&server->tkeyctx); server->tkeyctx = t; } /* * Bind the control port(s). */ CHECKM(ns_controls_configure(ns_g_server->controls, config, ns_g_aclconfctx), "binding control channel(s)"); /* * Bind the lwresd port(s). */ CHECKM(ns_lwresd_configure(ns_g_mctx, config), "binding lightweight resolver ports"); /* * Open the source of entropy. */ if (first_time) { obj = NULL; result = ns_config_get(maps, "random-device", &obj); if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "no source of entropy found"); } else { const char *randomdev = cfg_obj_asstring(obj); result = isc_entropy_createfilesource(ns_g_entropy, randomdev); if (result != ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "could not open entropy source " "%s: %s", randomdev, isc_result_totext(result)); #ifdef PATH_RANDOMDEV if (ns_g_fallbackentropy != NULL) { if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "using pre-chroot entropy source " "%s", PATH_RANDOMDEV); isc_entropy_detach(&ns_g_entropy); isc_entropy_attach(ns_g_fallbackentropy, &ns_g_entropy); } isc_entropy_detach(&ns_g_fallbackentropy); } #endif } } /* * Relinquish root privileges. */ if (first_time) ns_os_changeuser(); /* * Check that the working directory is writable. */ if (access(".", W_OK) != 0) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "the working directory is not writable"); } /* * Configure the logging system. * * Do this after changing UID to make sure that any log * files specified in named.conf get created by the * unprivileged user, not root. */ if (ns_g_logstderr) { const cfg_obj_t *logobj = NULL; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "not using config file logging " "statement for logging due to " "-g option"); (void)cfg_map_get(config, "logging", &logobj); if (logobj != NULL) { result = ns_log_configure(NULL, logobj); if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "checking logging configuration " "failed: %s", isc_result_totext(result)); goto cleanup; } } } else { const cfg_obj_t *logobj = NULL; CHECKM(isc_logconfig_create(ns_g_lctx, &logc), "creating new logging configuration"); logobj = NULL; (void)cfg_map_get(config, "logging", &logobj); if (logobj != NULL) { CHECKM(ns_log_configure(logc, logobj), "configuring logging"); } else { CHECKM(ns_log_setdefaultchannels(logc), "setting up default logging channels"); CHECKM(ns_log_setunmatchedcategory(logc), "setting up default 'category unmatched'"); CHECKM(ns_log_setdefaultcategory(logc), "setting up default 'category default'"); } CHECKM(isc_logconfig_use(ns_g_lctx, logc), "installing logging configuration"); logc = NULL; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), "now using logging configuration from " "config file"); } /* * Set the default value of the query logging flag depending * whether a "queries" category has been defined. This is * a disgusting hack, but we need to do this for BIND 8 * compatibility. */ if (first_time) { const cfg_obj_t *logobj = NULL; const cfg_obj_t *categories = NULL; obj = NULL; if (ns_config_get(maps, "querylog", &obj) == ISC_R_SUCCESS) { server->log_queries = cfg_obj_asboolean(obj); } else { (void)cfg_map_get(config, "logging", &logobj); if (logobj != NULL) (void)cfg_map_get(logobj, "category", &categories); if (categories != NULL) { const cfg_listelt_t *element; for (element = cfg_list_first(categories); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *catobj; const char *str; obj = cfg_listelt_value(element); catobj = cfg_tuple_get(obj, "name"); str = cfg_obj_asstring(catobj); if (strcasecmp(str, "queries") == 0) server->log_queries = ISC_TRUE; } } } } obj = NULL; if (options != NULL && cfg_map_get(options, "memstatistics", &obj) == ISC_R_SUCCESS) ns_g_memstatistics = cfg_obj_asboolean(obj); else ns_g_memstatistics = ISC_TF((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0); obj = NULL; if (ns_config_get(maps, "memstatistics-file", &obj) == ISC_R_SUCCESS) ns_main_setmemstats(cfg_obj_asstring(obj)); else if (ns_g_memstatistics) ns_main_setmemstats("named.memstats"); else ns_main_setmemstats(NULL); obj = NULL; result = ns_config_get(maps, "statistics-file", &obj); INSIST(result == ISC_R_SUCCESS); CHECKM(setstring(server, &server->statsfile, cfg_obj_asstring(obj)), "strdup"); obj = NULL; result = ns_config_get(maps, "dump-file", &obj); INSIST(result == ISC_R_SUCCESS); CHECKM(setstring(server, &server->dumpfile, cfg_obj_asstring(obj)), "strdup"); obj = NULL; result = ns_config_get(maps, "secroots-file", &obj); INSIST(result == ISC_R_SUCCESS); CHECKM(setstring(server, &server->secrootsfile, cfg_obj_asstring(obj)), "strdup"); obj = NULL; result = ns_config_get(maps, "recursing-file", &obj); INSIST(result == ISC_R_SUCCESS); CHECKM(setstring(server, &server->recfile, cfg_obj_asstring(obj)), "strdup"); obj = NULL; result = ns_config_get(maps, "version", &obj); if (result == ISC_R_SUCCESS) { CHECKM(setoptstring(server, &server->version, obj), "strdup"); server->version_set = ISC_TRUE; } else { server->version_set = ISC_FALSE; } obj = NULL; result = ns_config_get(maps, "hostname", &obj); if (result == ISC_R_SUCCESS) { CHECKM(setoptstring(server, &server->hostname, obj), "strdup"); server->hostname_set = ISC_TRUE; } else { server->hostname_set = ISC_FALSE; } obj = NULL; result = ns_config_get(maps, "server-id", &obj); server->server_usehostname = ISC_FALSE; if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) { /* The parser translates "hostname" to ISC_TRUE */ server->server_usehostname = cfg_obj_asboolean(obj); result = setstring(server, &server->server_id, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); } else if (result == ISC_R_SUCCESS) { /* Found a quoted string */ CHECKM(setoptstring(server, &server->server_id, obj), "strdup"); } else { result = setstring(server, &server->server_id, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); } obj = NULL; result = ns_config_get(maps, "flush-zones-on-shutdown", &obj); if (result == ISC_R_SUCCESS) { server->flushonshutdown = cfg_obj_asboolean(obj); } else { server->flushonshutdown = ISC_FALSE; } result = ISC_R_SUCCESS; cleanup: if (logc != NULL) isc_logconfig_destroy(&logc); if (v4portset != NULL) isc_portset_destroy(ns_g_mctx, &v4portset); if (v6portset != NULL) isc_portset_destroy(ns_g_mctx, &v6portset); if (conf_parser != NULL) { if (config != NULL) cfg_obj_destroy(conf_parser, &config); cfg_parser_destroy(&conf_parser); } if (bindkeys_parser != NULL) { if (bindkeys != NULL) cfg_obj_destroy(bindkeys_parser, &bindkeys); cfg_parser_destroy(&bindkeys_parser); } if (view != NULL) dns_view_detach(&view); /* * This cleans up either the old production view list * or our temporary list depending on whether they * were swapped above or not. */ for (view = ISC_LIST_HEAD(viewlist); view != NULL; view = view_next) { view_next = ISC_LIST_NEXT(view, link); ISC_LIST_UNLINK(viewlist, view, link); if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) (void)dns_zt_apply(view->zonetable, ISC_FALSE, removed, view); dns_view_detach(&view); } /* Same cleanup for cache list. */ while ((nsc = ISC_LIST_HEAD(cachelist)) != NULL) { ISC_LIST_UNLINK(cachelist, nsc, link); dns_cache_detach(&nsc->cache); isc_mem_put(server->mctx, nsc, sizeof(*nsc)); } /* * Adjust the listening interfaces in accordance with the source * addresses specified in views and zones. */ if (isc_net_probeipv6() == ISC_R_SUCCESS) adjust_interfaces(server, ns_g_mctx); /* Relinquish exclusive access to configuration data. */ if (exclusive) isc_task_endexclusive(server->task); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), "load_configuration: %s", isc_result_totext(result)); return (result); } static isc_result_t load_zones(ns_server_t *server, isc_boolean_t stop) { isc_result_t result; dns_view_t *view; result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); /* * Load zone data from disk. */ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { CHECK(dns_view_load(view, stop)); if (view->managed_keys != NULL) CHECK(dns_zone_load(view->managed_keys)); } /* * Force zone maintenance. Do this after loading * so that we know when we need to force AXFR of * slave zones whose master files are missing. */ CHECK(dns_zonemgr_forcemaint(server->zonemgr)); cleanup: isc_task_endexclusive(server->task); return (result); } static isc_result_t load_new_zones(ns_server_t *server, isc_boolean_t stop) { isc_result_t result; dns_view_t *view; result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); /* * Load zone data from disk. */ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { CHECK(dns_view_loadnew(view, stop)); /* Load managed-keys data */ if (view->managed_keys != NULL) CHECK(dns_zone_loadnew(view->managed_keys)); } /* * Resume zone XFRs. */ dns_zonemgr_resumexfrs(server->zonemgr); cleanup: isc_task_endexclusive(server->task); return (result); } static void run_server(isc_task_t *task, isc_event_t *event) { isc_result_t result; ns_server_t *server = (ns_server_t *)event->ev_arg; INSIST(task == server->task); isc_event_free(&event); CHECKFATAL(dns_dispatchmgr_create(ns_g_mctx, ns_g_entropy, &ns_g_dispatchmgr), "creating dispatch manager"); dns_dispatchmgr_setstats(ns_g_dispatchmgr, server->resolverstats); CHECKFATAL(ns_interfacemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_socketmgr, ns_g_dispatchmgr, &server->interfacemgr), "creating interface manager"); CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive, NULL, NULL, server->task, interface_timer_tick, server, &server->interface_timer), "creating interface timer"); CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive, NULL, NULL, server->task, heartbeat_timer_tick, server, &server->heartbeat_timer), "creating heartbeat timer"); CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive, NULL, NULL, server->task, pps_timer_tick, server, &server->pps_timer), "creating pps timer"); CHECKFATAL(cfg_parser_create(ns_g_mctx, NULL, &ns_g_parser), "creating default configuration parser"); if (ns_g_lwresdonly) CHECKFATAL(load_configuration(lwresd_g_conffile, server, ISC_TRUE), "loading configuration"); else CHECKFATAL(load_configuration(ns_g_conffile, server, ISC_TRUE), "loading configuration"); isc_hash_init(); CHECKFATAL(load_zones(server, ISC_FALSE), "loading zones"); ns_os_started(); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_NOTICE, "running"); } void ns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush) { REQUIRE(NS_SERVER_VALID(server)); server->flushonshutdown = flush; } static void shutdown_server(isc_task_t *task, isc_event_t *event) { isc_result_t result; dns_view_t *view, *view_next; ns_server_t *server = (ns_server_t *)event->ev_arg; isc_boolean_t flush = server->flushonshutdown; ns_cache_t *nsc; UNUSED(task); INSIST(task == server->task); result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "shutting down%s", flush ? ": flushing changes" : ""); ns_statschannels_shutdown(server); ns_controls_shutdown(server->controls); end_reserved_dispatches(server, ISC_TRUE); cleanup_session_key(server, server->mctx); if (ns_g_aclconfctx != NULL) cfg_aclconfctx_detach(&ns_g_aclconfctx); cfg_obj_destroy(ns_g_parser, &ns_g_config); cfg_parser_destroy(&ns_g_parser); for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = view_next) { view_next = ISC_LIST_NEXT(view, link); ISC_LIST_UNLINK(server->viewlist, view, link); if (flush) dns_view_flushanddetach(&view); else dns_view_detach(&view); } while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) { ISC_LIST_UNLINK(server->cachelist, nsc, link); dns_cache_detach(&nsc->cache); isc_mem_put(server->mctx, nsc, sizeof(*nsc)); } isc_timer_detach(&server->interface_timer); isc_timer_detach(&server->heartbeat_timer); isc_timer_detach(&server->pps_timer); ns_interfacemgr_shutdown(server->interfacemgr); ns_interfacemgr_detach(&server->interfacemgr); dns_dispatchmgr_destroy(&ns_g_dispatchmgr); dns_zonemgr_shutdown(server->zonemgr); if (ns_g_sessionkey != NULL) { dns_tsigkey_detach(&ns_g_sessionkey); dns_name_free(&ns_g_sessionkeyname, server->mctx); } if (server->blackholeacl != NULL) dns_acl_detach(&server->blackholeacl); dns_db_detach(&server->in_roothints); isc_task_endexclusive(server->task); isc_task_detach(&server->task); isc_event_free(&event); } void ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) { isc_result_t result; ns_server_t *server = isc_mem_get(mctx, sizeof(*server)); if (server == NULL) fatal("allocating server object", ISC_R_NOMEMORY); server->mctx = mctx; server->task = NULL; /* Initialize configuration data with default values. */ result = isc_quota_init(&server->xfroutquota, 10); RUNTIME_CHECK(result == ISC_R_SUCCESS); result = isc_quota_init(&server->tcpquota, 10); RUNTIME_CHECK(result == ISC_R_SUCCESS); result = isc_quota_init(&server->recursionquota, 100); RUNTIME_CHECK(result == ISC_R_SUCCESS); result = dns_aclenv_init(mctx, &server->aclenv); RUNTIME_CHECK(result == ISC_R_SUCCESS); /* Initialize server data structures. */ server->zonemgr = NULL; server->interfacemgr = NULL; ISC_LIST_INIT(server->viewlist); server->in_roothints = NULL; server->blackholeacl = NULL; CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL, &server->in_roothints), "setting up root hints"); CHECKFATAL(isc_mutex_init(&server->reload_event_lock), "initializing reload event lock"); server->reload_event = isc_event_allocate(ns_g_mctx, server, NS_EVENT_RELOAD, ns_server_reload, server, sizeof(isc_event_t)); CHECKFATAL(server->reload_event == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, "allocating reload event"); CHECKFATAL(dst_lib_init2(ns_g_mctx, ns_g_entropy, ns_g_engine, ISC_ENTROPY_GOODONLY), "initializing DST"); server->tkeyctx = NULL; CHECKFATAL(dns_tkeyctx_create(ns_g_mctx, ns_g_entropy, &server->tkeyctx), "creating TKEY context"); /* * Setup the server task, which is responsible for coordinating * startup and shutdown of the server, as well as all exclusive * tasks. */ CHECKFATAL(isc_task_create(ns_g_taskmgr, 0, &server->task), "creating server task"); isc_task_setname(server->task, "server", server); isc_taskmgr_setexcltask(ns_g_taskmgr, server->task); CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server), "isc_task_onshutdown"); CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server), "isc_app_onrun"); server->interface_timer = NULL; server->heartbeat_timer = NULL; server->pps_timer = NULL; server->interface_interval = 0; server->heartbeat_interval = 0; CHECKFATAL(dns_zonemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_timermgr, ns_g_socketmgr, &server->zonemgr), "dns_zonemgr_create"); CHECKFATAL(dns_zonemgr_setsize(server->zonemgr, 1000), "dns_zonemgr_setsize"); server->statsfile = isc_mem_strdup(server->mctx, "named.stats"); CHECKFATAL(server->statsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, "isc_mem_strdup"); server->nsstats = NULL; server->rcvquerystats = NULL; server->opcodestats = NULL; server->zonestats = NULL; server->resolverstats = NULL; server->sockstats = NULL; CHECKFATAL(isc_stats_create(server->mctx, &server->sockstats, isc_sockstatscounter_max), "isc_stats_create"); isc_socketmgr_setstats(ns_g_socketmgr, server->sockstats); server->bindkeysfile = isc_mem_strdup(server->mctx, "bind.keys"); CHECKFATAL(server->bindkeysfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, "isc_mem_strdup"); server->dumpfile = isc_mem_strdup(server->mctx, "named_dump.db"); CHECKFATAL(server->dumpfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, "isc_mem_strdup"); server->secrootsfile = isc_mem_strdup(server->mctx, "named.secroots"); CHECKFATAL(server->secrootsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, "isc_mem_strdup"); server->recfile = isc_mem_strdup(server->mctx, "named.recursing"); CHECKFATAL(server->recfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, "isc_mem_strdup"); server->hostname_set = ISC_FALSE; server->hostname = NULL; server->version_set = ISC_FALSE; server->version = NULL; server->server_usehostname = ISC_FALSE; server->server_id = NULL; CHECKFATAL(isc_stats_create(ns_g_mctx, &server->nsstats, dns_nsstatscounter_max), "dns_stats_create (server)"); CHECKFATAL(dns_rdatatypestats_create(ns_g_mctx, &server->rcvquerystats), "dns_stats_create (rcvquery)"); CHECKFATAL(dns_opcodestats_create(ns_g_mctx, &server->opcodestats), "dns_stats_create (opcode)"); CHECKFATAL(isc_stats_create(ns_g_mctx, &server->zonestats, dns_zonestatscounter_max), "dns_stats_create (zone)"); CHECKFATAL(isc_stats_create(ns_g_mctx, &server->resolverstats, dns_resstatscounter_max), "dns_stats_create (resolver)"); server->flushonshutdown = ISC_FALSE; server->log_queries = ISC_FALSE; server->controls = NULL; CHECKFATAL(ns_controls_create(server, &server->controls), "ns_controls_create"); server->dispatchgen = 0; ISC_LIST_INIT(server->dispatches); ISC_LIST_INIT(server->statschannels); ISC_LIST_INIT(server->cachelist); server->sessionkey = NULL; server->session_keyfile = NULL; server->session_keyname = NULL; server->session_keyalg = DST_ALG_UNKNOWN; server->session_keybits = 0; server->magic = NS_SERVER_MAGIC; *serverp = server; } void ns_server_destroy(ns_server_t **serverp) { ns_server_t *server = *serverp; REQUIRE(NS_SERVER_VALID(server)); ns_controls_destroy(&server->controls); isc_stats_detach(&server->nsstats); dns_stats_detach(&server->rcvquerystats); dns_stats_detach(&server->opcodestats); isc_stats_detach(&server->zonestats); isc_stats_detach(&server->resolverstats); isc_stats_detach(&server->sockstats); isc_mem_free(server->mctx, server->statsfile); isc_mem_free(server->mctx, server->bindkeysfile); isc_mem_free(server->mctx, server->dumpfile); isc_mem_free(server->mctx, server->secrootsfile); isc_mem_free(server->mctx, server->recfile); if (server->version != NULL) isc_mem_free(server->mctx, server->version); if (server->hostname != NULL) isc_mem_free(server->mctx, server->hostname); if (server->server_id != NULL) isc_mem_free(server->mctx, server->server_id); if (server->zonemgr != NULL) dns_zonemgr_detach(&server->zonemgr); if (server->tkeyctx != NULL) dns_tkeyctx_destroy(&server->tkeyctx); dst_lib_destroy(); isc_event_free(&server->reload_event); INSIST(ISC_LIST_EMPTY(server->viewlist)); INSIST(ISC_LIST_EMPTY(server->cachelist)); dns_aclenv_destroy(&server->aclenv); isc_quota_destroy(&server->recursionquota); isc_quota_destroy(&server->tcpquota); isc_quota_destroy(&server->xfroutquota); server->magic = 0; isc_mem_put(server->mctx, server, sizeof(*server)); *serverp = NULL; } static void fatal(const char *msg, isc_result_t result) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_CRITICAL, "%s: %s", msg, isc_result_totext(result)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_CRITICAL, "exiting (due to fatal error)"); exit(1); } static void start_reserved_dispatches(ns_server_t *server) { REQUIRE(NS_SERVER_VALID(server)); server->dispatchgen++; } static void end_reserved_dispatches(ns_server_t *server, isc_boolean_t all) { ns_dispatch_t *dispatch, *nextdispatch; REQUIRE(NS_SERVER_VALID(server)); for (dispatch = ISC_LIST_HEAD(server->dispatches); dispatch != NULL; dispatch = nextdispatch) { nextdispatch = ISC_LIST_NEXT(dispatch, link); if (!all && server->dispatchgen == dispatch-> dispatchgen) continue; ISC_LIST_UNLINK(server->dispatches, dispatch, link); dns_dispatch_detach(&dispatch->dispatch); isc_mem_put(server->mctx, dispatch, sizeof(*dispatch)); } } void ns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr) { ns_dispatch_t *dispatch; in_port_t port; char addrbuf[ISC_SOCKADDR_FORMATSIZE]; isc_result_t result; unsigned int attrs, attrmask; REQUIRE(NS_SERVER_VALID(server)); port = isc_sockaddr_getport(addr); if (port == 0 || port >= 1024) return; for (dispatch = ISC_LIST_HEAD(server->dispatches); dispatch != NULL; dispatch = ISC_LIST_NEXT(dispatch, link)) { if (isc_sockaddr_equal(&dispatch->addr, addr)) break; } if (dispatch != NULL) { dispatch->dispatchgen = server->dispatchgen; return; } dispatch = isc_mem_get(server->mctx, sizeof(*dispatch)); if (dispatch == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } dispatch->addr = *addr; dispatch->dispatchgen = server->dispatchgen; dispatch->dispatch = NULL; attrs = 0; attrs |= DNS_DISPATCHATTR_UDP; switch (isc_sockaddr_pf(addr)) { case AF_INET: attrs |= DNS_DISPATCHATTR_IPV4; break; case AF_INET6: attrs |= DNS_DISPATCHATTR_IPV6; break; default: result = ISC_R_NOTIMPLEMENTED; goto cleanup; } attrmask = 0; attrmask |= DNS_DISPATCHATTR_UDP; attrmask |= DNS_DISPATCHATTR_TCP; attrmask |= DNS_DISPATCHATTR_IPV4; attrmask |= DNS_DISPATCHATTR_IPV6; result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr, ns_g_taskmgr, &dispatch->addr, 4096, 1000, 32768, 16411, 16433, attrs, attrmask, &dispatch->dispatch); if (result != ISC_R_SUCCESS) goto cleanup; ISC_LIST_INITANDPREPEND(server->dispatches, dispatch, link); return; cleanup: if (dispatch != NULL) isc_mem_put(server->mctx, dispatch, sizeof(*dispatch)); isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "unable to create dispatch for reserved port %s: %s", addrbuf, isc_result_totext(result)); } static isc_result_t loadconfig(ns_server_t *server) { isc_result_t result; start_reserved_dispatches(server); result = load_configuration(ns_g_lwresdonly ? lwresd_g_conffile : ns_g_conffile, server, ISC_FALSE); if (result == ISC_R_SUCCESS) { end_reserved_dispatches(server, ISC_FALSE); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "reloading configuration succeeded"); } else { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "reloading configuration failed: %s", isc_result_totext(result)); } return (result); } static isc_result_t reload(ns_server_t *server) { isc_result_t result; CHECK(loadconfig(server)); result = load_zones(server, ISC_FALSE); if (result == ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "reloading zones succeeded"); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "reloading zones failed: %s", isc_result_totext(result)); cleanup: return (result); } static void reconfig(ns_server_t *server) { isc_result_t result; CHECK(loadconfig(server)); result = load_new_zones(server, ISC_FALSE); if (result == ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "any newly configured zones are now loaded"); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "loading new zones failed: %s", isc_result_totext(result)); cleanup: ; } /* * Handle a reload event (from SIGHUP). */ static void ns_server_reload(isc_task_t *task, isc_event_t *event) { ns_server_t *server = (ns_server_t *)event->ev_arg; INSIST(task = server->task); UNUSED(task); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "received SIGHUP signal to reload zones"); (void)reload(server); LOCK(&server->reload_event_lock); INSIST(server->reload_event == NULL); server->reload_event = event; UNLOCK(&server->reload_event_lock); } void ns_server_reloadwanted(ns_server_t *server) { LOCK(&server->reload_event_lock); if (server->reload_event != NULL) isc_task_send(server->task, &server->reload_event); UNLOCK(&server->reload_event_lock); } static char * next_token(char **stringp, const char *delim) { char *res; do { res = strsep(stringp, delim); if (res == NULL) break; } while (*res == '\0'); return (res); } /* * Find the zone specified in the control channel command 'args', * if any. If a zone is specified, point '*zonep' at it, otherwise * set '*zonep' to NULL. */ static isc_result_t zone_from_args(ns_server_t *server, char *args, dns_zone_t **zonep, const char **zonename, isc_buffer_t *text) { char *input, *ptr; const char *zonetxt; char *classtxt; const char *viewtxt = NULL; dns_fixedname_t fname; dns_name_t *name; isc_result_t result; dns_view_t *view = NULL; dns_rdataclass_t rdclass; char problem[DNS_NAME_FORMATSIZE + 500] = ""; REQUIRE(zonep != NULL && *zonep == NULL); REQUIRE(zonename == NULL || *zonename == NULL); input = args; /* Skip the command name. */ ptr = next_token(&input, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); /* Look for the zone name. */ zonetxt = next_token(&input, " \t"); if (zonetxt == NULL) return (ISC_R_SUCCESS); if (zonename != NULL) *zonename = zonetxt; /* Look for the optional class name. */ classtxt = next_token(&input, " \t"); if (classtxt != NULL) { /* Look for the optional view name. */ viewtxt = next_token(&input, " \t"); } dns_fixedname_init(&fname); name = dns_fixedname_name(&fname); CHECK(dns_name_fromstring(name, zonetxt, 0, NULL)); if (classtxt != NULL) { isc_textregion_t r; r.base = classtxt; r.length = strlen(classtxt); CHECK(dns_rdataclass_fromtext(&rdclass, &r)); } else rdclass = dns_rdataclass_in; if (viewtxt == NULL) { result = dns_viewlist_findzone(&server->viewlist, name, ISC_TF(classtxt == NULL), rdclass, zonep); if (result == ISC_R_NOTFOUND) snprintf(problem, sizeof(problem), "no matching zone '%s' in any view", zonetxt); } else { result = dns_viewlist_find(&server->viewlist, viewtxt, rdclass, &view); if (result != ISC_R_SUCCESS) { snprintf(problem, sizeof(problem), "no matching view '%s'", viewtxt); goto report; } result = dns_zt_find(view->zonetable, name, 0, NULL, zonep); if (result != ISC_R_SUCCESS) snprintf(problem, sizeof(problem), "no matching zone '%s' in view '%s'", zonetxt, viewtxt); } /* Partial match? */ if (result != ISC_R_SUCCESS && *zonep != NULL) dns_zone_detach(zonep); if (result == DNS_R_PARTIALMATCH) result = ISC_R_NOTFOUND; report: if (result != ISC_R_SUCCESS) { isc_result_t tresult; tresult = putstr(text, problem); if (tresult == ISC_R_SUCCESS && isc_buffer_availablelength(text) > 0U) isc_buffer_putuint8(text, 0); } cleanup: if (view != NULL) dns_view_detach(&view); return (result); } /* * Act on a "retransfer" command from the command channel. */ isc_result_t ns_server_retransfercommand(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result; dns_zone_t *zone = NULL; dns_zonetype_t type; result = zone_from_args(server, args, &zone, NULL, text); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) return (ISC_R_UNEXPECTEDEND); type = dns_zone_gettype(zone); if (type == dns_zone_slave || type == dns_zone_stub) dns_zone_forcereload(zone); else result = ISC_R_NOTFOUND; dns_zone_detach(&zone); return (result); } /* * Act on a "reload" command from the command channel. */ isc_result_t ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result; dns_zone_t *zone = NULL; dns_zonetype_t type; const char *msg = NULL; result = zone_from_args(server, args, &zone, NULL, text); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) { result = reload(server); if (result == ISC_R_SUCCESS) msg = "server reload successful"; } else { type = dns_zone_gettype(zone); if (type == dns_zone_slave || type == dns_zone_stub) { dns_zone_refresh(zone); dns_zone_detach(&zone); msg = "zone refresh queued"; } else { result = dns_zone_load(zone); dns_zone_detach(&zone); switch (result) { case ISC_R_SUCCESS: msg = "zone reload successful"; break; case DNS_R_CONTINUE: msg = "zone reload queued"; result = ISC_R_SUCCESS; break; case DNS_R_UPTODATE: msg = "zone reload up-to-date"; result = ISC_R_SUCCESS; break; default: /* failure message will be generated by rndc */ break; } } } if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text)) isc_buffer_putmem(text, (const unsigned char *)msg, strlen(msg) + 1); return (result); } /* * Act on a "reconfig" command from the command channel. */ isc_result_t ns_server_reconfigcommand(ns_server_t *server, char *args) { UNUSED(args); reconfig(server); return (ISC_R_SUCCESS); } /* * Act on a "notify" command from the command channel. */ isc_result_t ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result; dns_zone_t *zone = NULL; const unsigned char msg[] = "zone notify queued"; result = zone_from_args(server, args, &zone, NULL, text); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) return (ISC_R_UNEXPECTEDEND); dns_zone_notify(zone); dns_zone_detach(&zone); if (sizeof(msg) <= isc_buffer_availablelength(text)) isc_buffer_putmem(text, msg, sizeof(msg)); return (ISC_R_SUCCESS); } /* * Act on a "refresh" command from the command channel. */ isc_result_t ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result; dns_zone_t *zone = NULL; const unsigned char msg1[] = "zone refresh queued"; const unsigned char msg2[] = "not a slave or stub zone"; dns_zonetype_t type; result = zone_from_args(server, args, &zone, NULL, text); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) return (ISC_R_UNEXPECTEDEND); type = dns_zone_gettype(zone); if (type == dns_zone_slave || type == dns_zone_stub) { dns_zone_refresh(zone); dns_zone_detach(&zone); if (sizeof(msg1) <= isc_buffer_availablelength(text)) isc_buffer_putmem(text, msg1, sizeof(msg1)); return (ISC_R_SUCCESS); } dns_zone_detach(&zone); if (sizeof(msg2) <= isc_buffer_availablelength(text)) isc_buffer_putmem(text, msg2, sizeof(msg2)); return (ISC_R_FAILURE); } isc_result_t ns_server_togglequerylog(ns_server_t *server) { server->log_queries = server->log_queries ? ISC_FALSE : ISC_TRUE; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "query logging is now %s", server->log_queries ? "on" : "off"); return (ISC_R_SUCCESS); } static isc_result_t ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, ns_listenlist_t **target) { isc_result_t result; const cfg_listelt_t *element; ns_listenlist_t *dlist = NULL; REQUIRE(target != NULL && *target == NULL); result = ns_listenlist_create(mctx, &dlist); if (result != ISC_R_SUCCESS) return (result); for (element = cfg_list_first(listenlist); element != NULL; element = cfg_list_next(element)) { ns_listenelt_t *delt = NULL; const cfg_obj_t *listener = cfg_listelt_value(element); result = ns_listenelt_fromconfig(listener, config, actx, mctx, &delt); if (result != ISC_R_SUCCESS) goto cleanup; ISC_LIST_APPEND(dlist->elts, delt, link); } *target = dlist; return (ISC_R_SUCCESS); cleanup: ns_listenlist_detach(&dlist); return (result); } /* * Create a listen list from the corresponding configuration * data structure. */ static isc_result_t ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, ns_listenelt_t **target) { isc_result_t result; const cfg_obj_t *portobj; in_port_t port; ns_listenelt_t *delt = NULL; REQUIRE(target != NULL && *target == NULL); portobj = cfg_tuple_get(listener, "port"); if (!cfg_obj_isuint32(portobj)) { if (ns_g_port != 0) { port = ns_g_port; } else { result = ns_config_getport(config, &port); if (result != ISC_R_SUCCESS) return (result); } } else { if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port value '%u' is out of range", cfg_obj_asuint32(portobj)); return (ISC_R_RANGE); } port = (in_port_t)cfg_obj_asuint32(portobj); } result = ns_listenelt_create(mctx, port, NULL, &delt); if (result != ISC_R_SUCCESS) return (result); result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"), config, ns_g_lctx, actx, mctx, 0, &delt->acl); if (result != ISC_R_SUCCESS) { ns_listenelt_destroy(delt); return (result); } *target = delt; return (ISC_R_SUCCESS); } isc_result_t ns_server_dumpstats(ns_server_t *server) { isc_result_t result; FILE *fp = NULL; CHECKMF(isc_stdio_open(server->statsfile, "a", &fp), "could not open statistics dump file", server->statsfile); result = ns_stats_dump(server, fp); cleanup: if (fp != NULL) (void)isc_stdio_close(fp); if (result == ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dumpstats complete"); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "dumpstats failed: %s", dns_result_totext(result)); return (result); } static isc_result_t add_zone_tolist(dns_zone_t *zone, void *uap) { struct dumpcontext *dctx = uap; struct zonelistentry *zle; zle = isc_mem_get(dctx->mctx, sizeof *zle); if (zle == NULL) return (ISC_R_NOMEMORY); zle->zone = NULL; dns_zone_attach(zone, &zle->zone); ISC_LINK_INIT(zle, link); ISC_LIST_APPEND(ISC_LIST_TAIL(dctx->viewlist)->zonelist, zle, link); return (ISC_R_SUCCESS); } static isc_result_t add_view_tolist(struct dumpcontext *dctx, dns_view_t *view) { struct viewlistentry *vle; isc_result_t result = ISC_R_SUCCESS; /* * Prevent duplicate views. */ for (vle = ISC_LIST_HEAD(dctx->viewlist); vle != NULL; vle = ISC_LIST_NEXT(vle, link)) if (vle->view == view) return (ISC_R_SUCCESS); vle = isc_mem_get(dctx->mctx, sizeof *vle); if (vle == NULL) return (ISC_R_NOMEMORY); vle->view = NULL; dns_view_attach(view, &vle->view); ISC_LINK_INIT(vle, link); ISC_LIST_INIT(vle->zonelist); ISC_LIST_APPEND(dctx->viewlist, vle, link); if (dctx->dumpzones) result = dns_zt_apply(view->zonetable, ISC_TRUE, add_zone_tolist, dctx); return (result); } static void dumpcontext_destroy(struct dumpcontext *dctx) { struct viewlistentry *vle; struct zonelistentry *zle; vle = ISC_LIST_HEAD(dctx->viewlist); while (vle != NULL) { ISC_LIST_UNLINK(dctx->viewlist, vle, link); zle = ISC_LIST_HEAD(vle->zonelist); while (zle != NULL) { ISC_LIST_UNLINK(vle->zonelist, zle, link); dns_zone_detach(&zle->zone); isc_mem_put(dctx->mctx, zle, sizeof *zle); zle = ISC_LIST_HEAD(vle->zonelist); } dns_view_detach(&vle->view); isc_mem_put(dctx->mctx, vle, sizeof *vle); vle = ISC_LIST_HEAD(dctx->viewlist); } if (dctx->version != NULL) dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE); if (dctx->db != NULL) dns_db_detach(&dctx->db); if (dctx->cache != NULL) dns_db_detach(&dctx->cache); if (dctx->task != NULL) isc_task_detach(&dctx->task); if (dctx->fp != NULL) (void)isc_stdio_close(dctx->fp); if (dctx->mdctx != NULL) dns_dumpctx_detach(&dctx->mdctx); isc_mem_put(dctx->mctx, dctx, sizeof *dctx); } static void dumpdone(void *arg, isc_result_t result) { struct dumpcontext *dctx = arg; char buf[1024+32]; const dns_master_style_t *style; if (result != ISC_R_SUCCESS) goto cleanup; if (dctx->mdctx != NULL) dns_dumpctx_detach(&dctx->mdctx); if (dctx->view == NULL) { dctx->view = ISC_LIST_HEAD(dctx->viewlist); if (dctx->view == NULL) goto done; INSIST(dctx->zone == NULL); } else goto resume; nextview: fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name); resume: if (dctx->dumpcache && dns_view_iscacheshared(dctx->view->view)) { fprintf(dctx->fp, ";\n; Cache of view '%s' is shared as '%s'\n", dctx->view->view->name, dns_cache_getname(dctx->view->view->cache)); } else if (dctx->zone == NULL && dctx->cache == NULL && dctx->dumpcache) { style = &dns_master_style_cache; /* start cache dump */ if (dctx->view->view->cachedb != NULL) dns_db_attach(dctx->view->view->cachedb, &dctx->cache); if (dctx->cache != NULL) { fprintf(dctx->fp, ";\n; Cache dump of view '%s' (cache %s)\n;\n", dctx->view->view->name, dns_cache_getname(dctx->view->view->cache)); result = dns_master_dumptostreaminc(dctx->mctx, dctx->cache, NULL, style, dctx->fp, dctx->task, dumpdone, dctx, &dctx->mdctx); if (result == DNS_R_CONTINUE) return; if (result == ISC_R_NOTIMPLEMENTED) fprintf(dctx->fp, "; %s\n", dns_result_totext(result)); else if (result != ISC_R_SUCCESS) goto cleanup; } } if (dctx->cache != NULL) { dns_adb_dump(dctx->view->view->adb, dctx->fp); dns_resolver_printbadcache(dctx->view->view->resolver, dctx->fp); dns_db_detach(&dctx->cache); } if (dctx->dumpzones) { style = &dns_master_style_full; nextzone: if (dctx->version != NULL) dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE); if (dctx->db != NULL) dns_db_detach(&dctx->db); if (dctx->zone == NULL) dctx->zone = ISC_LIST_HEAD(dctx->view->zonelist); else dctx->zone = ISC_LIST_NEXT(dctx->zone, link); if (dctx->zone != NULL) { /* start zone dump */ dns_zone_name(dctx->zone->zone, buf, sizeof(buf)); fprintf(dctx->fp, ";\n; Zone dump of '%s'\n;\n", buf); result = dns_zone_getdb(dctx->zone->zone, &dctx->db); if (result != ISC_R_SUCCESS) { fprintf(dctx->fp, "; %s\n", dns_result_totext(result)); goto nextzone; } dns_db_currentversion(dctx->db, &dctx->version); result = dns_master_dumptostreaminc(dctx->mctx, dctx->db, dctx->version, style, dctx->fp, dctx->task, dumpdone, dctx, &dctx->mdctx); if (result == DNS_R_CONTINUE) return; if (result == ISC_R_NOTIMPLEMENTED) { fprintf(dctx->fp, "; %s\n", dns_result_totext(result)); result = ISC_R_SUCCESS; POST(result); goto nextzone; } if (result != ISC_R_SUCCESS) goto cleanup; } } if (dctx->view != NULL) dctx->view = ISC_LIST_NEXT(dctx->view, link); if (dctx->view != NULL) goto nextview; done: fprintf(dctx->fp, "; Dump complete\n"); result = isc_stdio_flush(dctx->fp); if (result == ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dumpdb complete"); cleanup: if (result != ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "dumpdb failed: %s", dns_result_totext(result)); dumpcontext_destroy(dctx); } isc_result_t ns_server_dumpdb(ns_server_t *server, char *args) { struct dumpcontext *dctx = NULL; dns_view_t *view; isc_result_t result; char *ptr; const char *sep; /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); dctx = isc_mem_get(server->mctx, sizeof(*dctx)); if (dctx == NULL) return (ISC_R_NOMEMORY); dctx->mctx = server->mctx; dctx->dumpcache = ISC_TRUE; dctx->dumpzones = ISC_FALSE; dctx->fp = NULL; ISC_LIST_INIT(dctx->viewlist); dctx->view = NULL; dctx->zone = NULL; dctx->cache = NULL; dctx->mdctx = NULL; dctx->db = NULL; dctx->cache = NULL; dctx->task = NULL; dctx->version = NULL; isc_task_attach(server->task, &dctx->task); CHECKMF(isc_stdio_open(server->dumpfile, "w", &dctx->fp), "could not open dump file", server->dumpfile); sep = (args == NULL) ? "" : ": "; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dumpdb started%s%s", sep, (args != NULL) ? args : ""); ptr = next_token(&args, " \t"); if (ptr != NULL && strcmp(ptr, "-all") == 0) { dctx->dumpzones = ISC_TRUE; dctx->dumpcache = ISC_TRUE; ptr = next_token(&args, " \t"); } else if (ptr != NULL && strcmp(ptr, "-cache") == 0) { dctx->dumpzones = ISC_FALSE; dctx->dumpcache = ISC_TRUE; ptr = next_token(&args, " \t"); } else if (ptr != NULL && strcmp(ptr, "-zones") == 0) { dctx->dumpzones = ISC_TRUE; dctx->dumpcache = ISC_FALSE; ptr = next_token(&args, " \t"); } nextview: for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (ptr != NULL && strcmp(view->name, ptr) != 0) continue; CHECK(add_view_tolist(dctx, view)); } if (ptr != NULL) { ptr = next_token(&args, " \t"); if (ptr != NULL) goto nextview; } dumpdone(dctx, ISC_R_SUCCESS); return (ISC_R_SUCCESS); cleanup: if (dctx != NULL) dumpcontext_destroy(dctx); return (result); } isc_result_t ns_server_dumpsecroots(ns_server_t *server, char *args) { dns_view_t *view; dns_keytable_t *secroots = NULL; isc_result_t result; char *ptr; FILE *fp = NULL; isc_time_t now; char tbuf[64]; /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); ptr = next_token(&args, " \t"); CHECKMF(isc_stdio_open(server->secrootsfile, "w", &fp), "could not open secroots dump file", server->secrootsfile); TIME_NOW(&now); isc_time_formattimestamp(&now, tbuf, sizeof(tbuf)); fprintf(fp, "%s\n", tbuf); do { for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (ptr != NULL && strcmp(view->name, ptr) != 0) continue; if (secroots != NULL) dns_keytable_detach(&secroots); result = dns_view_getsecroots(view, &secroots); if (result == ISC_R_NOTFOUND) { result = ISC_R_SUCCESS; continue; } fprintf(fp, "\n Start view %s\n\n", view->name); result = dns_keytable_dump(secroots, fp); if (result != ISC_R_SUCCESS) fprintf(fp, " dumpsecroots failed: %s\n", isc_result_totext(result)); } if (ptr != NULL) ptr = next_token(&args, " \t"); } while (ptr != NULL); cleanup: if (secroots != NULL) dns_keytable_detach(&secroots); if (fp != NULL) (void)isc_stdio_close(fp); if (result == ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dumpsecroots complete"); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "dumpsecroots failed: %s", dns_result_totext(result)); return (result); } isc_result_t ns_server_dumprecursing(ns_server_t *server) { FILE *fp = NULL; isc_result_t result; CHECKMF(isc_stdio_open(server->recfile, "w", &fp), "could not open dump file", server->recfile); fprintf(fp,";\n; Recursing Queries\n;\n"); ns_interfacemgr_dumprecursing(fp, server->interfacemgr); fprintf(fp, "; Dump complete\n"); cleanup: if (fp != NULL) result = isc_stdio_close(fp); if (result == ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dumprecursing complete"); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "dumprecursing failed: %s", dns_result_totext(result)); return (result); } isc_result_t ns_server_setdebuglevel(ns_server_t *server, char *args) { char *ptr; char *levelstr; char *endp; long newlevel; UNUSED(server); /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); /* Look for the new level name. */ levelstr = next_token(&args, " \t"); if (levelstr == NULL) { if (ns_g_debuglevel < 99) ns_g_debuglevel++; } else { newlevel = strtol(levelstr, &endp, 10); if (*endp != '\0' || newlevel < 0 || newlevel > 99) return (ISC_R_RANGE); ns_g_debuglevel = (unsigned int)newlevel; } isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "debug level is now %d", ns_g_debuglevel); return (ISC_R_SUCCESS); } isc_result_t ns_server_validation(ns_server_t *server, char *args) { char *ptr, *viewname; dns_view_t *view; isc_boolean_t changed = ISC_FALSE; isc_result_t result; isc_boolean_t enable; /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); /* Find out what we are to do. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") || !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true")) enable = ISC_TRUE; else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") || !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false")) enable = ISC_FALSE; else return (DNS_R_SYNTAX); /* Look for the view name. */ viewname = next_token(&args, " \t"); result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (viewname != NULL && strcasecmp(viewname, view->name) != 0) continue; result = dns_view_flushcache(view); if (result != ISC_R_SUCCESS) goto out; view->enablevalidation = enable; changed = ISC_TRUE; } if (changed) result = ISC_R_SUCCESS; else result = ISC_R_FAILURE; out: isc_task_endexclusive(server->task); return (result); } isc_result_t ns_server_flushcache(ns_server_t *server, char *args) { char *ptr, *viewname; dns_view_t *view; isc_boolean_t flushed; isc_boolean_t found; isc_result_t result; ns_cache_t *nsc; /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); /* Look for the view name. */ viewname = next_token(&args, " \t"); result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); flushed = ISC_TRUE; found = ISC_FALSE; /* * Flushing a cache is tricky when caches are shared by multiple views. * We first identify which caches should be flushed in the local cache * list, flush these caches, and then update other views that refer to * the flushed cache DB. */ if (viewname != NULL) { /* * Mark caches that need to be flushed. This is an O(#view^2) * operation in the very worst case, but should be normally * much more lightweight because only a few (most typically just * one) views will match. */ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (strcasecmp(viewname, view->name) != 0) continue; found = ISC_TRUE; for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL; nsc = ISC_LIST_NEXT(nsc, link)) { if (nsc->cache == view->cache) break; } INSIST(nsc != NULL); nsc->needflush = ISC_TRUE; } } else found = ISC_TRUE; /* Perform flush */ for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL; nsc = ISC_LIST_NEXT(nsc, link)) { if (viewname != NULL && !nsc->needflush) continue; nsc->needflush = ISC_TRUE; result = dns_view_flushcache2(nsc->primaryview, ISC_FALSE); if (result != ISC_R_SUCCESS) { flushed = ISC_FALSE; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "flushing cache in view '%s' failed: %s", nsc->primaryview->name, isc_result_totext(result)); } } /* * Fix up views that share a flushed cache: let the views update the * cache DB they're referring to. This could also be an expensive * operation, but should typically be marginal: the inner loop is only * necessary for views that share a cache, and if there are many such * views the number of shared cache should normally be small. * A worst case is that we have n views and n/2 caches, each shared by * two views. Then this will be a O(n^2/4) operation. */ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (!dns_view_iscacheshared(view)) continue; for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL; nsc = ISC_LIST_NEXT(nsc, link)) { if (!nsc->needflush || nsc->cache != view->cache) continue; result = dns_view_flushcache2(view, ISC_TRUE); if (result != ISC_R_SUCCESS) { flushed = ISC_FALSE; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "fixing cache in view '%s' " "failed: %s", view->name, isc_result_totext(result)); } } } /* Cleanup the cache list. */ for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL; nsc = ISC_LIST_NEXT(nsc, link)) { nsc->needflush = ISC_FALSE; } if (flushed && found) { if (viewname != NULL) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "flushing cache in view '%s' succeeded", viewname); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "flushing caches in all views succeeded"); result = ISC_R_SUCCESS; } else { if (!found) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "flushing cache in view '%s' failed: " "view not found", viewname); result = ISC_R_NOTFOUND; } else result = ISC_R_FAILURE; } isc_task_endexclusive(server->task); return (result); } isc_result_t ns_server_flushname(ns_server_t *server, char *args) { char *ptr, *target, *viewname; dns_view_t *view; isc_boolean_t flushed; isc_boolean_t found; isc_result_t result; isc_buffer_t b; dns_fixedname_t fixed; dns_name_t *name; /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); /* Find the domain name to flush. */ target = next_token(&args, " \t"); if (target == NULL) return (ISC_R_UNEXPECTEDEND); isc_buffer_constinit(&b, target, strlen(target)); isc_buffer_add(&b, strlen(target)); dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) return (result); /* Look for the view name. */ viewname = next_token(&args, " \t"); result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); flushed = ISC_TRUE; found = ISC_FALSE; for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (viewname != NULL && strcasecmp(viewname, view->name) != 0) continue; found = ISC_TRUE; /* * It's a little inefficient to try flushing name for all views * if some of the views share a single cache. But since the * operation is lightweight we prefer simplicity here. */ result = dns_view_flushname(view, name); if (result != ISC_R_SUCCESS) { flushed = ISC_FALSE; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "flushing name '%s' in cache view '%s' " "failed: %s", target, view->name, isc_result_totext(result)); } } if (flushed && found) { if (viewname != NULL) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "flushing name '%s' in cache view '%s' " "succeeded", target, viewname); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "flushing name '%s' in all cache views " "succeeded", target); result = ISC_R_SUCCESS; } else { if (!found) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "flushing name '%s' in cache view '%s' " "failed: view not found", target, viewname); result = ISC_R_FAILURE; } isc_task_endexclusive(server->task); return (result); } isc_result_t ns_server_status(ns_server_t *server, isc_buffer_t *text) { int zonecount, xferrunning, xferdeferred, soaqueries; unsigned int n; const char *ob = "", *cb = "", *alt = ""; if (ns_g_server->version_set) { ob = " ("; cb = ")"; if (ns_g_server->version == NULL) alt = "version.bind/txt/ch disabled"; else alt = ns_g_server->version; } zonecount = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_ANY); xferrunning = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_XFERRUNNING); xferdeferred = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_XFERDEFERRED); soaqueries = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_SOAQUERY); n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "version: %s%s%s%s \n" #ifdef ISC_PLATFORM_USETHREADS "CPUs found: %u\n" "worker threads: %u\n" #endif "number of zones: %u\n" "debug level: %d\n" "xfers running: %u\n" "xfers deferred: %u\n" "soa queries in progress: %u\n" "query logging is %s\n" "recursive clients: %d/%d/%d\n" "tcp clients: %d/%d\n" "server is up and running", ns_g_version, ob, alt, cb, ns_g_srcid, #ifdef ISC_PLATFORM_USETHREADS ns_g_cpus_detected, ns_g_cpus, #endif zonecount, ns_g_debuglevel, xferrunning, xferdeferred, soaqueries, server->log_queries ? "ON" : "OFF", server->recursionquota.used, server->recursionquota.soft, server->recursionquota.max, server->tcpquota.used, server->tcpquota.max); if (n >= isc_buffer_availablelength(text)) return (ISC_R_NOSPACE); isc_buffer_add(text, n); return (ISC_R_SUCCESS); } static isc_result_t delete_keynames(dns_tsig_keyring_t *ring, char *target, unsigned int *foundkeys) { char namestr[DNS_NAME_FORMATSIZE]; isc_result_t result; dns_rbtnodechain_t chain; dns_name_t foundname; dns_fixedname_t fixedorigin; dns_name_t *origin; dns_rbtnode_t *node; dns_tsigkey_t *tkey; dns_name_init(&foundname, NULL); dns_fixedname_init(&fixedorigin); origin = dns_fixedname_name(&fixedorigin); again: dns_rbtnodechain_init(&chain, ring->mctx); result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, origin); if (result == ISC_R_NOTFOUND) { dns_rbtnodechain_invalidate(&chain); return (ISC_R_SUCCESS); } if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { dns_rbtnodechain_invalidate(&chain); return (result); } for (;;) { node = NULL; dns_rbtnodechain_current(&chain, &foundname, origin, &node); tkey = node->data; if (tkey != NULL) { if (!tkey->generated) goto nextkey; dns_name_format(&tkey->name, namestr, sizeof(namestr)); if (strcmp(namestr, target) == 0) { (*foundkeys)++; dns_rbtnodechain_invalidate(&chain); (void)dns_rbt_deletename(ring->keys, &tkey->name, ISC_FALSE); goto again; } } nextkey: result = dns_rbtnodechain_next(&chain, &foundname, origin); if (result == ISC_R_NOMORE) break; if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { dns_rbtnodechain_invalidate(&chain); return (result); } } return (ISC_R_SUCCESS); } isc_result_t ns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text) { isc_result_t result; unsigned int n; dns_view_t *view; unsigned int foundkeys = 0; char *target; char *viewname; (void)next_token(&command, " \t"); /* skip command name */ target = next_token(&command, " \t"); if (target == NULL) return (ISC_R_UNEXPECTEDEND); viewname = next_token(&command, " \t"); result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (viewname == NULL || strcmp(view->name, viewname) == 0) { RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_write); result = delete_keynames(view->dynamickeys, target, &foundkeys); RWUNLOCK(&view->dynamickeys->lock, isc_rwlocktype_write); if (result != ISC_R_SUCCESS) { isc_task_endexclusive(server->task); return (result); } } } isc_task_endexclusive(server->task); n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "%d tsig keys deleted.\n", foundkeys); if (n >= isc_buffer_availablelength(text)) return (ISC_R_NOSPACE); isc_buffer_add(text, n); return (ISC_R_SUCCESS); } static isc_result_t list_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t *text, unsigned int *foundkeys) { char namestr[DNS_NAME_FORMATSIZE]; char creatorstr[DNS_NAME_FORMATSIZE]; isc_result_t result; dns_rbtnodechain_t chain; dns_name_t foundname; dns_fixedname_t fixedorigin; dns_name_t *origin; dns_rbtnode_t *node; dns_tsigkey_t *tkey; unsigned int n; const char *viewname; if (view != NULL) viewname = view->name; else viewname = "(global)"; dns_name_init(&foundname, NULL); dns_fixedname_init(&fixedorigin); origin = dns_fixedname_name(&fixedorigin); dns_rbtnodechain_init(&chain, ring->mctx); result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, origin); if (result == ISC_R_NOTFOUND) { dns_rbtnodechain_invalidate(&chain); return (ISC_R_SUCCESS); } if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { dns_rbtnodechain_invalidate(&chain); return (result); } for (;;) { node = NULL; dns_rbtnodechain_current(&chain, &foundname, origin, &node); tkey = node->data; if (tkey != NULL) { (*foundkeys)++; dns_name_format(&tkey->name, namestr, sizeof(namestr)); if (tkey->generated) { dns_name_format(tkey->creator, creatorstr, sizeof(creatorstr)); n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "view \"%s\"; type \"dynamic\"; key \"%s\"; creator \"%s\";\n", viewname, namestr, creatorstr); } else { n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "view \"%s\"; type \"static\"; key \"%s\";\n", viewname, namestr); } if (n >= isc_buffer_availablelength(text)) { dns_rbtnodechain_invalidate(&chain); return (ISC_R_NOSPACE); } isc_buffer_add(text, n); } result = dns_rbtnodechain_next(&chain, &foundname, origin); if (result == ISC_R_NOMORE) break; if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { dns_rbtnodechain_invalidate(&chain); return (result); } } return (ISC_R_SUCCESS); } isc_result_t ns_server_tsiglist(ns_server_t *server, isc_buffer_t *text) { isc_result_t result; unsigned int n; dns_view_t *view; unsigned int foundkeys = 0; result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { RWLOCK(&view->statickeys->lock, isc_rwlocktype_read); result = list_keynames(view, view->statickeys, text, &foundkeys); RWUNLOCK(&view->statickeys->lock, isc_rwlocktype_read); if (result != ISC_R_SUCCESS) { isc_task_endexclusive(server->task); return (result); } RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_read); result = list_keynames(view, view->dynamickeys, text, &foundkeys); RWUNLOCK(&view->dynamickeys->lock, isc_rwlocktype_read); if (result != ISC_R_SUCCESS) { isc_task_endexclusive(server->task); return (result); } } isc_task_endexclusive(server->task); if (foundkeys == 0) { n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "no tsig keys found.\n"); if (n >= isc_buffer_availablelength(text)) return (ISC_R_NOSPACE); isc_buffer_add(text, n); } return (ISC_R_SUCCESS); } /* * Act on a "sign" or "loadkeys" command from the command channel. */ isc_result_t ns_server_rekey(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result; dns_zone_t *zone = NULL; dns_zonetype_t type; isc_uint16_t keyopts; isc_boolean_t fullsign = ISC_FALSE; if (strncasecmp(args, NS_COMMAND_SIGN, strlen(NS_COMMAND_SIGN)) == 0) fullsign = ISC_TRUE; result = zone_from_args(server, args, &zone, NULL, text); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) return (ISC_R_UNEXPECTEDEND); /* XXX: or do all zones? */ type = dns_zone_gettype(zone); if (type != dns_zone_master) { dns_zone_detach(&zone); return (DNS_R_NOTMASTER); } keyopts = dns_zone_getkeyopts(zone); /* "rndc loadkeys" requires "auto-dnssec maintain". */ if ((keyopts & DNS_ZONEKEY_ALLOW) == 0) result = ISC_R_NOPERM; else if ((keyopts & DNS_ZONEKEY_MAINTAIN) == 0 && !fullsign) result = ISC_R_NOPERM; else dns_zone_rekey(zone, fullsign); dns_zone_detach(&zone); return (result); } /* * Act on a "freeze" or "thaw" command from the command channel. */ isc_result_t ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args, isc_buffer_t *text) { isc_result_t result, tresult; dns_zone_t *zone = NULL; dns_zonetype_t type; char classstr[DNS_RDATACLASS_FORMATSIZE]; char zonename[DNS_NAME_FORMATSIZE]; dns_view_t *view; char *journal; const char *vname, *sep; isc_boolean_t frozen; const char *msg = NULL; result = zone_from_args(server, args, &zone, NULL, text); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) { result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); tresult = ISC_R_SUCCESS; for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { result = dns_view_freezezones(view, freeze); if (result != ISC_R_SUCCESS && tresult == ISC_R_SUCCESS) tresult = result; } isc_task_endexclusive(server->task); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "%s all zones: %s", freeze ? "freezing" : "thawing", isc_result_totext(tresult)); return (tresult); } type = dns_zone_gettype(zone); if (type != dns_zone_master) { dns_zone_detach(&zone); return (DNS_R_NOTMASTER); } result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); frozen = dns_zone_getupdatedisabled(zone); if (freeze) { if (frozen) { msg = "WARNING: The zone was already frozen.\n" "Someone else may be editing it or " "it may still be re-loading."; result = DNS_R_FROZEN; } if (result == ISC_R_SUCCESS) { result = dns_zone_flush(zone); if (result != ISC_R_SUCCESS) msg = "Flushing the zone updates to " "disk failed."; } if (result == ISC_R_SUCCESS) { journal = dns_zone_getjournal(zone); if (journal != NULL) (void)isc_file_remove(journal); } if (result == ISC_R_SUCCESS) dns_zone_setupdatedisabled(zone, freeze); } else { if (frozen) { result = dns_zone_loadandthaw(zone); switch (result) { case ISC_R_SUCCESS: case DNS_R_UPTODATE: msg = "The zone reload and thaw was " "successful."; result = ISC_R_SUCCESS; break; case DNS_R_CONTINUE: msg = "A zone reload and thaw was started.\n" "Check the logs to see the result."; result = ISC_R_SUCCESS; break; } } } isc_task_endexclusive(server->task); if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text)) isc_buffer_putmem(text, (const unsigned char *)msg, strlen(msg) + 1); view = dns_zone_getview(zone); if (strcmp(view->name, "_default") == 0 || strcmp(view->name, "_bind") == 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)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "%s zone '%s/%s'%s%s: %s", freeze ? "freezing" : "thawing", zonename, classstr, sep, vname, isc_result_totext(result)); dns_zone_detach(&zone); return (result); } #ifdef HAVE_LIBSCF /* * This function adds a message for rndc to echo if named * is managed by smf and is also running chroot. */ isc_result_t ns_smf_add_message(isc_buffer_t *text) { unsigned int n; n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "use svcadm(1M) to manage named"); if (n >= isc_buffer_availablelength(text)) return (ISC_R_NOSPACE); isc_buffer_add(text, n); return (ISC_R_SUCCESS); } #endif /* HAVE_LIBSCF */ /* * Emit a comment at the top of the nzf file containing the viewname * Expects the fp to already be open for writing */ #define HEADER1 "# New zone file for view: " #define HEADER2 "\n# This file contains configuration for zones added by\n" \ "# the 'rndc addzone' command. DO NOT EDIT BY HAND.\n" isc_result_t add_comment(FILE *fp, const char *viewname) { isc_result_t result; CHECK(isc_stdio_write(HEADER1, sizeof(HEADER1) - 1, 1, fp, NULL)); CHECK(isc_stdio_write(viewname, strlen(viewname), 1, fp, NULL)); CHECK(isc_stdio_write(HEADER2, sizeof(HEADER2) - 1, 1, fp, NULL)); cleanup: return (result); } /* * Act on an "addzone" command from the command channel. */ isc_result_t ns_server_add_zone(ns_server_t *server, char *args) { isc_result_t result; isc_buffer_t argbuf; size_t arglen; cfg_parser_t *parser = NULL; cfg_obj_t *config = NULL; const cfg_obj_t *vconfig = NULL; const cfg_obj_t *views = NULL; const cfg_obj_t *parms = NULL; const cfg_obj_t *obj = NULL; const cfg_listelt_t *element; const char *zonename; const char *classname = NULL; const char *argp; const char *viewname = NULL; dns_rdataclass_t rdclass; dns_view_t *view = 0; isc_buffer_t buf; dns_fixedname_t fname; dns_name_t *dnsname; dns_zone_t *zone = NULL; FILE *fp = NULL; struct cfg_context *cfg = NULL; char namebuf[DNS_NAME_FORMATSIZE]; off_t offset; /* Try to parse the argument string */ arglen = strlen(args); isc_buffer_init(&argbuf, args, (unsigned int)arglen); isc_buffer_add(&argbuf, strlen(args)); CHECK(cfg_parser_create(server->mctx, ns_g_lctx, &parser)); CHECK(cfg_parse_buffer(parser, &argbuf, &cfg_type_addzoneconf, &config)); CHECK(cfg_map_get(config, "addzone", &parms)); zonename = cfg_obj_asstring(cfg_tuple_get(parms, "name")); isc_buffer_constinit(&buf, zonename, strlen(zonename)); isc_buffer_add(&buf, strlen(zonename)); dns_fixedname_init(&fname); dnsname = dns_fixedname_name(&fname); CHECK(dns_name_fromtext(dnsname, &buf, dns_rootname, ISC_FALSE, NULL)); /* Make sense of optional class argument */ obj = cfg_tuple_get(parms, "class"); CHECK(ns_config_getclass(obj, dns_rdataclass_in, &rdclass)); if (rdclass != dns_rdataclass_in && obj) classname = cfg_obj_asstring(obj); /* Make sense of optional view argument */ obj = cfg_tuple_get(parms, "view"); if (obj && cfg_obj_isstring(obj)) viewname = cfg_obj_asstring(obj); if (viewname == NULL || *viewname == '\0') viewname = "_default"; CHECK(dns_viewlist_find(&server->viewlist, viewname, rdclass, &view)); /* Are we accepting new zones? */ if (view->new_zone_file == NULL) { result = ISC_R_NOPERM; goto cleanup; } cfg = (struct cfg_context *) view->new_zone_config; if (cfg == NULL) { result = ISC_R_FAILURE; goto cleanup; } /* Zone shouldn't already exist */ result = dns_zt_find(view->zonetable, dnsname, 0, NULL, &zone); if (result == ISC_R_SUCCESS) { result = ISC_R_EXISTS; goto cleanup; } else if (result == DNS_R_PARTIALMATCH) { /* Create our sub-zone anyway */ dns_zone_detach(&zone); zone = NULL; } else if (result != ISC_R_NOTFOUND) goto cleanup; /* Find the view statement */ cfg_map_get(cfg->config, "view", &views); for (element = cfg_list_first(views); element != NULL; element = cfg_list_next(element)) { const char *vname; vconfig = cfg_listelt_value(element); vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name")); if (vname && !strcasecmp(vname, viewname)) break; vconfig = NULL; } /* Open save file for write configuration */ CHECK(isc_stdio_open(view->new_zone_file, "a", &fp)); CHECK(isc_stdio_tell(fp, &offset)); if (offset == 0) CHECK(add_comment(fp, view->name)); /* Mark view unfrozen so that zone can be added */ result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_view_thaw(view); result = configure_zone(cfg->config, parms, vconfig, server->mctx, view, cfg->actx, ISC_FALSE); dns_view_freeze(view); isc_task_endexclusive(server->task); if (result != ISC_R_SUCCESS) goto cleanup; /* Is it there yet? */ CHECK(dns_zt_find(view->zonetable, dnsname, 0, NULL, &zone)); /* * Load the zone from the master file. If this fails, we'll * need to undo the configuration we've done already. */ result = dns_zone_loadnew(zone); if (result != ISC_R_SUCCESS) { dns_db_t *dbp = NULL; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "addzone failed; reverting."); /* If the zone loaded partially, unload it */ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) { dns_db_detach(&dbp); dns_zone_unload(zone); } /* Remove the zone from the zone table */ dns_zt_unmount(view->zonetable, zone); goto cleanup; } /* Flag the zone as having been added at runtime */ dns_zone_setadded(zone, ISC_TRUE); /* Emit the zone name, quoted and escaped */ isc_buffer_init(&buf, namebuf, sizeof(namebuf)); CHECK(dns_name_totext(dnsname, ISC_TRUE, &buf)); isc_buffer_putuint8(&buf, 0); CHECK(isc_stdio_write("zone \"", 6, 1, fp, NULL)); CHECK(isc_stdio_write(namebuf, strlen(namebuf), 1, fp, NULL)); CHECK(isc_stdio_write("\" ", 2, 1, fp, NULL)); /* Classname, if not default */ if (classname != NULL && *classname != '\0') { CHECK(isc_stdio_write(classname, strlen(classname), 1, fp, NULL)); CHECK(isc_stdio_write(" ", 1, 1, fp, NULL)); } /* Find beginning of option block from args */ for (argp = args; *argp; argp++, arglen--) { if (*argp == '{') { /* Assume matching '}' */ /* Add that to our file */ CHECK(isc_stdio_write(argp, arglen, 1, fp, NULL)); /* Make sure we end with a LF */ if (argp[arglen-1] != '\n') { CHECK(isc_stdio_write("\n", 1, 1, fp, NULL)); } break; } } CHECK(isc_stdio_close(fp)); fp = NULL; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "zone %s added to view %s via addzone", zonename, viewname); result = ISC_R_SUCCESS; cleanup: if (fp != NULL) isc_stdio_close(fp); if (parser != NULL) { if (config != NULL) cfg_obj_destroy(parser, &config); cfg_parser_destroy(&parser); } if (zone != NULL) dns_zone_detach(&zone); if (view != NULL) dns_view_detach(&view); return (result); } /* * Act on a "delzone" command from the command channel. */ isc_result_t ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result; dns_zone_t *zone = NULL; dns_view_t *view = NULL; dns_db_t *dbp = NULL; const char *filename = NULL; char *tmpname = NULL; char buf[1024]; const char *zonename = NULL; size_t znamelen = 0; FILE *ifp = NULL, *ofp = NULL; isc_boolean_t inheader = ISC_TRUE; /* Parse parameters */ CHECK(zone_from_args(server, args, &zone, &zonename, text)); if (zone == NULL) { result = ISC_R_UNEXPECTEDEND; goto cleanup; } /* * Was this zone originally added at runtime? * If not, we can't delete it now. */ if (!dns_zone_getadded(zone)) { result = ISC_R_NOPERM; goto cleanup; } INSIST(zonename != NULL); znamelen = strlen(zonename); /* Dig out configuration for this zone */ view = dns_zone_getview(zone); filename = view->new_zone_file; if (filename == NULL) { /* No adding zones in this view */ result = ISC_R_FAILURE; goto cleanup; } /* Rewrite zone list */ result = isc_stdio_open(filename, "r", &ifp); if (ifp != NULL && result == ISC_R_SUCCESS) { char *found = NULL, *p = NULL; size_t n; /* Create a temporary file */ CHECK(isc_string_printf(buf, 1023, "%s.%ld", filename, (long)getpid())); if (!(tmpname = isc_mem_strdup(server->mctx, buf))) { result = ISC_R_NOMEMORY; goto cleanup; } CHECK(isc_stdio_open(tmpname, "w", &ofp)); CHECK(add_comment(ofp, view->name)); /* Look for the entry for that zone */ while (fgets(buf, 1024, ifp)) { /* Skip initial comment, if any */ if (inheader && *buf == '#') continue; if (*buf != '#') inheader = ISC_FALSE; /* * Any other lines not starting with zone, copy * them out and continue. */ if (strncasecmp(buf, "zone", 4) != 0) { fputs(buf, ofp); continue; } p = buf+4; /* This is a zone; find its name. */ while (*p && ((*p == '"') || isspace((unsigned char)*p))) p++; /* * If it's not the zone we're looking for, copy * it out and continue */ if (strncasecmp(p, zonename, znamelen) != 0) { fputs(buf, ofp); continue; } /* * But if it is the zone we want, skip over it * so it will be omitted from the new file */ p += znamelen; if (isspace((unsigned char)*p) || *p == '"' || *p == '{') { /* This must be the entry */ found = p; break; } /* Copy the rest of the buffer out and continue */ fputs(buf, ofp); } /* Skip over an option block (matching # of braces) */ if (found) { int obrace = 0, cbrace = 0; for (;;) { while (*p) { if (*p == '{') obrace++; if (*p == '}') cbrace++; p++; } if (obrace && (obrace == cbrace)) break; if (!fgets(buf, 1024, ifp)) break; p = buf; } /* Just spool the remainder of the file out */ result = isc_stdio_read(buf, 1, 1024, ifp, &n); while (n > 0U) { if (result == ISC_R_EOF) result = ISC_R_SUCCESS; CHECK(result); isc_stdio_write(buf, 1, n, ofp, NULL); result = isc_stdio_read(buf, 1, 1024, ifp, &n); } /* Move temporary into place */ CHECK(isc_file_rename(tmpname, view->new_zone_file)); } else { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "deleted zone %s was missing from " "new zone file", zonename); goto cleanup; } } /* Stop answering for this zone */ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) { dns_db_detach(&dbp); dns_zone_unload(zone); } CHECK(dns_zt_unmount(view->zonetable, zone)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "zone %s removed via delzone", zonename); result = ISC_R_SUCCESS; cleanup: if (ifp != NULL) isc_stdio_close(ifp); if (ofp != NULL) { isc_stdio_close(ofp); isc_file_remove(tmpname); } if (tmpname != NULL) isc_mem_free(server->mctx, tmpname); if (zone != NULL) dns_zone_detach(&zone); return (result); } static void newzone_cfgctx_destroy(void **cfgp) { struct cfg_context *cfg; REQUIRE(cfgp != NULL && *cfgp != NULL); cfg = *cfgp; if (cfg->actx != NULL) cfg_aclconfctx_detach(&cfg->actx); if (cfg->parser != NULL) { if (cfg->config != NULL) cfg_obj_destroy(cfg->parser, &cfg->config); cfg_parser_destroy(&cfg->parser); } if (cfg->nzparser != NULL) { if (cfg->nzconfig != NULL) cfg_obj_destroy(cfg->nzparser, &cfg->nzconfig); cfg_parser_destroy(&cfg->nzparser); } isc_mem_putanddetach(&cfg->mctx, cfg, sizeof(*cfg)); *cfgp = NULL; } static isc_result_t putstr(isc_buffer_t *b, const char *str) { size_t l = strlen(str); /* * Use >= to leave space for NUL termination. */ if (l >= isc_buffer_availablelength(b)) return (ISC_R_NOSPACE); isc_buffer_putmem(b, (const unsigned char *)str, l); return (ISC_R_SUCCESS); } Index: stable/8/contrib/bind9/lib/dns/adb.c =================================================================== --- stable/8/contrib/bind9/lib/dns/adb.c (revision 275668) +++ stable/8/contrib/bind9/lib/dns/adb.c (revision 275669) @@ -1,4149 +1,4170 @@ /* * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id$ */ /*! \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 #include #include #include #include #include #include /* Required for HP/UX (and others?) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #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; isc_boolean_t *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; isc_boolean_t *entry_sd; /*%< shutting down */ unsigned int *entry_refcnt; isc_event_t cevent; isc_boolean_t cevent_sent; isc_boolean_t shutting_down; isc_eventlist_t whenshutdown; isc_event_t growentries; isc_boolean_t growentries_sent; isc_event_t grownames; isc_boolean_t grownames_sent; }; /* * 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 flags; unsigned int srtt; isc_sockaddr_t sockaddr; isc_stdtime_t expires; /*%< * A nonzero 'expires' field indicates that the entry should * persist until that time. This allows entries found * using dns_adb_findaddrinfo() to persist for a limited time * even though they are not necessarily associated with a * name. */ ISC_LIST(dns_adblameinfo_t) lameinfo; ISC_LINK(dns_adbentry_t) plink; }; /* * Internal functions (and prototypes). */ static inline dns_adbname_t *new_adbname(dns_adb_t *, dns_name_t *); static inline void free_adbname(dns_adb_t *, dns_adbname_t **); static inline dns_adbnamehook_t *new_adbnamehook(dns_adb_t *, dns_adbentry_t *); static inline void free_adbnamehook(dns_adb_t *, dns_adbnamehook_t **); static inline dns_adblameinfo_t *new_adblameinfo(dns_adb_t *, dns_name_t *, dns_rdatatype_t); static inline void free_adblameinfo(dns_adb_t *, dns_adblameinfo_t **); static inline dns_adbentry_t *new_adbentry(dns_adb_t *); static inline void free_adbentry(dns_adb_t *, dns_adbentry_t **); static inline dns_adbfind_t *new_adbfind(dns_adb_t *); static inline isc_boolean_t free_adbfind(dns_adb_t *, dns_adbfind_t **); static inline dns_adbaddrinfo_t *new_adbaddrinfo(dns_adb_t *, dns_adbentry_t *, in_port_t); static inline dns_adbfetch_t *new_adbfetch(dns_adb_t *); static inline void free_adbfetch(dns_adb_t *, dns_adbfetch_t **); static inline dns_adbname_t *find_name_and_lock(dns_adb_t *, dns_name_t *, unsigned int, int *); static inline dns_adbentry_t *find_entry_and_lock(dns_adb_t *, isc_sockaddr_t *, int *, isc_stdtime_t); static void dump_adb(dns_adb_t *, FILE *, isc_boolean_t debug, isc_stdtime_t); static void print_dns_name(FILE *, dns_name_t *); static void print_namehook_list(FILE *, const char *legend, dns_adbnamehooklist_t *list, isc_boolean_t debug, isc_stdtime_t now); static void print_find_list(FILE *, dns_adbname_t *); static void print_fetch_list(FILE *, dns_adbname_t *); static inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *); static inline void inc_adb_irefcnt(dns_adb_t *); static inline void inc_adb_erefcnt(dns_adb_t *); static inline void inc_entry_refcnt(dns_adb_t *, dns_adbentry_t *, isc_boolean_t); static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, isc_boolean_t, dns_adbentry_t *, isc_boolean_t); static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *); static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *); 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 void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, unsigned int); static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t); static isc_boolean_t 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 *, isc_boolean_t, + unsigned int, isc_counter_t *qc, dns_rdatatype_t); static inline void check_exit(dns_adb_t *); static void destroy(dns_adb_t *); static isc_boolean_t shutdown_names(dns_adb_t *); static isc_boolean_t shutdown_entries(dns_adb_t *); static inline void link_name(dns_adb_t *, int, dns_adbname_t *); static inline isc_boolean_t unlink_name(dns_adb_t *, dns_adbname_t *); static inline void link_entry(dns_adb_t *, int, dns_adbentry_t *); static inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *); static isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t); static void water(void *, int); static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t); /* * MUST NOT overlap DNS_ADBFIND_* flags! */ #define FIND_EVENT_SENT 0x40000000 #define FIND_EVENT_FREED 0x80000000 #define FIND_EVENTSENT(h) (((h)->flags & FIND_EVENT_SENT) != 0) #define FIND_EVENTFREED(h) (((h)->flags & FIND_EVENT_FREED) != 0) #define NAME_NEEDS_POKE 0x80000000 #define NAME_IS_DEAD 0x40000000 #define NAME_HINT_OK DNS_ADBFIND_HINTOK #define NAME_GLUE_OK DNS_ADBFIND_GLUEOK #define NAME_STARTATZONE DNS_ADBFIND_STARTATZONE #define NAME_DEAD(n) (((n)->flags & NAME_IS_DEAD) != 0) #define NAME_NEEDSPOKE(n) (((n)->flags & NAME_NEEDS_POKE) != 0) #define NAME_GLUEOK(n) (((n)->flags & NAME_GLUE_OK) != 0) #define NAME_HINTOK(n) (((n)->flags & NAME_HINT_OK) != 0) /* * Private flag(s) for entries. * MUST NOT overlap FCTX_ADDRINFO_xxx and DNS_FETCHOPT_NOEDNS0. */ #define ENTRY_IS_DEAD 0x80000000 /* * 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); } 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; isc_boolean_t *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] = ISC_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, ISC_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, ISC_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; /* * Only on success do we set adb->growentries_sent to ISC_FALSE. * This will prevent us being continuously being called on error. */ adb->growentries_sent = ISC_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; isc_boolean_t *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] = ISC_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, ISC_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, ISC_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; /* * Only on success do we set adb->grownames_sent to ISC_FALSE. * This will prevent us being continuously being called on error. */ adb->grownames_sent = ISC_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; isc_boolean_t new_addresses_added; dns_rdatatype_t rdtype; unsigned int findoptions; dns_adbnamehooklist_t *hookhead; INSIST(DNS_ADBNAME_VALID(adbname)); adb = adbname->adb; INSIST(DNS_ADB_VALID(adb)); rdtype = rdataset->type; INSIST((rdtype == dns_rdatatype_a) || (rdtype == dns_rdatatype_aaaa)); if (rdtype == dns_rdatatype_a) findoptions = DNS_ADBFIND_INET; else findoptions = DNS_ADBFIND_INET6; addr_bucket = DNS_ADB_INVALIDBUCKET; new_addresses_added = ISC_FALSE; nh = NULL; result = dns_rdataset_first(rdataset); while (result == ISC_R_SUCCESS) { dns_rdata_reset(&rdata); dns_rdataset_current(rdataset, &rdata); if (rdtype == dns_rdatatype_a) { INSIST(rdata.length == 4); 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; nh->entry = entry; link_entry(adb, addr_bucket, entry); } else { for (anh = ISC_LIST_HEAD(*hookhead); anh != NULL; anh = ISC_LIST_NEXT(anh, plink)) if (anh->entry == foundentry) break; if (anh == NULL) { foundentry->refcnt++; nh->entry = foundentry; } else free_adbnamehook(adb, &nh); } new_addresses_added = ISC_TRUE; if (nh != NULL) ISC_LIST_APPEND(*hookhead, nh, plink); nh = NULL; result = dns_rdataset_next(rdataset); } fail: if (nh != NULL) free_adbnamehook(adb, &nh); if (addr_bucket != DNS_ADB_INVALIDBUCKET) UNLOCK(&adb->entrylocks[addr_bucket]); if (rdataset->trust == dns_trust_glue || rdataset->trust == dns_trust_additional) rdataset->ttl = ADB_CACHE_MINIMUM; else 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, now + rdataset->ttl); } else { DP(NCACHE_LEVEL, "expire_v6 set to MIN(%u,%u) import_rdataset", adbname->expire_v6, now + rdataset->ttl); adbname->expire_v6 = ISC_MIN(adbname->expire_v6, now + rdataset->ttl); } if (new_addresses_added) { /* * Lie a little here. This is more or less so code that cares * can find out if any new information was added or not. */ return (ISC_R_SUCCESS); } return (result); } /* * Requires the name's bucket be locked. */ static isc_boolean_t kill_name(dns_adbname_t **n, isc_eventtype_t ev) { dns_adbname_t *name; isc_boolean_t result = ISC_FALSE; isc_boolean_t 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 = ISC_TF(result4 || result6); /* * If fetches are running, cancel them. If none are running, we can * just kill the name here. */ if (!NAME_FETCH(name)) { INSIST(result == ISC_FALSE); result = unlink_name(adb, name); free_adbname(adb, &name); if (result) result = dec_adb_irefcnt(adb); } else { 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 isc_boolean_t check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) { dns_adb_t *adb; isc_boolean_t result4 = ISC_FALSE; isc_boolean_t result6 = ISC_FALSE; INSIST(DNS_ADBNAME_VALID(name)); adb = name->adb; INSIST(DNS_ADB_VALID(adb)); /* * Check to see if we need to remove the v4 addresses */ if (!NAME_FETCH_V4(name) && EXPIRE_OK(name->expire_v4, now)) { if (NAME_HAS_V4(name)) { DP(DEF_LEVEL, "expiring v4 for name %p", name); result4 = clean_namehooks(adb, &name->v4); 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 (ISC_TF(result4 || result6)); } /* * Requires the name's bucket be locked. */ static inline void link_name(dns_adb_t *adb, int bucket, dns_adbname_t *name) { INSIST(name->lock_bucket == DNS_ADB_INVALIDBUCKET); ISC_LIST_PREPEND(adb->names[bucket], name, plink); name->lock_bucket = bucket; adb->name_refcnt[bucket]++; } /* * Requires the name's bucket be locked. */ static inline isc_boolean_t unlink_name(dns_adb_t *adb, dns_adbname_t *name) { int bucket; isc_boolean_t result = ISC_FALSE; bucket = name->lock_bucket; INSIST(bucket != DNS_ADB_INVALIDBUCKET); 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 = ISC_TRUE; return (result); } /* * Requires the entry's bucket be locked. */ static inline void link_entry(dns_adb_t *adb, int bucket, dns_adbentry_t *entry) { 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 isc_boolean_t unlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) { int bucket; isc_boolean_t result = ISC_FALSE; bucket = entry->lock_bucket; INSIST(bucket != DNS_ADB_INVALIDBUCKET); 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 = ISC_TRUE; return (result); } static inline void violate_locking_hierarchy(isc_mutex_t *have, isc_mutex_t *want) { if (isc_mutex_trylock(want) != ISC_R_SUCCESS) { UNLOCK(have); LOCK(want); LOCK(have); } } /* * The ADB _MUST_ be locked before calling. Also, exit conditions must be * checked after calling this function. */ static isc_boolean_t shutdown_names(dns_adb_t *adb) { unsigned int bucket; isc_boolean_t result = ISC_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] = ISC_TRUE; name = ISC_LIST_HEAD(adb->names[bucket]); if (name == NULL) { /* * This bucket has no names. We must decrement the * irefcnt ourselves, since it will not be * automatically triggered by a name being unlinked. */ INSIST(result == ISC_FALSE); result = dec_adb_irefcnt(adb); } else { /* * Run through the list. For each name, clean up finds * found there, and cancel any fetches running. When * all the fetches are canceled, the name will destroy * itself. */ while (name != NULL) { next_name = ISC_LIST_NEXT(name, plink); INSIST(result == ISC_FALSE); result = kill_name(&name, DNS_EVENT_ADBSHUTDOWN); name = next_name; } } UNLOCK(&adb->namelocks[bucket]); } return (result); } /* * The ADB _MUST_ be locked before calling. Also, exit conditions must be * checked after calling this function. */ static isc_boolean_t shutdown_entries(dns_adb_t *adb) { unsigned int bucket; isc_boolean_t result = ISC_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] = ISC_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 isc_boolean_t clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) { dns_adbentry_t *entry; dns_adbnamehook_t *namehook; int addr_bucket; isc_boolean_t result = ISC_FALSE; isc_boolean_t 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]); } result = dec_entry_refcnt(adb, overmem, entry, ISC_FALSE); } /* * Free the namehook */ namehook->entry = NULL; ISC_LIST_UNLINK(*namehooks, namehook, plink); free_adbnamehook(adb, &namehook); namehook = ISC_LIST_HEAD(*namehooks); } if (addr_bucket != DNS_ADB_INVALIDBUCKET) UNLOCK(&adb->entrylocks[addr_bucket]); return (result); } static void clean_target(dns_adb_t *adb, dns_name_t *target) { if (dns_name_countlabels(target) > 0) { dns_name_free(target, adb->mctx); dns_name_init(target, NULL); } } static isc_result_t set_target(dns_adb_t *adb, dns_name_t *name, dns_name_t *fname, dns_rdataset_t *rdataset, dns_name_t *target) { isc_result_t result; dns_namereln_t namereln; unsigned int nlabels; int order; dns_rdata_t rdata = DNS_RDATA_INIT; dns_fixedname_t fixed1, fixed2; dns_name_t *prefix, *new_target; REQUIRE(dns_name_countlabels(target) == 0); if (rdataset->type == dns_rdatatype_cname) { dns_rdata_cname_t cname; /* * Copy the CNAME's target into the target name. */ result = dns_rdataset_first(rdataset); if (result != ISC_R_SUCCESS) return (result); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &cname, NULL); if (result != ISC_R_SUCCESS) return (result); result = dns_name_dup(&cname.cname, adb->mctx, target); dns_rdata_freestruct(&cname); if (result != ISC_R_SUCCESS) return (result); } else { dns_rdata_dname_t dname; INSIST(rdataset->type == dns_rdatatype_dname); namereln = dns_name_fullcompare(name, fname, &order, &nlabels); INSIST(namereln == dns_namereln_subdomain); /* * Get the target name of the DNAME. */ result = dns_rdataset_first(rdataset); if (result != ISC_R_SUCCESS) return (result); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &dname, NULL); if (result != ISC_R_SUCCESS) return (result); /* * Construct the new target name. */ dns_fixedname_init(&fixed1); prefix = dns_fixedname_name(&fixed1); dns_fixedname_init(&fixed2); new_target = dns_fixedname_name(&fixed2); dns_name_split(name, nlabels, prefix, NULL); result = dns_name_concatenate(prefix, &dname.dname, new_target, NULL); dns_rdata_freestruct(&dname); if (result != ISC_R_SUCCESS) return (result); result = dns_name_dup(new_target, adb->mctx, target); if (result != ISC_R_SUCCESS) return (result); } return (ISC_R_SUCCESS); } /* * Assumes nothing is locked, since this is called by the client. */ static void event_free(isc_event_t *event) { dns_adbfind_t *find; INSIST(event != NULL); find = event->ev_destroy_arg; INSIST(DNS_ADBFIND_VALID(find)); LOCK(&find->lock); find->flags |= FIND_EVENT_FREED; event->ev_destroy_arg = NULL; UNLOCK(&find->lock); } /* * Assumes the name bucket is locked. */ static void clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype, unsigned int addrs) { isc_event_t *ev; isc_task_t *task; dns_adbfind_t *find; dns_adbfind_t *next_find; isc_boolean_t process; unsigned int wanted, notify; DP(ENTER_LEVEL, "ENTER clean_finds_at_name, name %p, evtype %08x, addrs %08x", name, evtype, addrs); find = ISC_LIST_HEAD(name->finds); while (find != NULL) { LOCK(&find->lock); next_find = ISC_LIST_NEXT(find, plink); process = ISC_FALSE; wanted = find->flags & DNS_ADBFIND_ADDRESSMASK; notify = wanted & addrs; switch (evtype) { case DNS_EVENT_ADBMOREADDRESSES: DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBMOREADDRESSES"); if ((notify) != 0) { find->flags &= ~addrs; process = ISC_TRUE; } break; case DNS_EVENT_ADBNOMOREADDRESSES: DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBNOMOREADDRESSES"); find->flags &= ~addrs; wanted = find->flags & DNS_ADBFIND_ADDRESSMASK; if (wanted == 0) process = ISC_TRUE; break; default: find->flags &= ~addrs; process = ISC_TRUE; } if (process) { DP(DEF_LEVEL, "cfan: processing find %p", find); /* * Unlink the find from the name, letting the caller * call dns_adb_destroyfind() on it to clean it up * later. */ ISC_LIST_UNLINK(name->finds, find, plink); find->adbname = NULL; find->name_bucket = DNS_ADB_INVALIDBUCKET; INSIST(!FIND_EVENTSENT(find)); ev = &find->event; task = ev->ev_sender; ev->ev_sender = find; find->result_v4 = find_err_map[name->fetch_err]; find->result_v6 = find_err_map[name->fetch6_err]; ev->ev_type = evtype; ev->ev_destroy = event_free; ev->ev_destroy_arg = find; DP(DEF_LEVEL, "sending event %p to task %p for find %p", ev, task, find); isc_task_sendanddetach(&task, (isc_event_t **)&ev); } else { DP(DEF_LEVEL, "cfan: skipping find %p", find); } UNLOCK(&find->lock); find = next_find; } DP(ENTER_LEVEL, "EXIT clean_finds_at_name, name %p", name); } static inline void check_exit(dns_adb_t *adb) { isc_event_t *event; /* * The caller must be holding the adb lock. */ if (adb->shutting_down) { /* * If there aren't any external references either, we're * done. Send the control event to initiate shutdown. */ INSIST(!adb->cevent_sent); /* Sanity check. */ event = &adb->cevent; isc_task_send(adb->task, &event); adb->cevent_sent = ISC_TRUE; } } static inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *adb) { isc_event_t *event; isc_task_t *etask; isc_boolean_t result = ISC_FALSE; LOCK(&adb->reflock); INSIST(adb->irefcnt > 0); adb->irefcnt--; if (adb->irefcnt == 0) { event = ISC_LIST_HEAD(adb->whenshutdown); while (event != NULL) { ISC_LIST_UNLINK(adb->whenshutdown, event, ev_link); etask = event->ev_sender; event->ev_sender = adb; isc_task_sendanddetach(&etask, &event); event = ISC_LIST_HEAD(adb->whenshutdown); } } if (adb->irefcnt == 0 && adb->erefcnt == 0) result = ISC_TRUE; UNLOCK(&adb->reflock); return (result); } static inline void inc_adb_irefcnt(dns_adb_t *adb) { LOCK(&adb->reflock); adb->irefcnt++; UNLOCK(&adb->reflock); } static inline void inc_adb_erefcnt(dns_adb_t *adb) { LOCK(&adb->reflock); adb->erefcnt++; UNLOCK(&adb->reflock); } static inline void inc_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) { int bucket; bucket = entry->lock_bucket; if (lock) LOCK(&adb->entrylocks[bucket]); entry->refcnt++; if (lock) UNLOCK(&adb->entrylocks[bucket]); } static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *adb, isc_boolean_t overmem, dns_adbentry_t *entry, isc_boolean_t lock) { int bucket; isc_boolean_t destroy_entry; isc_boolean_t result = ISC_FALSE; bucket = entry->lock_bucket; if (lock) LOCK(&adb->entrylocks[bucket]); INSIST(entry->refcnt > 0); entry->refcnt--; destroy_entry = ISC_FALSE; if (entry->refcnt == 0 && (adb->entry_sd[bucket] || entry->expires == 0 || overmem || (entry->flags & ENTRY_IS_DEAD) != 0)) { destroy_entry = ISC_TRUE; result = unlink_entry(adb, entry); } if (lock) UNLOCK(&adb->entrylocks[bucket]); if (!destroy_entry) return (result); entry->lock_bucket = DNS_ADB_INVALIDBUCKET; free_adbentry(adb, &entry); if (result) result = dec_adb_irefcnt(adb); return (result); } static inline dns_adbname_t * new_adbname(dns_adb_t *adb, dns_name_t *dnsname) { dns_adbname_t *name; name = isc_mempool_get(adb->nmp); if (name == NULL) return (NULL); dns_name_init(&name->name, NULL); if (dns_name_dup(dnsname, adb->mctx, &name->name) != ISC_R_SUCCESS) { isc_mempool_put(adb->nmp, name); return (NULL); } dns_name_init(&name->target, NULL); name->magic = DNS_ADBNAME_MAGIC; name->adb = adb; name->partial_result = 0; name->flags = 0; name->expire_v4 = INT_MAX; name->expire_v6 = INT_MAX; name->expire_target = INT_MAX; name->chains = 0; name->lock_bucket = DNS_ADB_INVALIDBUCKET; ISC_LIST_INIT(name->v4); ISC_LIST_INIT(name->v6); name->fetch_a = NULL; name->fetch_aaaa = NULL; name->fetch_err = FIND_ERR_UNEXPECTED; name->fetch6_err = FIND_ERR_UNEXPECTED; ISC_LIST_INIT(name->finds); ISC_LINK_INIT(name, plink); LOCK(&adb->namescntlock); adb->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 = ISC_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--; 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; isc_uint32_t r; e = isc_mempool_get(adb->emp); if (e == NULL) return (NULL); e->magic = DNS_ADBENTRY_MAGIC; e->lock_bucket = DNS_ADB_INVALIDBUCKET; e->refcnt = 0; e->flags = 0; isc_random_get(&r); e->srtt = (r & 0x1f) + 1; e->expires = 0; ISC_LIST_INIT(e->lameinfo); ISC_LINK_INIT(e, plink); LOCK(&adb->entriescntlock); adb->entriescnt++; if (!adb->growentries_sent && adb->growentries_sent && adb->entriescnt > (adb->nentries * 8)) { isc_event_t *event = &adb->growentries; inc_adb_irefcnt(adb); isc_task_send(adb->task, &event); adb->growentries_sent = ISC_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; 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--; 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 isc_boolean_t free_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) { dns_adbfind_t *find; INSIST(findp != NULL && DNS_ADBFIND_VALID(*findp)); find = *findp; *findp = NULL; INSIST(!FIND_HAS_ADDRS(find)); INSIST(!ISC_LINK_LINKED(find, publink)); INSIST(!ISC_LINK_LINKED(find, plink)); INSIST(find->name_bucket == DNS_ADB_INVALIDBUCKET); INSIST(find->adbname == NULL); find->magic = 0; DESTROYLOCK(&find->lock); isc_mempool_put(adb->ahmp, find); return (dec_adb_irefcnt(adb)); } /* * Copy bits from the entry into the newly allocated addrinfo. The entry * must be locked, and the reference count must be bumped up by one * if this function returns a valid pointer. */ static inline dns_adbaddrinfo_t * new_adbaddrinfo(dns_adb_t *adb, dns_adbentry_t *entry, in_port_t port) { dns_adbaddrinfo_t *ai; ai = isc_mempool_get(adb->aimp); if (ai == NULL) return (NULL); ai->magic = DNS_ADBADDRINFO_MAGIC; ai->sockaddr = entry->sockaddr; isc_sockaddr_setport(&ai->sockaddr, port); ai->srtt = entry->srtt; ai->flags = entry->flags; ai->entry = entry; ISC_LINK_INIT(ai, publink); return (ai); } static inline void free_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) { dns_adbaddrinfo_t *ai; INSIST(ainfo != NULL && DNS_ADBADDRINFO_VALID(*ainfo)); ai = *ainfo; *ainfo = NULL; INSIST(ai->entry == NULL); INSIST(!ISC_LINK_LINKED(ai, publink)); ai->magic = 0; isc_mempool_put(adb->aimp, ai); } /* * Search for the name. NOTE: The bucket is kept locked on both * success and failure, so it must always be unlocked by the caller! * * On the first call to this function, *bucketp must be set to * DNS_ADB_INVALIDBUCKET. */ static inline dns_adbname_t * find_name_and_lock(dns_adb_t *adb, dns_name_t *name, unsigned int options, int *bucketp) { dns_adbname_t *adbname; int bucket; bucket = dns_name_fullhash(name, ISC_FALSE) % 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, ISC_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 && 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 isc_boolean_t entry_is_lame(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *qname, dns_rdatatype_t qtype, isc_stdtime_t now) { dns_adblameinfo_t *li, *next_li; isc_boolean_t is_bad; is_bad = ISC_FALSE; li = ISC_LIST_HEAD(entry->lameinfo); if (li == NULL) return (ISC_FALSE); while (li != NULL) { next_li = ISC_LIST_NEXT(li, plink); /* * Has the entry expired? */ if (li->lame_timer < now) { ISC_LIST_UNLINK(entry->lameinfo, li, plink); free_adblameinfo(adb, &li); } /* * Order tests from least to most expensive. * * We do not break out of the main loop here as * we use the loop for house keeping. */ if (li != NULL && !is_bad && li->qtype == qtype && dns_name_equal(qname, &li->qname)) is_bad = ISC_TRUE; li = next_li; } return (is_bad); } static void copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname, dns_rdatatype_t qtype, dns_adbname_t *name, isc_stdtime_t now) { dns_adbnamehook_t *namehook; dns_adbaddrinfo_t *addrinfo; dns_adbentry_t *entry; int bucket; bucket = DNS_ADB_INVALIDBUCKET; if (find->options & DNS_ADBFIND_INET) { namehook = ISC_LIST_HEAD(name->v4); while (namehook != NULL) { entry = namehook->entry; bucket = entry->lock_bucket; INSIST(bucket != DNS_ADB_INVALIDBUCKET); LOCK(&adb->entrylocks[bucket]); if (!FIND_RETURNLAME(find) && entry_is_lame(adb, entry, qname, qtype, now)) { find->options |= DNS_ADBFIND_LAMEPRUNED; goto nextv4; } addrinfo = new_adbaddrinfo(adb, entry, find->port); if (addrinfo == NULL) { find->partial_result |= DNS_ADBFIND_INET; goto out; } /* * Found a valid entry. Add it to the find's list. */ inc_entry_refcnt(adb, entry, ISC_FALSE); ISC_LIST_APPEND(find->list, addrinfo, publink); addrinfo = NULL; nextv4: UNLOCK(&adb->entrylocks[bucket]); bucket = DNS_ADB_INVALIDBUCKET; namehook = ISC_LIST_NEXT(namehook, plink); } } if (find->options & DNS_ADBFIND_INET6) { namehook = ISC_LIST_HEAD(name->v6); while (namehook != NULL) { entry = namehook->entry; bucket = entry->lock_bucket; INSIST(bucket != DNS_ADB_INVALIDBUCKET); LOCK(&adb->entrylocks[bucket]); if (!FIND_RETURNLAME(find) && entry_is_lame(adb, entry, qname, qtype, now)) { find->options |= DNS_ADBFIND_LAMEPRUNED; goto nextv6; } addrinfo = new_adbaddrinfo(adb, entry, find->port); if (addrinfo == NULL) { find->partial_result |= DNS_ADBFIND_INET6; goto out; } /* * Found a valid entry. Add it to the find's list. */ inc_entry_refcnt(adb, entry, ISC_FALSE); ISC_LIST_APPEND(find->list, addrinfo, publink); addrinfo = NULL; nextv6: UNLOCK(&adb->entrylocks[bucket]); bucket = DNS_ADB_INVALIDBUCKET; namehook = ISC_LIST_NEXT(namehook, plink); } } out: if (bucket != DNS_ADB_INVALIDBUCKET) UNLOCK(&adb->entrylocks[bucket]); } static void shutdown_task(isc_task_t *task, isc_event_t *ev) { dns_adb_t *adb; UNUSED(task); adb = ev->ev_arg; INSIST(DNS_ADB_VALID(adb)); isc_event_free(&ev); /* * Wait for lock around check_exit() call to be released. */ LOCK(&adb->lock); UNLOCK(&adb->lock); destroy(adb); } /* * Name bucket must be locked; adb may be locked; no other locks held. */ static isc_boolean_t check_expire_name(dns_adbname_t **namep, isc_stdtime_t now) { dns_adbname_t *name; isc_boolean_t result = ISC_FALSE; INSIST(namep != NULL && DNS_ADBNAME_VALID(*namep)); name = *namep; if (NAME_HAS_V4(name) || NAME_HAS_V6(name)) return (result); if (NAME_FETCH(name)) return (result); if (!EXPIRE_OK(name->expire_v4, now)) return (result); if (!EXPIRE_OK(name->expire_v6, now)) return (result); if (!EXPIRE_OK(name->expire_target, now)) return (result); /* * The name is empty. Delete it. */ result = kill_name(&name, DNS_EVENT_ADBEXPIRED); *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; isc_boolean_t 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) == ISC_FALSE); victims++; } next: if (!overmem) break; } } /* * Entry bucket must be locked; adb may be locked; no other locks held. */ static isc_boolean_t check_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now) { dns_adbentry_t *entry; isc_boolean_t result = ISC_FALSE; INSIST(entryp != NULL && DNS_ADBENTRY_VALID(*entryp)); entry = *entryp; if (entry->refcnt != 0) return (result); if (entry->expires == 0 || entry->expires > now) return (result); /* * The entry is not in use. Delete it. */ DP(DEF_LEVEL, "killing entry %p", entry); INSIST(ISC_LINK_LINKED(entry, plink)); result = unlink_entry(adb, entry); free_adbentry(adb, &entry); if (result) dec_adb_irefcnt(adb); *entryp = NULL; return (result); } /* * ADB must be locked, and no other locks held. */ static isc_boolean_t cleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) { dns_adbname_t *name; dns_adbname_t *next_name; isc_boolean_t result = ISC_FALSE; DP(CLEAN_LEVEL, "cleaning name bucket %d", bucket); LOCK(&adb->namelocks[bucket]); if (adb->name_sd[bucket]) { UNLOCK(&adb->namelocks[bucket]); return (result); } name = ISC_LIST_HEAD(adb->names[bucket]); while (name != NULL) { next_name = ISC_LIST_NEXT(name, plink); INSIST(result == ISC_FALSE); result = check_expire_namehooks(name, now); if (!result) result = check_expire_name(&name, now); name = next_name; } UNLOCK(&adb->namelocks[bucket]); return (result); } /* * ADB must be locked, and no other locks held. */ static isc_boolean_t cleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) { dns_adbentry_t *entry, *next_entry; isc_boolean_t result = ISC_FALSE; DP(CLEAN_LEVEL, "cleaning entry bucket %d", bucket); LOCK(&adb->entrylocks[bucket]); entry = ISC_LIST_HEAD(adb->entries[bucket]); while (entry != NULL) { next_entry = ISC_LIST_NEXT(entry, plink); INSIST(result == ISC_FALSE); result = check_expire_entry(adb, &entry, now); entry = next_entry; } UNLOCK(&adb->entrylocks[bucket]); return (result); } static void destroy(dns_adb_t *adb) { adb->magic = 0; isc_task_detach(&adb->task); 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, DNS_EVENT_ADBCONTROL, shutdown_task, adb, adb, NULL, NULL); adb->cevent_sent = ISC_FALSE; adb->shutting_down = ISC_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 = ISC_FALSE; 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 = ISC_FALSE; result = isc_taskmgr_excltask(adb->taskmgr, &adb->excl); if (result != ISC_R_SUCCESS) { DP(ISC_LOG_INFO, "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] = ISC_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] = ISC_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); /* * 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: isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t)); return (result); } void dns_adb_attach(dns_adb_t *adb, dns_adb_t **adbx) { REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(adbx != NULL && *adbx == NULL); inc_adb_erefcnt(adb); *adbx = adb; } void dns_adb_detach(dns_adb_t **adbx) { dns_adb_t *adb; isc_boolean_t need_exit_check; REQUIRE(adbx != NULL && DNS_ADB_VALID(*adbx)); adb = *adbx; *adbx = NULL; INSIST(adb->erefcnt > 0); LOCK(&adb->reflock); adb->erefcnt--; need_exit_check = ISC_TF(adb->erefcnt == 0 && adb->irefcnt == 0); UNLOCK(&adb->reflock); if (need_exit_check) { LOCK(&adb->lock); INSIST(adb->shutting_down); check_exit(adb); UNLOCK(&adb->lock); } } void dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) { isc_task_t *clone; isc_event_t *event; isc_boolean_t zeroirefcnt = ISC_FALSE; /* * Send '*eventp' to 'task' when 'adb' has shutdown. */ REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(eventp != NULL); event = *eventp; *eventp = NULL; LOCK(&adb->lock); LOCK(&adb->reflock); zeroirefcnt = ISC_TF(adb->irefcnt == 0); if (adb->shutting_down && zeroirefcnt && isc_mempool_getallocated(adb->ahmp) == 0) { /* * We're already shutdown. Send the event. */ event->ev_sender = adb; isc_task_send(task, &event); } else { clone = NULL; isc_task_attach(task, &clone); event->ev_sender = clone; ISC_LIST_APPEND(adb->whenshutdown, event, ev_link); } UNLOCK(&adb->reflock); UNLOCK(&adb->lock); } void dns_adb_shutdown(dns_adb_t *adb) { isc_boolean_t need_check_exit; /* * Shutdown 'adb'. */ LOCK(&adb->lock); if (!adb->shutting_down) { adb->shutting_down = ISC_TRUE; isc_mem_setwater(adb->mctx, water, adb, 0, 0); need_check_exit = shutdown_names(adb); if (!need_check_exit) need_check_exit = shutdown_entries(adb); if (need_check_exit) check_exit(adb); } UNLOCK(&adb->lock); } isc_result_t dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, void *arg, dns_name_t *name, dns_name_t *qname, dns_rdatatype_t qtype, unsigned int options, isc_stdtime_t now, dns_name_t *target, in_port_t port, dns_adbfind_t **findp) { + 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; isc_boolean_t want_event, start_at_zone, alias, have_address; isc_result_t result; unsigned int wanted_addresses; unsigned int wanted_fetches; unsigned int query_pending; REQUIRE(DNS_ADB_VALID(adb)); if (task != NULL) { REQUIRE(action != NULL); } REQUIRE(name != NULL); REQUIRE(qname != NULL); REQUIRE(findp != NULL && *findp == NULL); REQUIRE(target == NULL || dns_name_hasbuffer(target)); REQUIRE((options & DNS_ADBFIND_ADDRESSMASK) != 0); result = ISC_R_UNEXPECTED; POST(result); wanted_addresses = (options & DNS_ADBFIND_ADDRESSMASK); wanted_fetches = 0; query_pending = 0; want_event = ISC_FALSE; start_at_zone = ISC_FALSE; alias = ISC_FALSE; if (now == 0) isc_stdtime_get(&now); /* * XXXMLG Move this comment somewhere else! * * Look up the name in our internal database. * * Possibilities: Note that these are not always exclusive. * * No name found. In this case, allocate a new name header and * an initial namehook or two. If any of these allocations * fail, clean up and return ISC_R_NOMEMORY. * * Name found, valid addresses present. Allocate one addrinfo * structure for each found and append it to the linked list * of addresses for this header. * * Name found, queries pending. In this case, if a task was * passed in, allocate a job id, attach it to the name's job * list and remember to tell the caller that there will be * more info coming later. */ find = new_adbfind(adb); if (find == NULL) return (ISC_R_NOMEMORY); find->port = port; /* * Remember what types of addresses we are interested in. */ find->options = options; find->flags |= wanted_addresses; if (FIND_WANTEVENT(find)) { REQUIRE(task != NULL); } /* * Try to see if we know anything about this name at all. */ bucket = DNS_ADB_INVALIDBUCKET; adbname = find_name_and_lock(adb, name, find->options, &bucket); 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) == ISC_FALSE); result = ISC_R_SHUTTINGDOWN; goto out; } /* * Nothing found. Allocate a new adbname structure for this name. */ if (adbname == NULL) { /* * See if there is any stale name at the end of list, and purge * it if so. */ check_stale_name(adb, bucket, now); adbname = new_adbname(adb, name); if (adbname == NULL) { RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE); result = ISC_R_NOMEMORY; goto out; } link_name(adb, bucket, adbname); if (FIND_HINTOK(find)) adbname->flags |= NAME_HINT_OK; if (FIND_GLUEOK(find)) adbname->flags |= NAME_GLUE_OK; if (FIND_STARTATZONE(find)) adbname->flags |= NAME_STARTATZONE; } else { /* Move this name forward in the LRU list */ ISC_LIST_UNLINK(adb->names[bucket], adbname, plink); ISC_LIST_PREPEND(adb->names[bucket], adbname, plink); } adbname->last_used = now; /* * Expire old entries, etc. */ RUNTIME_CHECK(check_expire_namehooks(adbname, now) == ISC_FALSE); /* * Do we know that the name is an alias? */ if (!EXPIRE_OK(adbname->expire_target, now)) { /* * Yes, it is. */ DP(DEF_LEVEL, "dns_adb_createfind: name %p is an alias (cached)", adbname); alias = ISC_TRUE; goto post_copy; } /* * Try to populate the name from the database and/or * start fetches. First try looking for an A record * in the database. */ if (!NAME_HAS_V4(adbname) && EXPIRE_OK(adbname->expire_v4, now) && WANT_INET(wanted_addresses)) { result = dbfind_name(adbname, now, dns_rdatatype_a); if (result == ISC_R_SUCCESS) { DP(DEF_LEVEL, "dns_adb_createfind: found A for name %p in db", adbname); goto v6; } /* * Did we get a CNAME or DNAME? */ if (result == DNS_R_ALIAS) { DP(DEF_LEVEL, "dns_adb_createfind: name %p is an alias", adbname); alias = ISC_TRUE; goto post_copy; } /* * If the name doesn't exist at all, don't bother with * v6 queries; they won't work. * * If the name does exist but we didn't get our data, go * ahead and try AAAA. * * If the result is neither of these, try a fetch for A. */ if (NXDOMAIN_RESULT(result)) goto fetch; else if (NXRRSET_RESULT(result)) goto v6; if (!NAME_FETCH_V4(adbname)) wanted_fetches |= DNS_ADBFIND_INET; } v6: if (!NAME_HAS_V6(adbname) && EXPIRE_OK(adbname->expire_v6, now) && WANT_INET6(wanted_addresses)) { result = dbfind_name(adbname, now, dns_rdatatype_aaaa); if (result == ISC_R_SUCCESS) { DP(DEF_LEVEL, "dns_adb_createfind: found AAAA for name %p", adbname); goto fetch; } /* * Did we get a CNAME or DNAME? */ if (result == DNS_R_ALIAS) { DP(DEF_LEVEL, "dns_adb_createfind: name %p is an alias", adbname); alias = ISC_TRUE; goto post_copy; } /* * Listen to negative cache hints, and don't start * another query. */ if (NCACHE_RESULT(result) || AUTH_NX(result)) goto fetch; if (!NAME_FETCH_V6(adbname)) wanted_fetches |= DNS_ADBFIND_INET6; } fetch: if ((WANT_INET(wanted_addresses) && NAME_HAS_V4(adbname)) || (WANT_INET6(wanted_addresses) && NAME_HAS_V6(adbname))) have_address = ISC_TRUE; else have_address = ISC_FALSE; if (wanted_fetches != 0 && ! (FIND_AVOIDFETCHES(find) && have_address)) { /* * We're missing at least one address family. Either the * caller hasn't instructed us to avoid fetches, or we don't * know anything about any of the address families that would * be acceptable so we have to launch fetches. */ if (FIND_STARTATZONE(find)) start_at_zone = ISC_TRUE; /* * Start V4. */ if (WANT_INET(wanted_fetches) && - fetch_name(adbname, start_at_zone, + 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 %p", adbname); } /* * Start V6. */ if (WANT_INET6(wanted_fetches) && - fetch_name(adbname, start_at_zone, + 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 %p", adbname); } } /* * Run through the name and copy out the bits we are * interested in. */ copy_namehook_lists(adb, find, qname, qtype, adbname, now); post_copy: if (NAME_FETCH_V4(adbname)) query_pending |= DNS_ADBFIND_INET; if (NAME_FETCH_V6(adbname)) query_pending |= DNS_ADBFIND_INET6; /* * Attach to the name's query list if there are queries * already running, and we have been asked to. */ want_event = ISC_TRUE; if (!FIND_WANTEVENT(find)) want_event = ISC_FALSE; if (FIND_WANTEMPTYEVENT(find) && FIND_HAS_ADDRS(find)) want_event = ISC_FALSE; if ((wanted_addresses & query_pending) == 0) want_event = ISC_FALSE; if (alias) want_event = ISC_FALSE; if (want_event) { find->adbname = adbname; find->name_bucket = bucket; ISC_LIST_APPEND(adbname->finds, find, plink); find->query_pending = (query_pending & wanted_addresses); find->flags &= ~DNS_ADBFIND_ADDRESSMASK; find->flags |= (find->query_pending & DNS_ADBFIND_ADDRESSMASK); DP(DEF_LEVEL, "createfind: attaching find %p to adbname %p", find, adbname); } else { /* * Remove the flag so the caller knows there will never * be an event, and set internal flags to fake that * the event was sent and freed, so dns_adb_destroyfind() will * do the right thing. */ find->query_pending = (query_pending & wanted_addresses); find->options &= ~DNS_ADBFIND_WANTEVENT; find->flags |= (FIND_EVENT_SENT | FIND_EVENT_FREED); find->flags &= ~DNS_ADBFIND_ADDRESSMASK; } find->partial_result |= (adbname->partial_result & wanted_addresses); if (alias) { if (target != NULL) { result = dns_name_copy(&adbname->target, target, NULL); if (result != ISC_R_SUCCESS) goto out; } result = DNS_R_ALIAS; } else result = ISC_R_SUCCESS; /* * Copy out error flags from the name structure into the find. */ find->result_v4 = find_err_map[adbname->fetch_err]; find->result_v6 = find_err_map[adbname->fetch6_err]; out: if (find != NULL) { *findp = find; if (want_event) { isc_task_t *taskp; INSIST((find->flags & DNS_ADBFIND_ADDRESSMASK) != 0); taskp = NULL; isc_task_attach(task, &taskp); find->event.ev_sender = taskp; find->event.ev_action = action; find->event.ev_arg = arg; } } UNLOCK(&adb->namelocks[bucket]); return (result); } void dns_adb_destroyfind(dns_adbfind_t **findp) { dns_adbfind_t *find; dns_adbentry_t *entry; dns_adbaddrinfo_t *ai; int bucket; dns_adb_t *adb; isc_boolean_t 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, ISC_TRUE) == ISC_FALSE); free_adbaddrinfo(adb, &ai); ai = ISC_LIST_HEAD(find->list); } /* * WARNING: The find is freed with the adb locked. This is done * to avoid a race condition where we free the find, some other * thread tests to see if it should be destroyed, detects it should * be, destroys it, and then we try to lock it for our check, but the * lock is destroyed. */ LOCK(&adb->lock); if (free_adbfind(adb, &find)) check_exit(adb); UNLOCK(&adb->lock); } void dns_adb_cancelfind(dns_adbfind_t *find) { isc_event_t *ev; isc_task_t *task; dns_adb_t *adb; int bucket; int unlock_bucket; LOCK(&find->lock); DP(DEF_LEVEL, "dns_adb_cancelfind on find %p", find); adb = find->adb; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(!FIND_EVENTFREED(find)); REQUIRE(FIND_WANTEVENT(find)); bucket = find->name_bucket; if (bucket == DNS_ADB_INVALIDBUCKET) goto cleanup; /* * We need to get the adbname's lock to unlink the find. */ unlock_bucket = bucket; violate_locking_hierarchy(&find->lock, &adb->namelocks[unlock_bucket]); bucket = find->name_bucket; if (bucket != DNS_ADB_INVALIDBUCKET) { ISC_LIST_UNLINK(find->adbname->finds, find, plink); find->adbname = NULL; find->name_bucket = DNS_ADB_INVALIDBUCKET; } UNLOCK(&adb->namelocks[unlock_bucket]); bucket = DNS_ADB_INVALIDBUCKET; 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) == ISC_FALSE); for (i = 0; i < adb->nentries; i++) RUNTIME_CHECK(cleanup_entries(adb, i, now) == ISC_FALSE); dump_adb(adb, f, ISC_FALSE, now); UNLOCK(&adb->lock); } static void dump_ttl(FILE *f, const char *legend, isc_stdtime_t value, isc_stdtime_t now) { if (value == INT_MAX) return; fprintf(f, " [%s TTL %d]", legend, value - now); } static void dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) { unsigned int i; dns_adbname_t *name; dns_adbentry_t *entry; fprintf(f, ";\n; Address database dump\n;\n"); if (debug) fprintf(f, "; addr %p, erefcnt %u, irefcnt %u, finds out %u\n", adb, adb->erefcnt, adb->irefcnt, isc_mempool_getallocated(adb->nhmp)); for (i = 0; i < 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 %d\n", i); for (; name != NULL; name = ISC_LIST_NEXT(name, plink)) { if (debug) fprintf(f, "; name %p (flags %08x)\n", name, name->flags); fprintf(f, "; "); print_dns_name(f, &name->name); if (dns_name_countlabels(&name->target) > 0) { fprintf(f, " alias "); print_dns_name(f, &name->target); } dump_ttl(f, "v4", name->expire_v4, now); dump_ttl(f, "v6", name->expire_v6, now); dump_ttl(f, "target", name->expire_target, now); fprintf(f, " [v4 %s] [v6 %s]", errnames[name->fetch_err], errnames[name->fetch6_err]); fprintf(f, "\n"); print_namehook_list(f, "v4", &name->v4, debug, now); print_namehook_list(f, "v6", &name->v6, debug, now); if (debug) print_fetch_list(f, name); if (debug) print_find_list(f, name); } } fprintf(f, ";\n; Unassociated entries\n;\n"); for (i = 0; i < adb->nentries; i++) { entry = ISC_LIST_HEAD(adb->entries[i]); while (entry != NULL) { if (entry->refcnt == 0) dump_entry(f, entry, debug, now); entry = ISC_LIST_NEXT(entry, plink); } } /* * Unlock everything */ for (i = 0; i < 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_adbentry_t *entry, isc_boolean_t debug, isc_stdtime_t now) { char addrbuf[ISC_NETADDR_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; isc_netaddr_t netaddr; dns_adblameinfo_t *li; isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr); isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf)); if (debug) fprintf(f, ";\t%p: refcnt %u\n", entry, entry->refcnt); fprintf(f, ";\t%s [srtt %u] [flags %08x]", addrbuf, entry->srtt, entry->flags); if (entry->expires != 0) fprintf(f, " [ttl %d]", entry->expires - now); fprintf(f, "\n"); for (li = ISC_LIST_HEAD(entry->lameinfo); li != NULL; li = ISC_LIST_NEXT(li, plink)) { fprintf(f, ";\t\t"); print_dns_name(f, &li->qname); dns_rdatatype_format(li->qtype, typebuf, sizeof(typebuf)); fprintf(f, " %s [lame TTL %d]\n", typebuf, li->lame_timer - now); } } void dns_adb_dumpfind(dns_adbfind_t *find, FILE *f) { char tmp[512]; const char *tmpp; dns_adbaddrinfo_t *ai; isc_sockaddr_t *sa; /* * Not used currently, in the API Just In Case we * want to dump out the name and/or entries too. */ LOCK(&find->lock); fprintf(f, ";Find %p\n", find); fprintf(f, ";\tqpending %08x partial %08x options %08x flags %08x\n", find->query_pending, find->partial_result, find->options, find->flags); fprintf(f, ";\tname_bucket %d, name %p, event sender %p\n", find->name_bucket, find->adbname, find->event.ev_sender); ai = ISC_LIST_HEAD(find->list); if (ai != NULL) fprintf(f, "\tAddresses:\n"); while (ai != NULL) { sa = &ai->sockaddr; switch (sa->type.sa.sa_family) { case AF_INET: tmpp = inet_ntop(AF_INET, &sa->type.sin.sin_addr, tmp, sizeof(tmp)); break; case AF_INET6: tmpp = inet_ntop(AF_INET6, &sa->type.sin6.sin6_addr, tmp, sizeof(tmp)); break; default: tmpp = "UnkFamily"; } if (tmpp == NULL) tmpp = "BadAddress"; fprintf(f, "\t\tentry %p, flags %08x" " srtt %u addr %s\n", ai->entry, ai->flags, ai->srtt, tmpp); ai = ISC_LIST_NEXT(ai, publink); } UNLOCK(&find->lock); } static void print_dns_name(FILE *f, dns_name_t *name) { char buf[DNS_NAME_FORMATSIZE]; INSIST(f != NULL); dns_name_format(name, buf, sizeof(buf)); fprintf(f, "%s", buf); } static void print_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list, isc_boolean_t debug, isc_stdtime_t now) { dns_adbnamehook_t *nh; for (nh = ISC_LIST_HEAD(*list); nh != NULL; nh = ISC_LIST_NEXT(nh, plink)) { if (debug) fprintf(f, ";\tHook(%s) %p\n", legend, nh); dump_entry(f, nh->entry, debug, now); } } static inline void print_fetch(FILE *f, dns_adbfetch_t *ft, const char *type) { fprintf(f, "\t\tFetch(%s): %p -> { 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); dns_fixedname_init(&foundname); fname = dns_fixedname_name(&foundname); dns_rdataset_init(&rdataset); if (rdtype == dns_rdatatype_a) adbname->fetch_err = FIND_ERR_UNEXPECTED; else adbname->fetch6_err = FIND_ERR_UNEXPECTED; /* * 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, ISC_TF(NAME_HINTOK(adbname)), (adbname->flags & NAME_STARTATZONE) != 0 ? ISC_TRUE : ISC_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; isc_boolean_t want_check_exit = ISC_FALSE; UNUSED(task); INSIST(ev->ev_type == DNS_EVENT_FETCHDONE); dev = (dns_fetchevent_t *)ev; name = ev->ev_arg; INSIST(DNS_ADBNAME_VALID(name)); adb = name->adb; INSIST(DNS_ADB_VALID(adb)); bucket = name->lock_bucket; LOCK(&adb->namelocks[bucket]); INSIST(NAME_FETCH_A(name) || NAME_FETCH_AAAA(name)); address_type = 0; if (NAME_FETCH_A(name) && (name->fetch_a->fetch == dev->fetch)) { address_type = DNS_ADBFIND_INET; fetch = name->fetch_a; name->fetch_a = NULL; } else if (NAME_FETCH_AAAA(name) && (name->fetch_aaaa->fetch == dev->fetch)) { address_type = DNS_ADBFIND_INET6; fetch = name->fetch_aaaa; name->fetch_aaaa = NULL; } else fetch = NULL; INSIST(address_type != 0 && fetch != NULL); dns_resolver_destroyfetch(&fetch->fetch); dev->fetch = NULL; ev_status = DNS_EVENT_ADBNOMOREADDRESSES; /* * Cleanup things we don't care about. */ if (dev->node != NULL) dns_db_detachnode(dev->db, &dev->node); if (dev->db != NULL) dns_db_detach(&dev->db); /* * If this name is marked as dead, clean up, throwing away * potentially good data. */ if (NAME_DEAD(name)) { free_adbfetch(adb, &fetch); isc_event_free(&ev); want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED); 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 + 300); name->fetch_err = FIND_ERR_FAILURE; inc_stats(adb, dns_resstatscounter_gluefetchv4fail); } else { name->expire_v6 = ISC_MIN(name->expire_v6, now + 300); name->fetch6_err = FIND_ERR_FAILURE; inc_stats(adb, dns_resstatscounter_gluefetchv6fail); } goto out; } /* * We got something potentially useful. */ result = import_rdataset(name, &fetch->rdataset, now); check_result: if (result == ISC_R_SUCCESS) { ev_status = DNS_EVENT_ADBMOREADDRESSES; if (address_type == DNS_ADBFIND_INET) name->fetch_err = FIND_ERR_SUCCESS; else name->fetch6_err = FIND_ERR_SUCCESS; } out: free_adbfetch(adb, &fetch); isc_event_free(&ev); clean_finds_at_name(name, ev_status, address_type); UNLOCK(&adb->namelocks[bucket]); } static isc_result_t -fetch_name(dns_adbname_t *adbname, - isc_boolean_t start_at_zone, - dns_rdatatype_t type) +fetch_name(dns_adbname_t *adbname, isc_boolean_t 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); dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); result = dns_view_findzonecut2(adb->view, &adbname->name, name, 0, 0, ISC_TRUE, ISC_FALSE, &rdataset, NULL); if (result != ISC_R_SUCCESS && result != DNS_R_HINT) goto cleanup; nameservers = &rdataset; options |= DNS_FETCHOPT_UNSHARED; } fetch = new_adbfetch(adb); if (fetch == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } + fetch->depth = depth; - result = dns_resolver_createfetch(adb->view->resolver, &adbname->name, - type, name, nameservers, NULL, - options, adb->task, fetch_callback, - adbname, &fetch->rdataset, NULL, - &fetch->fetch); + 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; unsigned int new_srtt; isc_stdtime_t now; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); REQUIRE(factor <= 10); bucket = addr->entry->lock_bucket; LOCK(&adb->entrylocks[bucket]); if (factor == DNS_ADB_RTTADJAGE) new_srtt = addr->entry->srtt * 98 / 100; else new_srtt = (addr->entry->srtt / 10 * factor) + (rtt / 10 * (10 - factor)); addr->entry->srtt = new_srtt; addr->srtt = new_srtt; if (addr->entry->expires == 0) { isc_stdtime_get(&now); addr->entry->expires = now + ADB_ENTRY_WINDOW; } UNLOCK(&adb->entrylocks[bucket]); } void dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int bits, unsigned int mask) { int bucket; isc_stdtime_t now; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); bucket = addr->entry->lock_bucket; LOCK(&adb->entrylocks[bucket]); addr->entry->flags = (addr->entry->flags & ~mask) | (bits & mask); 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]); } 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, ISC_FALSE); *addrp = addr; } unlock: UNLOCK(&adb->entrylocks[bucket]); return (result); } void dns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp) { dns_adbaddrinfo_t *addr; dns_adbentry_t *entry; int bucket; isc_stdtime_t now; isc_boolean_t want_check_exit = ISC_FALSE; isc_boolean_t 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, ISC_FALSE); UNLOCK(&adb->entrylocks[bucket]); addr->entry = NULL; free_adbaddrinfo(adb, &addr); if (want_check_exit) { LOCK(&adb->lock); check_exit(adb); UNLOCK(&adb->lock); } } void dns_adb_flush(dns_adb_t *adb) { unsigned int i; INSIST(DNS_ADB_VALID(adb)); LOCK(&adb->lock); /* * Call our cleanup routines. */ for (i = 0; i < adb->nnames; i++) RUNTIME_CHECK(cleanup_names(adb, i, INT_MAX) == ISC_FALSE); for (i = 0; i < adb->nentries; i++) RUNTIME_CHECK(cleanup_entries(adb, i, INT_MAX) == ISC_FALSE); #ifdef DUMP_ADB_AFTER_CLEANING dump_adb(adb, stdout, ISC_TRUE, INT_MAX); #endif UNLOCK(&adb->lock); } void dns_adb_flushname(dns_adb_t *adb, dns_name_t *name) { dns_adbname_t *adbname; dns_adbname_t *nextname; int bucket; INSIST(DNS_ADB_VALID(adb)); LOCK(&adb->lock); bucket = dns_name_hash(name, ISC_FALSE) % 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) == ISC_FALSE); } adbname = nextname; } UNLOCK(&adb->namelocks[bucket]); 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; isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER); REQUIRE(DNS_ADB_VALID(adb)); DP(ISC_LOG_DEBUG(1), "adb reached %s water mark", overmem ? "high" : "low"); } void dns_adb_setadbsize(dns_adb_t *adb, isc_uint32_t size) { isc_uint32_t hiwater; isc_uint32_t lowater; INSIST(DNS_ADB_VALID(adb)); if (size != 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); } Index: stable/8/contrib/bind9/lib/dns/include/dns/adb.h =================================================================== --- stable/8/contrib/bind9/lib/dns/include/dns/adb.h (revision 275668) +++ stable/8/contrib/bind9/lib/dns/include/dns/adb.h (revision 275669) @@ -1,634 +1,641 @@ /* * Copyright (C) 2004-2008, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id$ */ #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 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 #include #include #include #include #include ISC_LANG_BEGINDECLS /*** *** Magic number checks ***/ #define DNS_ADBFIND_MAGIC ISC_MAGIC('a','d','b','H') #define DNS_ADBFIND_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFIND_MAGIC) #define DNS_ADBADDRINFO_MAGIC ISC_MAGIC('a','d','A','I') #define DNS_ADBADDRINFO_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBADDRINFO_MAGIC) /*** *** TYPES ***/ typedef struct dns_adbname dns_adbname_t; /*! *\brief * Represents a lookup for a single name. * * On return, the client can safely use "list", and can reorder the list. * Items may not be _deleted_ from this list, however, or added to it * other than by using the dns_adb_*() API. */ struct dns_adbfind { /* Public */ unsigned int magic; /*%< RO: magic */ dns_adbaddrinfolist_t list; /*%< RO: list of addrs */ unsigned int query_pending; /*%< RO: partial list */ unsigned int partial_result; /*%< RO: addrs missing */ unsigned int options; /*%< RO: options */ isc_result_t result_v4; /*%< RO: v4 result */ isc_result_t result_v6; /*%< RO: v6 result */ ISC_LINK(dns_adbfind_t) publink; /*%< RW: client use */ /* Private */ isc_mutex_t lock; /* locks all below */ in_port_t port; int name_bucket; unsigned int flags; dns_adbname_t *adbname; dns_adb_t *adb; isc_event_t event; ISC_LINK(dns_adbfind_t) plink; }; /* * _INET: * _INET6: * return addresses of that type. * * _EMPTYEVENT: * Only schedule an event if no addresses are known. * Must set _WANTEVENT for this to be meaningful. * * _WANTEVENT: * An event is desired. Check this bit in the returned find to see * if one will actually be generated. * * _AVOIDFETCHES: * If set, fetches will not be generated unless no addresses are * available in any of the address families requested. * * _STARTATZONE: * Fetches will start using the closest zone data or use the root servers. * This is useful for reestablishing glue that has expired. * * _GLUEOK: * _HINTOK: * Glue or hints are ok. These are used when matching names already * in the adb, and when dns databases are searched. * * _RETURNLAME: * Return lame servers in a find, so that all addresses are returned. * * _LAMEPRUNED: * At least one address was omitted from the list because it was lame. * This bit will NEVER be set if _RETURNLAME is set in the createfind(). */ /*% Return addresses of type INET. */ #define DNS_ADBFIND_INET 0x00000001 /*% Return addresses of type INET6. */ #define DNS_ADBFIND_INET6 0x00000002 #define DNS_ADBFIND_ADDRESSMASK 0x00000003 /*% * Only schedule an event if no addresses are known. * Must set _WANTEVENT for this to be meaningful. */ #define DNS_ADBFIND_EMPTYEVENT 0x00000004 /*% * An event is desired. Check this bit in the returned find to see * if one will actually be generated. */ #define DNS_ADBFIND_WANTEVENT 0x00000008 /*% * If set, fetches will not be generated unless no addresses are * available in any of the address families requested. */ #define DNS_ADBFIND_AVOIDFETCHES 0x00000010 /*% * Fetches will start using the closest zone data or use the root servers. * This is useful for reestablishing glue that has expired. */ #define DNS_ADBFIND_STARTATZONE 0x00000020 /*% * Glue or hints are ok. These are used when matching names already * in the adb, and when dns databases are searched. */ #define DNS_ADBFIND_GLUEOK 0x00000040 /*% * Glue or hints are ok. These are used when matching names already * in the adb, and when dns databases are searched. */ #define DNS_ADBFIND_HINTOK 0x00000080 /*% * Return lame servers in a find, so that all addresses are returned. */ #define DNS_ADBFIND_RETURNLAME 0x00000100 /*% * Only schedule an event if no addresses are known. * Must set _WANTEVENT for this to be meaningful. */ #define DNS_ADBFIND_LAMEPRUNED 0x00000200 /*% * The answers to queries come back as a list of these. */ struct dns_adbaddrinfo { unsigned int magic; /*%< private */ isc_sockaddr_t sockaddr; /*%< [rw] */ unsigned int srtt; /*%< [rw] microseconds */ unsigned int flags; /*%< [rw] */ dns_adbentry_t *entry; /*%< private */ ISC_LINK(dns_adbaddrinfo_t) publink; }; /*!< * The event sent to the caller task is just a plain old isc_event_t. It * contains no data other than a simple status, passed in the "type" field * to indicate that another address resolved, or all partially resolved * addresses have failed to resolve. * * "sender" is the dns_adbfind_t used to issue this query. * * This is simply a standard event, with the "type" set to: * *\li #DNS_EVENT_ADBMOREADDRESSES -- another address resolved. *\li #DNS_EVENT_ADBNOMOREADDRESSES -- all pending addresses failed, * were canceled, or otherwise will * not be usable. *\li #DNS_EVENT_ADBCANCELED -- The request was canceled by a * 3rd party. *\li #DNS_EVENT_ADBNAMEDELETED -- The name was deleted, so this request * was canceled. * * In each of these cases, the addresses returned by the initial call * to dns_adb_createfind() can still be used until they are no longer needed. */ /**** **** FUNCTIONS ****/ isc_result_t dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *tmgr, isc_taskmgr_t *taskmgr, dns_adb_t **newadb); /*%< * Create a new ADB. * * Notes: * *\li Generally, applications should not create an ADB directly, but * should instead call dns_view_createresolver(). * * Requires: * *\li 'mem' must be a valid memory context. * *\li 'view' be a pointer to a valid view. * *\li 'tmgr' be a pointer to a valid timer manager. * *\li 'taskmgr' be a pointer to a valid task manager. * *\li 'newadb' != NULL && '*newadb' == NULL. * * Returns: * *\li #ISC_R_SUCCESS after happiness. *\li #ISC_R_NOMEMORY after resource allocation failure. */ void dns_adb_attach(dns_adb_t *adb, dns_adb_t **adbp); /*% * Attach to an 'adb' to 'adbp'. * * Requires: *\li 'adb' to be a valid dns_adb_t, created via dns_adb_create(). *\li 'adbp' to be a valid pointer to a *dns_adb_t which is initialized * to NULL. */ void dns_adb_detach(dns_adb_t **adb); /*% * Delete the ADB. Sets *ADB to NULL. Cancels any outstanding requests. * * Requires: * *\li 'adb' be non-NULL and '*adb' be a valid dns_adb_t, created via * dns_adb_create(). */ void dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp); /*% * Send '*eventp' to 'task' when 'adb' has shutdown. * * Requires: * *\li '*adb' is a valid dns_adb_t. * *\li eventp != NULL && *eventp is a valid event. * * Ensures: * *\li *eventp == NULL * *\li The event's sender field is set to the value of adb when the event * is sent. */ void dns_adb_shutdown(dns_adb_t *adb); /*%< * Shutdown 'adb'. * * Requires: * * \li '*adb' is a valid dns_adb_t. */ isc_result_t dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, void *arg, dns_name_t *name, dns_name_t *qname, dns_rdatatype_t qtype, unsigned int options, isc_stdtime_t now, dns_name_t *target, in_port_t port, dns_adbfind_t **find); +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 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 . expire_time should * be set to the time when the entry should expire. That is, if it is to * expire 10 minutes in the future, it should set it to (now + 10 * 60). * * Requires: * *\li adb be valid. * *\li addr be valid. * *\li qname be the qname used in the dns_adb_createfind() call. * * Returns: * *\li #ISC_R_SUCCESS -- all is well. *\li #ISC_R_NOMEMORY -- could not mark address as lame. */ /* * A reasonable default for RTT adjustments */ #define DNS_ADB_RTTADJDEFAULT 7 /*%< default scale */ #define DNS_ADB_RTTADJREPLACE 0 /*%< replace with our rtt */ #define DNS_ADB_RTTADJAGE 10 /*%< age this rtt */ void dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int rtt, unsigned int factor); /*%< * Mix the round trip time into the existing smoothed rtt. * The formula used * (where srtt is the existing rtt value, and rtt and factor are arguments to * this function): * *\code * new_srtt = (old_srtt / 10 * factor) + (rtt / 10 * (10 - factor)); *\endcode * * XXXRTH Do we want to publish the formula? What if we want to change how * this works later on? Recommend/require that the units are * microseconds? * * Requires: * *\li adb be valid. * *\li addr be valid. * *\li 0 <= factor <= 10 * * Note: * *\li The srtt in addr will be updated to reflect the new global * srtt value. This may include changes made by others. */ void dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int bits, unsigned int mask); /*% * Change Flags. * * Set the flags as given by: * *\li newflags = (oldflags & ~mask) | (bits & mask); * * Requires: * *\li adb be valid. * *\li addr be valid. */ isc_result_t dns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa, dns_adbaddrinfo_t **addrp, isc_stdtime_t now); /*%< * Return a dns_adbaddrinfo_t that is associated with address 'sa'. * * Requires: * *\li adb is valid. * *\li sa is valid. * *\li addrp != NULL && *addrp == NULL * * Returns: *\li #ISC_R_SUCCESS *\li #ISC_R_NOMEMORY *\li #ISC_R_SHUTTINGDOWN */ void dns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp); /*%< * Free a dns_adbaddrinfo_t allocated by dns_adb_findaddrinfo(). * * Requires: * *\li adb is valid. * *\li *addrp is a valid dns_adbaddrinfo_t *. */ void dns_adb_flush(dns_adb_t *adb); /*%< * Flushes all cached data from the adb. * * Requires: *\li adb is valid. */ void dns_adb_setadbsize(dns_adb_t *adb, isc_uint32_t size); /*%< * Set a target memory size. If memory usage exceeds the target * size entries will be removed before they would have expired on * a random basis. * * If 'size' is 0 then memory usage is unlimited. * * Requires: *\li 'adb' is valid. */ void dns_adb_flushname(dns_adb_t *adb, dns_name_t *name); /*%< * Flush 'name' from the adb cache. * * Requires: *\li 'adb' is valid. *\li 'name' is valid. */ ISC_LANG_ENDDECLS #endif /* DNS_ADB_H */ Index: stable/8/contrib/bind9/lib/dns/include/dns/resolver.h =================================================================== --- stable/8/contrib/bind9/lib/dns/include/dns/resolver.h (revision 275668) +++ stable/8/contrib/bind9/lib/dns/include/dns/resolver.h (revision 275669) @@ -1,577 +1,613 @@ /* * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id$ */ #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 #include #include #include 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; /* * Options that modify how a 'fetch' is done. */ #define DNS_FETCHOPT_TCP 0x01 /*%< Use TCP. */ #define DNS_FETCHOPT_UNSHARED 0x02 /*%< See below. */ #define DNS_FETCHOPT_RECURSIVE 0x04 /*%< Set RD? */ #define DNS_FETCHOPT_NOEDNS0 0x08 /*%< Do not use EDNS. */ #define DNS_FETCHOPT_FORWARDONLY 0x10 /*%< Only use forwarders. */ #define DNS_FETCHOPT_NOVALIDATE 0x20 /*%< Disable validation. */ #define DNS_FETCHOPT_EDNS512 0x40 /*%< Advertise a 512 byte UDP buffer. */ #define DNS_FETCHOPT_WANTNSID 0x80 /*%< Request NSID */ #define DNS_FETCHOPT_EDNSVERSIONSET 0x00800000 #define DNS_FETCHOPT_EDNSVERSIONMASK 0xff000000 #define DNS_FETCHOPT_EDNSVERSIONSHIFT 24 /* * 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, isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr, unsigned int options, dns_dispatchmgr_t *dispatchmgr, dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6, dns_resolver_t **resp); /*%< * Create a resolver. * * Notes: * *\li Generally, applications should not create a resolver directly, but * should instead call dns_view_createresolver(). * * Requires: * *\li 'view' is a valid view. * *\li 'taskmgr' is a valid task manager. * *\li 'ntasks' > 0. * *\li 'socketmgr' is a valid socket manager. * *\li 'timermgr' is a valid timer manager. * *\li 'dispatchv4' is a valid dispatcher with an IPv4 UDP socket, or is NULL. * *\li 'dispatchv6' is a valid dispatcher with an IPv6 UDP socket, or is NULL. * *\li resp != NULL && *resp == NULL. * * Returns: * *\li #ISC_R_SUCCESS On success. * *\li Anything else Failure. */ void dns_resolver_freeze(dns_resolver_t *res); /*%< * Freeze resolver. * * Notes: * *\li Certain configuration changes cannot be made after the resolver * is frozen. Fetches cannot be created until the resolver is frozen. * * Requires: * *\li 'res' is a valid resolver. * * Ensures: * *\li 'res' is frozen. */ void dns_resolver_prime(dns_resolver_t *res); /*%< * Prime resolver. * * Notes: * *\li Resolvers which have a forwarding policy other than dns_fwdpolicy_only * need to be primed with the root nameservers, otherwise the root * nameserver hints data may be used indefinitely. This function requests * that the resolver start a priming fetch, if it isn't already priming. * * Requires: * *\li 'res' is a valid, frozen resolver. */ void dns_resolver_whenshutdown(dns_resolver_t *res, isc_task_t *task, isc_event_t **eventp); /*%< * Send '*eventp' to 'task' when 'res' has completed shutdown. * * Notes: * *\li It is not safe to detach the last reference to 'res' until * shutdown is complete. * * Requires: * *\li 'res' is a valid resolver. * *\li 'task' is a valid task. * *\li *eventp is a valid event. * * Ensures: * *\li *eventp == NULL. */ void dns_resolver_shutdown(dns_resolver_t *res); /*%< * Start the shutdown process for 'res'. * * Notes: * *\li This call has no effect if the resolver is already shutting down. * * Requires: * *\li 'res' is a valid resolver. */ void dns_resolver_attach(dns_resolver_t *source, dns_resolver_t **targetp); void dns_resolver_detach(dns_resolver_t **resp); isc_result_t dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, dns_name_t *domain, dns_rdataset_t *nameservers, dns_forwarders_t *forwarders, unsigned int options, isc_task_t *task, isc_taskaction_t action, void *arg, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, dns_fetch_t **fetchp); isc_result_t dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, dns_name_t *domain, dns_rdataset_t *nameservers, dns_forwarders_t *forwarders, isc_sockaddr_t *client, isc_uint16_t id, unsigned int options, isc_task_t *task, isc_taskaction_t action, void *arg, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, dns_fetch_t **fetchp); +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, isc_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, isc_boolean_t 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); isc_uint32_t dns_resolver_getlamettl(dns_resolver_t *resolver); /*%< * Get the resolver's lame-ttl. zero => no lame processing. * * Requires: *\li 'resolver' to be valid. */ void dns_resolver_setlamettl(dns_resolver_t *resolver, isc_uint32_t lame_ttl); /*%< * Set the resolver's lame-ttl. zero => no lame processing. * * Requires: *\li 'resolver' to be valid. */ unsigned int dns_resolver_nrunning(dns_resolver_t *resolver); /*%< * Return the number of currently running resolutions in this * resolver. This is may be less than the number of outstanding * fetches due to multiple identical fetches, or more than the * number of of outstanding fetches due to the fact that resolution * can continue even though a fetch has been canceled. */ isc_result_t dns_resolver_addalternate(dns_resolver_t *resolver, isc_sockaddr_t *alt, dns_name_t *name, in_port_t port); /*%< * Add alternate addresses to be tried in the event that the nameservers * for a zone are not available in the address families supported by the * operating system. * * Require: * \li only one of 'name' or 'alt' to be valid. */ void dns_resolver_setudpsize(dns_resolver_t *resolver, isc_uint16_t udpsize); /*%< * Set the EDNS UDP buffer size advertised by the server. */ isc_uint16_t dns_resolver_getudpsize(dns_resolver_t *resolver); /*%< * Get the current EDNS UDP buffer size. */ void dns_resolver_reset_algorithms(dns_resolver_t *resolver); /*%< * Clear the disabled DNSSEC algorithms. */ isc_result_t dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name, unsigned int alg); /*%< * Mark the give DNSSEC algorithm as disabled and below 'name'. * Valid algorithms are less than 256. * * Returns: *\li #ISC_R_SUCCESS *\li #ISC_R_RANGE *\li #ISC_R_NOMEMORY */ isc_boolean_t dns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name, unsigned int alg); /*%< * Check if the given algorithm is supported by this resolver. * This checks if the algorithm has been disabled via * dns_resolver_disable_algorithm() then the underlying * crypto libraries if not specifically disabled. */ isc_boolean_t dns_resolver_digest_supported(dns_resolver_t *resolver, unsigned int digest_type); /*%< * Is this digest type supported. */ void dns_resolver_resetmustbesecure(dns_resolver_t *resolver); isc_result_t dns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name, isc_boolean_t value); isc_boolean_t dns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name); void dns_resolver_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, isc_uint32_t min, isc_uint32_t max); void dns_resolver_getclientsperquery(dns_resolver_t *resolver, isc_uint32_t *cur, isc_uint32_t *min, isc_uint32_t *max); isc_boolean_t dns_resolver_getzeronosoattl(dns_resolver_t *resolver); void dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state); unsigned int dns_resolver_getoptions(dns_resolver_t *resolver); 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 that will expire at 'expire'. * * Requires: * \li resolver to be valid. * \li name to be valid. */ isc_boolean_t 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 * . * * 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_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_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. */ ISC_LANG_ENDDECLS #endif /* DNS_RESOLVER_H */ Index: stable/8/contrib/bind9/lib/dns/resolver.c =================================================================== --- stable/8/contrib/bind9/lib/dns/resolver.c (revision 275668) +++ stable/8/contrib/bind9/lib/dns/resolver.c (revision 275669) @@ -1,9046 +1,9148 @@ /* * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id$ */ /*! \file */ #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DNS_RESOLVER_TRACE #ifdef DNS_RESOLVER_TRACE #define RTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "res %p: %s", res, (m)) #define RRTRACE(r, m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "res %p: %s", (r), (m)) #define FCTXTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "fctx %p(%s): %s", fctx, fctx->info, (m)) #define FCTXTRACE2(m1, m2) \ isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "fctx %p(%s): %s %s", \ fctx, fctx->info, (m1), (m2)) #define FTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "fetch %p (fctx %p(%s)): %s", \ fetch, fetch->private, \ fetch->private->info, (m)) #define QTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "resquery %p (fctx %p(%s)): %s", \ query, query->fctx, \ query->fctx->info, (m)) #else #define RTRACE(m) #define RRTRACE(r, m) #define FCTXTRACE(m) #define FCTXTRACE2(m1, m2) #define FTRACE(m) #define QTRACE(m) #endif #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 #ifndef MAXIMUM_QUERY_TIMEOUT #define MAXIMUM_QUERY_TIMEOUT 30 /* The maximum time in seconds for the whole query to live. */ #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 50 +#endif + /*% * Maximum EDNS0 input packet size. */ #define RECV_BUFFER_SIZE 4096 /* XXXRTH Constant. */ #define EDNSOPTS 2 /*% * This defines the maximum number of timeouts we will permit before we * disable EDNS0 on the query. */ #define MAX_EDNS0_TIMEOUTS 3 typedef struct fetchctx fetchctx_t; typedef struct query { /* Locked by task event serialization. */ unsigned int magic; fetchctx_t * fctx; isc_mem_t * mctx; dns_dispatchmgr_t * dispatchmgr; dns_dispatch_t * dispatch; isc_boolean_t exclusivesocket; dns_adbaddrinfo_t * addrinfo; isc_socket_t * tcpsocket; isc_time_t start; dns_messageid_t id; dns_dispentry_t * dispentry; ISC_LINK(struct query) link; isc_buffer_t buffer; isc_buffer_t *tsig; dns_tsigkey_t *tsigkey; unsigned int options; unsigned int attributes; unsigned int sends; unsigned int connects; unsigned char data[512]; } resquery_t; #define QUERY_MAGIC ISC_MAGIC('Q', '!', '!', '!') #define VALID_QUERY(query) ISC_MAGIC_VALID(query, QUERY_MAGIC) #define RESQUERY_ATTR_CANCELED 0x02 #define RESQUERY_CONNECTING(q) ((q)->connects > 0) #define RESQUERY_CANCELED(q) (((q)->attributes & \ RESQUERY_ATTR_CANCELED) != 0) #define RESQUERY_SENDING(q) ((q)->sends > 0) typedef enum { fetchstate_init = 0, /*%< Start event has not run yet. */ fetchstate_active, fetchstate_done /*%< FETCHDONE events posted. */ } fetchstate; 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; char * info; isc_mem_t * mctx; /*% Locked by appropriate bucket lock. */ fetchstate state; isc_boolean_t want_shutdown; isc_boolean_t cloned; isc_boolean_t spilled; unsigned int references; isc_event_t control_event; ISC_LINK(struct fetchctx) link; ISC_LIST(dns_fetchevent_t) events; /*% Locked by task event serialization. */ dns_name_t domain; dns_rdataset_t nameservers; unsigned int attributes; isc_timer_t * timer; isc_time_t expires; isc_interval_t interval; dns_message_t * qmessage; dns_message_t * rmessage; ISC_LIST(resquery_t) queries; dns_adbfindlist_t finds; dns_adbfind_t * find; dns_adbfindlist_t altfinds; dns_adbfind_t * altfind; dns_adbaddrinfolist_t forwaddrs; dns_adbaddrinfolist_t altaddrs; isc_sockaddrlist_t forwarders; dns_fwdpolicy_t fwdpolicy; isc_sockaddrlist_t bad; isc_sockaddrlist_t edns; isc_sockaddrlist_t edns512; isc_sockaddrlist_t bad_edns; - dns_validator_t *validator; + dns_validator_t * validator; ISC_LIST(dns_validator_t) validators; dns_db_t * cache; dns_adb_t * adb; isc_boolean_t ns_ttl_ok; isc_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. */ isc_uint32_t rand_buf; isc_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; isc_uint64_t duration; isc_boolean_t logged; unsigned int querysent; unsigned int referrals; unsigned int lamecount; unsigned int neterr; unsigned int badresp; unsigned int adberr; unsigned int findfail; unsigned int valfail; isc_boolean_t 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; fetchctx_t * private; }; #define DNS_FETCH_MAGIC ISC_MAGIC('F', 't', 'c', 'h') #define DNS_FETCH_VALID(fetch) ISC_MAGIC_VALID(fetch, DNS_FETCH_MAGIC) typedef struct fctxbucket { isc_task_t * task; isc_mutex_t lock; ISC_LIST(fetchctx_t) fctxs; isc_boolean_t exiting; isc_mem_t * mctx; } fctxbucket_t; typedef struct alternate { isc_boolean_t isaddress; union { isc_sockaddr_t addr; struct { dns_name_t name; in_port_t port; } _n; } _u; ISC_LINK(struct alternate) link; } alternate_t; typedef struct dns_badcache dns_badcache_t; struct dns_badcache { dns_badcache_t * next; dns_rdatatype_t type; isc_time_t expire; unsigned int hashval; dns_name_t name; }; #define DNS_BADCACHE_SIZE 1021 #define DNS_BADCACHE_TTL(fctx) \ (((fctx)->res->lame_ttl > 30 ) ? (fctx)->res->lame_ttl : 30) struct dns_resolver { /* Unlocked. */ unsigned int magic; isc_mem_t * mctx; isc_mutex_t lock; isc_mutex_t nlock; isc_mutex_t primelock; dns_rdataclass_t rdclass; isc_socketmgr_t * socketmgr; isc_timermgr_t * timermgr; isc_taskmgr_t * taskmgr; dns_view_t * view; isc_boolean_t frozen; unsigned int options; dns_dispatchmgr_t * dispatchmgr; dns_dispatch_t * dispatchv4; isc_boolean_t exclusivev4; dns_dispatch_t * dispatchv6; isc_boolean_t exclusivev6; unsigned int ndisps; unsigned int nbuckets; fctxbucket_t * buckets; isc_uint32_t lame_ttl; ISC_LIST(alternate_t) alternates; isc_uint16_t udpsize; #if USE_ALGLOCK isc_rwlock_t alglock; #endif dns_rbt_t * algorithms; #if USE_MBSLOCK isc_rwlock_t mbslock; #endif dns_rbt_t * mustbesecure; unsigned int spillatmax; unsigned int spillatmin; isc_timer_t * spillattimer; isc_boolean_t zero_no_soa_ttl; unsigned int query_timeout; + unsigned int maxdepth; + unsigned int maxqueries; /* Locked by lock. */ unsigned int references; isc_boolean_t exiting; isc_eventlist_t whenshutdown; unsigned int activebuckets; isc_boolean_t priming; unsigned int spillat; /* clients-per-query */ unsigned int nextdisp; /* Bad cache. */ dns_badcache_t ** badcache; unsigned int badcount; unsigned int badhash; unsigned int badsweep; /* Locked by primelock. */ dns_fetch_t * primefetch; /* Locked by nlock. */ unsigned int nfctx; }; #define RES_MAGIC ISC_MAGIC('R', 'e', 's', '!') #define VALID_RESOLVER(res) ISC_MAGIC_VALID(res, RES_MAGIC) /*% * Private addrinfo flags. These must not conflict with DNS_FETCHOPT_NOEDNS0, * which we also use as an addrinfo flag. */ #define FCTX_ADDRINFO_MARK 0x0001 #define FCTX_ADDRINFO_FORWARDER 0x1000 #define FCTX_ADDRINFO_TRIED 0x2000 #define UNMARKED(a) (((a)->flags & FCTX_ADDRINFO_MARK) \ == 0) #define ISFORWARDER(a) (((a)->flags & \ FCTX_ADDRINFO_FORWARDER) != 0) #define TRIED(a) (((a)->flags & \ FCTX_ADDRINFO_TRIED) != 0) #define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0) #define NEGATIVE(r) (((r)->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) static void destroy(dns_resolver_t *res); static void empty_bucket(dns_resolver_t *res); static isc_result_t resquery_send(resquery_t *query); static void resquery_response(isc_task_t *task, isc_event_t *event); static void resquery_connected(isc_task_t *task, isc_event_t *event); static void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache); static void fctx_destroy(fetchctx_t *fctx); static isc_boolean_t 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, isc_boolean_t optout, isc_boolean_t secure, dns_rdataset_t *ardataset, isc_result_t *eresultp); static void validated(isc_task_t *task, isc_event_t *event); static isc_boolean_t maybe_destroy(fetchctx_t *fctx, isc_boolean_t 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); /*% * 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 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)) INSIST((valoptions & DNS_VALIDATOR_DEFER) != 0); result = dns_validator_create(fctx->res->view, name, type, rdataset, sigrdataset, fctx->rmessage, valoptions, task, validated, valarg, &validator); if (result == ISC_R_SUCCESS) { inc_stats(fctx->res, dns_resstatscounter_val); if ((valoptions & DNS_VALIDATOR_DEFER) == 0) { INSIST(fctx->validator == NULL); fctx->validator = validator; } ISC_LIST_APPEND(fctx->validators, validator, link); } else isc_mem_put(fctx->mctx, valarg, sizeof(*valarg)); return (result); } static isc_boolean_t 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 (ISC_TRUE); dns_rdata_reset(&rdata); } return (ISC_FALSE); } static isc_boolean_t fix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) { dns_name_t *name; dns_name_t *domain = &fctx->domain; dns_rdataset_t *rdataset; dns_rdatatype_t type; isc_result_t result; isc_boolean_t keep_auth = ISC_FALSE; if (message->rcode == dns_rcode_nxdomain) return (ISC_FALSE); /* * 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 (ISC_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 (ISC_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 (ISC_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 (ISC_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 = ISC_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 (ISC_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 (ISC_FALSE); } else if (dns_name_issubdomain(name, domain)) { /* Referral or answer from child zone. */ return (ISC_FALSE); } } result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); } munge: message->rcode = dns_rcode_nxdomain; message->counts[DNS_SECTION_ANSWER] = 0; if (!keep_auth) message->counts[DNS_SECTION_AUTHORITY] = 0; message->counts[DNS_SECTION_ADDITIONAL] = 0; return (ISC_TRUE); } static inline isc_result_t fctx_starttimer(fetchctx_t *fctx) { /* * Start the lifetime timer for fctx. * * This is also used for stopping the idle timer; in that * case we must purge events already posted to ensure that * no further idle events are delivered. */ return (isc_timer_reset(fctx->timer, isc_timertype_once, &fctx->expires, NULL, ISC_TRUE)); } static inline void fctx_stoptimer(fetchctx_t *fctx) { isc_result_t result; /* * We don't return a result if resetting the timer to inactive fails * since there's nothing to be done about it. Resetting to inactive * should never fail anyway, since the code as currently written * cannot fail in that case. */ result = isc_timer_reset(fctx->timer, isc_timertype_inactive, NULL, NULL, ISC_TRUE); if (result != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_timer_reset(): %s", isc_result_totext(result)); } } static inline isc_result_t fctx_startidletimer(fetchctx_t *fctx, isc_interval_t *interval) { /* * Start the idle timer for fctx. The lifetime timer continues * to be in effect. */ return (isc_timer_reset(fctx->timer, isc_timertype_once, &fctx->expires, interval, ISC_FALSE)); } /* * Stopping the idle timer is equivalent to calling fctx_starttimer(), but * we use fctx_stopidletimer for readability in the code below. */ #define fctx_stopidletimer fctx_starttimer static inline void resquery_destroy(resquery_t **queryp) { resquery_t *query; REQUIRE(queryp != NULL); query = *queryp; REQUIRE(!ISC_LINK_LINKED(query, link)); INSIST(query->tcpsocket == NULL); query->fctx->nqueries--; if (SHUTTINGDOWN(query->fctx)) { dns_resolver_t *res = query->fctx->res; if (maybe_destroy(query->fctx, ISC_FALSE)) empty_bucket(res); } query->magic = 0; isc_mem_put(query->mctx, query, sizeof(*query)); *queryp = NULL; } static void fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp, isc_time_t *finish, isc_boolean_t no_response) { fetchctx_t *fctx; resquery_t *query; unsigned int rtt, rttms; unsigned int factor; dns_adbfind_t *find; dns_adbaddrinfo_t *addrinfo; isc_socket_t *socket; query = *queryp; fctx = query->fctx; FCTXTRACE("cancelquery"); REQUIRE(!RESQUERY_CANCELED(query)); query->attributes |= RESQUERY_ATTR_CANCELED; /* * Should we update the RTT? */ if (finish != NULL || no_response) { if (finish != NULL) { /* * We have both the start and finish times for this * packet, so we can compute a real RTT. */ rtt = (unsigned int)isc_time_microdiff(finish, &query->start); factor = DNS_ADB_RTTADJDEFAULT; 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 { /* * We don't have an RTT for this query. Maybe the * packet was lost, or maybe this server is very * slow. We don't know. Increase the RTT. */ INSIST(no_response); rtt = query->addrinfo->srtt + 200000; if (rtt > 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); } /* Remember that the server has been tried. */ if (!TRIED(query->addrinfo)) { dns_adb_changeflags(fctx->adb, query->addrinfo, FCTX_ADDRINFO_TRIED, FCTX_ADDRINFO_TRIED); } /* * Age RTTs of servers not tried. */ factor = DNS_ADB_RTTADJAGE; if (finish != NULL) for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) if (UNMARKED(addrinfo)) dns_adb_adjustsrtt(fctx->adb, addrinfo, 0, factor); if (finish != NULL && TRIEDFIND(fctx)) for (find = ISC_LIST_HEAD(fctx->finds); find != NULL; find = ISC_LIST_NEXT(find, publink)) for (addrinfo = ISC_LIST_HEAD(find->list); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) if (UNMARKED(addrinfo)) dns_adb_adjustsrtt(fctx->adb, addrinfo, 0, factor); if (finish != NULL && TRIEDALT(fctx)) { for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) if (UNMARKED(addrinfo)) dns_adb_adjustsrtt(fctx->adb, addrinfo, 0, factor); for (find = ISC_LIST_HEAD(fctx->altfinds); find != NULL; find = ISC_LIST_NEXT(find, publink)) for (addrinfo = ISC_LIST_HEAD(find->list); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) if (UNMARKED(addrinfo)) dns_adb_adjustsrtt(fctx->adb, addrinfo, 0, factor); } /* * Check for any outstanding socket events. If they exist, cancel * them and let the event handlers finish the cleanup. The resolver * only needs to worry about managing the connect and send events; * the dispatcher manages the recv events. */ if (RESQUERY_CONNECTING(query)) { /* * Cancel the connect. */ if (query->tcpsocket != NULL) { isc_socket_cancel(query->tcpsocket, NULL, ISC_SOCKCANCEL_CONNECT); } else if (query->dispentry != NULL) { INSIST(query->exclusivesocket); socket = dns_dispatch_getentrysocket(query->dispentry); if (socket != NULL) isc_socket_cancel(socket, NULL, ISC_SOCKCANCEL_CONNECT); } } else if (RESQUERY_SENDING(query)) { /* * Cancel the pending send. */ if (query->exclusivesocket && query->dispentry != NULL) socket = dns_dispatch_getentrysocket(query->dispentry); else socket = dns_dispatch_getsocket(query->dispatch); if (socket != NULL) isc_socket_cancel(socket, NULL, ISC_SOCKCANCEL_SEND); } if (query->dispentry != NULL) dns_dispatch_removeresponse(&query->dispentry, deventp); ISC_LIST_UNLINK(fctx->queries, query, link); if (query->tsig != NULL) isc_buffer_free(&query->tsig); if (query->tsigkey != NULL) dns_tsigkey_detach(&query->tsigkey); if (query->dispatch != NULL) dns_dispatch_detach(&query->dispatch); if (! (RESQUERY_CONNECTING(query) || RESQUERY_SENDING(query))) /* * It's safe to destroy the query now. */ resquery_destroy(&query); } static void fctx_cancelqueries(fetchctx_t *fctx, isc_boolean_t no_response) { resquery_t *query, *next_query; FCTXTRACE("cancelqueries"); for (query = ISC_LIST_HEAD(fctx->queries); query != NULL; query = next_query) { next_query = ISC_LIST_NEXT(query, link); fctx_cancelquery(&query, NULL, NULL, no_response); } } static void fctx_cleanupfinds(fetchctx_t *fctx) { dns_adbfind_t *find, *next_find; REQUIRE(ISC_LIST_EMPTY(fctx->queries)); for (find = ISC_LIST_HEAD(fctx->finds); find != NULL; find = next_find) { next_find = ISC_LIST_NEXT(find, publink); ISC_LIST_UNLINK(fctx->finds, find, publink); dns_adb_destroyfind(&find); } fctx->find = NULL; } static void fctx_cleanupaltfinds(fetchctx_t *fctx) { dns_adbfind_t *find, *next_find; REQUIRE(ISC_LIST_EMPTY(fctx->queries)); for (find = ISC_LIST_HEAD(fctx->altfinds); find != NULL; find = next_find) { next_find = ISC_LIST_NEXT(find, publink); ISC_LIST_UNLINK(fctx->altfinds, find, publink); dns_adb_destroyfind(&find); } fctx->altfind = NULL; } static void fctx_cleanupforwaddrs(fetchctx_t *fctx) { dns_adbaddrinfo_t *addr, *next_addr; REQUIRE(ISC_LIST_EMPTY(fctx->queries)); for (addr = ISC_LIST_HEAD(fctx->forwaddrs); addr != NULL; addr = next_addr) { next_addr = ISC_LIST_NEXT(addr, publink); ISC_LIST_UNLINK(fctx->forwaddrs, addr, publink); dns_adb_freeaddrinfo(fctx->adb, &addr); } } static void fctx_cleanupaltaddrs(fetchctx_t *fctx) { dns_adbaddrinfo_t *addr, *next_addr; REQUIRE(ISC_LIST_EMPTY(fctx->queries)); for (addr = ISC_LIST_HEAD(fctx->altaddrs); addr != NULL; addr = next_addr) { next_addr = ISC_LIST_NEXT(addr, publink); ISC_LIST_UNLINK(fctx->altaddrs, addr, publink); dns_adb_freeaddrinfo(fctx->adb, &addr); } } static inline void fctx_stopeverything(fetchctx_t *fctx, isc_boolean_t no_response) { FCTXTRACE("stopeverything"); fctx_cancelqueries(fctx, no_response); fctx_cleanupfinds(fctx); fctx_cleanupaltfinds(fctx); fctx_cleanupforwaddrs(fctx); fctx_cleanupaltaddrs(fctx); fctx_stoptimer(fctx); } static inline void fctx_sendevents(fetchctx_t *fctx, isc_result_t result, int line) { dns_fetchevent_t *event, *next_event; isc_task_t *task; unsigned int count = 0; isc_interval_t i; isc_boolean_t logit = ISC_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(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 = ISC_TRUE; } isc_interval_set(&i, 20 * 60, 0); result = isc_timer_reset(fctx->res->spillattimer, isc_timertype_ticker, NULL, &i, ISC_TRUE); RUNTIME_CHECK(result == ISC_R_SUCCESS); } UNLOCK(&fctx->res->lock); if (logit) isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, "clients-per-query increased to %u", new_spillat); } } static inline void log_edns(fetchctx_t *fctx) { char domainbuf[DNS_NAME_FORMATSIZE]; if (fctx->reason == NULL) return; /* * 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; isc_boolean_t no_response; REQUIRE(line >= 0); FCTXTRACE("done"); res = fctx->res; if (result == ISC_R_SUCCESS) { /*% * Log any deferred EDNS timeout messages. */ log_edns(fctx); no_response = ISC_TRUE; } else no_response = ISC_FALSE; fctx->reason = NULL; fctx_stopeverything(fctx, no_response); LOCK(&res->buckets[fctx->bucketnum].lock); fctx->state = fetchstate_done; fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; fctx_sendevents(fctx, result, 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; isc_boolean_t retry = ISC_FALSE; isc_result_t result; fetchctx_t *fctx; fctx = query->fctx; if (RESQUERY_CANCELED(query)) { if (query->sends == 0 && query->connects == 0) { /* * This query was canceled while the * isc_socket_sendto/connect() was in progress. */ if (query->tcpsocket != NULL) isc_socket_detach(&query->tcpsocket); resquery_destroy(&query); } } else { switch (sevent->result) { case ISC_R_SUCCESS: break; case ISC_R_HOSTUNREACH: case ISC_R_NETUNREACH: case ISC_R_NOPERM: case ISC_R_ADDRNOTAVAIL: case ISC_R_CONNREFUSED: /* * No route to remote. */ add_bad(fctx, query->addrinfo, sevent->result, badns_unreachable); fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); retry = ISC_TRUE; break; default: fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); break; } } isc_event_free(&event); if (retry) { /* * Behave as if the idle timer has expired. For TCP * this may not actually reflect the latest timer. */ fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; result = fctx_stopidletimer(fctx); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); else fctx_try(fctx, ISC_TRUE, ISC_FALSE); } } static void resquery_udpconnected(isc_task_t *task, isc_event_t *event) { resquery_t *query = event->ev_arg; REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT); QTRACE("udpconnected"); UNUSED(task); INSIST(RESQUERY_CONNECTING(query)); query->connects--; process_sendevent(query, event); } static void resquery_senddone(isc_task_t *task, isc_event_t *event) { resquery_t *query = event->ev_arg; REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE); QTRACE("senddone"); /* * XXXRTH * * Currently we don't wait for the senddone event before retrying * a query. This means that if we get really behind, we may end * up doing extra work! */ UNUSED(task); INSIST(RESQUERY_SENDING(query)); query->sends--; process_sendevent(query, event); } static inline isc_result_t fctx_addopt(dns_message_t *message, unsigned int version, isc_uint16_t udpsize, 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; isc_boolean_t have_addr = ISC_FALSE; unsigned int srtt; 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; /* * Note that the caller MUST guarantee that 'addrinfo' will remain * valid until this query is canceled. */ query->addrinfo = addrinfo; TIME_NOW(&query->start); /* * If this is a TCP query, then we need to make a socket and * a dispatch for it here. Otherwise we use the resolver's * shared dispatch. */ query->dispatchmgr = res->dispatchmgr; query->dispatch = NULL; query->exclusivesocket = ISC_FALSE; query->tcpsocket = NULL; if (res->view->peers != NULL) { dns_peer_t *peer = NULL; isc_netaddr_t dstip; isc_netaddr_fromsockaddr(&dstip, &addrinfo->sockaddr); result = dns_peerlist_peerbyaddr(res->view->peers, &dstip, &peer); if (result == ISC_R_SUCCESS) { result = dns_peer_getquerysource(peer, &addr); if (result == ISC_R_SUCCESS) have_addr = ISC_TRUE; } } if ((query->options & DNS_FETCHOPT_TCP) != 0) { int pf; pf = isc_sockaddr_pf(&addrinfo->sockaddr); if (!have_addr) { switch (pf) { case PF_INET: result = dns_dispatch_getlocaladdress(res->dispatchv4, &addr); break; case PF_INET6: result = dns_dispatch_getlocaladdress(res->dispatchv6, &addr); break; default: result = ISC_R_NOTIMPLEMENTED; break; } if (result != ISC_R_SUCCESS) goto cleanup_query; } isc_sockaddr_setport(&addr, 0); result = isc_socket_create(res->socketmgr, pf, isc_sockettype_tcp, &query->tcpsocket); if (result != ISC_R_SUCCESS) goto cleanup_query; #ifndef BROKEN_TCP_BIND_BEFORE_CONNECT result = isc_socket_bind(query->tcpsocket, &addr, 0); if (result != ISC_R_SUCCESS) goto cleanup_socket; #endif /* * A dispatch will be created once the connect succeeds. */ } else { if (have_addr) { unsigned int attrs, attrmask; attrs = DNS_DISPATCHATTR_UDP; switch (isc_sockaddr_pf(&addr)) { case AF_INET: attrs |= DNS_DISPATCHATTR_IPV4; break; case AF_INET6: attrs |= DNS_DISPATCHATTR_IPV6; break; default: result = ISC_R_NOTIMPLEMENTED; goto cleanup_query; } attrmask = DNS_DISPATCHATTR_UDP; attrmask |= DNS_DISPATCHATTR_TCP; attrmask |= DNS_DISPATCHATTR_IPV4; attrmask |= DNS_DISPATCHATTR_IPV6; result = dns_dispatch_getudp(res->dispatchmgr, res->socketmgr, res->taskmgr, &addr, 4096, 1000, 32768, 16411, 16433, attrs, attrmask, &query->dispatch); if (result != ISC_R_SUCCESS) goto cleanup_query; } else { switch (isc_sockaddr_pf(&addrinfo->sockaddr)) { case PF_INET: dns_dispatch_attach(res->dispatchv4, &query->dispatch); query->exclusivesocket = res->exclusivev4; break; case PF_INET6: dns_dispatch_attach(res->dispatchv6, &query->dispatch); query->exclusivesocket = res->exclusivev6; break; default: result = ISC_R_NOTIMPLEMENTED; goto cleanup_query; } } /* * We should always have a valid dispatcher here. If we * don't support a protocol family, then its dispatcher * will be NULL, but we shouldn't be finding addresses for * protocol types we don't support, so the dispatcher * we found should never be NULL. */ INSIST(query->dispatch != NULL); } query->dispentry = NULL; query->fctx = fctx; query->tsig = NULL; query->tsigkey = NULL; ISC_LINK_INIT(query, link); query->magic = QUERY_MAGIC; if ((query->options & DNS_FETCHOPT_TCP) != 0) { /* * Connect to the remote server. * * XXXRTH Should we attach to the socket? */ result = isc_socket_connect(query->tcpsocket, &addrinfo->sockaddr, task, resquery_connected, query); if (result != ISC_R_SUCCESS) goto cleanup_socket; query->connects++; QTRACE("connecting via TCP"); } else { result = resquery_send(query); if (result != ISC_R_SUCCESS) goto cleanup_dispatch; } + 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 isc_boolean_t 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 (ISC_TRUE); } return (ISC_FALSE); } static void add_bad_edns(fetchctx_t *fctx, isc_sockaddr_t *address) { isc_sockaddr_t *sa; 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 isc_boolean_t triededns(fetchctx_t *fctx, isc_sockaddr_t *address) { isc_sockaddr_t *sa; for (sa = ISC_LIST_HEAD(fctx->edns); sa != NULL; sa = ISC_LIST_NEXT(sa, link)) { if (isc_sockaddr_equal(sa, address)) return (ISC_TRUE); } return (ISC_FALSE); } static void add_triededns(fetchctx_t *fctx, isc_sockaddr_t *address) { isc_sockaddr_t *sa; if (triededns(fctx, address)) return; sa = isc_mem_get(fctx->mctx, sizeof(*sa)); if (sa == NULL) return; *sa = *address; ISC_LIST_INITANDAPPEND(fctx->edns, sa, link); } static isc_boolean_t triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) { isc_sockaddr_t *sa; for (sa = ISC_LIST_HEAD(fctx->edns512); sa != NULL; sa = ISC_LIST_NEXT(sa, link)) { if (isc_sockaddr_equal(sa, address)) return (ISC_TRUE); } return (ISC_FALSE); } static void add_triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) { isc_sockaddr_t *sa; if (triededns512(fctx, address)) return; sa = isc_mem_get(fctx->mctx, sizeof(*sa)); if (sa == NULL) return; *sa = *address; ISC_LIST_INITANDAPPEND(fctx->edns512, sa, link); } static isc_result_t resquery_send(resquery_t *query) { fetchctx_t *fctx; isc_result_t result; dns_name_t *qname = NULL; dns_rdataset_t *qrdataset = NULL; isc_region_t r; dns_resolver_t *res; isc_task_t *task; isc_socket_t *socket; isc_buffer_t tcpbuffer; isc_sockaddr_t *address; isc_buffer_t *buffer; isc_netaddr_t ipaddr; dns_tsigkey_t *tsigkey = NULL; dns_peer_t *peer = NULL; isc_boolean_t useedns; dns_compress_t cctx; isc_boolean_t cleanup_cctx = ISC_FALSE; isc_boolean_t secure_domain; isc_boolean_t connecting = ISC_FALSE; dns_ednsopt_t ednsopts[EDNSOPTS]; unsigned ednsopt = 0; fctx = query->fctx; QTRACE("send"); res = fctx->res; task = res->buckets[fctx->bucketnum].task; address = NULL; if ((query->options & DNS_FETCHOPT_TCP) != 0) { /* * Reserve space for the TCP message length. */ isc_buffer_init(&tcpbuffer, query->data, sizeof(query->data)); isc_buffer_init(&query->buffer, query->data + 2, sizeof(query->data) - 2); buffer = &tcpbuffer; } else { isc_buffer_init(&query->buffer, query->data, sizeof(query->data)); buffer = &query->buffer; } result = dns_message_gettempname(fctx->qmessage, &qname); if (result != ISC_R_SUCCESS) goto cleanup_temps; result = dns_message_gettemprdataset(fctx->qmessage, &qrdataset); if (result != ISC_R_SUCCESS) goto cleanup_temps; /* * Get a query id from the dispatch. */ result = dns_dispatch_addresponse2(query->dispatch, &query->addrinfo->sockaddr, task, resquery_response, query, &query->id, &query->dispentry, res->socketmgr); if (result != ISC_R_SUCCESS) goto cleanup_temps; fctx->qmessage->opcode = dns_opcode_query; /* * Set up question. */ dns_name_init(qname, NULL); dns_name_clone(&fctx->name, qname); dns_rdataset_init(qrdataset); dns_rdataset_makequestion(qrdataset, res->rdclass, fctx->type); ISC_LIST_APPEND(qname->list, qrdataset, link); dns_message_addname(fctx->qmessage, qname, DNS_SECTION_QUESTION); qname = NULL; qrdataset = NULL; /* * Set RD if the client has requested that we do a recursive query, * or if we're sending to a forwarder. */ if ((query->options & DNS_FETCHOPT_RECURSIVE) != 0 || ISFORWARDER(query->addrinfo)) fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD; /* * Set CD if the client says don't validate or the question is * under a secure entry point. */ if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) { fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; } else if (res->view->enablevalidation) { result = dns_view_issecuredomain(res->view, &fctx->name, &secure_domain); if (result != ISC_R_SUCCESS) secure_domain = ISC_FALSE; if (res->view->dlv != NULL) secure_domain = ISC_TRUE; if (secure_domain) fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; } /* * We don't have to set opcode because it defaults to query. */ fctx->qmessage->id = query->id; /* * Convert the question to wire format. */ result = dns_compress_init(&cctx, -1, fctx->res->mctx); if (result != ISC_R_SUCCESS) goto cleanup_message; cleanup_cctx = ISC_TRUE; result = dns_message_renderbegin(fctx->qmessage, &cctx, &query->buffer); if (result != ISC_R_SUCCESS) goto cleanup_message; result = dns_message_rendersection(fctx->qmessage, DNS_SECTION_QUESTION, 0); if (result != ISC_R_SUCCESS) goto cleanup_message; peer = NULL; isc_netaddr_fromsockaddr(&ipaddr, &query->addrinfo->sockaddr); (void) dns_peerlist_peerbyaddr(fctx->res->view->peers, &ipaddr, &peer); /* * The ADB does not know about servers with "edns no". Check this, * and then inform the ADB for future use. */ if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0 && peer != NULL && dns_peer_getsupportedns(peer, &useedns) == ISC_R_SUCCESS && !useedns) { query->options |= DNS_FETCHOPT_NOEDNS0; dns_adb_changeflags(fctx->adb, query->addrinfo, DNS_FETCHOPT_NOEDNS0, DNS_FETCHOPT_NOEDNS0); } /* Sync NOEDNS0 flag in addrinfo->flags and options now. */ if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) != 0) query->options |= DNS_FETCHOPT_NOEDNS0; /* * Handle timeouts by reducing the UDP response size to 512 bytes * then if that doesn't work disabling EDNS (includes DO) and CD. * * These timeout can be due to: * * broken nameservers that don't respond to EDNS queries. * * broken/misconfigured firewalls and NAT implementations * that don't handle IP fragmentation. * * broken/misconfigured firewalls that don't handle responses * greater than 512 bytes. * * broken/misconfigured firewalls that don't handle EDNS, DO * or CD. * * packet loss / link outage. */ if (fctx->timeout) { if ((triededns512(fctx, &query->addrinfo->sockaddr) || fctx->timeouts >= (MAX_EDNS0_TIMEOUTS * 2)) && (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { query->options |= DNS_FETCHOPT_NOEDNS0; fctx->reason = "disabling EDNS"; } else if ((triededns(fctx, &query->addrinfo->sockaddr) || fctx->timeouts >= MAX_EDNS0_TIMEOUTS) && (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { query->options |= DNS_FETCHOPT_EDNS512; fctx->reason = "reducing the advertised EDNS UDP " "packet size to 512 octets"; } fctx->timeout = ISC_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 = 0; /* Default version. */ unsigned int flags; isc_uint16_t udpsize = res->udpsize; isc_boolean_t reqnsid = res->view->requestnsid; flags = query->addrinfo->flags; if ((flags & DNS_FETCHOPT_EDNSVERSIONSET) != 0) { version = flags & DNS_FETCHOPT_EDNSVERSIONMASK; version >>= DNS_FETCHOPT_EDNSVERSIONSHIFT; } if ((query->options & DNS_FETCHOPT_EDNS512) != 0) udpsize = 512; else if (peer != NULL) (void)dns_peer_getudpsize(peer, &udpsize); /* request NSID for current view or peer? */ if (peer != NULL) (void) dns_peer_getrequestnsid(peer, &reqnsid); if (reqnsid) { INSIST(ednsopt < EDNSOPTS); ednsopts[ednsopt].code = DNS_OPT_NSID; ednsopts[ednsopt].length = 0; ednsopts[ednsopt].value = NULL; ednsopt++; } 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; } } else { /* * We know this server doesn't like EDNS0, so we * won't use it. Set the NOEDNS0 bit since we're * not using EDNS0. */ query->options |= DNS_FETCHOPT_NOEDNS0; } } /* * If we need EDNS0 to do this query and aren't using it, we lose. */ if (NEEDEDNS0(fctx) && (query->options & DNS_FETCHOPT_NOEDNS0) != 0) { result = DNS_R_SERVFAIL; goto cleanup_message; } if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) add_triededns(fctx, &query->addrinfo->sockaddr); if ((query->options & DNS_FETCHOPT_EDNS512) != 0) add_triededns512(fctx, &query->addrinfo->sockaddr); /* * Clear CD if EDNS is not in use. */ if ((query->options & DNS_FETCHOPT_NOEDNS0) != 0) fctx->qmessage->flags &= ~DNS_MESSAGEFLAG_CD; /* * Add TSIG record tailored to the current recipient. */ result = dns_view_getpeertsig(fctx->res->view, &ipaddr, &tsigkey); if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) goto cleanup_message; if (tsigkey != NULL) { result = dns_message_settsigkey(fctx->qmessage, tsigkey); dns_tsigkey_detach(&tsigkey); if (result != ISC_R_SUCCESS) goto cleanup_message; } result = dns_message_rendersection(fctx->qmessage, DNS_SECTION_ADDITIONAL, 0); if (result != ISC_R_SUCCESS) goto cleanup_message; result = dns_message_renderend(fctx->qmessage); if (result != ISC_R_SUCCESS) goto cleanup_message; dns_compress_invalidate(&cctx); cleanup_cctx = ISC_FALSE; if (dns_message_gettsigkey(fctx->qmessage) != NULL) { dns_tsigkey_attach(dns_message_gettsigkey(fctx->qmessage), &query->tsigkey); result = dns_message_getquerytsig(fctx->qmessage, fctx->res->mctx, &query->tsig); if (result != ISC_R_SUCCESS) goto cleanup_message; } /* * If using TCP, write the length of the message at the beginning * of the buffer. */ if ((query->options & DNS_FETCHOPT_TCP) != 0) { isc_buffer_usedregion(&query->buffer, &r); isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t)r.length); isc_buffer_add(&tcpbuffer, r.length); } /* * We're now done with the query message. */ dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); if (query->exclusivesocket) socket = dns_dispatch_getentrysocket(query->dispentry); else socket = dns_dispatch_getsocket(query->dispatch); /* * Send the query! */ if ((query->options & DNS_FETCHOPT_TCP) == 0) { address = &query->addrinfo->sockaddr; if (query->exclusivesocket) { result = isc_socket_connect(socket, address, task, resquery_udpconnected, query); if (result != ISC_R_SUCCESS) goto cleanup_message; connecting = ISC_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. */ result = isc_socket_sendto(socket, &r, task, resquery_senddone, query, address, NULL); 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"); return (ISC_R_SUCCESS); cleanup_message: if (cleanup_cctx) dns_compress_invalidate(&cctx); dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); /* * Stop the dispatcher from listening. */ dns_dispatch_removeresponse(&query->dispentry, NULL); cleanup_temps: if (qname != NULL) dns_message_puttempname(fctx->qmessage, &qname); if (qrdataset != NULL) dns_message_puttemprdataset(fctx->qmessage, &qrdataset); return (result); } static void resquery_connected(isc_task_t *task, isc_event_t *event) { isc_socketevent_t *sevent = (isc_socketevent_t *)event; resquery_t *query = event->ev_arg; isc_boolean_t retry = ISC_FALSE; isc_interval_t interval; isc_result_t result; unsigned int attrs; fetchctx_t *fctx; REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT); REQUIRE(VALID_QUERY(query)); QTRACE("connected"); UNUSED(task); /* * XXXRTH * * Currently we don't wait for the connect event before retrying * a query. This means that if we get really behind, we may end * up doing extra work! */ query->connects--; fctx = query->fctx; if (RESQUERY_CANCELED(query)) { /* * This query was canceled while the connect() was in * progress. */ isc_socket_detach(&query->tcpsocket); resquery_destroy(&query); } else { switch (sevent->result) { case ISC_R_SUCCESS: /* * Extend the idle timer for TCP. 20 seconds * should be long enough for a TCP connection to be * established, a single DNS request to be sent, * and the response received. */ isc_interval_set(&interval, 20, 0); result = fctx_startidletimer(query->fctx, &interval); if (result != ISC_R_SUCCESS) { fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); fctx_done(fctx, result, __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) { fctx_cancelquery(&query, NULL, NULL, ISC_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: /* * No route to remote. */ isc_socket_detach(&query->tcpsocket); fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); retry = ISC_TRUE; break; default: isc_socket_detach(&query->tcpsocket); fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); break; } } isc_event_free(&event); if (retry) { /* * Behave as if the idle timer has expired. For TCP * connections this may not actually reflect the latest timer. */ fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; result = fctx_stopidletimer(fctx); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); else fctx_try(fctx, ISC_TRUE, ISC_FALSE); } } static void fctx_finddone(isc_task_t *task, isc_event_t *event) { fetchctx_t *fctx; dns_adbfind_t *find; dns_resolver_t *res; isc_boolean_t want_try = ISC_FALSE; isc_boolean_t want_done = ISC_FALSE; isc_boolean_t bucket_empty = ISC_FALSE; unsigned int bucketnum; isc_boolean_t destroy = ISC_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)); fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; - if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) + if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) { want_try = ISC_TRUE; - else { + } 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. */ want_done = ISC_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); destroy = ISC_TRUE; } } UNLOCK(&res->buckets[bucketnum].lock); isc_event_free(&event); dns_adb_destroyfind(&find); if (want_try) fctx_try(fctx, ISC_TRUE, ISC_FALSE); else if (want_done) fctx_done(fctx, ISC_R_FAILURE, __LINE__); else if (destroy) { - fctx_destroy(fctx); + fctx_destroy(fctx); if (bucket_empty) empty_bucket(res); } } static inline isc_boolean_t bad_server(fetchctx_t *fctx, isc_sockaddr_t *address) { isc_sockaddr_t *sa; for (sa = ISC_LIST_HEAD(fctx->bad); sa != NULL; sa = ISC_LIST_NEXT(sa, link)) { if (isc_sockaddr_equal(sa, address)) return (ISC_TRUE); } return (ISC_FALSE); } static inline isc_boolean_t mark_bad(fetchctx_t *fctx) { dns_adbfind_t *curr; dns_adbaddrinfo_t *addrinfo; isc_boolean_t all_bad = ISC_TRUE; /* * Mark all known bad servers, so we don't try to talk to them * again. */ /* * Mark any bad nameservers. */ for (curr = ISC_LIST_HEAD(fctx->finds); curr != NULL; curr = ISC_LIST_NEXT(curr, publink)) { for (addrinfo = ISC_LIST_HEAD(curr->list); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (bad_server(fctx, &addrinfo->sockaddr)) addrinfo->flags |= FCTX_ADDRINFO_MARK; else all_bad = ISC_FALSE; } } /* * Mark any bad forwarders. */ for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (bad_server(fctx, &addrinfo->sockaddr)) addrinfo->flags |= FCTX_ADDRINFO_MARK; else all_bad = ISC_FALSE; } /* * Mark any bad alternates. */ for (curr = ISC_LIST_HEAD(fctx->altfinds); curr != NULL; curr = ISC_LIST_NEXT(curr, publink)) { for (addrinfo = ISC_LIST_HEAD(curr->list); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (bad_server(fctx, &addrinfo->sockaddr)) addrinfo->flags |= FCTX_ADDRINFO_MARK; else all_bad = ISC_FALSE; } } for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (bad_server(fctx, &addrinfo->sockaddr)) addrinfo->flags |= FCTX_ADDRINFO_MARK; else all_bad = ISC_FALSE; } return (all_bad); } static void add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason, 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; 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, "error (%s%s%s) resolving '%s/%s/%s': %s", dns_result_totext(reason), spc, code, namebuf, typebuf, classbuf, addrbuf); } /* * Sort addrinfo list by RTT. */ static void sort_adbfind(dns_adbfind_t *find) { dns_adbaddrinfo_t *best, *curr; dns_adbaddrinfolist_t sorted; /* Lame N^2 bubble sort. */ ISC_LIST_INIT(sorted); while (!ISC_LIST_EMPTY(find->list)) { best = ISC_LIST_HEAD(find->list); curr = ISC_LIST_NEXT(best, publink); while (curr != NULL) { if (curr->srtt < best->srtt) best = curr; curr = ISC_LIST_NEXT(curr, publink); } ISC_LIST_UNLINK(find->list, best, publink); ISC_LIST_APPEND(sorted, best, publink); } find->list = sorted; } /* * Sort a list of finds by server RTT. */ static void sort_finds(dns_adbfindlist_t *findlist) { dns_adbfind_t *best, *curr; dns_adbfindlist_t sorted; dns_adbaddrinfo_t *addrinfo, *bestaddrinfo; /* Sort each find's addrinfo list by SRTT. */ for (curr = ISC_LIST_HEAD(*findlist); curr != NULL; curr = ISC_LIST_NEXT(curr, publink)) sort_adbfind(curr); /* Lame N^2 bubble sort. */ ISC_LIST_INIT(sorted); while (!ISC_LIST_EMPTY(*findlist)) { best = ISC_LIST_HEAD(*findlist); bestaddrinfo = ISC_LIST_HEAD(best->list); INSIST(bestaddrinfo != NULL); curr = ISC_LIST_NEXT(best, publink); while (curr != NULL) { addrinfo = ISC_LIST_HEAD(curr->list); INSIST(addrinfo != NULL); if (addrinfo->srtt < bestaddrinfo->srtt) { best = curr; bestaddrinfo = addrinfo; } curr = ISC_LIST_NEXT(curr, publink); } ISC_LIST_UNLINK(*findlist, best, publink); ISC_LIST_APPEND(sorted, best, publink); } *findlist = sorted; } static void findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port, unsigned int options, unsigned int flags, isc_stdtime_t now, isc_boolean_t *need_alternate) { dns_adbaddrinfo_t *ai; dns_adbfind_t *find; dns_resolver_t *res; isc_boolean_t unshared; isc_result_t result; res = fctx->res; unshared = ISC_TF((fctx->options & DNS_FETCHOPT_UNSHARED) != 0); /* * If this name is a subdomain of the query domain, tell * the ADB to start looking using zone/hint data. This keeps us * from getting stuck if the nameserver is beneath the zone cut * and we don't know its address (e.g. because the A record has * expired). */ if (dns_name_issubdomain(name, &fctx->domain)) options |= DNS_ADBFIND_STARTATZONE; options |= DNS_ADBFIND_GLUEOK; options |= DNS_ADBFIND_HINTOK; /* * See what we know about this address. */ find = NULL; - result = dns_adb_createfind(fctx->adb, - res->buckets[fctx->bucketnum].task, - fctx_finddone, fctx, name, - &fctx->name, fctx->type, - options, now, NULL, - res->view->dstport, &find); + 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) { /* * XXXRTH Follow the CNAME/DNAME chain? */ dns_adb_destroyfind(&find); fctx->adberr++; } } else if (!ISC_LIST_EMPTY(find->list)) { /* * We have at least some of the addresses for the * name. */ INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0); if (flags != 0 || port != 0) { for (ai = ISC_LIST_HEAD(find->list); ai != NULL; ai = ISC_LIST_NEXT(ai, publink)) { ai->flags |= flags; if (port != 0) isc_sockaddr_setport(&ai->sockaddr, port); } } if ((flags & FCTX_ADDRINFO_FORWARDER) != 0) ISC_LIST_APPEND(fctx->altfinds, find, publink); else ISC_LIST_APPEND(fctx->finds, find, publink); } else { /* * We don't know any of the addresses for this * name. */ if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) { /* * We're looking for them and will get an * event about it later. */ fctx->pending++; /* * Bootstrap. */ if (need_alternate != NULL && !*need_alternate && unshared && ((res->dispatchv4 == NULL && find->result_v6 != DNS_R_NXDOMAIN) || (res->dispatchv6 == NULL && find->result_v4 != DNS_R_NXDOMAIN))) *need_alternate = ISC_TRUE; } else { if ((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->dispatchv4 == NULL && find->result_v6 == DNS_R_NXRRSET) || (res->dispatchv6 == NULL && find->result_v4 == DNS_R_NXRRSET))) *need_alternate = ISC_TRUE; dns_adb_destroyfind(&find); } } } static isc_boolean_t 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 (ISC_TF(namereln == dns_namereln_subdomain)); } static isc_result_t fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) { dns_rdata_t rdata = DNS_RDATA_INIT; isc_result_t result; dns_resolver_t *res; isc_stdtime_t now; unsigned int stdoptions = 0; isc_sockaddr_t *sa; dns_adbaddrinfo_t *ai; isc_boolean_t all_bad; dns_rdata_ns_t ns; isc_boolean_t need_alternate = ISC_FALSE; FCTXTRACE("getaddresses"); /* * Don't pound on remote servers. (Failsafe!) */ fctx->restarts++; if (fctx->restarts > 10) { FCTXTRACE("too many restarts"); return (DNS_R_SERVFAIL); } res = fctx->res; + 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'", + fctx->info); + 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). */ sa = ISC_LIST_HEAD(fctx->forwarders); if (sa == NULL) { dns_forwarders_t *forwarders = NULL; dns_name_t *name = &fctx->name; dns_name_t suffix; unsigned int labels; 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; } dns_fixedname_init(&fixed); domain = dns_fixedname_name(&fixed); result = dns_fwdtable_find2(fctx->res->view->fwdtable, name, domain, &forwarders); if (result == ISC_R_SUCCESS) { sa = ISC_LIST_HEAD(forwarders->addrs); fctx->fwdpolicy = forwarders->fwdpolicy; if (fctx->fwdpolicy == dns_fwdpolicy_only && isstrictsubdomain(domain, &fctx->domain)) { 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); } } } while (sa != NULL) { if ((isc_sockaddr_pf(sa) == AF_INET && fctx->res->dispatchv4 == NULL) || (isc_sockaddr_pf(sa) == AF_INET6 && fctx->res->dispatchv6 == NULL)) { sa = ISC_LIST_NEXT(sa, link); continue; } ai = NULL; result = dns_adb_findaddrinfo(fctx->adb, sa, &ai, 0); /* XXXMLG */ if (result == ISC_R_SUCCESS) { dns_adbaddrinfo_t *cur; ai->flags |= FCTX_ADDRINFO_FORWARDER; cur = ISC_LIST_HEAD(fctx->forwaddrs); while (cur != NULL && cur->srtt < ai->srtt) cur = ISC_LIST_NEXT(cur, publink); if (cur != NULL) ISC_LIST_INSERTBEFORE(fctx->forwaddrs, cur, ai, publink); else ISC_LIST_APPEND(fctx->forwaddrs, ai, publink); } sa = ISC_LIST_NEXT(sa, link); } /* * If the forwarding policy is "only", we don't need the addresses * of the nameservers. */ if (fctx->fwdpolicy == dns_fwdpolicy_only) goto out; /* * Normal nameservers. */ stdoptions = DNS_ADBFIND_WANTEVENT | DNS_ADBFIND_EMPTYEVENT; if (fctx->restarts == 1) { /* * To avoid sending out a flood of queries likely to * result in NXRRSET, we suppress fetches for address * families we don't have the first time through, * provided that we have addresses in some family we * can use. * * We don't want to set this option all the time, since * if fctx->restarts > 1, we've clearly been having trouble * with the addresses we had, so getting more could help. */ stdoptions |= DNS_ADBFIND_AVOIDFETCHES; } if (res->dispatchv4 != NULL) stdoptions |= DNS_ADBFIND_INET; if (res->dispatchv6 != NULL) stdoptions |= DNS_ADBFIND_INET6; isc_stdtime_get(&now); INSIST(ISC_LIST_EMPTY(fctx->finds)); INSIST(ISC_LIST_EMPTY(fctx->altfinds)); for (result = dns_rdataset_first(&fctx->nameservers); result == ISC_R_SUCCESS; result = dns_rdataset_next(&fctx->nameservers)) { dns_rdataset_current(&fctx->nameservers, &rdata); /* * Extract the name from the NS record. */ result = dns_rdata_tostruct(&rdata, &ns, NULL); if (result != ISC_R_SUCCESS) continue; findname(fctx, &ns.name, 0, stdoptions, 0, now, &need_alternate); dns_rdata_reset(&rdata); dns_rdata_freestruct(&ns); } if (result != ISC_R_NOMORE) return (result); /* * Do we need to use 6 to 4? */ if (need_alternate) { int family; alternate_t *a; family = (res->dispatchv6 != NULL) ? AF_INET6 : AF_INET; for (a = ISC_LIST_HEAD(fctx->res->alternates); a != NULL; a = ISC_LIST_NEXT(a, link)) { if (!a->isaddress) { findname(fctx, &a->_u._n.name, a->_u._n.port, stdoptions, FCTX_ADDRINFO_FORWARDER, now, NULL); continue; } if (isc_sockaddr_pf(&a->_u.addr) != family) continue; ai = NULL; result = dns_adb_findaddrinfo(fctx->adb, &a->_u.addr, &ai, 0); if (result == ISC_R_SUCCESS) { dns_adbaddrinfo_t *cur; ai->flags |= FCTX_ADDRINFO_FORWARDER; cur = ISC_LIST_HEAD(fctx->altaddrs); while (cur != NULL && cur->srtt < ai->srtt) cur = ISC_LIST_NEXT(cur, publink); if (cur != NULL) ISC_LIST_INSERTBEFORE(fctx->altaddrs, cur, ai, publink); else ISC_LIST_APPEND(fctx->altaddrs, ai, publink); } } } out: /* * Mark all known bad servers. */ all_bad = mark_bad(fctx); /* * How are we doing? */ if (all_bad) { /* * We've got no addresses. */ if (fctx->pending > 0) { /* * We're fetching the addresses, but don't have any * yet. Tell the caller to wait for an answer. */ result = DNS_R_WAIT; } else { 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_BADCACHE_TTL(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(fctx->res, &fctx->name, fctx->type, &expire); result = ISC_R_FAILURE; } } else { /* * We've found some addresses. We might still be looking * for more addresses. */ sort_finds(&fctx->finds); sort_finds(&fctx->altfinds); result = ISC_R_SUCCESS; } return (result); } static inline void possibly_mark(fetchctx_t *fctx, dns_adbaddrinfo_t *addr) { isc_netaddr_t na; char buf[ISC_NETADDR_FORMATSIZE]; isc_sockaddr_t *sa; isc_boolean_t aborted = ISC_FALSE; isc_boolean_t bogus; dns_acl_t *blackhole; isc_netaddr_t ipaddr; dns_peer_t *peer = NULL; dns_resolver_t *res; const char *msg = NULL; sa = &addr->sockaddr; res = fctx->res; isc_netaddr_fromsockaddr(&ipaddr, sa); blackhole = dns_dispatchmgr_getblackhole(res->dispatchmgr); (void) dns_peerlist_peerbyaddr(res->view->peers, &ipaddr, &peer); if (blackhole != NULL) { int match; if (dns_acl_match(&ipaddr, NULL, blackhole, &res->view->aclenv, &match, NULL) == ISC_R_SUCCESS && match > 0) aborted = ISC_TRUE; } if (peer != NULL && dns_peer_getbogus(peer, &bogus) == ISC_R_SUCCESS && bogus) aborted = ISC_TRUE; if (aborted) { addr->flags |= FCTX_ADDRINFO_MARK; msg = "ignoring blackholed / bogus server: "; } else if (isc_sockaddr_ismulticast(sa)) { addr->flags |= FCTX_ADDRINFO_MARK; msg = "ignoring multicast address: "; } else if (isc_sockaddr_isexperimental(sa)) { addr->flags |= FCTX_ADDRINFO_MARK; msg = "ignoring experimental address: "; } else if (sa->type.sa.sa_family != AF_INET6) { return; } else if (IN6_IS_ADDR_V4MAPPED(&sa->type.sin6.sin6_addr)) { addr->flags |= FCTX_ADDRINFO_MARK; msg = "ignoring IPv6 mapped IPV4 address: "; } else if (IN6_IS_ADDR_V4COMPAT(&sa->type.sin6.sin6_addr)) { addr->flags |= FCTX_ADDRINFO_MARK; msg = "ignoring IPv6 compatibility IPV4 address: "; } else return; if (!isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3))) return; isc_netaddr_fromsockaddr(&na, sa); isc_netaddr_format(&na, buf, sizeof(buf)); FCTXTRACE2(msg, buf); } static inline dns_adbaddrinfo_t * fctx_nextaddress(fetchctx_t *fctx) { dns_adbfind_t *find, *start; dns_adbaddrinfo_t *addrinfo; dns_adbaddrinfo_t *faddrinfo; /* * Return the next untried address, if any. */ /* * Find the first unmarked forwarder (if any). */ for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (!UNMARKED(addrinfo)) continue; possibly_mark(fctx, addrinfo); if (UNMARKED(addrinfo)) { addrinfo->flags |= FCTX_ADDRINFO_MARK; fctx->find = NULL; return (addrinfo); } } /* * No forwarders. Move to the next find. */ fctx->attributes |= FCTX_ATTR_TRIEDFIND; find = fctx->find; if (find == NULL) find = ISC_LIST_HEAD(fctx->finds); else { find = ISC_LIST_NEXT(find, publink); if (find == NULL) find = ISC_LIST_HEAD(fctx->finds); } /* * Find the first unmarked addrinfo. */ addrinfo = NULL; if (find != NULL) { start = find; do { for (addrinfo = ISC_LIST_HEAD(find->list); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (!UNMARKED(addrinfo)) continue; possibly_mark(fctx, addrinfo); if (UNMARKED(addrinfo)) { addrinfo->flags |= FCTX_ADDRINFO_MARK; break; } } if (addrinfo != NULL) break; find = ISC_LIST_NEXT(find, publink); if (find == NULL) find = ISC_LIST_HEAD(fctx->finds); } while (find != start); } fctx->find = find; if (addrinfo != NULL) return (addrinfo); /* * No nameservers left. Try alternates. */ fctx->attributes |= FCTX_ATTR_TRIEDALT; find = fctx->altfind; if (find == NULL) find = ISC_LIST_HEAD(fctx->altfinds); else { find = ISC_LIST_NEXT(find, publink); if (find == NULL) find = ISC_LIST_HEAD(fctx->altfinds); } /* * Find the first unmarked addrinfo. */ addrinfo = NULL; if (find != NULL) { start = find; do { for (addrinfo = ISC_LIST_HEAD(find->list); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (!UNMARKED(addrinfo)) continue; possibly_mark(fctx, addrinfo); if (UNMARKED(addrinfo)) { addrinfo->flags |= FCTX_ADDRINFO_MARK; break; } } if (addrinfo != NULL) break; find = ISC_LIST_NEXT(find, publink); if (find == NULL) find = ISC_LIST_HEAD(fctx->altfinds); } while (find != start); } faddrinfo = addrinfo; /* * See if we have a better alternate server by address. */ for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (!UNMARKED(addrinfo)) continue; possibly_mark(fctx, addrinfo); if (UNMARKED(addrinfo) && (faddrinfo == NULL || addrinfo->srtt < faddrinfo->srtt)) { if (faddrinfo != NULL) faddrinfo->flags &= ~FCTX_ADDRINFO_MARK; addrinfo->flags |= FCTX_ADDRINFO_MARK; break; } } if (addrinfo == NULL) { addrinfo = faddrinfo; fctx->altfind = find; } return (addrinfo); } static void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) { isc_result_t result; dns_adbaddrinfo_t *addrinfo; FCTXTRACE("try"); REQUIRE(!ADDRWAIT(fctx)); addrinfo = fctx_nextaddress(fctx); if (addrinfo == NULL) { /* * We have no more addresses. Start over. */ fctx_cancelqueries(fctx, ISC_TRUE); fctx_cleanupfinds(fctx); fctx_cleanupaltfinds(fctx); fctx_cleanupforwaddrs(fctx); fctx_cleanupaltaddrs(fctx); result = fctx_getaddresses(fctx, 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 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; } } + 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; + } + result = fctx_query(fctx, addrinfo, fctx->options); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); else if (retrying) inc_stats(fctx->res, dns_resstatscounter_retry); } static isc_boolean_t 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); if (res->buckets[bucketnum].exiting && ISC_LIST_EMPTY(res->buckets[bucketnum].fctxs)) return (ISC_TRUE); return (ISC_FALSE); } static void fctx_destroy(fetchctx_t *fctx) { isc_sockaddr_t *sa, *next_sa; 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 (sa = ISC_LIST_HEAD(fctx->edns); sa != NULL; sa = next_sa) { next_sa = ISC_LIST_NEXT(sa, link); ISC_LIST_UNLINK(fctx->edns, sa, link); isc_mem_put(fctx->mctx, sa, sizeof(*sa)); } for (sa = ISC_LIST_HEAD(fctx->edns512); sa != NULL; sa = next_sa) { next_sa = ISC_LIST_NEXT(sa, link); ISC_LIST_UNLINK(fctx->edns512, sa, link); isc_mem_put(fctx->mctx, sa, sizeof(*sa)); } 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); 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 = ISC_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) { fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); } fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; /* * Our timer has triggered. Reestablish the fctx lifetime * timer. */ result = fctx_starttimer(fctx); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); else /* * Keep trying. */ fctx_try(fctx, ISC_TRUE, ISC_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 = ISC_TRUE; /* * Unless we're still initializing (in which case the * control event is still outstanding), we need to post * the control event to tell the fetch we want it to * exit. */ if (fctx->state != fetchstate_init) { cevent = &fctx->control_event; isc_task_send(fctx->res->buckets[fctx->bucketnum].task, &cevent); } } static void fctx_doshutdown(isc_task_t *task, isc_event_t *event) { fetchctx_t *fctx = event->ev_arg; isc_boolean_t bucket_empty = ISC_FALSE; dns_resolver_t *res; unsigned int bucketnum; dns_validator_t *validator; isc_boolean_t destroy = ISC_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 that is still running on behalf of this * fetch. To avoid deadlock with the ADB, we must do this * before we lock the bucket lock. */ fctx_stopeverything(fctx, ISC_FALSE); LOCK(&res->buckets[bucketnum].lock); fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN; INSIST(fctx->state == fetchstate_active || fctx->state == fetchstate_done); INSIST(fctx->want_shutdown); if (fctx->state != fetchstate_done) { fctx->state = fetchstate_done; fctx_sendevents(fctx, ISC_R_CANCELED, __LINE__); } if (fctx->references == 0 && fctx->pending == 0 && fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) { bucket_empty = fctx_unlink(fctx); destroy = ISC_TRUE; } UNLOCK(&res->buckets[bucketnum].lock); if (destroy) { 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; isc_boolean_t done = ISC_FALSE, bucket_empty = ISC_FALSE; dns_resolver_t *res; unsigned int bucketnum; isc_boolean_t destroy = ISC_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); destroy = ISC_TRUE; } done = ISC_TRUE; } else { /* * Normal fctx startup. */ fctx->state = fetchstate_active; /* * Reset the control event for later use in shutting down * the fctx. */ ISC_EVENT_INIT(event, sizeof(*event), 0, NULL, DNS_EVENT_FETCHCONTROL, fctx_doshutdown, fctx, NULL, NULL, NULL); } UNLOCK(&res->buckets[bucketnum].lock); if (!done) { isc_result_t result; INSIST(!destroy); /* * 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, ISC_FALSE, ISC_FALSE); } else if (destroy) { 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 *clone; dns_fetchevent_t *event; FCTXTRACE("join"); /* * We store the task we're going to send this event to in the * sender field. We'll make the fetch the sender when we actually * send the event. */ clone = NULL; isc_task_attach(task, &clone); event = (dns_fetchevent_t *) isc_event_allocate(fctx->res->mctx, clone, DNS_EVENT_FETCHDONE, action, arg, sizeof(*event)); if (event == NULL) { isc_task_detach(&clone); return (ISC_R_NOMEMORY); } event->result = DNS_R_SERVFAIL; event->qtype = fctx->type; event->db = NULL; event->node = NULL; event->rdataset = rdataset; event->sigrdataset = sigrdataset; event->fetch = fetch; event->client = client; event->id = id; dns_fixedname_init(&event->foundname); /* * Make sure that we can store the sigrdataset in the * first event if it is needed by any of the events. */ if (event->sigrdataset != NULL) ISC_LIST_PREPEND(fctx->events, event, ev_link); else ISC_LIST_APPEND(fctx->events, event, ev_link); fctx->references++; 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, fetchctx_t **fctxp) + 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]; dns_name_t suffix; 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)); strcat(buf, "/"); /* checked */ strcat(buf, typebuf); /* checked */ fctx->info = isc_mem_strdup(mctx, buf); if (fctx->info == NULL) { result = ISC_R_NOMEMORY; - goto cleanup_fetch; + 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->state = fetchstate_init; fctx->want_shutdown = ISC_FALSE; fctx->cloned = ISC_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->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 = ISC_FALSE; fctx->attributes = 0; fctx->spilled = ISC_FALSE; fctx->nqueries = 0; fctx->reason = NULL; fctx->rand_buf = 0; fctx->rand_bits = 0; fctx->timeout = ISC_FALSE; fctx->addrinfo = NULL; fctx->client = NULL; fctx->ns_ttl = 0; fctx->ns_ttl_ok = ISC_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; /* * 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); fwdname = &suffix; } dns_fixedname_init(&fixed); domain = dns_fixedname_name(&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, fwdname, domain, 0, findoptions, ISC_TRUE, &fctx->nameservers, NULL); if (result != ISC_R_SUCCESS) goto cleanup_name; result = dns_name_dup(domain, mctx, &fctx->domain); if (result != ISC_R_SUCCESS) { dns_rdataset_disassociate(&fctx->nameservers); goto cleanup_name; } fctx->ns_ttl = fctx->nameservers.ttl; fctx->ns_ttl_ok = ISC_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 = ISC_TRUE; } 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_domain; 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); *fctxp = fctx; return (ISC_R_SUCCESS); cleanup_rmessage: dns_message_destroy(&fctx->rmessage); cleanup_qmessage: dns_message_destroy(&fctx->qmessage); cleanup_domain: if (dns_name_countlabels(&fctx->domain) > 0) dns_name_free(&fctx->domain, mctx); 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 isc_boolean_t is_lame(fetchctx_t *fctx) { dns_message_t *message = fctx->rmessage; dns_name_t *name; dns_rdataset_t *rdataset; isc_result_t result; if (message->rcode != dns_rcode_noerror && message->rcode != dns_rcode_nxdomain) return (ISC_FALSE); if (message->counts[DNS_SECTION_ANSWER] != 0) return (ISC_FALSE); if (message->counts[DNS_SECTION_AUTHORITY] == 0) return (ISC_FALSE); result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); while (result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { dns_namereln_t namereln; int order; unsigned int labels; if (rdataset->type != dns_rdatatype_ns) continue; namereln = dns_name_fullcompare(name, &fctx->domain, &order, &labels); if (namereln == dns_namereln_equal && (message->flags & DNS_MESSAGEFLAG_AA) != 0) return (ISC_FALSE); if (namereln == dns_namereln_subdomain) return (ISC_FALSE); return (ISC_TRUE); } result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); } return (ISC_FALSE); } static inline void log_lame(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo) { char namebuf[DNS_NAME_FORMATSIZE]; char domainbuf[DNS_NAME_FORMATSIZE]; char addrbuf[ISC_SOCKADDR_FORMATSIZE]; dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); isc_sockaddr_format(&addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS, DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, "lame server resolving '%s' (in '%s'?): %s", namebuf, domainbuf, addrbuf); } static inline 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 inline isc_result_t same_question(fetchctx_t *fctx) { isc_result_t result; dns_message_t *message = fctx->rmessage; dns_name_t *name; dns_rdataset_t *rdataset; /* * Caller must be holding the fctx lock. */ /* * XXXRTH Currently we support only one question. */ if (message->counts[DNS_SECTION_QUESTION] != 1) { 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 class[DNS_RDATACLASS_FORMATSIZE]; char type[DNS_RDATATYPE_FORMATSIZE]; dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdataclass_format(rdataset->rdclass, class, sizeof(class)); dns_rdatatype_format(rdataset->type, type, sizeof(type)); log_formerr(fctx, "question section mismatch: got %s/%s/%s", namebuf, class, type); return (DNS_R_FORMERR); } return (ISC_R_SUCCESS); } static void clone_results(fetchctx_t *fctx) { dns_fetchevent_t *event, *hevent; isc_result_t result; dns_name_t *name, *hname; FCTXTRACE("clone_results"); /* * Set up any other events to have the same data as the first * event. * * Caller must be holding the appropriate lock. */ fctx->cloned = ISC_TRUE; hevent = ISC_LIST_HEAD(fctx->events); if (hevent == NULL) return; hname = dns_fixedname_name(&hevent->foundname); for (event = ISC_LIST_NEXT(hevent, ev_link); event != NULL; event = ISC_LIST_NEXT(event, ev_link)) { name = dns_fixedname_name(&event->foundname); result = dns_name_copy(hname, name, NULL); if (result != ISC_R_SUCCESS) event->result = result; else event->result = hevent->result; dns_db_attach(hevent->db, &event->db); dns_db_attachnode(hevent->db, hevent->node, &event->node); INSIST(hevent->rdataset != NULL); INSIST(event->rdataset != NULL); if (dns_rdataset_isassociated(hevent->rdataset)) dns_rdataset_clone(hevent->rdataset, event->rdataset); INSIST(! (hevent->sigrdataset == NULL && event->sigrdataset != NULL)); if (hevent->sigrdataset != NULL && dns_rdataset_isassociated(hevent->sigrdataset) && event->sigrdataset != NULL) dns_rdataset_clone(hevent->sigrdataset, event->sigrdataset); } } #define CACHE(r) (((r)->attributes & DNS_RDATASETATTR_CACHE) != 0) #define ANSWER(r) (((r)->attributes & DNS_RDATASETATTR_ANSWER) != 0) #define ANSWERSIG(r) (((r)->attributes & DNS_RDATASETATTR_ANSWERSIG) != 0) #define EXTERNAL(r) (((r)->attributes & DNS_RDATASETATTR_EXTERNAL) != 0) #define CHAINING(r) (((r)->attributes & DNS_RDATASETATTR_CHAINING) != 0) #define CHASE(r) (((r)->attributes & DNS_RDATASETATTR_CHASE) != 0) #define CHECKNAMES(r) (((r)->attributes & DNS_RDATASETATTR_CHECKNAMES) != 0) /* * Destroy '*fctx' if it is ready to be destroyed (i.e., if it has * no references and is no longer waiting for any events). * * Requires: * '*fctx' is shutting down. * * Returns: * true if the resolver is exiting and this is the last fctx in the bucket. */ static isc_boolean_t maybe_destroy(fetchctx_t *fctx, isc_boolean_t locked) { unsigned int bucketnum; isc_boolean_t bucket_empty = ISC_FALSE; dns_resolver_t *res = fctx->res; dns_validator_t *validator, *next_validator; isc_boolean_t destroy = ISC_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); destroy = ISC_TRUE; } unlock: if (!locked) UNLOCK(&res->buckets[bucketnum].lock); if (destroy) 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; isc_boolean_t chaining; isc_boolean_t negative; isc_boolean_t sentresponse; isc_result_t eresult = ISC_R_SUCCESS; isc_result_t result = ISC_R_SUCCESS; isc_stdtime_t now; isc_uint32_t ttl; 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"); LOCK(&res->buckets[fctx->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 = ISC_TF(vevent->rdataset == NULL); sentresponse = ISC_TF((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0); /* * If shutting down, ignore the results. Check to see if we're * done waiting for validator completions and ADB pending events; if * so, destroy the fctx. */ if (SHUTTINGDOWN(fctx) && !sentresponse) { isc_uint32_t bucketnum = fctx->bucketnum; isc_boolean_t bucket_empty; bucket_empty = maybe_destroy(fctx, ISC_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 = ISC_TRUE; } else chaining = ISC_FALSE; /* * Either we're not shutting down, or we are shutting down but want * to cache the result anyway (if this was a validation started by * a query with cd set) */ hevent = ISC_LIST_HEAD(fctx->events); if (hevent != NULL) { if (!negative && !chaining && (fctx->type == dns_rdatatype_any || fctx->type == dns_rdatatype_rrsig || fctx->type == dns_rdatatype_sig)) { /* * Don't bind rdatasets; the caller * will iterate the node. */ } else { ardataset = hevent->rdataset; asigrdataset = hevent->sigrdataset; } } if (vevent->result != ISC_R_SUCCESS) { FCTXTRACE("validation failed"); inc_stats(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, ISC_TRUE, &node); if (result == ISC_R_SUCCESS) (void)dns_db_deleterdataset(fctx->cache, node, NULL, vevent->type, 0); if (result == ISC_R_SUCCESS && vevent->sigrdataset != NULL) (void)dns_db_deleterdataset(fctx->cache, node, NULL, dns_rdatatype_rrsig, vevent->type); if (result == ISC_R_SUCCESS) dns_db_detachnode(fctx->cache, &node); } 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, ISC_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[fctx->bucketnum].lock); INSIST(fctx->validator == NULL); fctx->validator = ISC_LIST_HEAD(fctx->validators); if (fctx->validator != NULL) dns_validator_send(fctx->validator); else if (sentresponse) fctx_done(fctx, result, __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_BADCACHE_TTL(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, ISC_TRUE, ISC_TRUE); /* Locks bucket. */ return; } if (negative) { dns_rdatatype_t covers; FCTXTRACE("nonexistence validation OK"); inc_stats(res, dns_resstatscounter_valnegsuccess); if (fctx->rmessage->rcode == dns_rcode_nxdomain) covers = dns_rdatatype_any; else covers = fctx->type; result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE, &node); if (result != ISC_R_SUCCESS) goto noanswer_response; /* * If we are asking for a SOA record set the cache time * to zero to facilitate locating the containing zone of * a 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, ISC_TRUE, &node); if (result != ISC_R_SUCCESS) goto noanswer_response; result = dns_db_addrdataset(fctx->cache, node, NULL, now, vevent->rdataset, 0, ardataset); if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) goto noanswer_response; if (ardataset != NULL && 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, 0, asigrdataset); if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) goto noanswer_response; } if (sentresponse) { isc_boolean_t bucket_empty = ISC_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, ISC_TRUE); UNLOCK(&res->buckets[fctx->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[fctx->bucketnum].lock); dns_validator_send(ISC_LIST_HEAD(fctx->validators)); goto cleanup_event; } answer_response: /* * Cache any NS/NSEC records that happened to be validated. */ result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY); while (result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY, &name); for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if ((rdataset->type != dns_rdatatype_ns && rdataset->type != dns_rdatatype_nsec) || rdataset->trust != dns_trust_secure) continue; for (sigrdataset = ISC_LIST_HEAD(name->list); sigrdataset != NULL; sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { if (sigrdataset->type != dns_rdatatype_rrsig || sigrdataset->covers != rdataset->type) continue; break; } if (sigrdataset == NULL || sigrdataset->trust != dns_trust_secure) continue; result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &nsnode); if (result != ISC_R_SUCCESS) continue; result = dns_db_addrdataset(fctx->cache, nsnode, NULL, now, rdataset, 0, NULL); if (result == ISC_R_SUCCESS) result = dns_db_addrdataset(fctx->cache, nsnode, NULL, now, sigrdataset, 0, NULL); dns_db_detachnode(fctx->cache, &nsnode); 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[fctx->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); dns_fixedname_init(&fzonename); zonename = dns_fixedname_name(&fzonename); dns_fixedname_init(&fclosest); closest = dns_fixedname_name(&fclosest); dns_fixedname_init(&fnearest); nearest = dns_fixedname_name(&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) { isc_boolean_t data = ISC_FALSE, exists = ISC_FALSE; isc_boolean_t optout = ISC_FALSE, unknown = ISC_FALSE; isc_boolean_t setclosest = ISC_FALSE; isc_boolean_t setnearest = ISC_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, *sigrdataset; dns_rdataset_t *addedrdataset, *ardataset, *asigrdataset; dns_rdataset_t *valrdataset = NULL, *valsigrdataset = NULL; dns_dbnode_t *node, **anodep; dns_db_t **adbp; dns_name_t *aname; dns_resolver_t *res; isc_boolean_t need_validation, secure_domain, have_answer; isc_result_t result, eresult; dns_fetchevent_t *event; unsigned int options; isc_task_t *task; isc_boolean_t fail; unsigned int valoptions = 0; /* * The appropriate bucket lock must be held. */ res = fctx->res; need_validation = ISC_FALSE; POST(need_validation); secure_domain = ISC_FALSE; have_answer = ISC_FALSE; eresult = ISC_R_SUCCESS; task = res->buckets[fctx->bucketnum].task; /* * Is DNSSEC validation required for this name? */ if (res->view->enablevalidation) { result = dns_view_issecuredomain(res->view, name, &secure_domain); if (result != ISC_R_SUCCESS) return (result); if (!secure_domain && res->view->dlv != NULL) { valoptions = DNS_VALIDATOR_DLV; secure_domain = ISC_TRUE; } } if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) need_validation = ISC_FALSE; else need_validation = secure_domain; adbp = NULL; aname = NULL; anodep = NULL; ardataset = NULL; asigrdataset = NULL; event = NULL; if ((name->attributes & DNS_NAMEATTR_ANSWER) != 0 && !need_validation) { have_answer = ISC_TRUE; event = ISC_LIST_HEAD(fctx->events); if (event != NULL) { adbp = &event->db; aname = dns_fixedname_name(&event->foundname); result = dns_name_copy(name, aname, NULL); if (result != ISC_R_SUCCESS) return (result); anodep = &event->node; /* * If this is an ANY, SIG or RRSIG query, we're not * going to return any rdatasets, unless we encountered * a CNAME or DNAME as "the answer". In this case, * we're going to return DNS_R_CNAME or DNS_R_DNAME * and we must set up the rdatasets. */ if ((fctx->type != dns_rdatatype_any && fctx->type != dns_rdatatype_rrsig && fctx->type != dns_rdatatype_sig) || (name->attributes & DNS_NAMEATTR_CHAINING) != 0) { ardataset = event->rdataset; asigrdataset = event->sigrdataset; } } } /* * Find or create the cache node. */ node = NULL; result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node); if (result != ISC_R_SUCCESS) return (result); /* * Cache or validate each cacheable rdataset. */ fail = ISC_TF((fctx->res->options & DNS_RESOLVER_CHECKNAMESFAIL) != 0); for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if (!CACHE(rdataset)) continue; if (CHECKNAMES(rdataset)) { char namebuf[DNS_NAME_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; char classbuf[DNS_RDATATYPE_FORMATSIZE]; dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf)); dns_rdataclass_format(rdataset->rdclass, classbuf, sizeof(classbuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, "check-names %s %s/%s/%s", fail ? "failure" : "warning", namebuf, typebuf, classbuf); if (fail) { if (ANSWER(rdataset)) { dns_db_detachnode(fctx->cache, &node); return (DNS_R_BADNAME); } continue; } } /* * Enforce the configure maximum cache TTL. */ if (rdataset->ttl > res->view->maxcachettl) rdataset->ttl = res->view->maxcachettl; /* * 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) { if (!ANSWER(rdataset) && need_validation) { /* * Ignore non-answer rdatasets that * are missing signatures. */ continue; } } /* * Normalize the rdataset and sigrdataset TTLs. */ if (sigrdataset != NULL) { rdataset->ttl = ISC_MIN(rdataset->ttl, sigrdataset->ttl); sigrdataset->ttl = rdataset->ttl; } /* * Cache this rdataset/sigrdataset pair as * pending data. Track whether it was additional * or not. */ 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)) { 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) { tresult = dns_rdataset_addnoqname( rdataset, noqname); RUNTIME_CHECK(tresult == ISC_R_SUCCESS); } } addedrdataset = ardataset; result = dns_db_addrdataset(fctx->cache, node, NULL, now, rdataset, 0, addedrdataset); if (result == DNS_R_UNCHANGED) { result = ISC_R_SUCCESS; if (!need_validation && ardataset != NULL && 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, 0, addedrdataset); if (result == DNS_R_UNCHANGED) result = ISC_R_SUCCESS; if (result != ISC_R_SUCCESS) break; } else if (!ANSWER(rdataset)) continue; } if (ANSWER(rdataset) && need_validation) { if (fctx->type != dns_rdatatype_any && fctx->type != dns_rdatatype_rrsig && fctx->type != dns_rdatatype_sig) { /* * This is The Answer. We will * validate it, but first we cache * the rest of the response - it may * contain useful keys. */ INSIST(valrdataset == NULL && valsigrdataset == NULL); valrdataset = rdataset; valsigrdataset = sigrdataset; } else { /* * This is one of (potentially) * multiple answers to an ANY * or SIG query. To keep things * simple, we just start the * validator right away rather * than caching first and * having to remember which * rdatasets needed validation. */ result = valcreate(fctx, addrinfo, name, rdataset->type, rdataset, sigrdataset, valoptions, task); /* * Defer any further validations. * This prevents multiple validators * from manipulating fctx->rmessage * simultaneously. */ valoptions |= DNS_VALIDATOR_DEFER; } } else if (CHAINING(rdataset)) { if (rdataset->type == dns_rdatatype_cname) eresult = DNS_R_CNAME; else { INSIST(rdataset->type == dns_rdatatype_dname); eresult = DNS_R_DNAME; } } } else if (!EXTERNAL(rdataset)) { /* * It's OK to cache this rdataset now. */ if (ANSWER(rdataset)) addedrdataset = ardataset; else if (ANSWERSIG(rdataset)) addedrdataset = asigrdataset; else addedrdataset = NULL; if (CHAINING(rdataset)) { if (rdataset->type == dns_rdatatype_cname) eresult = DNS_R_CNAME; else { INSIST(rdataset->type == dns_rdatatype_dname); eresult = DNS_R_DNAME; } } if (rdataset->trust == dns_trust_glue && (rdataset->type == dns_rdatatype_ns || (rdataset->type == dns_rdatatype_rrsig && rdataset->covers == dns_rdatatype_ns))) { /* * If the trust level is 'dns_trust_glue' * then we are adding data from a referral * we got while executing the search algorithm. * New referral data always takes precedence * over the existing cache contents. */ options = DNS_DBADD_FORCE; } else options = 0; 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) { tresult = dns_rdataset_addnoqname( rdataset, noqname); RUNTIME_CHECK(tresult == ISC_R_SUCCESS); } } /* * 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) result = valcreate(fctx, addrinfo, name, fctx->type, valrdataset, valsigrdataset, valoptions, task); if (result == ISC_R_SUCCESS && have_answer) { fctx->attributes |= FCTX_ATTR_HAVEANSWER; if (event != NULL) { /* * Negative results must be indicated in event->result. */ if (dns_rdataset_isassociated(event->rdataset) && NEGATIVE(event->rdataset)) { INSIST(eresult == DNS_R_NCACHENXDOMAIN || eresult == DNS_R_NCACHENXRRSET); } event->result = eresult; dns_db_attach(fctx->cache, adbp); dns_db_transfernode(fctx->cache, &node, anodep); clone_results(fctx); } } if (node != NULL) dns_db_detachnode(fctx->cache, &node); return (result); } static inline isc_result_t cache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now) { isc_result_t result; dns_section_t section; dns_name_t *name; FCTXTRACE("cache_message"); fctx->attributes &= ~FCTX_ATTR_WANTCACHE; LOCK(&fctx->res->buckets[fctx->bucketnum].lock); for (section = DNS_SECTION_ANSWER; section <= DNS_SECTION_ADDITIONAL; section++) { result = dns_message_firstname(fctx->rmessage, section); while (result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(fctx->rmessage, section, &name); if ((name->attributes & DNS_NAMEATTR_CACHE) != 0) { result = cache_name(fctx, name, addrinfo, now); if (result != ISC_R_SUCCESS) break; } result = dns_message_nextname(fctx->rmessage, section); } if (result != ISC_R_NOMORE) break; } if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); return (result); } /* * Do what dns_ncache_addoptout() does, and then compute an appropriate eresult. */ static isc_result_t ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl, isc_boolean_t optout, isc_boolean_t 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; isc_boolean_t need_validation, secure_domain; dns_name_t *aname; dns_fetchevent_t *event; isc_uint32_t ttl; unsigned int valoptions = 0; FCTXTRACE("ncache_message"); fctx->attributes &= ~FCTX_ATTR_WANTNCACHE; res = fctx->res; need_validation = ISC_FALSE; POST(need_validation); secure_domain = ISC_FALSE; eresult = ISC_R_SUCCESS; name = &fctx->name; node = NULL; /* * XXXMPA remove when we follow cnames and adjust the setting * of FCTX_ATTR_WANTNCACHE in noanswer_response(). */ INSIST(fctx->rmessage->counts[DNS_SECTION_ANSWER] == 0); /* * Is DNSSEC validation required for this name? */ if (fctx->res->view->enablevalidation) { result = dns_view_issecuredomain(res->view, name, &secure_domain); if (result != ISC_R_SUCCESS) return (result); if (!secure_domain && res->view->dlv != NULL) { valoptions = DNS_VALIDATOR_DLV; secure_domain = ISC_TRUE; } } if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) need_validation = ISC_FALSE; else need_validation = secure_domain; if (secure_domain) { /* * Mark all rdatasets as pending. */ dns_rdataset_t *trdataset; dns_name_t *tname; result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY); while (result == ISC_R_SUCCESS) { tname = NULL; dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY, &tname); for (trdataset = ISC_LIST_HEAD(tname->list); trdataset != NULL; trdataset = ISC_LIST_NEXT(trdataset, link)) trdataset->trust = dns_trust_pending_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, ISC_TRUE, &node); if (result != ISC_R_SUCCESS) goto unlock; /* * If we are asking for a SOA record set the cache time * to zero to facilitate locating the containing zone of * a 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, ISC_FALSE, ISC_FALSE, ardataset, &eresult); if (result != ISC_R_SUCCESS) goto unlock; if (!HAVE_ANSWER(fctx)) { fctx->attributes |= FCTX_ATTR_HAVEANSWER; if (event != NULL) { event->result = eresult; dns_db_attach(fctx->cache, adbp); dns_db_transfernode(fctx->cache, &node, anodep); clone_results(fctx); } } unlock: UNLOCK(&res->buckets[fctx->bucketnum].lock); if (node != NULL) dns_db_detachnode(fctx->cache, &node); return (result); } static inline void mark_related(dns_name_t *name, dns_rdataset_t *rdataset, isc_boolean_t external, isc_boolean_t gluing) { name->attributes |= DNS_NAMEATTR_CACHE; if (gluing) { rdataset->trust = dns_trust_glue; /* * Glue with 0 TTL causes problems. We force the TTL to * 1 second to prevent this. */ if (rdataset->ttl == 0) rdataset->ttl = 1; } else rdataset->trust = dns_trust_additional; /* * Avoid infinite loops by only marking new rdatasets. */ if (!CACHE(rdataset)) { name->attributes |= DNS_NAMEATTR_CHASE; rdataset->attributes |= DNS_RDATASETATTR_CHASE; } rdataset->attributes |= DNS_RDATASETATTR_CACHE; if (external) rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL; } static isc_result_t check_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; dns_rdataset_t *rdataset; isc_boolean_t external; dns_rdatatype_t rtype; isc_boolean_t 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 if (GLUING(fctx)) gluing = ISC_TRUE; else gluing = ISC_FALSE; name = NULL; rdataset = NULL; result = dns_message_findname(fctx->rmessage, section, addname, dns_rdatatype_any, 0, &name, NULL); if (result == ISC_R_SUCCESS) { external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); if (type == dns_rdatatype_a) { for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if (rdataset->type == dns_rdatatype_rrsig) rtype = rdataset->covers; else rtype = rdataset->type; if (rtype == dns_rdatatype_a || rtype == dns_rdatatype_aaaa) mark_related(name, rdataset, external, gluing); } } else { result = dns_message_findtype(name, type, 0, &rdataset); if (result == ISC_R_SUCCESS) { mark_related(name, rdataset, external, gluing); /* * Do we have its SIG too? */ rdataset = NULL; result = dns_message_findtype(name, dns_rdatatype_rrsig, type, &rdataset); if (result == ISC_R_SUCCESS) mark_related(name, rdataset, external, gluing); } } } return (ISC_R_SUCCESS); } static 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) { isc_boolean_t rescan; dns_section_t section = DNS_SECTION_ADDITIONAL; isc_result_t result; again: rescan = ISC_FALSE; for (result = dns_message_firstname(fctx->rmessage, section); result == ISC_R_SUCCESS; result = dns_message_nextname(fctx->rmessage, section)) { dns_name_t *name = NULL; dns_rdataset_t *rdataset; dns_message_currentname(fctx->rmessage, DNS_SECTION_ADDITIONAL, &name); if ((name->attributes & DNS_NAMEATTR_CHASE) == 0) continue; name->attributes &= ~DNS_NAMEATTR_CHASE; for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if (CHASE(rdataset)) { rdataset->attributes &= ~DNS_RDATASETATTR_CHASE; (void)dns_rdataset_additionaldata(rdataset, check_related, fctx); rescan = ISC_TRUE; } } } if (rescan) goto again; } static inline isc_result_t cname_target(dns_rdataset_t *rdataset, dns_name_t *tname) { isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_cname_t cname; result = dns_rdataset_first(rdataset); if (result != ISC_R_SUCCESS) return (result); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &cname, NULL); if (result != ISC_R_SUCCESS) return (result); dns_name_init(tname, NULL); dns_name_clone(&cname.cname, tname); dns_rdata_freestruct(&cname); return (ISC_R_SUCCESS); } static inline isc_result_t dname_target(fetchctx_t *fctx, dns_rdataset_t *rdataset, dns_name_t *qname, dns_name_t *oname, dns_fixedname_t *fixeddname) { isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; unsigned int nlabels; int order; dns_namereln_t namereln; dns_rdata_dname_t dname; dns_fixedname_t prefix; /* * Get the target name of the DNAME. */ result = dns_rdataset_first(rdataset); if (result != ISC_R_SUCCESS) return (result); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &dname, NULL); if (result != ISC_R_SUCCESS) return (result); /* * Get the prefix of qname. */ namereln = dns_name_fullcompare(qname, oname, &order, &nlabels); if (namereln != dns_namereln_subdomain) { char qbuf[DNS_NAME_FORMATSIZE]; char obuf[DNS_NAME_FORMATSIZE]; dns_rdata_freestruct(&dname); dns_name_format(qname, qbuf, sizeof(qbuf)); dns_name_format(oname, obuf, sizeof(obuf)); log_formerr(fctx, "unrelated DNAME in answer: " "%s is not in %s", qbuf, obuf); return (DNS_R_FORMERR); } dns_fixedname_init(&prefix); dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL); dns_fixedname_init(fixeddname); result = dns_name_concatenate(dns_fixedname_name(&prefix), &dname.dname, dns_fixedname_name(fixeddname), NULL); dns_rdata_freestruct(&dname); return (result); } static isc_boolean_t 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 (ISC_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 (ISC_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 (ISC_FALSE); } } return (ISC_TRUE); } static isc_boolean_t is_answertarget_allowed(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, dns_name_t *tname, dns_name_t *domain) { 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]; /* By default, we allow any target name. */ if (view->denyanswernames == NULL) return (ISC_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, name, NULL, &node, NULL, 0, NULL, NULL); if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) return (ISC_TRUE); } /* * If the target name is a subdomain of the search domain, allow it. */ if (dns_name_issubdomain(tname, domain)) return (ISC_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(name, qnamebuf, sizeof(qnamebuf)); dns_name_format(tname, tnamebuf, sizeof(tnamebuf)); dns_rdatatype_format(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 (ISC_FALSE); } return (ISC_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; isc_boolean_t 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 = ISC_TRUE; else aa = ISC_FALSE; qname = &fctx->name; } else { /* * We're being invoked by answer_response() after it has * followed a CNAME/DNAME chain. */ qname = oqname; aa = ISC_FALSE; /* * If the current qname is not a subdomain of the query * domain, there's no point in looking at the authority * section without doing DNSSEC validation. * * Until we do that validation, we'll just return success * in this case. */ if (!dns_name_issubdomain(qname, &fctx->domain)) return (ISC_R_SUCCESS); } /* * We have to figure out if this is a negative response, or a * referral. */ /* * Sometimes we can tell if its a negative response by looking at * the message header. */ negative_response = ISC_FALSE; if (message->rcode == dns_rcode_nxdomain || (message->counts[DNS_SECTION_ANSWER] == 0 && message->counts[DNS_SECTION_AUTHORITY] == 0)) negative_response = ISC_TRUE; /* * Process the authority section. */ ns_name = NULL; ns_rdataset = NULL; soa_name = NULL; ds_name = NULL; 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(fctx->type, tbuf, + 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, qbuf, nbuf); + tbuf, nbuf, qbuf); return (DNS_R_FORMERR); } if (type == dns_rdatatype_ns) { /* * NS or RRSIG NS. * * Only one set of NS RRs is allowed. */ if (rdataset->type == dns_rdatatype_ns) { if (ns_name != NULL && name != ns_name) { 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; } } } 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 = ISC_TRUE; result = dns_message_firstname(message, section); while (result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(message, section, &name); if (dns_name_issubdomain(name, &fctx->domain)) { for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { type = rdataset->type; if (type == dns_rdatatype_rrsig) type = rdataset->covers; if (type == dns_rdatatype_nsec || type == dns_rdatatype_nsec3) { /* * NSEC or RRSIG NSEC. */ if (negative_response) { name->attributes |= DNS_NAMEATTR_NCACHE; rdataset->attributes |= DNS_RDATASETATTR_NCACHE; } else if (type == dns_rdatatype_nsec) { name->attributes |= DNS_NAMEATTR_CACHE; rdataset->attributes |= DNS_RDATASETATTR_CACHE; } if (aa) rdataset->trust = dns_trust_authauthority; else 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); 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); fctx->attributes |= FCTX_ATTR_WANTCACHE; fctx->ns_ttl_ok = ISC_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 isc_result_t answer_response(fetchctx_t *fctx) { isc_result_t result; dns_message_t *message; dns_name_t *name, *qname, tname, *ns_name; dns_rdataset_t *rdataset, *ns_rdataset; isc_boolean_t done, external, chaining, aa, found, want_chaining; isc_boolean_t have_answer, found_cname, found_type, wanted_chaining; unsigned int aflag; dns_rdatatype_t type; dns_fixedname_t dname, fqname; dns_view_t *view; FCTXTRACE("answer_response"); message = fctx->rmessage; /* * Examine the answer section, marking those rdatasets which are * part of the answer and should be cached. */ done = ISC_FALSE; found_cname = ISC_FALSE; found_type = ISC_FALSE; chaining = ISC_FALSE; have_answer = ISC_FALSE; want_chaining = ISC_FALSE; POST(want_chaining); if ((message->flags & DNS_MESSAGEFLAG_AA) != 0) aa = ISC_TRUE; else aa = ISC_FALSE; qname = &fctx->name; type = fctx->type; view = fctx->res->view; result = dns_message_firstname(message, DNS_SECTION_ANSWER); while (!done && result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(message, DNS_SECTION_ANSWER, &name); external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); if (dns_name_equal(name, qname)) { wanted_chaining = ISC_FALSE; for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { found = ISC_FALSE; want_chaining = ISC_FALSE; aflag = 0; if (rdataset->type == dns_rdatatype_nsec3) { /* * NSEC3 records are not allowed to * appear in the answer section. */ log_formerr(fctx, "NSEC3 in answer"); return (DNS_R_FORMERR); } /* * Apply filters, if given, on answers to reject * a malicious attempt of rebinding. */ if ((rdataset->type == dns_rdatatype_a || rdataset->type == dns_rdatatype_aaaa) && !is_answeraddress_allowed(view, name, rdataset)) { return (DNS_R_SERVFAIL); } if (rdataset->type == type && !found_cname) { /* * We've found an ordinary answer. */ found = ISC_TRUE; found_type = ISC_TRUE; done = ISC_TRUE; aflag = DNS_RDATASETATTR_ANSWER; } else if (type == dns_rdatatype_any) { /* * We've found an answer matching * an ANY query. There may be * more. */ found = ISC_TRUE; aflag = DNS_RDATASETATTR_ANSWER; } else if (rdataset->type == dns_rdatatype_rrsig && rdataset->covers == type && !found_cname) { /* * We've found a signature that * covers the type we're looking for. */ found = ISC_TRUE; found_type = ISC_TRUE; aflag = DNS_RDATASETATTR_ANSWERSIG; } else if (rdataset->type == dns_rdatatype_cname && !found_type) { /* * We're looking for something else, * but we found a CNAME. * * Getting a CNAME response for some * query types is an error, see * RFC 4035, Section 2.5. */ if (type == dns_rdatatype_rrsig || type == dns_rdatatype_key || type == dns_rdatatype_nsec) { char buf[DNS_RDATATYPE_FORMATSIZE]; dns_rdatatype_format(fctx->type, buf, sizeof(buf)); log_formerr(fctx, "CNAME response " "for %s RR", buf); return (DNS_R_FORMERR); } found = ISC_TRUE; found_cname = ISC_TRUE; want_chaining = ISC_TRUE; aflag = DNS_RDATASETATTR_ANSWER; result = cname_target(rdataset, &tname); if (result != ISC_R_SUCCESS) return (result); /* Apply filters on the target name. */ if (!is_answertarget_allowed(view, name, rdataset->type, &tname, &fctx->domain)) { return (DNS_R_SERVFAIL); } } else if (rdataset->type == dns_rdatatype_rrsig && rdataset->covers == dns_rdatatype_cname && !found_type) { /* * We're looking for something else, * but we found a SIG CNAME. */ found = ISC_TRUE; found_cname = ISC_TRUE; aflag = DNS_RDATASETATTR_ANSWERSIG; } if (found) { /* * We've found an answer to our * question. */ name->attributes |= DNS_NAMEATTR_CACHE; rdataset->attributes |= DNS_RDATASETATTR_CACHE; rdataset->trust = dns_trust_answer; if (!chaining) { /* * This data is "the" answer * to our question only if * we're not chaining (i.e. * if we haven't followed * a CNAME or DNAME). */ INSIST(!external); if (aflag == DNS_RDATASETATTR_ANSWER) have_answer = ISC_TRUE; name->attributes |= DNS_NAMEATTR_ANSWER; rdataset->attributes |= aflag; if (aa) rdataset->trust = dns_trust_authanswer; } else if (external) { /* * This data is outside of * our query domain, and * may not be cached. */ rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL; } /* * Mark any additional data related * to this rdataset. */ (void)dns_rdataset_additionaldata( rdataset, check_related, fctx); /* * CNAME chaining. */ if (want_chaining) { wanted_chaining = ISC_TRUE; name->attributes |= DNS_NAMEATTR_CHAINING; rdataset->attributes |= DNS_RDATASETATTR_CHAINING; qname = &tname; } } /* * We could add an "else" clause here and * log that we're ignoring this rdataset. */ } /* * If wanted_chaining is true, we've done * some chaining as the result of processing * this node, and thus we need to set * chaining to true. * * We don't set chaining inside of the * rdataset loop because doing that would * cause us to ignore the signatures of * CNAMEs. */ if (wanted_chaining) chaining = ISC_TRUE; } else { /* * Look for a DNAME (or its SIG). Anything else is * ignored. */ wanted_chaining = ISC_FALSE; for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { isc_boolean_t found_dname = ISC_FALSE; dns_name_t *dname_name; found = ISC_FALSE; aflag = 0; if (rdataset->type == dns_rdatatype_dname) { /* * We're looking for something else, * but we found a DNAME. * * If we're not chaining, then the * DNAME should not be external. */ if (!chaining && external) { log_formerr(fctx, "external DNAME"); return (DNS_R_FORMERR); } found = ISC_TRUE; want_chaining = ISC_TRUE; POST(want_chaining); aflag = DNS_RDATASETATTR_ANSWER; result = dname_target(fctx, rdataset, qname, name, &dname); if (result == ISC_R_NOSPACE) { /* * We can't construct the * DNAME target. Do not * try to continue. */ want_chaining = ISC_FALSE; POST(want_chaining); } else if (result != ISC_R_SUCCESS) return (result); else found_dname = ISC_TRUE; dname_name = dns_fixedname_name(&dname); if (!is_answertarget_allowed(view, qname, rdataset->type, dname_name, &fctx->domain)) { return (DNS_R_SERVFAIL); } } else if (rdataset->type == dns_rdatatype_rrsig && rdataset->covers == dns_rdatatype_dname) { /* * We've found a signature that * covers the DNAME. */ found = ISC_TRUE; aflag = DNS_RDATASETATTR_ANSWERSIG; } if (found) { /* * We've found an answer to our * question. */ name->attributes |= DNS_NAMEATTR_CACHE; rdataset->attributes |= DNS_RDATASETATTR_CACHE; rdataset->trust = dns_trust_answer; if (!chaining) { /* * This data is "the" answer * to our question only if * we're not chaining. */ INSIST(!external); if (aflag == DNS_RDATASETATTR_ANSWER) have_answer = ISC_TRUE; name->attributes |= DNS_NAMEATTR_ANSWER; rdataset->attributes |= aflag; if (aa) rdataset->trust = dns_trust_authanswer; } else if (external) { rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL; } /* * DNAME chaining. */ if (found_dname) { /* * Copy the dname into the * qname fixed name. * * Although we check for * failure of the copy * operation, in practice it * should never fail since * we already know that the * result fits in a fixedname. */ dns_fixedname_init(&fqname); result = dns_name_copy( dns_fixedname_name(&dname), dns_fixedname_name(&fqname), NULL); if (result != ISC_R_SUCCESS) return (result); wanted_chaining = ISC_TRUE; name->attributes |= DNS_NAMEATTR_CHAINING; rdataset->attributes |= DNS_RDATASETATTR_CHAINING; qname = dns_fixedname_name( &fqname); } } } if (wanted_chaining) chaining = ISC_TRUE; } result = dns_message_nextname(message, DNS_SECTION_ANSWER); } if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; if (result != ISC_R_SUCCESS) return (result); /* * We should have found an answer. */ if (!have_answer) { 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) { /* * Yes. This may be a negative reply, so hand off * authority section processing to the noanswer code. * If it isn't a noanswer response, no harm will be * done. */ return (noanswer_response(fctx, qname, 0)); } /* * 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. */ done = ISC_FALSE; ns_name = NULL; ns_rdataset = NULL; result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); while (!done && result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); if (!external) { /* * We expect to find NS or SIG NS rdatasets, and * nothing else. */ for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if (rdataset->type == dns_rdatatype_ns || (rdataset->type == dns_rdatatype_rrsig && rdataset->covers == dns_rdatatype_ns)) { name->attributes |= DNS_NAMEATTR_CACHE; rdataset->attributes |= DNS_RDATASETATTR_CACHE; if (aa && !chaining) rdataset->trust = dns_trust_authauthority; else rdataset->trust = dns_trust_additional; 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 = ISC_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 isc_boolean_t fctx_decreference(fetchctx_t *fctx) { isc_boolean_t bucket_empty = ISC_FALSE; 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; isc_boolean_t bucket_empty; isc_boolean_t locked = ISC_FALSE; unsigned int bucketnum; dns_rdataset_t nameservers; dns_fixedname_t fixed; dns_name_t *domain; REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); fevent = (dns_fetchevent_t *)event; fctx = event->ev_arg; REQUIRE(VALID_FCTX(fctx)); res = fctx->res; UNUSED(task); FCTXTRACE("resume_dslookup"); if (fevent->node != NULL) dns_db_detachnode(fevent->db, &fevent->node); if (fevent->db != NULL) dns_db_detach(&fevent->db); dns_rdataset_init(&nameservers); bucketnum = fctx->bucketnum; if (fevent->result == ISC_R_CANCELED) { dns_resolver_destroyfetch(&fctx->nsfetch); fctx_done(fctx, ISC_R_CANCELED, __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 = ISC_TRUE; log_ns_ttl(fctx, "resume_dslookup"); 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; } /* * Try again. */ fctx_try(fctx, ISC_TRUE, ISC_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)) { 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); FCTXTRACE("continuing to look for parent's NS records"); result = dns_resolver_createfetch(fctx->res, &fctx->nsname, dns_rdatatype_ns, domain, nsrdataset, NULL, 0, task, resume_dslookup, fctx, &fctx->nsrrset, NULL, &fctx->nsfetch); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); else { LOCK(&res->buckets[bucketnum].lock); locked = ISC_TRUE; fctx->references++; } } cleanup: if (dns_rdataset_isassociated(&nameservers)) dns_rdataset_disassociate(&nameservers); if (dns_rdataset_isassociated(fevent->rdataset)) dns_rdataset_disassociate(fevent->rdataset); INSIST(fevent->sigrdataset == NULL); isc_event_free(&event); if (!locked) LOCK(&res->buckets[bucketnum].lock); 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, ISC_FALSE) || !dns_rdata_checknames(&rdata, name, NULL)) { rdataset->attributes |= DNS_RDATASETATTR_CHECKNAMES; } dns_rdata_reset(&rdata); } } } } static void checknames(dns_message_t *message) { checknamessection(message, DNS_SECTION_ANSWER); checknamessection(message, DNS_SECTION_AUTHORITY); checknamessection(message, DNS_SECTION_ADDITIONAL); } /* * Log server NSID at log level 'level' */ static 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]; isc_uint16_t buflen, i; unsigned char *p, *buf, *nsid; /* Allocate buffer for storing hex version of the NSID */ buflen = (isc_uint16_t)nsid_len * 2 + 1; buf = isc_mem_get(mctx, buflen); if (buf == NULL) return; /* Convert to hex */ p = buf; nsid = isc_buffer_current(opt); for (i = 0; i < nsid_len; i++) { *p++ = hex[(nsid[0] >> 4) & 0xf]; *p++ = hex[nsid[0] & 0xf]; nsid++; } *p = '\0'; isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, level, "received NSID '%s' from %s", buf, addrbuf); /* Clean up */ isc_mem_put(mctx, buf, buflen); return; } static void log_packet(dns_message_t *message, int level, isc_mem_t *mctx) { isc_buffer_t buffer; char *buf = NULL; int len = 1024; isc_result_t result; if (! isc_log_wouldlog(dns_lctx, level)) return; /* * Note that these are multiline debug messages. We want a newline * to appear in the log after each message. */ do { buf = isc_mem_get(mctx, len); if (buf == NULL) break; isc_buffer_init(&buffer, buf, len); result = dns_message_totext(message, &dns_master_style_debug, 0, &buffer); if (result == ISC_R_NOSPACE) { isc_mem_put(mctx, buf, len); len += 1024; } else if (result == ISC_R_SUCCESS) isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, level, "received packet:\n%.*s", (int)isc_buffer_usedlength(&buffer), buf); } while (result == ISC_R_NOSPACE); if (buf != NULL) isc_mem_put(mctx, buf, len); } static isc_boolean_t 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 ? ISC_TRUE : ISC_FALSE); } static isc_boolean_t 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 (ISC_TRUE); } return (ISC_FALSE); } static void process_opt(resquery_t *query, dns_rdataset_t *opt) { dns_rdata_t rdata; isc_buffer_t optbuf; isc_result_t result; isc_uint16_t optcode; isc_uint16_t optlen; 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 (query->options & DNS_FETCHOPT_WANTNSID) log_nsid(&optbuf, optlen, query, ISC_LOG_INFO, query->fctx->res->mctx); isc_buffer_forward(&optbuf, optlen); 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; isc_boolean_t keep_trying, get_nameservers, resend; isc_boolean_t truncated; dns_message_t *message; dns_rdataset_t *opt; fetchctx_t *fctx; dns_name_t *fname; dns_fixedname_t foundname; isc_stdtime_t now; isc_time_t tnow, *finish; dns_adbaddrinfo_t *addrinfo; unsigned int options; unsigned int findoptions; isc_result_t broken_server; badnstype_t broken_type = badns_response; isc_boolean_t no_response; REQUIRE(VALID_QUERY(query)); fctx = query->fctx; options = query->options; REQUIRE(VALID_FCTX(fctx)); REQUIRE(event->ev_type == DNS_EVENT_DISPATCH); QTRACE("response"); if (isc_sockaddr_pf(&query->addrinfo->sockaddr) == PF_INET) inc_stats(fctx->res, dns_resstatscounter_responsev4); else inc_stats(fctx->res, dns_resstatscounter_responsev6); (void)isc_timer_touch(fctx->timer); keep_trying = ISC_FALSE; broken_server = ISC_R_SUCCESS; get_nameservers = ISC_FALSE; resend = ISC_FALSE; truncated = ISC_FALSE; finish = NULL; no_response = ISC_FALSE; if (fctx->res->exiting) { result = ISC_R_SHUTTINGDOWN; goto done; } fctx->timeouts = 0; fctx->timeout = ISC_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 = ISC_TRUE; add_bad_edns(fctx, &query->addrinfo->sockaddr); } else { /* * There's no hope for this query. */ keep_trying = ISC_TRUE; /* * If this is a network error on an exclusive query * socket, mark the server as bad so that we won't try * it for this fetch again. 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 = ISC_TRUE; } } goto done; } message = fctx->rmessage; if (query->tsig != NULL) { result = dns_message_setquerytsig(message, query->tsig); if (result != ISC_R_SUCCESS) goto done; } if (query->tsigkey) { result = dns_message_settsigkey(message, query->tsigkey); if (result != ISC_R_SUCCESS) goto done; } result = dns_message_parse(message, &devent->buffer, 0); if (result != ISC_R_SUCCESS) { switch (result) { case ISC_R_UNEXPECTEDEND: if (!message->question_ok || (message->flags & DNS_MESSAGEFLAG_TC) == 0 || (options & DNS_FETCHOPT_TCP) != 0) { /* * Either the message ended prematurely, * and/or wasn't marked as being truncated, * and/or this is a response to a query we * sent over TCP. In all of these cases, * something is wrong with the remote * server and we don't want to retry using * TCP. */ if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) { /* * The problem might be that they * don't understand EDNS0. Turn it * off and try again. */ options |= DNS_FETCHOPT_NOEDNS0; resend = ISC_TRUE; add_bad_edns(fctx, &query->addrinfo->sockaddr); inc_stats(fctx->res, dns_resstatscounter_edns0fail); } else { broken_server = result; keep_trying = ISC_TRUE; } goto done; } /* * We defer retrying via TCP for a bit so we can * check out this message further. */ truncated = ISC_TRUE; break; case DNS_R_FORMERR: if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) { /* * The problem might be that they * don't understand EDNS0. Turn it * off and try again. */ options |= DNS_FETCHOPT_NOEDNS0; resend = ISC_TRUE; add_bad_edns(fctx, &query->addrinfo->sockaddr); inc_stats(fctx->res, dns_resstatscounter_edns0fail); } else { broken_server = DNS_R_UNEXPECTEDRCODE; keep_trying = ISC_TRUE; } goto done; default: /* * Something bad has happened. */ goto done; } } /* * Log the incoming packet. */ log_packet(message, ISC_LOG_DEBUG(10), fctx->res->mctx); /* * Process receive opt record. */ opt = dns_message_getopt(message); if (opt != NULL) process_opt(query, opt); /* * If the message is signed, check the signature. If not, this * returns success anyway. */ result = dns_message_checksig(message, fctx->res->view); if (result != ISC_R_SUCCESS) goto done; /* * The dispatcher should ensure we only get responses with QR set. */ INSIST((message->flags & DNS_MESSAGEFLAG_QR) != 0); /* * INSIST() that the message comes from the place we sent it to, * since the dispatch code should ensure this. * * INSIST() that the message id is correct (this should also be * ensured by the dispatch code). */ /* * 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 && (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)) { char addrbuf[ISC_SOCKADDR_FORMATSIZE]; isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); dns_adb_changeflags(fctx->adb, query->addrinfo, DNS_FETCHOPT_NOEDNS0, DNS_FETCHOPT_NOEDNS0); } /* * Deal with truncated responses by retrying using TCP. */ if ((message->flags & DNS_MESSAGEFLAG_TC) != 0) truncated = ISC_TRUE; if (truncated) { inc_stats(fctx->res, dns_resstatscounter_truncated); if ((options & DNS_FETCHOPT_TCP) != 0) { broken_server = DNS_R_TRUNCATEDTCP; keep_trying = ISC_TRUE; } else { options |= DNS_FETCHOPT_TCP; resend = ISC_TRUE; } goto done; } /* * Is it a query response? */ if (message->opcode != dns_opcode_query) { /* XXXRTH Log */ broken_server = DNS_R_UNEXPECTEDOPCODE; keep_trying = ISC_TRUE; goto done; } /* * Update statistics about erroneous responses. */ if (message->rcode != dns_rcode_noerror) { switch (message->rcode) { case dns_rcode_nxdomain: inc_stats(fctx->res, dns_resstatscounter_nxdomain); break; case dns_rcode_servfail: inc_stats(fctx->res, dns_resstatscounter_servfail); break; case dns_rcode_formerr: inc_stats(fctx->res, dns_resstatscounter_formerr); break; default: inc_stats(fctx->res, dns_resstatscounter_othererror); break; } } /* * Is the remote server broken, or does it dislike us? */ if (message->rcode != dns_rcode_noerror && message->rcode != dns_rcode_nxdomain) { if (((message->rcode == dns_rcode_formerr || message->rcode == dns_rcode_notimp) || (message->rcode == dns_rcode_servfail && dns_message_getopt(message) == NULL)) && (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { /* * It's very likely they don't like EDNS0. * If the response code is SERVFAIL, also check if the * response contains an OPT RR and don't cache the * failure since it can be returned for various other * reasons. * * XXXRTH We should check if the question * we're asking requires EDNS0, and * if so, we should bail out. */ options |= DNS_FETCHOPT_NOEDNS0; resend = ISC_TRUE; /* * Remember that they may not like EDNS0. */ add_bad_edns(fctx, &query->addrinfo->sockaddr); inc_stats(fctx->res, dns_resstatscounter_edns0fail); } else if (message->rcode == dns_rcode_formerr) { if (ISFORWARDER(query->addrinfo)) { /* * This forwarder doesn't understand us, * but other forwarders might. Keep trying. */ broken_server = DNS_R_REMOTEFORMERR; keep_trying = ISC_TRUE; } else { /* * The server doesn't understand us. Since * all servers for a zone need similar * capabilities, we assume that we will get * FORMERR from all servers, and thus we * cannot make any more progress with this * fetch. */ log_formerr(fctx, "server sent FORMERR"); result = DNS_R_FORMERR; } } else if (message->rcode == dns_rcode_yxdomain) { /* * DNAME mapping failed because the new name * was too long. There's no chance of success * for this fetch. */ result = DNS_R_YXDOMAIN; } else if (message->rcode == dns_rcode_badvers) { unsigned int flags, mask; unsigned int version; resend = ISC_TRUE; INSIST(opt != NULL); version = (opt->ttl >> 16) & 0xff; flags = (version << DNS_FETCHOPT_EDNSVERSIONSHIFT) | DNS_FETCHOPT_EDNSVERSIONSET; mask = DNS_FETCHOPT_EDNSVERSIONMASK | DNS_FETCHOPT_EDNSVERSIONSET; switch (version) { case 0: dns_adb_changeflags(fctx->adb, query->addrinfo, flags, mask); break; default: broken_server = DNS_R_BADVERS; keep_trying = ISC_TRUE; break; } } else { /* * XXXRTH log. */ broken_server = DNS_R_UNEXPECTEDRCODE; INSIST(broken_server != ISC_R_SUCCESS); keep_trying = ISC_TRUE; } goto done; } /* * Is the question the same as the one we asked? */ result = same_question(fctx); if (result != ISC_R_SUCCESS) { /* XXXRTH Log */ if (result == DNS_R_FORMERR) keep_trying = ISC_TRUE; goto done; } /* * Is the server lame? */ if (fctx->res->lame_ttl != 0 && !ISFORWARDER(query->addrinfo) && is_lame(fctx)) { inc_stats(fctx->res, dns_resstatscounter_lame); log_lame(fctx, query->addrinfo); result = dns_adb_marklame(fctx->adb, query->addrinfo, &fctx->name, fctx->type, now + fctx->res->lame_ttl); if (result != ISC_R_SUCCESS) isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR, "could not mark server as lame: %s", isc_result_totext(result)); broken_server = DNS_R_LAME; keep_trying = ISC_TRUE; goto done; } /* * Enforce delegations only zones like NET and COM. */ if (!ISFORWARDER(query->addrinfo) && dns_view_isdelegationonly(fctx->res->view, &fctx->domain) && !dns_name_equal(&fctx->domain, &fctx->name) && fix_mustbedelegationornxdomain(message, fctx)) { char namebuf[DNS_NAME_FORMATSIZE]; char domainbuf[DNS_NAME_FORMATSIZE]; char addrbuf[ISC_SOCKADDR_FORMATSIZE]; char classbuf[64]; char typebuf[64]; dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf)); dns_rdataclass_format(fctx->res->rdclass, classbuf, sizeof(classbuf)); isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DELEGATION_ONLY, DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, "enforced delegation-only for '%s' (%s/%s/%s) " "from %s", domainbuf, namebuf, typebuf, classbuf, addrbuf); } if ((fctx->res->options & DNS_RESOLVER_CHECKNAMES) != 0) checknames(message); /* * Clear cache bits. */ fctx->attributes &= ~(FCTX_ATTR_WANTNCACHE | FCTX_ATTR_WANTCACHE); /* * Did we get any answers? */ if (message->counts[DNS_SECTION_ANSWER] > 0 && (message->rcode == dns_rcode_noerror || message->rcode == dns_rcode_nxdomain)) { /* * [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); 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); } else if (fctx->type != dns_rdatatype_ns && !betterreferral(fctx)) { /* * Lame response !!!. */ result = answer_response(fctx); } 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); } 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 != 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 = ISC_TRUE; goto done; } goto force_referral; } if (result != ISC_R_SUCCESS) { if (result == DNS_R_FORMERR) keep_trying = ISC_TRUE; goto done; } } else if (message->counts[DNS_SECTION_AUTHORITY] > 0 || message->rcode == dns_rcode_noerror || message->rcode == dns_rcode_nxdomain) { /* * NXDOMAIN, NXRDATASET, or referral. */ result = noanswer_response(fctx, NULL, 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 = ISC_TRUE; keep_trying = ISC_TRUE; /* * We have a new set of name servers, and it * has not experienced any restarts yet. */ fctx->restarts = 0; /* * Update local statistics counters collected for each * new zone. */ fctx->referrals++; fctx->querysent = 0; fctx->lamecount = 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 = ISC_TRUE; goto done; } } else { /* * The server is insane. */ /* XXXRTH Log */ broken_server = DNS_R_UNEXPECTEDRCODE; keep_trying = ISC_TRUE; goto done; } /* * Follow additional section data chains. */ chase_additional(fctx); /* * Cache the cacheable parts of the message. This may also cause * work to be queued to the DNSSEC validator. */ if (WANTCACHE(fctx)) { result = cache_message(fctx, query->addrinfo, now); if (result != ISC_R_SUCCESS) goto done; } /* * Ncache the negatively cacheable parts of the message. This may * also cause work to be queued to the DNSSEC validator. */ if (WANTNCACHE(fctx)) { dns_rdatatype_t covers; if (message->rcode == dns_rcode_nxdomain) covers = dns_rdatatype_any; else covers = fctx->type; /* * Cache any negative cache entries in the message. */ result = ncache_message(fctx, query->addrinfo, covers, now); } done: /* * Remember the query's addrinfo, in case we need to mark the * server as broken. */ addrinfo = query->addrinfo; /* * Cancel the query. * * XXXRTH Don't cancel the query if waiting for validation? */ fctx_cancelquery(&query, &devent, finish, no_response); 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; dns_fixedname_init(&foundname); fname = dns_fixedname_name(&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(fctx->res->view, name, fname, now, findoptions, ISC_TRUE, &fctx->nameservers, NULL); if (result != ISC_R_SUCCESS) { FCTXTRACE("couldn't find a zonecut"); fctx_done(fctx, DNS_R_SERVFAIL, __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; } 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; } fctx->ns_ttl = fctx->nameservers.ttl; fctx->ns_ttl_ok = ISC_TRUE; fctx_cancelqueries(fctx, ISC_TRUE); fctx_cleanupfinds(fctx); fctx_cleanupaltfinds(fctx); fctx_cleanupforwaddrs(fctx); fctx_cleanupaltaddrs(fctx); } /* * Try again. */ fctx_try(fctx, !get_nameservers, ISC_FALSE); } else if (resend) { /* * Resend (probably with changed options). */ FCTXTRACE("resend"); inc_stats(fctx->res, dns_resstatscounter_retry); result = fctx_query(fctx, addrinfo, options); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __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, ISC_TRUE); /* * We must not retransmit while the validator is working; * it has references to the current rmessage. */ result = fctx_stopidletimer(fctx); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); } else if (result == DNS_R_CHASEDSSERVERS) { unsigned int n; add_bad(fctx, addrinfo, result, broken_type); fctx_cancelqueries(fctx, ISC_TRUE); fctx_cleanupfinds(fctx); fctx_cleanupforwaddrs(fctx); n = dns_name_countlabels(&fctx->name); dns_name_getlabelsequence(&fctx->name, 1, n - 1, &fctx->nsname); FCTXTRACE("suspending DS lookup to find parent's NS records"); result = dns_resolver_createfetch(fctx->res, &fctx->nsname, dns_rdatatype_ns, NULL, NULL, NULL, 0, task, resume_dslookup, fctx, &fctx->nsrrset, NULL, &fctx->nsfetch); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); else { LOCK(&fctx->res->buckets[fctx->bucketnum].lock); fctx->references++; UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); result = fctx_stopidletimer(fctx); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); } } else { /* * We're done. */ fctx_done(fctx, result, __LINE__); } } /*** *** Resolver Methods ***/ static void destroy_badcache(dns_resolver_t *res) { dns_badcache_t *bad, *next; unsigned int i; if (res->badcache != NULL) { for (i = 0; i < res->badhash; i++) for (bad = res->badcache[i]; bad != NULL; bad = next) { next = bad->next; isc_mem_put(res->mctx, bad, sizeof(*bad) + bad->name.length); res->badcount--; } isc_mem_put(res->mctx, res->badcache, sizeof(*res->badcache) * res->badhash); res->badcache = NULL; res->badhash = 0; INSIST(res->badcount == 0); } } static void destroy(dns_resolver_t *res) { unsigned int i; alternate_t *a; REQUIRE(res->references == 0); REQUIRE(!res->priming); REQUIRE(res->primefetch == NULL); RTRACE("destroy"); INSIST(res->nfctx == 0); DESTROYLOCK(&res->primelock); DESTROYLOCK(&res->nlock); DESTROYLOCK(&res->lock); for (i = 0; i < res->nbuckets; i++) { INSIST(ISC_LIST_EMPTY(res->buckets[i].fctxs)); isc_task_shutdown(res->buckets[i].task); isc_task_detach(&res->buckets[i].task); DESTROYLOCK(&res->buckets[i].lock); isc_mem_detach(&res->buckets[i].mctx); } isc_mem_put(res->mctx, res->buckets, res->nbuckets * sizeof(fctxbucket_t)); if (res->dispatchv4 != NULL) dns_dispatch_detach(&res->dispatchv4); if (res->dispatchv6 != NULL) dns_dispatch_detach(&res->dispatchv6); while ((a = ISC_LIST_HEAD(res->alternates)) != NULL) { ISC_LIST_UNLINK(res->alternates, a, link); if (!a->isaddress) dns_name_free(&a->_u._n.name, res->mctx); isc_mem_put(res->mctx, a, sizeof(*a)); } dns_resolver_reset_algorithms(res); destroy_badcache(res); dns_resolver_resetmustbesecure(res); #if USE_ALGLOCK isc_rwlock_destroy(&res->alglock); #endif #if USE_MBSLOCK isc_rwlock_destroy(&res->mbslock); #endif isc_timer_detach(&res->spillattimer); res->magic = 0; isc_mem_put(res->mctx, res, sizeof(*res)); } static void send_shutdown_events(dns_resolver_t *res) { isc_event_t *event, *next_event; isc_task_t *etask; /* * Caller must be holding the resolver lock. */ for (event = ISC_LIST_HEAD(res->whenshutdown); event != NULL; event = next_event) { next_event = ISC_LIST_NEXT(event, ev_link); ISC_LIST_UNLINK(res->whenshutdown, event, ev_link); etask = event->ev_sender; event->ev_sender = res; isc_task_sendanddetach(&etask, &event); } } static void empty_bucket(dns_resolver_t *res) { RTRACE("empty_bucket"); LOCK(&res->lock); INSIST(res->activebuckets > 0); res->activebuckets--; if (res->activebuckets == 0) send_shutdown_events(res); UNLOCK(&res->lock); } static void spillattimer_countdown(isc_task_t *task, isc_event_t *event) { dns_resolver_t *res = event->ev_arg; isc_result_t result; unsigned int count; isc_boolean_t logit = ISC_FALSE; REQUIRE(VALID_RESOLVER(res)); UNUSED(task); LOCK(&res->lock); INSIST(!res->exiting); if (res->spillat > res->spillatmin) { res->spillat--; logit = ISC_TRUE; } if (res->spillat <= res->spillatmin) { result = isc_timer_reset(res->spillattimer, isc_timertype_inactive, NULL, NULL, ISC_TRUE); RUNTIME_CHECK(result == ISC_R_SUCCESS); } count = res->spillat; UNLOCK(&res->lock); if (logit) isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, "clients-per-query decreased to %u", count); isc_event_free(&event); } isc_result_t dns_resolver_create(dns_view_t *view, isc_taskmgr_t *taskmgr, unsigned int ntasks, isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr, unsigned int options, dns_dispatchmgr_t *dispatchmgr, dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6, dns_resolver_t **resp) { dns_resolver_t *res; isc_result_t result = ISC_R_SUCCESS; unsigned int i, buckets_created = 0; isc_task_t *task = NULL; char name[16]; unsigned dispattr; /* * Create a resolver. */ REQUIRE(DNS_VIEW_VALID(view)); REQUIRE(ntasks > 0); REQUIRE(resp != NULL && *resp == NULL); REQUIRE(dispatchmgr != NULL); REQUIRE(dispatchv4 != NULL || dispatchv6 != NULL); res = isc_mem_get(view->mctx, sizeof(*res)); if (res == NULL) return (ISC_R_NOMEMORY); RTRACE("create"); res->mctx = view->mctx; res->rdclass = view->rdclass; res->socketmgr = socketmgr; res->timermgr = timermgr; res->taskmgr = taskmgr; res->dispatchmgr = dispatchmgr; res->view = view; res->options = options; res->lame_ttl = 0; ISC_LIST_INIT(res->alternates); res->udpsize = RECV_BUFFER_SIZE; res->algorithms = NULL; res->badcache = NULL; res->badcount = 0; res->badhash = 0; res->badsweep = 0; res->mustbesecure = NULL; res->spillatmin = res->spillat = 10; res->spillatmax = 100; res->spillattimer = NULL; res->zero_no_soa_ttl = ISC_FALSE; res->query_timeout = DEFAULT_QUERY_TIMEOUT; res->ndisps = 0; res->nextdisp = 0; /* meaningless at this point, but init it */ + res->maxdepth = DEFAULT_RECURSION_DEPTH; + res->maxqueries = DEFAULT_MAX_QUERIES; res->nbuckets = ntasks; res->activebuckets = ntasks; res->buckets = isc_mem_get(view->mctx, ntasks * sizeof(fctxbucket_t)); if (res->buckets == NULL) { result = ISC_R_NOMEMORY; goto cleanup_res; } for (i = 0; i < ntasks; i++) { result = isc_mutex_init(&res->buckets[i].lock); if (result != ISC_R_SUCCESS) goto cleanup_buckets; res->buckets[i].task = NULL; result = isc_task_create(taskmgr, 0, &res->buckets[i].task); if (result != ISC_R_SUCCESS) { DESTROYLOCK(&res->buckets[i].lock); goto cleanup_buckets; } res->buckets[i].mctx = NULL; snprintf(name, sizeof(name), "res%u", i); #ifdef ISC_PLATFORM_USETHREADS /* * Use a separate memory context for each bucket to reduce * contention among multiple threads. Do this only when * enabling threads because it will be require more memory. */ result = isc_mem_create(0, 0, &res->buckets[i].mctx); if (result != ISC_R_SUCCESS) { isc_task_detach(&res->buckets[i].task); DESTROYLOCK(&res->buckets[i].lock); goto cleanup_buckets; } isc_mem_setname(res->buckets[i].mctx, name, NULL); #else isc_mem_attach(view->mctx, &res->buckets[i].mctx); #endif isc_task_setname(res->buckets[i].task, name, res); ISC_LIST_INIT(res->buckets[i].fctxs); res->buckets[i].exiting = ISC_FALSE; buckets_created++; } res->dispatchv4 = NULL; if (dispatchv4 != NULL) { dns_dispatch_attach(dispatchv4, &res->dispatchv4); dispattr = dns_dispatch_getattributes(dispatchv4); res->exclusivev4 = ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0); } res->dispatchv6 = NULL; if (dispatchv6 != NULL) { dns_dispatch_attach(dispatchv6, &res->dispatchv6); dispattr = dns_dispatch_getattributes(dispatchv6); res->exclusivev6 = ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0); } res->references = 1; res->exiting = ISC_FALSE; res->frozen = ISC_FALSE; ISC_LIST_INIT(res->whenshutdown); res->priming = ISC_FALSE; res->primefetch = NULL; res->nfctx = 0; result = isc_mutex_init(&res->lock); if (result != ISC_R_SUCCESS) goto cleanup_dispatches; result = isc_mutex_init(&res->nlock); if (result != ISC_R_SUCCESS) goto cleanup_lock; result = isc_mutex_init(&res->primelock); if (result != ISC_R_SUCCESS) goto cleanup_nlock; task = NULL; result = isc_task_create(taskmgr, 0, &task); if (result != ISC_R_SUCCESS) goto cleanup_primelock; result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL, task, spillattimer_countdown, res, &res->spillattimer); isc_task_detach(&task); if (result != ISC_R_SUCCESS) goto cleanup_primelock; #if USE_ALGLOCK result = isc_rwlock_init(&res->alglock, 0, 0); if (result != ISC_R_SUCCESS) goto cleanup_spillattimer; #endif #if USE_MBSLOCK result = isc_rwlock_init(&res->mbslock, 0, 0); if (result != ISC_R_SUCCESS) goto cleanup_alglock; #endif res->magic = RES_MAGIC; *resp = res; return (ISC_R_SUCCESS); #if USE_MBSLOCK cleanup_alglock: #if USE_ALGLOCK isc_rwlock_destroy(&res->alglock); #endif #endif #if USE_ALGLOCK || USE_MBSLOCK cleanup_spillattimer: isc_timer_detach(&res->spillattimer); #endif cleanup_primelock: DESTROYLOCK(&res->primelock); cleanup_nlock: DESTROYLOCK(&res->nlock); cleanup_lock: DESTROYLOCK(&res->lock); cleanup_dispatches: if (res->dispatchv6 != NULL) dns_dispatch_detach(&res->dispatchv6); if (res->dispatchv4 != NULL) dns_dispatch_detach(&res->dispatchv4); cleanup_buckets: for (i = 0; i < buckets_created; i++) { isc_mem_detach(&res->buckets[i].mctx); DESTROYLOCK(&res->buckets[i].lock); isc_task_shutdown(res->buckets[i].task); isc_task_detach(&res->buckets[i].task); } isc_mem_put(view->mctx, res->buckets, res->nbuckets * sizeof(fctxbucket_t)); cleanup_res: isc_mem_put(view->mctx, res, sizeof(*res)); return (result); } #ifdef BIND9 static void prime_done(isc_task_t *task, isc_event_t *event) { dns_resolver_t *res; dns_fetchevent_t *fevent; dns_fetch_t *fetch; dns_db_t *db = NULL; REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); fevent = (dns_fetchevent_t *)event; res = event->ev_arg; REQUIRE(VALID_RESOLVER(res)); UNUSED(task); LOCK(&res->lock); INSIST(res->priming); res->priming = ISC_FALSE; LOCK(&res->primelock); fetch = res->primefetch; res->primefetch = NULL; UNLOCK(&res->primelock); UNLOCK(&res->lock); if (fevent->result == ISC_R_SUCCESS && res->view->cache != NULL && res->view->hints != NULL) { dns_cache_attachdb(res->view->cache, &db); dns_root_checkhints(res->view, res->view->hints, db); dns_db_detach(&db); } if (fevent->node != NULL) dns_db_detachnode(fevent->db, &fevent->node); if (fevent->db != NULL) dns_db_detach(&fevent->db); if (dns_rdataset_isassociated(fevent->rdataset)) dns_rdataset_disassociate(fevent->rdataset); INSIST(fevent->sigrdataset == NULL); isc_mem_put(res->mctx, fevent->rdataset, sizeof(*fevent->rdataset)); isc_event_free(&event); dns_resolver_destroyfetch(&fetch); } void dns_resolver_prime(dns_resolver_t *res) { isc_boolean_t want_priming = ISC_FALSE; dns_rdataset_t *rdataset; isc_result_t result; REQUIRE(VALID_RESOLVER(res)); REQUIRE(res->frozen); RTRACE("dns_resolver_prime"); LOCK(&res->lock); if (!res->exiting && !res->priming) { INSIST(res->primefetch == NULL); res->priming = ISC_TRUE; want_priming = ISC_TRUE; } UNLOCK(&res->lock); if (want_priming) { /* * To avoid any possible recursive locking problems, we * start the priming fetch like any other fetch, and holding * no resolver locks. No one else will try to start it * because we're the ones who set res->priming to true. * Any other callers of dns_resolver_prime() while we're * running will see that res->priming is already true and * do nothing. */ RTRACE("priming"); rdataset = isc_mem_get(res->mctx, sizeof(*rdataset)); if (rdataset == NULL) { LOCK(&res->lock); INSIST(res->priming); INSIST(res->primefetch == NULL); res->priming = ISC_FALSE; UNLOCK(&res->lock); return; } dns_rdataset_init(rdataset); LOCK(&res->primelock); result = dns_resolver_createfetch(res, dns_rootname, dns_rdatatype_ns, NULL, NULL, NULL, 0, res->buckets[0].task, prime_done, res, rdataset, NULL, &res->primefetch); UNLOCK(&res->primelock); if (result != ISC_R_SUCCESS) { LOCK(&res->lock); INSIST(res->priming); res->priming = ISC_FALSE; UNLOCK(&res->lock); } } } #endif /* BIND9 */ void dns_resolver_freeze(dns_resolver_t *res) { /* * Freeze resolver. */ REQUIRE(VALID_RESOLVER(res)); res->frozen = ISC_TRUE; } void dns_resolver_attach(dns_resolver_t *source, dns_resolver_t **targetp) { REQUIRE(VALID_RESOLVER(source)); REQUIRE(targetp != NULL && *targetp == NULL); RRTRACE(source, "attach"); LOCK(&source->lock); REQUIRE(!source->exiting); INSIST(source->references > 0); source->references++; INSIST(source->references != 0); UNLOCK(&source->lock); *targetp = source; } void dns_resolver_whenshutdown(dns_resolver_t *res, isc_task_t *task, isc_event_t **eventp) { isc_task_t *clone; isc_event_t *event; REQUIRE(VALID_RESOLVER(res)); REQUIRE(eventp != NULL); event = *eventp; *eventp = NULL; LOCK(&res->lock); if (res->exiting && res->activebuckets == 0) { /* * We're already shutdown. Send the event. */ event->ev_sender = res; isc_task_send(task, &event); } else { clone = NULL; isc_task_attach(task, &clone); event->ev_sender = clone; ISC_LIST_APPEND(res->whenshutdown, event, ev_link); } UNLOCK(&res->lock); } void dns_resolver_shutdown(dns_resolver_t *res) { unsigned int i; fetchctx_t *fctx; isc_socket_t *sock; isc_result_t result; REQUIRE(VALID_RESOLVER(res)); RTRACE("shutdown"); LOCK(&res->lock); if (!res->exiting) { RTRACE("exiting"); res->exiting = ISC_TRUE; for (i = 0; i < res->nbuckets; i++) { LOCK(&res->buckets[i].lock); for (fctx = ISC_LIST_HEAD(res->buckets[i].fctxs); fctx != NULL; fctx = ISC_LIST_NEXT(fctx, link)) fctx_shutdown(fctx); if (res->dispatchv4 != NULL && !res->exclusivev4) { sock = dns_dispatch_getsocket(res->dispatchv4); isc_socket_cancel(sock, res->buckets[i].task, ISC_SOCKCANCEL_ALL); } if (res->dispatchv6 != NULL && !res->exclusivev6) { sock = dns_dispatch_getsocket(res->dispatchv6); isc_socket_cancel(sock, res->buckets[i].task, ISC_SOCKCANCEL_ALL); } res->buckets[i].exiting = ISC_TRUE; if (ISC_LIST_EMPTY(res->buckets[i].fctxs)) { INSIST(res->activebuckets > 0); res->activebuckets--; } UNLOCK(&res->buckets[i].lock); } if (res->activebuckets == 0) send_shutdown_events(res); result = isc_timer_reset(res->spillattimer, isc_timertype_inactive, NULL, NULL, ISC_TRUE); RUNTIME_CHECK(result == ISC_R_SUCCESS); } UNLOCK(&res->lock); } void dns_resolver_detach(dns_resolver_t **resp) { dns_resolver_t *res; isc_boolean_t need_destroy = ISC_FALSE; REQUIRE(resp != NULL); res = *resp; REQUIRE(VALID_RESOLVER(res)); RTRACE("detach"); LOCK(&res->lock); INSIST(res->references > 0); res->references--; if (res->references == 0) { INSIST(res->exiting && res->activebuckets == 0); need_destroy = ISC_TRUE; } UNLOCK(&res->lock); if (need_destroy) destroy(res); *resp = NULL; } static inline isc_boolean_t fctx_match(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type, unsigned int options) { /* * Don't match fetch contexts that are shutting down. */ if (fctx->cloned || fctx->state == fetchstate_done || ISC_LIST_EMPTY(fctx->events)) return (ISC_FALSE); if (fctx->type != type || fctx->options != options) return (ISC_FALSE); return (dns_name_equal(&fctx->name, name)); } static inline void log_fetch(dns_name_t *name, dns_rdatatype_t type) { char namebuf[DNS_NAME_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; int level = ISC_LOG_DEBUG(1); if (! isc_log_wouldlog(dns_lctx, level)) return; dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdatatype_format(type, typebuf, sizeof(typebuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, level, "createfetch: %s %s", namebuf, typebuf); } isc_result_t dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, dns_name_t *domain, dns_rdataset_t *nameservers, dns_forwarders_t *forwarders, unsigned int options, isc_task_t *task, isc_taskaction_t action, void *arg, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, dns_fetch_t **fetchp) { - return (dns_resolver_createfetch2(res, name, type, domain, + return (dns_resolver_createfetch3(res, name, type, domain, nameservers, forwarders, NULL, 0, - options, task, action, arg, + 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; isc_boolean_t new_fctx = ISC_FALSE; isc_event_t *event; unsigned int count = 0; unsigned int spillat; unsigned int spillatmin; isc_boolean_t destroy = ISC_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); bucketnum = dns_name_fullhash(name, ISC_FALSE) % res->nbuckets; LOCK(&res->lock); spillat = res->spillat; spillatmin = res->spillatmin; UNLOCK(&res->lock); LOCK(&res->buckets[bucketnum].lock); if (res->buckets[bucketnum].exiting) { result = ISC_R_SHUTTINGDOWN; goto unlock; } if ((options & DNS_FETCHOPT_UNSHARED) == 0) { for (fctx = ISC_LIST_HEAD(res->buckets[bucketnum].fctxs); fctx != NULL; fctx = ISC_LIST_NEXT(fctx, link)) { if (fctx_match(fctx, name, type, options)) break; } } /* * Is this a duplicate? */ if (fctx != NULL && client != NULL) { dns_fetchevent_t *fevent; for (fevent = ISC_LIST_HEAD(fctx->events); fevent != NULL; fevent = ISC_LIST_NEXT(fevent, ev_link)) { if (fevent->client != NULL && fevent->id == id && isc_sockaddr_equal(fevent->client, client)) { result = DNS_R_DUPLICATE; goto unlock; } count++; } } if (count >= spillatmin && spillatmin != 0) { INSIST(fctx != NULL); if (count >= spillat) fctx->spilled = ISC_TRUE; if (fctx->spilled) { result = DNS_R_DROP; goto unlock; } } if (fctx == NULL) { result = fctx_create(res, name, type, domain, nameservers, - options, bucketnum, &fctx); + options, bucketnum, depth, qc, &fctx); if (result != ISC_R_SUCCESS) goto unlock; new_fctx = ISC_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); destroy = ISC_TRUE; } } unlock: UNLOCK(&res->buckets[bucketnum].lock); if (destroy) fctx_destroy(fctx); if (result == ISC_R_SUCCESS) { FTRACE("created"); *fetchp = fetch; } else isc_mem_put(res->mctx, fetch, sizeof(*fetch)); return (result); } void dns_resolver_cancelfetch(dns_fetch_t *fetch) { fetchctx_t *fctx; dns_resolver_t *res; dns_fetchevent_t *event, *next_event; isc_task_t *etask; REQUIRE(DNS_FETCH_VALID(fetch)); fctx = fetch->private; REQUIRE(VALID_FCTX(fctx)); res = fctx->res; FTRACE("cancelfetch"); LOCK(&res->buckets[fctx->bucketnum].lock); /* * Find the completion event for this fetch (as opposed * to those for other fetches that have joined the same * fctx) and send it with result = ISC_R_CANCELED. */ event = NULL; if (fctx->state != fetchstate_done) { for (event = ISC_LIST_HEAD(fctx->events); event != NULL; event = next_event) { next_event = ISC_LIST_NEXT(event, ev_link); if (event->fetch == fetch) { ISC_LIST_UNLINK(fctx->events, event, ev_link); break; } } } if (event != NULL) { etask = event->ev_sender; event->ev_sender = fctx; event->result = ISC_R_CANCELED; isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event)); } /* * The fctx continues running even if no fetches remain; * the answer is still cached. */ UNLOCK(&res->buckets[fctx->bucketnum].lock); } void dns_resolver_destroyfetch(dns_fetch_t **fetchp) { dns_fetch_t *fetch; dns_resolver_t *res; dns_fetchevent_t *event, *next_event; fetchctx_t *fctx; unsigned int bucketnum; isc_boolean_t bucket_empty; 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_put(res->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, isc_boolean_t 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 " "%" ISC_PRINT_QUADFORMAT "u." "%06" ISC_PRINT_QUADFORMAT "u: %s/%s " "[domain:%s,referral:%u,restart:%u,qrysent:%u," "timeout:%u,lame:%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->neterr, fctx->badresp, fctx->adberr, fctx->findfail, fctx->valfail); fctx->logged = ISC_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 (resolver->dispatchv4); } dns_dispatch_t * dns_resolver_dispatchv6(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); return (resolver->dispatchv6); } isc_socketmgr_t * dns_resolver_socketmgr(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); return (resolver->socketmgr); } isc_taskmgr_t * dns_resolver_taskmgr(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); return (resolver->taskmgr); } isc_uint32_t dns_resolver_getlamettl(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); return (resolver->lame_ttl); } void dns_resolver_setlamettl(dns_resolver_t *resolver, isc_uint32_t lame_ttl) { REQUIRE(VALID_RESOLVER(resolver)); resolver->lame_ttl = lame_ttl; } unsigned int dns_resolver_nrunning(dns_resolver_t *resolver) { unsigned int n; LOCK(&resolver->nlock); n = resolver->nfctx; UNLOCK(&resolver->nlock); return (n); } isc_result_t dns_resolver_addalternate(dns_resolver_t *resolver, isc_sockaddr_t *alt, dns_name_t *name, in_port_t port) { alternate_t *a; isc_result_t result; REQUIRE(VALID_RESOLVER(resolver)); REQUIRE(!resolver->frozen); REQUIRE((alt == NULL) ^ (name == NULL)); a = isc_mem_get(resolver->mctx, sizeof(*a)); if (a == NULL) return (ISC_R_NOMEMORY); if (alt != NULL) { a->isaddress = ISC_TRUE; a->_u.addr = *alt; } else { a->isaddress = ISC_FALSE; a->_u._n.port = port; dns_name_init(&a->_u._n.name, NULL); result = dns_name_dup(name, resolver->mctx, &a->_u._n.name); if (result != ISC_R_SUCCESS) { isc_mem_put(resolver->mctx, a, sizeof(*a)); return (result); } } ISC_LINK_INIT(a, link); ISC_LIST_APPEND(resolver->alternates, a, link); return (ISC_R_SUCCESS); } void dns_resolver_setudpsize(dns_resolver_t *resolver, isc_uint16_t udpsize) { REQUIRE(VALID_RESOLVER(resolver)); resolver->udpsize = udpsize; } isc_uint16_t dns_resolver_getudpsize(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); return (resolver->udpsize); } void dns_resolver_flushbadcache(dns_resolver_t *resolver, dns_name_t *name) { unsigned int i; dns_badcache_t *bad, *prev, *next; REQUIRE(VALID_RESOLVER(resolver)); LOCK(&resolver->lock); if (resolver->badcache == NULL) goto unlock; if (name != NULL) { isc_time_t now; isc_result_t result; result = isc_time_now(&now); if (result != ISC_R_SUCCESS) isc_time_settoepoch(&now); i = dns_name_hash(name, ISC_FALSE) % resolver->badhash; prev = NULL; for (bad = resolver->badcache[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) resolver->badcache[i] = bad->next; else prev->next = bad->next; isc_mem_put(resolver->mctx, bad, sizeof(*bad) + bad->name.length); resolver->badcount--; } else prev = bad; } } else destroy_badcache(resolver); unlock: UNLOCK(&resolver->lock); } static void resizehash(dns_resolver_t *resolver, isc_time_t *now, isc_boolean_t grow) { unsigned int newsize; dns_badcache_t **new, *bad, *next; unsigned int i; if (grow) newsize = resolver->badhash * 2 + 1; else newsize = (resolver->badhash - 1) / 2; new = isc_mem_get(resolver->mctx, sizeof(*resolver->badcache) * newsize); if (new == NULL) return; memset(new, 0, sizeof(*resolver->badcache) * newsize); for (i = 0; i < resolver->badhash; i++) { for (bad = resolver->badcache[i]; bad != NULL; bad = next) { next = bad->next; if (isc_time_compare(&bad->expire, now) < 0) { isc_mem_put(resolver->mctx, bad, sizeof(*bad) + bad->name.length); resolver->badcount--; } else { bad->next = new[bad->hashval % newsize]; new[bad->hashval % newsize] = bad; } } } isc_mem_put(resolver->mctx, resolver->badcache, sizeof(*resolver->badcache) * resolver->badhash); resolver->badhash = newsize; resolver->badcache = new; } void dns_resolver_addbadcache(dns_resolver_t *resolver, dns_name_t *name, dns_rdatatype_t type, isc_time_t *expire) { isc_time_t now; isc_result_t result = ISC_R_SUCCESS; unsigned int i, hashval; dns_badcache_t *bad, *prev, *next; REQUIRE(VALID_RESOLVER(resolver)); LOCK(&resolver->lock); if (resolver->badcache == NULL) { resolver->badcache = isc_mem_get(resolver->mctx, sizeof(*resolver->badcache) * DNS_BADCACHE_SIZE); if (resolver->badcache == NULL) goto cleanup; resolver->badhash = DNS_BADCACHE_SIZE; memset(resolver->badcache, 0, sizeof(*resolver->badcache) * resolver->badhash); } result = isc_time_now(&now); if (result != ISC_R_SUCCESS) isc_time_settoepoch(&now); hashval = dns_name_hash(name, ISC_FALSE); i = hashval % resolver->badhash; prev = NULL; for (bad = resolver->badcache[i]; bad != NULL; bad = next) { next = bad->next; if (bad->type == type && dns_name_equal(name, &bad->name)) break; if (isc_time_compare(&bad->expire, &now) < 0) { if (prev == NULL) resolver->badcache[i] = bad->next; else prev->next = bad->next; isc_mem_put(resolver->mctx, bad, sizeof(*bad) + bad->name.length); resolver->badcount--; } else prev = bad; } if (bad == NULL) { isc_buffer_t buffer; bad = isc_mem_get(resolver->mctx, sizeof(*bad) + name->length); if (bad == NULL) goto cleanup; bad->type = type; bad->hashval = hashval; bad->expire = *expire; isc_buffer_init(&buffer, bad + 1, name->length); dns_name_init(&bad->name, NULL); dns_name_copy(name, &bad->name, &buffer); bad->next = resolver->badcache[i]; resolver->badcache[i] = bad; resolver->badcount++; if (resolver->badcount > resolver->badhash * 8) resizehash(resolver, &now, ISC_TRUE); if (resolver->badcount < resolver->badhash * 2 && resolver->badhash > DNS_BADCACHE_SIZE) resizehash(resolver, &now, ISC_FALSE); } else bad->expire = *expire; cleanup: UNLOCK(&resolver->lock); } isc_boolean_t dns_resolver_getbadcache(dns_resolver_t *resolver, dns_name_t *name, dns_rdatatype_t type, isc_time_t *now) { dns_badcache_t *bad, *prev, *next; isc_boolean_t answer = ISC_FALSE; unsigned int i; REQUIRE(VALID_RESOLVER(resolver)); LOCK(&resolver->lock); if (resolver->badcache == NULL) goto unlock; i = dns_name_hash(name, ISC_FALSE) % resolver->badhash; prev = NULL; for (bad = resolver->badcache[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 resolver->badcache[i] = bad->next; isc_mem_put(resolver->mctx, bad, sizeof(*bad) + bad->name.length); resolver->badcount--; continue; } if (bad->type == type && dns_name_equal(name, &bad->name)) { answer = ISC_TRUE; break; } prev = bad; } /* * Slow sweep to clean out stale records. */ i = resolver->badsweep++ % resolver->badhash; bad = resolver->badcache[i]; if (bad != NULL && isc_time_compare(&bad->expire, now) < 0) { resolver->badcache[i] = bad->next; isc_mem_put(resolver->mctx, bad, sizeof(*bad) + bad->name.length); resolver->badcount--; } unlock: UNLOCK(&resolver->lock); return (answer); } void dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp) { char namebuf[DNS_NAME_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; dns_badcache_t *bad, *next, *prev; isc_time_t now; unsigned int i; isc_uint64_t t; LOCK(&resolver->lock); fprintf(fp, ";\n; Bad cache\n;\n"); if (resolver->badcache == NULL) goto unlock; TIME_NOW(&now); for (i = 0; i < resolver->badhash; i++) { prev = NULL; for (bad = resolver->badcache[i]; bad != NULL; bad = next) { next = bad->next; if (isc_time_compare(&bad->expire, &now) < 0) { if (prev != NULL) prev->next = bad->next; else resolver->badcache[i] = bad->next; isc_mem_put(resolver->mctx, bad, sizeof(*bad) + bad->name.length); resolver->badcount--; 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 " "%" ISC_PLATFORM_QUADFORMAT "u]\n", namebuf, typebuf, t); } } unlock: UNLOCK(&resolver->lock); } static void free_algorithm(void *node, void *arg) { unsigned char *algorithms = node; isc_mem_t *mctx = arg; isc_mem_put(mctx, algorithms, *algorithms); } void dns_resolver_reset_algorithms(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); #if USE_ALGLOCK RWLOCK(&resolver->alglock, isc_rwlocktype_write); #endif if (resolver->algorithms != NULL) dns_rbt_destroy(&resolver->algorithms); #if USE_ALGLOCK RWUNLOCK(&resolver->alglock, isc_rwlocktype_write); #endif } isc_result_t dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name, unsigned int alg) { unsigned int len, mask; unsigned char *new; unsigned char *algorithms; isc_result_t result; dns_rbtnode_t *node = NULL; REQUIRE(VALID_RESOLVER(resolver)); if (alg > 255) return (ISC_R_RANGE); #if USE_ALGLOCK RWLOCK(&resolver->alglock, isc_rwlocktype_write); #endif if (resolver->algorithms == NULL) { result = dns_rbt_create(resolver->mctx, free_algorithm, resolver->mctx, &resolver->algorithms); if (result != ISC_R_SUCCESS) goto cleanup; } len = alg/8 + 2; mask = 1 << (alg%8); result = dns_rbt_addnode(resolver->algorithms, name, &node); if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) { algorithms = node->data; if (algorithms == NULL || len > *algorithms) { new = isc_mem_get(resolver->mctx, len); if (new == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } memset(new, 0, len); if (algorithms != NULL) memmove(new, algorithms, *algorithms); new[len-1] |= mask; *new = len; node->data = new; if (algorithms != NULL) isc_mem_put(resolver->mctx, algorithms, *algorithms); } else algorithms[len-1] |= mask; } result = ISC_R_SUCCESS; cleanup: #if USE_ALGLOCK RWUNLOCK(&resolver->alglock, isc_rwlocktype_write); #endif return (result); } isc_boolean_t dns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name, unsigned int alg) { unsigned int len, mask; unsigned char *algorithms; void *data = NULL; isc_result_t result; isc_boolean_t found = ISC_FALSE; REQUIRE(VALID_RESOLVER(resolver)); #if USE_ALGLOCK RWLOCK(&resolver->alglock, isc_rwlocktype_read); #endif if (resolver->algorithms == NULL) goto unlock; result = dns_rbt_findname(resolver->algorithms, name, 0, NULL, &data); if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { len = alg/8 + 2; mask = 1 << (alg%8); algorithms = data; if (len <= *algorithms && (algorithms[len-1] & mask) != 0) found = ISC_TRUE; } unlock: #if USE_ALGLOCK RWUNLOCK(&resolver->alglock, isc_rwlocktype_read); #endif if (found) return (ISC_FALSE); return (dst_algorithm_supported(alg)); } isc_boolean_t dns_resolver_digest_supported(dns_resolver_t *resolver, unsigned int digest) { UNUSED(resolver); return (dns_ds_digest_supported(digest)); } void dns_resolver_resetmustbesecure(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); #if USE_MBSLOCK RWLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif if (resolver->mustbesecure != NULL) dns_rbt_destroy(&resolver->mustbesecure); #if USE_MBSLOCK RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif } static isc_boolean_t yes = ISC_TRUE, no = ISC_FALSE; isc_result_t dns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name, isc_boolean_t value) { isc_result_t result; REQUIRE(VALID_RESOLVER(resolver)); #if USE_MBSLOCK RWLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif if (resolver->mustbesecure == NULL) { result = dns_rbt_create(resolver->mctx, NULL, NULL, &resolver->mustbesecure); if (result != ISC_R_SUCCESS) goto cleanup; } result = dns_rbt_addname(resolver->mustbesecure, name, value ? &yes : &no); cleanup: #if USE_MBSLOCK RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif return (result); } isc_boolean_t dns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name) { void *data = NULL; isc_boolean_t value = ISC_FALSE; isc_result_t result; REQUIRE(VALID_RESOLVER(resolver)); #if USE_MBSLOCK RWLOCK(&resolver->mbslock, isc_rwlocktype_read); #endif if (resolver->mustbesecure == NULL) goto unlock; result = dns_rbt_findname(resolver->mustbesecure, name, 0, NULL, &data); if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) value = *(isc_boolean_t*)data; unlock: #if USE_MBSLOCK RWUNLOCK(&resolver->mbslock, isc_rwlocktype_read); #endif return (value); } void dns_resolver_getclientsperquery(dns_resolver_t *resolver, isc_uint32_t *cur, isc_uint32_t *min, isc_uint32_t *max) { REQUIRE(VALID_RESOLVER(resolver)); LOCK(&resolver->lock); if (cur != NULL) *cur = resolver->spillat; if (min != NULL) *min = resolver->spillatmin; if (max != NULL) *max = resolver->spillatmax; UNLOCK(&resolver->lock); } void dns_resolver_setclientsperquery(dns_resolver_t *resolver, isc_uint32_t min, isc_uint32_t max) { REQUIRE(VALID_RESOLVER(resolver)); LOCK(&resolver->lock); resolver->spillatmin = resolver->spillat = min; resolver->spillatmax = max; UNLOCK(&resolver->lock); } isc_boolean_t dns_resolver_getzeronosoattl(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); return (resolver->zero_no_soa_ttl); } void dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state) { REQUIRE(VALID_RESOLVER(resolver)); resolver->zero_no_soa_ttl = state; } unsigned int dns_resolver_getoptions(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); return (resolver->options); } 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_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); } Index: stable/8/contrib/bind9/lib/export/isc/Makefile.in =================================================================== --- stable/8/contrib/bind9/lib/export/isc/Makefile.in (revision 275668) +++ stable/8/contrib/bind9/lib/export/isc/Makefile.in (revision 275669) @@ -1,139 +1,139 @@ # Copyright (C) 2009, 2010, 2012, 2013 Internet Systems Consortium, Inc. ("ISC") # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. # $Id: Makefile.in,v 1.8 2010/06/09 23:50:58 tbox Exp $ top_srcdir = @top_srcdir@ srcdir = @top_srcdir@/lib/isc export_srcdir = @top_srcdir@/lib/export @BIND9_VERSION@ @LIBISC_API@ CINCLUDES = -I${srcdir}/unix/include \ -I${srcdir}/@ISC_THREAD_DIR@/include \ -I${srcdir}/@ISC_ARCH_DIR@/include \ -I${export_srcdir}/isc/include -I${srcdir}/include \ @ISC_OPENSSL_INC@ CDEFINES = @USE_OPENSSL@ -DUSE_APPIMPREGISTER -DUSE_MEMIMPREGISTER \ -DUSE_SOCKETIMPREGISTER -DUSE_TASKIMPREGISTER \ -DUSE_TIMERIMPREGISTER CWARNINGS = # Alphabetically # {file,dir}.c is necessary for isclog # symtab.c is necessary for isccfg APIOBJS = app_api.@O@ mem_api.@O@ socket_api.@O@ \ task_api.@O@ timer_api.@O@ ISCDRIVEROBJS = mem.@O@ unix/socket.@O@ task.@O@ timer.@O@ lib.@O@ \ heap.@O@ #timer module depends on this UNIXOBJS = @ISC_ISCIPV6_O@ \ unix/app.@O@ \ unix/dir.@O@ \ unix/errno2result.@O@ \ unix/file.@O@ \ unix/fsaccess.@O@ \ unix/stdio.@O@ \ unix/stdtime.@O@ unix/strerror.@O@ unix/time.@O@ NLSOBJS = nls/msgcat.@O@ THREADOPTOBJS = @ISC_THREAD_DIR@/condition.@O@ @ISC_THREAD_DIR@/mutex.@O@ THREADOBJS = @THREADOPTOBJS@ @ISC_THREAD_DIR@/thread.@O@ WIN32OBJS = win32/condition.@O@ win32/dir.@O@ win32/file.@O@ \ win32/fsaccess.@O@ win32/once.@O@ win32/stdtime.@O@ \ win32/thread.@O@ win32/time.@O@ # Alphabetically OBJS = @ISC_EXTRA_OBJS@ \ assertions.@O@ backtrace.@O@ backtrace-emptytbl.@O@ base32.@O@ \ - base64.@O@ buffer.@O@ bufferlist.@O@ \ + base64.@O@ buffer.@O@ bufferlist.@O@ counter.@O@ \ error.@O@ event.@O@ \ hash.@O@ hex.@O@ hmacmd5.@O@ hmacsha.@O@ \ inet_aton.@O@ iterated_hash.@O@ lex.@O@ lfsr.@O@ log.@O@ \ md5.@O@ mutexblock.@O@ netaddr.@O@ netscope.@O@ \ ondestroy.@O@ parseint.@O@ portset.@O@ radix.@O@ \ random.@O@ refcount.@O@ region.@O@ regex.@O@ result.@O@ \ rwlock.@O@ safe.@O@ serial.@O@ sha1.@O@ sha2.@O@ \ sockaddr.@O@ stats.@O@ string.@O@ \ symtab.@O@ \ version.@O@ \ ${APIOBJS} ${ISCDRIVEROBJS} \ ${UNIXOBJS} ${NLSOBJS} ${THREADOBJS} # Alphabetically APISRCS = app_api.c mem_api.c socket_api.c \ task_api.c timer_api.c ISCDRIVERSRCS = mem.c task.c lib.c timer.c heap.c SRCS = @ISC_EXTRA_SRCS@ \ assertions.c backtrace.c backtrace-emptytbl.c base32.c \ - base64.c buffer.c bufferlist.c \ + base64.c buffer.c bufferlist.c counter.c \ error.c event.c \ hash.c hex.c hmacmd5.c hmacsha.c \ inet_aton.c iterated_hash.c lex.c log.c lfsr.c \ md5.c mutexblock.c \ netaddr.c netscope.c \ ondestroy.c \ parseint.c portset.c radix.c \ random.c refcount.c region.c regex.c result.c rwlock.c \ safe.c serial.c sha1.c sha2.c sockaddr.c \ stats.c string.c symtab.c \ version.c \ ${APISRCS} ${ISCDRIVERSRCS} LIBS = @LIBS@ SUBDIRS = include unix nls @ISC_THREAD_DIR@ TARGETS = timestamp @BIND9_MAKE_RULES@ version.@O@: ${srcdir}/version.c ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \ -DVERSION=\"${VERSION}\" \ -DLIBINTERFACE=${LIBINTERFACE} \ -DLIBREVISION=${LIBREVISION} \ -DLIBAGE=${LIBAGE} \ -c ${srcdir}/version.c libisc.@SA@: ${OBJS} ${AR} ${ARFLAGS} $@ ${OBJS} ${RANLIB} $@ libisc.la: ${OBJS} ${LIBTOOL_MODE_LINK} \ ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libisc.la \ -rpath ${export_libdir} \ -version-info ${LIBINTERFACE}:${LIBREVISION}:${LIBAGE} \ ${OBJS} ${LIBS} timestamp: libisc.@A@ touch timestamp installdirs: $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${export_libdir} install:: timestamp installdirs ${LIBTOOL_MODE_INSTALL} ${INSTALL_DATA} libisc.@A@ \ ${DESTDIR}${export_libdir} clean distclean:: rm -f libisc.@A@ libisc.la timestamp Index: stable/8/contrib/bind9/lib/isc/Makefile.in =================================================================== --- stable/8/contrib/bind9/lib/isc/Makefile.in (revision 275668) +++ stable/8/contrib/bind9/lib/isc/Makefile.in (revision 275669) @@ -1,140 +1,138 @@ # Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") # Copyright (C) 1998-2003 Internet Software Consortium. # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id$ - srcdir = @srcdir@ VPATH = @srcdir@ top_srcdir = @top_srcdir@ @BIND9_VERSION@ @LIBISC_API@ CINCLUDES = -I${srcdir}/unix/include \ -I${srcdir}/@ISC_THREAD_DIR@/include \ -I${srcdir}/@ISC_ARCH_DIR@/include \ -I./include \ -I${srcdir}/include @ISC_OPENSSL_INC@ CDEFINES = @USE_OPENSSL@ CWARNINGS = # Alphabetically UNIXOBJS = @ISC_ISCIPV6_O@ \ unix/app.@O@ unix/dir.@O@ unix/entropy.@O@ \ unix/errno2result.@O@ unix/file.@O@ unix/fsaccess.@O@ \ unix/interfaceiter.@O@ unix/keyboard.@O@ unix/net.@O@ \ unix/os.@O@ unix/resource.@O@ unix/socket.@O@ unix/stdio.@O@ \ unix/stdtime.@O@ unix/strerror.@O@ unix/syslog.@O@ unix/time.@O@ NLSOBJS = nls/msgcat.@O@ THREADOPTOBJS = @ISC_THREAD_DIR@/condition.@O@ @ISC_THREAD_DIR@/mutex.@O@ THREADOBJS = @THREADOPTOBJS@ @ISC_THREAD_DIR@/thread.@O@ WIN32OBJS = win32/condition.@O@ win32/dir.@O@ win32/file.@O@ \ win32/fsaccess.@O@ win32/once.@O@ win32/stdtime.@O@ \ win32/thread.@O@ win32/time.@O@ # Alphabetically OBJS = @ISC_EXTRA_OBJS@ \ assertions.@O@ backtrace.@O@ base32.@O@ base64.@O@ \ bitstring.@O@ buffer.@O@ bufferlist.@O@ commandline.@O@ \ - error.@O@ event.@O@ \ + counter.@O@ error.@O@ event.@O@ \ hash.@O@ heap.@O@ hex.@O@ hmacmd5.@O@ hmacsha.@O@ \ httpd.@O@ inet_aton.@O@ iterated_hash.@O@ \ lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \ md5.@O@ mem.@O@ mutexblock.@O@ \ netaddr.@O@ netscope.@O@ ondestroy.@O@ \ parseint.@O@ portset.@O@ quota.@O@ radix.@O@ random.@O@ \ ratelimiter.@O@ refcount.@O@ region.@O@ regex.@O@ result.@O@ \ rwlock.@O@ \ safe.@O@ serial.@O@ sha1.@O@ sha2.@O@ sockaddr.@O@ stats.@O@ \ string.@O@ strtoul.@O@ symtab.@O@ task.@O@ taskpool.@O@ \ timer.@O@ version.@O@ ${UNIXOBJS} ${NLSOBJS} ${THREADOBJS} SYMTBLOBJS = backtrace-emptytbl.@O@ # Alphabetically SRCS = @ISC_EXTRA_SRCS@ \ assertions.c backtrace.c base32.c base64.c bitstring.c \ - buffer.c bufferlist.c commandline.c error.c event.c \ - heap.c hex.c hmacmd5.c hmacsha.c \ + buffer.c bufferlist.c commandline.c counter.c \ + error.c event.c heap.c hex.c hmacmd5.c hmacsha.c \ httpd.c inet_aton.c iterated_hash.c \ lex.c lfsr.c lib.c log.c \ md5.c mem.c mutexblock.c \ netaddr.c netscope.c ondestroy.c \ parseint.c portset.c quota.c radix.c random.c \ ratelimiter.c refcount.c region.c regex.c result.c rwlock.c \ safe.c serial.c sha1.c sha2.c sockaddr.c stats.c string.c \ strtoul.c symtab.c task.c taskpool.c timer.c \ version.c LIBS = @LIBS@ # Note: the order of SUBDIRS is important. # Attempt to disable parallel processing. .NOTPARALLEL: .NO_PARALLEL: SUBDIRS = include unix nls @ISC_THREAD_DIR@ @ISC_ARCH_DIR@ TARGETS = timestamp TESTDIRS = @UNITTESTS@ @BIND9_MAKE_RULES@ safe.@O@: safe.c ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} @CCNOOPT@ \ -c ${srcdir}/safe.c version.@O@: version.c ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \ -DVERSION=\"${VERSION}\" \ -DLIBINTERFACE=${LIBINTERFACE} \ -DLIBREVISION=${LIBREVISION} \ -DLIBAGE=${LIBAGE} \ -c ${srcdir}/version.c libisc.@SA@: ${OBJS} ${SYMTBLOBJS} ${AR} ${ARFLAGS} $@ ${OBJS} ${SYMTBLOBJS} ${RANLIB} $@ libisc-nosymtbl.@SA@: ${OBJS} ${AR} ${ARFLAGS} $@ ${OBJS} ${RANLIB} $@ libisc.la: ${OBJS} ${SYMTBLOBJS} ${LIBTOOL_MODE_LINK} \ ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libisc.la -rpath ${libdir} \ -version-info ${LIBINTERFACE}:${LIBREVISION}:${LIBAGE} \ ${OBJS} ${SYMTBLOBJS} ${LIBS} libisc-nosymtbl.la: ${OBJS} ${LIBTOOL_MODE_LINK} \ ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libisc-nosymtbl.la -rpath ${libdir} \ -version-info ${LIBINTERFACE}:${LIBREVISION}:${LIBAGE} \ ${OBJS} ${LIBS} timestamp: libisc.@A@ libisc-nosymtbl.@A@ touch timestamp installdirs: $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${libdir} install:: timestamp installdirs ${LIBTOOL_MODE_INSTALL} ${INSTALL_DATA} libisc.@A@ ${DESTDIR}${libdir} clean distclean:: rm -f libisc.@A@ libisc-nosymtbl.@A@ libisc.la \ libisc-nosymtbl.la timestamp Index: stable/8/contrib/bind9/lib/isc/counter.c =================================================================== --- stable/8/contrib/bind9/lib/isc/counter.c (nonexistent) +++ stable/8/contrib/bind9/lib/isc/counter.c (revision 275669) @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*! \file */ + +#include + +#include + +#include +#include +#include +#include + +#define COUNTER_MAGIC ISC_MAGIC('C', 'n', 't', 'r') +#define VALID_COUNTER(r) ISC_MAGIC_VALID(r, COUNTER_MAGIC) + +struct isc_counter { + unsigned int magic; + isc_mem_t *mctx; + isc_mutex_t lock; + unsigned int references; + unsigned int limit; + unsigned int used; +}; + +isc_result_t +isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp) { + isc_result_t result; + isc_counter_t *counter; + + REQUIRE(counterp != NULL && *counterp == NULL); + + counter = isc_mem_get(mctx, sizeof(*counter)); + if (counter == NULL) + return (ISC_R_NOMEMORY); + + result = isc_mutex_init(&counter->lock); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, counter, sizeof(*counter)); + return (result); + } + + counter->mctx = NULL; + isc_mem_attach(mctx, &counter->mctx); + + counter->references = 1; + counter->limit = limit; + counter->used = 0; + + counter->magic = COUNTER_MAGIC; + *counterp = counter; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_counter_increment(isc_counter_t *counter) { + isc_result_t result = ISC_R_SUCCESS; + + LOCK(&counter->lock); + counter->used++; + if (counter->limit != 0 && counter->used >= counter->limit) + result = ISC_R_QUOTA; + UNLOCK(&counter->lock); + + return (result); +} + +unsigned int +isc_counter_used(isc_counter_t *counter) { + REQUIRE(VALID_COUNTER(counter)); + + return (counter->used); +} + +void +isc_counter_setlimit(isc_counter_t *counter, int limit) { + REQUIRE(VALID_COUNTER(counter)); + + LOCK(&counter->lock); + counter->limit = limit; + UNLOCK(&counter->lock); +} + +void +isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp) { + REQUIRE(VALID_COUNTER(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + LOCK(&source->lock); + source->references++; + INSIST(source->references > 0); + UNLOCK(&source->lock); + + *targetp = source; +} + +static void +destroy(isc_counter_t *counter) { + counter->magic = 0; + isc_mutex_destroy(&counter->lock); + isc_mem_putanddetach(&counter->mctx, counter, sizeof(*counter)); +} + +void +isc_counter_detach(isc_counter_t **counterp) { + isc_counter_t *counter; + isc_boolean_t want_destroy = ISC_FALSE; + + REQUIRE(counterp != NULL && *counterp != NULL); + counter = *counterp; + REQUIRE(VALID_COUNTER(counter)); + + *counterp = NULL; + + LOCK(&counter->lock); + INSIST(counter->references > 0); + counter->references--; + if (counter->references == 0) + want_destroy = ISC_TRUE; + UNLOCK(&counter->lock); + + if (want_destroy) + destroy(counter); +} Property changes on: stable/8/contrib/bind9/lib/isc/counter.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/8/contrib/bind9/lib/isc/include/isc/Makefile.in =================================================================== --- stable/8/contrib/bind9/lib/isc/include/isc/Makefile.in (revision 275668) +++ stable/8/contrib/bind9/lib/isc/include/isc/Makefile.in (revision 275669) @@ -1,61 +1,60 @@ # Copyright (C) 2004-2009, 2012, 2013 Internet Systems Consortium, Inc. ("ISC") # Copyright (C) 1998-2001, 2003 Internet Software Consortium. # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id$ - srcdir = @srcdir@ VPATH = @srcdir@ top_srcdir = @top_srcdir@ @BIND9_VERSION@ # # Only list headers that are to be installed and are not # machine generated. The latter are handled specially in the # install target below. # HEADERS = app.h assertions.h base64.h bind9.h bitstring.h boolean.h \ - buffer.h bufferlist.h commandline.h entropy.h error.h event.h \ + buffer.h bufferlist.h \ + commandline.h counter.h entropy.h error.h event.h \ eventclass.h file.h formatcheck.h fsaccess.h \ hash.h heap.h hex.h hmacmd5.h hmacsha.h \ httpd.h \ interfaceiter.h @ISC_IPV6_H@ iterated_hash.h lang.h lex.h \ lfsr.h lib.h list.h log.h \ magic.h md5.h mem.h msgcat.h msgs.h mutexblock.h \ namespace.h netaddr.h ondestroy.h os.h parseint.h \ print.h quota.h radix.h random.h ratelimiter.h \ refcount.h regex.h region.h resource.h \ result.h resultclass.h rwlock.h safe.h serial.h sha1.h sha2.h \ sockaddr.h socket.h stdio.h stdlib.h string.h \ symtab.h \ task.h taskpool.h timer.h types.h util.h version.h \ xml.h SUBDIRS = TARGETS = @BIND9_MAKE_RULES@ installdirs: $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/isc install:: installdirs for i in ${HEADERS}; do \ ${INSTALL_DATA} ${srcdir}/$$i ${DESTDIR}${includedir}/isc ; \ done ${INSTALL_DATA} platform.h ${DESTDIR}${includedir}/isc distclean:: rm -f platform.h Index: stable/8/contrib/bind9/lib/isc/include/isc/counter.h =================================================================== --- stable/8/contrib/bind9/lib/isc/include/isc/counter.h (nonexistent) +++ stable/8/contrib/bind9/lib/isc/include/isc/counter.h (revision 275669) @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef ISC_COUNTER_H +#define ISC_COUNTER_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/counter.h + * + * \brief The isc_counter_t object is a simplified version of the + * isc_quota_t object; it tracks the consumption of limited + * resources, returning an error condition when the quota is + * exceeded. However, unlike isc_quota_t, attaching and detaching + * from a counter object does not increment or decrement the counter. + */ + +/*** + *** Imports. + ***/ + +#include +#include +#include + +/***** + ***** Types. + *****/ + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp); +/*%< + * Allocate and initialize a counter object. + */ + +isc_result_t +isc_counter_increment(isc_counter_t *counter); +/*%< + * Increment the counter. + * + * If the counter limit is nonzero and has been reached, then + * return ISC_R_QUOTA, otherwise ISC_R_SUCCESS. (The counter is + * incremented regardless of return value.) + */ + +unsigned int +isc_counter_used(isc_counter_t *counter); +/*%< + * Return the current counter value. + */ + +void +isc_counter_setlimit(isc_counter_t *counter, int limit); +/*%< + * Set the counter limit. + */ + +void +isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp); +/*%< + * Attach to a counter object, increasing its reference counter. + */ + +void +isc_counter_detach(isc_counter_t **counterp); +/*%< + * Detach (and destroy if reference counter has dropped to zero) + * a counter object. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_COUNTER_H */ Property changes on: stable/8/contrib/bind9/lib/isc/include/isc/counter.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/8/contrib/bind9/lib/isc/include/isc/types.h =================================================================== --- stable/8/contrib/bind9/lib/isc/include/isc/types.h (revision 275668) +++ stable/8/contrib/bind9/lib/isc/include/isc/types.h (revision 275669) @@ -1,129 +1,130 @@ /* * Copyright (C) 2004-2009, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id$ */ #ifndef ISC_TYPES_H #define ISC_TYPES_H 1 #include #include /*! \file isc/types.h * \brief * OS-specific types, from the OS-specific include directories. */ #include #include /* * XXXDCL should isc_boolean_t be moved here, requiring an explicit include * of when ISC_TRUE/ISC_FALSE/ISC_TF() are desired? */ #include /* * XXXDCL This is just for ISC_LIST and ISC_LINK, but gets all of the other * list macros too. */ #include /* Core Types. Alphabetized by defined type. */ typedef struct isc_appctx isc_appctx_t; /*%< Application context */ typedef struct isc_backtrace_symmap isc_backtrace_symmap_t; /*%< Symbol Table Entry */ typedef struct isc_bitstring isc_bitstring_t; /*%< Bitstring */ typedef struct isc_buffer isc_buffer_t; /*%< Buffer */ typedef ISC_LIST(isc_buffer_t) isc_bufferlist_t; /*%< Buffer List */ typedef struct isc_constregion isc_constregion_t; /*%< Const region */ typedef struct isc_consttextregion isc_consttextregion_t; /*%< Const Text Region */ +typedef struct isc_counter isc_counter_t; /*%< Counter */ typedef struct isc_entropy isc_entropy_t; /*%< Entropy */ typedef struct isc_entropysource isc_entropysource_t; /*%< Entropy Source */ typedef struct isc_event isc_event_t; /*%< Event */ typedef ISC_LIST(isc_event_t) isc_eventlist_t; /*%< Event List */ typedef unsigned int isc_eventtype_t; /*%< Event Type */ typedef isc_uint32_t isc_fsaccess_t; /*%< FS Access */ typedef struct isc_hash isc_hash_t; /*%< Hash */ typedef struct isc_httpd isc_httpd_t; /*%< HTTP client */ typedef void (isc_httpdfree_t)(isc_buffer_t *, void *); /*%< HTTP free function */ typedef struct isc_httpdmgr isc_httpdmgr_t; /*%< HTTP manager */ typedef struct isc_httpdurl isc_httpdurl_t; /*%< HTTP URL */ typedef void (isc_httpdondestroy_t)(void *); /*%< Callback on destroying httpd */ typedef struct isc_interface isc_interface_t; /*%< Interface */ typedef struct isc_interfaceiter isc_interfaceiter_t; /*%< Interface Iterator */ typedef struct isc_interval isc_interval_t; /*%< Interval */ typedef struct isc_lex isc_lex_t; /*%< Lex */ typedef struct isc_log isc_log_t; /*%< Log */ typedef struct isc_logcategory isc_logcategory_t; /*%< Log Category */ typedef struct isc_logconfig isc_logconfig_t; /*%< Log Configuration */ typedef struct isc_logmodule isc_logmodule_t; /*%< Log Module */ typedef struct isc_mem isc_mem_t; /*%< Memory */ typedef struct isc_mempool isc_mempool_t; /*%< Memory Pool */ typedef struct isc_msgcat isc_msgcat_t; /*%< Message Catalog */ typedef struct isc_ondestroy isc_ondestroy_t; /*%< On Destroy */ typedef struct isc_netaddr isc_netaddr_t; /*%< Net Address */ typedef struct isc_portset isc_portset_t; /*%< Port Set */ typedef struct isc_quota isc_quota_t; /*%< Quota */ typedef struct isc_random isc_random_t; /*%< Random */ typedef struct isc_ratelimiter isc_ratelimiter_t; /*%< Rate Limiter */ typedef struct isc_region isc_region_t; /*%< Region */ typedef isc_uint64_t isc_resourcevalue_t; /*%< Resource Value */ typedef unsigned int isc_result_t; /*%< Result */ typedef struct isc_rwlock isc_rwlock_t; /*%< Read Write Lock */ typedef struct isc_sockaddr isc_sockaddr_t; /*%< Socket Address */ typedef struct isc_socket isc_socket_t; /*%< Socket */ typedef struct isc_socketevent isc_socketevent_t; /*%< Socket Event */ typedef struct isc_socketmgr isc_socketmgr_t; /*%< Socket Manager */ typedef struct isc_stats isc_stats_t; /*%< Statistics */ typedef int isc_statscounter_t; /*%< Statistics Counter */ typedef struct isc_symtab isc_symtab_t; /*%< Symbol Table */ typedef struct isc_task isc_task_t; /*%< Task */ typedef ISC_LIST(isc_task_t) isc_tasklist_t; /*%< Task List */ typedef struct isc_taskmgr isc_taskmgr_t; /*%< Task Manager */ typedef struct isc_textregion isc_textregion_t; /*%< Text Region */ typedef struct isc_time isc_time_t; /*%< Time */ typedef struct isc_timer isc_timer_t; /*%< Timer */ typedef struct isc_timermgr isc_timermgr_t; /*%< Timer Manager */ typedef void (*isc_taskaction_t)(isc_task_t *, isc_event_t *); typedef int (*isc_sockfdwatch_t)(isc_task_t *, isc_socket_t *, void *, int); /* The following cannot be listed alphabetically due to forward reference */ typedef isc_result_t (isc_httpdaction_t)(const char *url, const char *querystring, void *arg, unsigned int *retcode, const char **retmsg, const char **mimetype, isc_buffer_t *body, isc_httpdfree_t **freecb, void **freecb_args); typedef isc_boolean_t (isc_httpdclientok_t)(const isc_sockaddr_t *, void *); /*% Resource */ typedef enum { isc_resource_coresize = 1, isc_resource_cputime, isc_resource_datasize, isc_resource_filesize, isc_resource_lockedmemory, isc_resource_openfiles, isc_resource_processes, isc_resource_residentsize, isc_resource_stacksize } isc_resource_t; #endif /* ISC_TYPES_H */ Index: stable/8/contrib/bind9/lib/isccfg/namedconf.c =================================================================== --- stable/8/contrib/bind9/lib/isccfg/namedconf.c (revision 275668) +++ stable/8/contrib/bind9/lib/isccfg/namedconf.c (revision 275669) @@ -1,2852 +1,2852 @@ /* * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2002, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ - /*! \file */ #include #include #include #include #include #include #include #include #include #include #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base) /*% Check a return value. */ #define CHECK(op) \ do { result = (op); \ if (result != ISC_R_SUCCESS) goto cleanup; \ } while (0) /*% Clean up a configuration object if non-NULL. */ #define CLEANUP_OBJ(obj) \ do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0) /*% * Forward declarations of static functions. */ static isc_result_t parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype, const cfg_type_t *othertype, cfg_obj_t **ret); static isc_result_t parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); static isc_result_t parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); static isc_result_t parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); static void print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj); static void doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type); static void print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj); static void doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type); static void doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type); static cfg_type_t cfg_type_acl; static cfg_type_t cfg_type_addrmatchelt; static cfg_type_t cfg_type_bracketed_aml; static cfg_type_t cfg_type_bracketed_namesockaddrkeylist; static cfg_type_t cfg_type_bracketed_sockaddrlist; static cfg_type_t cfg_type_bracketed_sockaddrnameportlist; static cfg_type_t cfg_type_controls; static cfg_type_t cfg_type_controls_sockaddr; static cfg_type_t cfg_type_destinationlist; static cfg_type_t cfg_type_dialuptype; static cfg_type_t cfg_type_ixfrdifftype; static cfg_type_t cfg_type_key; static cfg_type_t cfg_type_logfile; static cfg_type_t cfg_type_logging; static cfg_type_t cfg_type_logseverity; static cfg_type_t cfg_type_lwres; static cfg_type_t cfg_type_masterselement; static cfg_type_t cfg_type_nameportiplist; static cfg_type_t cfg_type_negated; static cfg_type_t cfg_type_notifytype; static cfg_type_t cfg_type_optional_allow; static cfg_type_t cfg_type_optional_class; static cfg_type_t cfg_type_optional_facility; static cfg_type_t cfg_type_optional_keyref; static cfg_type_t cfg_type_optional_port; static cfg_type_t cfg_type_options; static cfg_type_t cfg_type_portiplist; static cfg_type_t cfg_type_querysource4; static cfg_type_t cfg_type_querysource6; static cfg_type_t cfg_type_querysource; static cfg_type_t cfg_type_server; static cfg_type_t cfg_type_server_key_kludge; static cfg_type_t cfg_type_size; static cfg_type_t cfg_type_sizenodefault; static cfg_type_t cfg_type_sockaddr4wild; static cfg_type_t cfg_type_sockaddr6wild; static cfg_type_t cfg_type_statschannels; static cfg_type_t cfg_type_view; static cfg_type_t cfg_type_viewopts; static cfg_type_t cfg_type_zone; static cfg_type_t cfg_type_zoneopts; static cfg_type_t cfg_type_dynamically_loadable_zones; static cfg_type_t cfg_type_dynamically_loadable_zones_opts; static cfg_type_t cfg_type_v4_aaaa; /* * Clauses that can be found in a 'dynamically loadable zones' statement */ static cfg_clausedef_t dynamically_loadable_zones_clauses[] = { { "database", &cfg_type_astring, 0 }, { NULL, NULL, 0 } }; /* * A dynamically loadable zones statement. */ static cfg_tuplefielddef_t dynamically_loadable_zones_fields[] = { { "name", &cfg_type_astring, 0 }, { "options", &cfg_type_dynamically_loadable_zones_opts, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_dynamically_loadable_zones = { "dlz", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, dynamically_loadable_zones_fields }; /*% tkey-dhkey */ static cfg_tuplefielddef_t tkey_dhkey_fields[] = { { "name", &cfg_type_qstring, 0 }, { "keyid", &cfg_type_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_tkey_dhkey = { "tkey-dhkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, tkey_dhkey_fields }; /*% listen-on */ static cfg_tuplefielddef_t listenon_fields[] = { { "port", &cfg_type_optional_port, 0 }, { "acl", &cfg_type_bracketed_aml, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_listenon = { "listenon", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, listenon_fields }; /*% acl */ static cfg_tuplefielddef_t acl_fields[] = { { "name", &cfg_type_astring, 0 }, { "value", &cfg_type_bracketed_aml, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_acl = { "acl", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, acl_fields }; /*% masters */ static cfg_tuplefielddef_t masters_fields[] = { { "name", &cfg_type_astring, 0 }, { "port", &cfg_type_optional_port, 0 }, { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_masters = { "masters", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, masters_fields }; /*% * "sockaddrkeylist", a list of socket addresses with optional keys * and an optional default port, as used in the masters option. * E.g., * "port 1234 { mymasters; 10.0.0.1 key foo; 1::2 port 69; }" */ static cfg_tuplefielddef_t namesockaddrkey_fields[] = { { "masterselement", &cfg_type_masterselement, 0 }, { "key", &cfg_type_optional_keyref, 0 }, { NULL, NULL, 0 }, }; static cfg_type_t cfg_type_namesockaddrkey = { "namesockaddrkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, namesockaddrkey_fields }; static cfg_type_t cfg_type_bracketed_namesockaddrkeylist = { "bracketed_namesockaddrkeylist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_namesockaddrkey }; static cfg_tuplefielddef_t namesockaddrkeylist_fields[] = { { "port", &cfg_type_optional_port, 0 }, { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_namesockaddrkeylist = { "sockaddrkeylist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, namesockaddrkeylist_fields }; /*% * A list of socket addresses with an optional default port, * as used in the also-notify option. E.g., * "port 1234 { 10.0.0.1; 1::2 port 69; }" */ static cfg_tuplefielddef_t portiplist_fields[] = { { "port", &cfg_type_optional_port, 0 }, { "addresses", &cfg_type_bracketed_sockaddrlist, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_portiplist = { "portiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, portiplist_fields }; /*% * A public key, as in the "pubkey" statement. */ static cfg_tuplefielddef_t pubkey_fields[] = { { "flags", &cfg_type_uint32, 0 }, { "protocol", &cfg_type_uint32, 0 }, { "algorithm", &cfg_type_uint32, 0 }, { "key", &cfg_type_qstring, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_pubkey = { "pubkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, pubkey_fields }; /*% * A list of RR types, used in grant statements. * Note that the old parser allows quotes around the RR type names. */ static cfg_type_t cfg_type_rrtypelist = { "rrtypelist", cfg_parse_spacelist, cfg_print_spacelist, cfg_doc_terminal, &cfg_rep_list, &cfg_type_astring }; static const char *mode_enums[] = { "grant", "deny", NULL }; static cfg_type_t cfg_type_mode = { "mode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &mode_enums }; static isc_result_t parse_matchtype(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "zonesub") == 0) { pctx->flags |= CFG_PCTX_SKIP; } return (cfg_parse_enum(pctx, type, ret)); cleanup: return (result); } static isc_result_t parse_matchname(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; if ((pctx->flags & CFG_PCTX_SKIP) != 0) { pctx->flags &= ~CFG_PCTX_SKIP; CHECK(cfg_parse_void(pctx, NULL, &obj)); } else result = cfg_parse_astring(pctx, type, &obj); *ret = obj; cleanup: return (result); } static void doc_matchname(cfg_printer_t *pctx, const cfg_type_t *type) { cfg_print_chars(pctx, "[ ", 2); cfg_doc_obj(pctx, type->of); cfg_print_chars(pctx, " ]", 2); } static const char *matchtype_enums[] = { "name", "subdomain", "wildcard", "self", "selfsub", "selfwild", "krb5-self", "ms-self", "krb5-subdomain", "ms-subdomain", "tcp-self", "6to4-self", "zonesub", "external", NULL }; static cfg_type_t cfg_type_matchtype = { "matchtype", parse_matchtype, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &matchtype_enums }; static cfg_type_t cfg_type_matchname = { "optional_matchname", parse_matchname, cfg_print_ustring, &doc_matchname, &cfg_rep_tuple, &cfg_type_ustring }; /*% * A grant statement, used in the update policy. */ static cfg_tuplefielddef_t grant_fields[] = { { "mode", &cfg_type_mode, 0 }, { "identity", &cfg_type_astring, 0 }, /* domain name */ { "matchtype", &cfg_type_matchtype, 0 }, { "name", &cfg_type_matchname, 0 }, /* domain name */ { "types", &cfg_type_rrtypelist, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_grant = { "grant", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, grant_fields }; static cfg_type_t cfg_type_updatepolicy = { "update_policy", parse_updatepolicy, print_updatepolicy, doc_updatepolicy, &cfg_rep_list, &cfg_type_grant }; static isc_result_t parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_gettoken(pctx, 0)); if (pctx->token.type == isc_tokentype_special && pctx->token.value.as_char == '{') { cfg_ungettoken(pctx); return (cfg_parse_bracketed_list(pctx, type, ret)); } if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "local") == 0) { cfg_obj_t *obj = NULL; CHECK(cfg_create_obj(pctx, &cfg_type_ustring, &obj)); obj->value.string.length = strlen("local"); obj->value.string.base = isc_mem_get(pctx->mctx, obj->value.string.length + 1); if (obj->value.string.base == NULL) { isc_mem_put(pctx->mctx, obj, sizeof(*obj)); return (ISC_R_NOMEMORY); } memmove(obj->value.string.base, "local", 5); obj->value.string.base[5] = '\0'; *ret = obj; return (ISC_R_SUCCESS); } cfg_ungettoken(pctx); return (ISC_R_UNEXPECTEDTOKEN); cleanup: return (result); } static void print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj) { if (cfg_obj_isstring(obj)) cfg_print_ustring(pctx, obj); else cfg_print_bracketed_list(pctx, obj); } static void doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type) { cfg_print_cstr(pctx, "( local | { "); cfg_doc_obj(pctx, type->of); cfg_print_cstr(pctx, "; ... }"); } /*% * A view statement. */ static cfg_tuplefielddef_t view_fields[] = { { "name", &cfg_type_astring, 0 }, { "class", &cfg_type_optional_class, 0 }, { "options", &cfg_type_viewopts, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_view = { "view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, view_fields }; /*% * A zone statement. */ static cfg_tuplefielddef_t zone_fields[] = { { "name", &cfg_type_astring, 0 }, { "class", &cfg_type_optional_class, 0 }, { "options", &cfg_type_zoneopts, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_zone = { "zone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, zone_fields }; /*% * A "category" clause in the "logging" statement. */ static cfg_tuplefielddef_t category_fields[] = { { "name", &cfg_type_astring, 0 }, { "destinations", &cfg_type_destinationlist,0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_category = { "category", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, category_fields }; /*% * A dnssec key, as used in the "trusted-keys" statement. */ static cfg_tuplefielddef_t dnsseckey_fields[] = { { "name", &cfg_type_astring, 0 }, { "flags", &cfg_type_uint32, 0 }, { "protocol", &cfg_type_uint32, 0 }, { "algorithm", &cfg_type_uint32, 0 }, { "key", &cfg_type_qstring, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_dnsseckey = { "dnsseckey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, dnsseckey_fields }; /*% * A managed key initialization specifier, as used in the * "managed-keys" statement. */ static cfg_tuplefielddef_t managedkey_fields[] = { { "name", &cfg_type_astring, 0 }, { "init", &cfg_type_ustring, 0 }, /* must be literal "initial-key" */ { "flags", &cfg_type_uint32, 0 }, { "protocol", &cfg_type_uint32, 0 }, { "algorithm", &cfg_type_uint32, 0 }, { "key", &cfg_type_qstring, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_managedkey = { "managedkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, managedkey_fields }; static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring }; static cfg_type_t cfg_type_optional_wild_class = { "optional_wild_class", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_class_kw }; static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring }; static cfg_type_t cfg_type_optional_wild_type = { "optional_wild_type", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_type_kw }; static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring }; static cfg_type_t cfg_type_optional_wild_name = { "optional_wild_name", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_name_kw }; /*% * An rrset ordering element. */ static cfg_tuplefielddef_t rrsetorderingelement_fields[] = { { "class", &cfg_type_optional_wild_class, 0 }, { "type", &cfg_type_optional_wild_type, 0 }, { "name", &cfg_type_optional_wild_name, 0 }, { "order", &cfg_type_ustring, 0 }, /* must be literal "order" */ { "ordering", &cfg_type_ustring, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_rrsetorderingelement = { "rrsetorderingelement", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, rrsetorderingelement_fields }; /*% * A global or view "check-names" option. Note that the zone * "check-names" option has a different syntax. */ static const char *checktype_enums[] = { "master", "slave", "response", NULL }; static cfg_type_t cfg_type_checktype = { "checktype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &checktype_enums }; static const char *checkmode_enums[] = { "fail", "warn", "ignore", NULL }; static cfg_type_t cfg_type_checkmode = { "checkmode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &checkmode_enums }; static const char *warn_enums[] = { "warn", "ignore", NULL }; static cfg_type_t cfg_type_warn = { "warn", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &warn_enums }; static cfg_tuplefielddef_t checknames_fields[] = { { "type", &cfg_type_checktype, 0 }, { "mode", &cfg_type_checkmode, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_checknames = { "checknames", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, checknames_fields }; static cfg_type_t cfg_type_bracketed_sockaddrlist = { "bracketed_sockaddrlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_sockaddr }; static const char *autodnssec_enums[] = { "allow", "maintain", "off", NULL }; static cfg_type_t cfg_type_autodnssec = { "autodnssec", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &autodnssec_enums }; static cfg_type_t cfg_type_rrsetorder = { "rrsetorder", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_rrsetorderingelement }; static keyword_type_t port_kw = { "port", &cfg_type_uint32 }; static cfg_type_t cfg_type_optional_port = { "optional_port", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_uint32, &port_kw }; /*% A list of keys, as in the "key" clause of the controls statement. */ static cfg_type_t cfg_type_keylist = { "keylist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring }; /*% A list of dnssec keys, as in "trusted-keys" */ static cfg_type_t cfg_type_dnsseckeys = { "dnsseckeys", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_dnsseckey }; /*% * A list of managed key entries, as in "trusted-keys". Currently * (9.7.0) this has a format similar to dnssec keys, except the keyname * is followed by the keyword "initial-key". In future releases, this * keyword may take other values indicating different methods for the * key to be initialized. */ static cfg_type_t cfg_type_managedkeys = { "managedkeys", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_managedkey }; static const char *forwardtype_enums[] = { "first", "only", NULL }; static cfg_type_t cfg_type_forwardtype = { "forwardtype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &forwardtype_enums }; static const char *zonetype_enums[] = { "master", "slave", "stub", "static-stub", "hint", "forward", "delegation-only", NULL }; static cfg_type_t cfg_type_zonetype = { "zonetype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &zonetype_enums }; static const char *loglevel_enums[] = { "critical", "error", "warning", "notice", "info", "dynamic", NULL }; static cfg_type_t cfg_type_loglevel = { "loglevel", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &loglevel_enums }; static const char *transferformat_enums[] = { "many-answers", "one-answer", NULL }; static cfg_type_t cfg_type_transferformat = { "transferformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &transferformat_enums }; /*% * The special keyword "none", as used in the pid-file option. */ static void print_none(cfg_printer_t *pctx, const cfg_obj_t *obj) { UNUSED(obj); cfg_print_cstr(pctx, "none"); } static cfg_type_t cfg_type_none = { "none", NULL, print_none, NULL, &cfg_rep_void, NULL }; /*% * A quoted string or the special keyword "none". Used in the pid-file option. */ static isc_result_t parse_qstringornone(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "none") == 0) return (cfg_create_obj(pctx, &cfg_type_none, ret)); cfg_ungettoken(pctx); return (cfg_parse_qstring(pctx, type, ret)); cleanup: return (result); } static void doc_qstringornone(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, "( | none )"); } static cfg_type_t cfg_type_qstringornone = { "qstringornone", parse_qstringornone, NULL, doc_qstringornone, NULL, NULL }; /*% * A boolean ("yes" or "no"), or the special keyword "auto". * Used in the dnssec-validation option. */ static void print_auto(cfg_printer_t *pctx, const cfg_obj_t *obj) { UNUSED(obj); cfg_print_cstr(pctx, "auto"); } static cfg_type_t cfg_type_auto = { "auto", NULL, print_auto, NULL, &cfg_rep_void, NULL }; static isc_result_t parse_boolorauto(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "auto") == 0) return (cfg_create_obj(pctx, &cfg_type_auto, ret)); cfg_ungettoken(pctx); return (cfg_parse_boolean(pctx, type, ret)); cleanup: return (result); } static void print_boolorauto(cfg_printer_t *pctx, const cfg_obj_t *obj) { if (obj->type->rep == &cfg_rep_void) cfg_print_chars(pctx, "auto", 4); else if (obj->value.boolean) cfg_print_chars(pctx, "yes", 3); else cfg_print_chars(pctx, "no", 2); } static void doc_boolorauto(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, "( yes | no | auto )"); } static cfg_type_t cfg_type_boolorauto = { "boolorauto", parse_boolorauto, print_boolorauto, doc_boolorauto, NULL, NULL }; /*% * keyword hostname */ static void print_hostname(cfg_printer_t *pctx, const cfg_obj_t *obj) { UNUSED(obj); cfg_print_cstr(pctx, "hostname"); } static cfg_type_t cfg_type_hostname = { "hostname", NULL, print_hostname, NULL, &cfg_rep_boolean, NULL }; /*% * "server-id" argument. */ static isc_result_t parse_serverid(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "none") == 0) return (cfg_create_obj(pctx, &cfg_type_none, ret)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "hostname") == 0) { return (cfg_create_obj(pctx, &cfg_type_hostname, ret)); } cfg_ungettoken(pctx); return (cfg_parse_qstring(pctx, type, ret)); cleanup: return (result); } static void doc_serverid(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, "( | none | hostname )"); } static cfg_type_t cfg_type_serverid = { "serverid", parse_serverid, NULL, doc_serverid, NULL, NULL }; /*% * Port list. */ static cfg_tuplefielddef_t porttuple_fields[] = { { "loport", &cfg_type_uint32, 0 }, { "hiport", &cfg_type_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_porttuple = { "porttuple", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, porttuple_fields }; static isc_result_t parse_port(cfg_parser_t *pctx, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_parse_uint32(pctx, NULL, ret)); if ((*ret)->value.uint32 > 0xffff) { cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid port"); cfg_obj_destroy(pctx, ret); result = ISC_R_RANGE; } cleanup: return (result); } static isc_result_t parse_portrange(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; UNUSED(type); CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER)); if (pctx->token.type == isc_tokentype_number) CHECK(parse_port(pctx, ret)); else { CHECK(cfg_gettoken(pctx, 0)); if (pctx->token.type != isc_tokentype_string || strcasecmp(TOKEN_STRING(pctx), "range") != 0) { cfg_parser_error(pctx, CFG_LOG_NEAR, "expected integer or 'range'"); return (ISC_R_UNEXPECTEDTOKEN); } CHECK(cfg_create_tuple(pctx, &cfg_type_porttuple, &obj)); CHECK(parse_port(pctx, &obj->value.tuple[0])); CHECK(parse_port(pctx, &obj->value.tuple[1])); if (obj->value.tuple[0]->value.uint32 > obj->value.tuple[1]->value.uint32) { cfg_parser_error(pctx, CFG_LOG_NOPREP, "low port '%u' must not be larger " "than high port", obj->value.tuple[0]->value.uint32); result = ISC_R_RANGE; goto cleanup; } *ret = obj; obj = NULL; } cleanup: if (obj != NULL) cfg_obj_destroy(pctx, &obj); return (result); } static cfg_type_t cfg_type_portrange = { "portrange", parse_portrange, NULL, cfg_doc_terminal, NULL, NULL }; static cfg_type_t cfg_type_bracketed_portlist = { "bracketed_sockaddrlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_portrange }; /*% * Clauses that can be found within the top level of the named.conf * file only. */ static cfg_clausedef_t namedconf_clauses[] = { { "options", &cfg_type_options, 0 }, { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI }, { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI }, { "masters", &cfg_type_masters, CFG_CLAUSEFLAG_MULTI }, { "logging", &cfg_type_logging, 0 }, { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI }, { "lwres", &cfg_type_lwres, CFG_CLAUSEFLAG_MULTI }, { "statistics-channels", &cfg_type_statschannels, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; /*% * Clauses that can occur at the top level or in the view * statement, but not in the options block. */ static cfg_clausedef_t namedconf_or_view_clauses[] = { { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI }, /* only 1 DLZ per view allowed */ { "dlz", &cfg_type_dynamically_loadable_zones, 0 }, { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI }, { "trusted-keys", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI }, { "managed-keys", &cfg_type_managedkeys, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; /*% * Clauses that can occur in the bind.keys file. */ static cfg_clausedef_t bindkeys_clauses[] = { { "trusted-keys", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI }, { "managed-keys", &cfg_type_managedkeys, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; /*% * Clauses that can be found within the 'options' statement. */ static cfg_clausedef_t options_clauses[] = { { "avoid-v4-udp-ports", &cfg_type_bracketed_portlist, 0 }, { "avoid-v6-udp-ports", &cfg_type_bracketed_portlist, 0 }, { "bindkeys-file", &cfg_type_qstring, 0 }, { "blackhole", &cfg_type_bracketed_aml, 0 }, { "coresize", &cfg_type_size, 0 }, { "datasize", &cfg_type_size, 0 }, { "session-keyfile", &cfg_type_qstringornone, 0 }, { "session-keyname", &cfg_type_astring, 0 }, { "session-keyalg", &cfg_type_astring, 0 }, { "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK }, { "dump-file", &cfg_type_qstring, 0 }, { "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "files", &cfg_type_size, 0 }, { "flush-zones-on-shutdown", &cfg_type_boolean, 0 }, { "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "heartbeat-interval", &cfg_type_uint32, 0 }, { "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP }, { "host-statistics-max", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP }, { "hostname", &cfg_type_qstringornone, 0 }, { "interface-interval", &cfg_type_uint32, 0 }, { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI }, { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI }, { "managed-keys-directory", &cfg_type_qstring, 0 }, { "match-mapped-addresses", &cfg_type_boolean, 0 }, { "memstatistics-file", &cfg_type_qstring, 0 }, { "memstatistics", &cfg_type_boolean, 0 }, { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, { "pid-file", &cfg_type_qstringornone, 0 }, { "port", &cfg_type_uint32, 0 }, { "querylog", &cfg_type_boolean, 0 }, { "recursing-file", &cfg_type_qstring, 0 }, { "random-device", &cfg_type_qstring, 0 }, { "recursive-clients", &cfg_type_uint32, 0 }, { "reserved-sockets", &cfg_type_uint32, 0 }, { "secroots-file", &cfg_type_qstring, 0 }, { "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE }, { "serial-query-rate", &cfg_type_uint32, 0 }, { "server-id", &cfg_type_serverid, 0 }, { "stacksize", &cfg_type_size, 0 }, { "statistics-file", &cfg_type_qstring, 0 }, { "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI }, { "tcp-clients", &cfg_type_uint32, 0 }, { "tcp-listen-queue", &cfg_type_uint32, 0 }, { "tkey-dhkey", &cfg_type_tkey_dhkey, 0 }, { "tkey-gssapi-credential", &cfg_type_qstring, 0 }, { "tkey-gssapi-keytab", &cfg_type_qstring, 0 }, { "tkey-domain", &cfg_type_qstring, 0 }, { "transfers-per-ns", &cfg_type_uint32, 0 }, { "transfers-in", &cfg_type_uint32, 0 }, { "transfers-out", &cfg_type_uint32, 0 }, { "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "use-ixfr", &cfg_type_boolean, 0 }, { "use-v4-udp-ports", &cfg_type_bracketed_portlist, 0 }, { "use-v6-udp-ports", &cfg_type_bracketed_portlist, 0 }, { "version", &cfg_type_qstringornone, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_namelist = { "namelist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_qstring }; static keyword_type_t exclude_kw = { "exclude", &cfg_type_namelist }; static cfg_type_t cfg_type_optional_exclude = { "optional_exclude", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_list, &exclude_kw }; static keyword_type_t exceptionnames_kw = { "except-from", &cfg_type_namelist }; static cfg_type_t cfg_type_optional_exceptionnames = { "optional_allow", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_list, &exceptionnames_kw }; static cfg_tuplefielddef_t denyaddresses_fields[] = { { "acl", &cfg_type_bracketed_aml, 0 }, { "except-from", &cfg_type_optional_exceptionnames, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_denyaddresses = { "denyaddresses", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, denyaddresses_fields }; static cfg_tuplefielddef_t denyaliases_fields[] = { { "name", &cfg_type_namelist, 0 }, { "except-from", &cfg_type_optional_exceptionnames, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_denyaliases = { "denyaliases", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, denyaliases_fields }; static cfg_type_t cfg_type_algorithmlist = { "algorithmlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring }; static cfg_tuplefielddef_t disablealgorithm_fields[] = { { "name", &cfg_type_astring, 0 }, { "algorithms", &cfg_type_algorithmlist, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_disablealgorithm = { "disablealgorithm", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, disablealgorithm_fields }; static cfg_tuplefielddef_t mustbesecure_fields[] = { { "name", &cfg_type_astring, 0 }, { "value", &cfg_type_boolean, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_mustbesecure = { "mustbesecure", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, mustbesecure_fields }; static const char *masterformat_enums[] = { "text", "raw", NULL }; static cfg_type_t cfg_type_masterformat = { "masterformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &masterformat_enums }; /*% * response-policy { * zone [ policy (given|disabled|passthru| * nxdomain|nodata|cname ) ] * [ recursive-only yes|no ] [ max-policy-ttl number ] ; * } [ recursive-only yes|no ] [ max-policy-ttl number ] ; * [ break-dnssec yes|no ] [ min-ns-dots number ] ; */ static void doc_rpz_policy(cfg_printer_t *pctx, const cfg_type_t *type) { const char * const *p; /* * This is cfg_doc_enum() without the trailing " )". */ cfg_print_chars(pctx, "( ", 2); for (p = type->of; *p != NULL; p++) { cfg_print_cstr(pctx, *p); if (p[1] != NULL) cfg_print_chars(pctx, " | ", 3); } } static void doc_rpz_cname(cfg_printer_t *pctx, const cfg_type_t *type) { cfg_doc_terminal(pctx, type); cfg_print_chars(pctx, " )", 2); } /* * Parse * given|disabled|passthru|nxdomain|nodata|cname */ static isc_result_t cfg_parse_rpz_policy(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj; const cfg_tuplefielddef_t *fields; CHECK(cfg_create_tuple(pctx, type, &obj)); fields = type->of; CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0])); /* * parse cname domain only after "policy cname" */ if (strcasecmp("cname", cfg_obj_asstring(obj->value.tuple[0])) != 0) { CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1])); } else { CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1])); } *ret = obj; return (ISC_R_SUCCESS); cleanup: CLEANUP_OBJ(obj); return (result); } /* * Parse a tuple consisting of any kind of required field followed * by 2 or more optional keyvalues that can be in any order. */ static isc_result_t cfg_parse_kv_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { const cfg_tuplefielddef_t *fields, *f; cfg_obj_t *obj; int fn; isc_result_t result; obj = NULL; CHECK(cfg_create_tuple(pctx, type, &obj)); /* * The zone first field is required and always first. */ fields = type->of; CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0])); for (;;) { CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type != isc_tokentype_string) break; for (fn = 1, f = &fields[1]; ; ++fn, ++f) { if (f->name == NULL) { cfg_parser_error(pctx, 0, "unexpected '%s'", TOKEN_STRING(pctx)); result = ISC_R_UNEXPECTEDTOKEN; goto cleanup; } if (obj->value.tuple[fn] == NULL && strcasecmp(f->name, TOKEN_STRING(pctx)) == 0) break; } CHECK(cfg_gettoken(pctx, 0)); CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[fn])); } for (fn = 1, f = &fields[1]; f->name != NULL; ++fn, ++f) { if (obj->value.tuple[fn] == NULL) CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[fn])); } *ret = obj; return (ISC_R_SUCCESS); cleanup: CLEANUP_OBJ(obj); return (result); } static void cfg_print_kv_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) { unsigned int i; const cfg_tuplefielddef_t *fields, *f; const cfg_obj_t *fieldobj; fields = obj->type->of; for (f = fields, i = 0; f->name != NULL; f++, i++) { fieldobj = obj->value.tuple[i]; if (fieldobj->type->print == cfg_print_void) continue; if (i != 0) { cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, f->name); cfg_print_chars(pctx, " ", 1); } cfg_print_obj(pctx, fieldobj); } } static void cfg_doc_kv_tuple(cfg_printer_t *pctx, const cfg_type_t *type) { const cfg_tuplefielddef_t *fields, *f; fields = type->of; for (f = fields; f->name != NULL; f++) { if (f != fields) { cfg_print_chars(pctx, " [ ", 3); cfg_print_cstr(pctx, f->name); if (f->type->doc != cfg_doc_void) cfg_print_chars(pctx, " ", 1); } cfg_doc_obj(pctx, f->type); if (f != fields) cfg_print_chars(pctx, " ]", 2); } } static keyword_type_t zone_kw = {"zone", &cfg_type_qstring}; static cfg_type_t cfg_type_rpz_zone = { "zone", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_string, &zone_kw }; static const char *rpz_policies[] = { "given", "disabled", "passthru", "no-op", "nxdomain", "nodata", "cname", NULL }; static cfg_type_t cfg_type_rpz_policy_name = { "policy name", cfg_parse_enum, cfg_print_ustring, doc_rpz_policy, &cfg_rep_string, &rpz_policies }; static cfg_type_t cfg_type_rpz_cname = { "quoted_string", cfg_parse_astring, NULL, doc_rpz_cname, &cfg_rep_string, NULL }; static cfg_tuplefielddef_t rpz_policy_fields[] = { { "policy name", &cfg_type_rpz_policy_name, 0 }, { "cname", &cfg_type_rpz_cname, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_rpz_policy = { "policy tuple", cfg_parse_rpz_policy, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, rpz_policy_fields }; static cfg_tuplefielddef_t rpz_zone_fields[] = { { "zone name", &cfg_type_rpz_zone, 0 }, { "policy", &cfg_type_rpz_policy, 0 }, { "recursive-only", &cfg_type_boolean, 0 }, { "max-policy-ttl", &cfg_type_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_rpz_tuple = { "rpz tuple", cfg_parse_kv_tuple, cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple, rpz_zone_fields }; static cfg_type_t cfg_type_rpz_list = { "zone list", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_rpz_tuple }; static cfg_tuplefielddef_t rpz_fields[] = { { "zone list", &cfg_type_rpz_list, 0 }, { "recursive-only", &cfg_type_boolean, 0 }, { "break-dnssec", &cfg_type_boolean, 0 }, { "max-policy-ttl", &cfg_type_uint32, 0 }, { "min-ns-dots", &cfg_type_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_rpz = { "rpz", cfg_parse_kv_tuple, cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple, rpz_fields }; /*% * dnssec-lookaside */ static void print_lookaside(cfg_printer_t *pctx, const cfg_obj_t *obj) { const cfg_obj_t *domain = obj->value.tuple[0]; if (domain->value.string.length == 4 && strncmp(domain->value.string.base, "auto", 4) == 0) cfg_print_cstr(pctx, "auto"); else cfg_print_tuple(pctx, obj); } static void doc_lookaside(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, "( trust-anchor | auto | no )"); } static keyword_type_t trustanchor_kw = { "trust-anchor", &cfg_type_astring }; static cfg_type_t cfg_type_optional_trustanchor = { "optional_trustanchor", parse_optional_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_string, &trustanchor_kw }; static cfg_tuplefielddef_t lookaside_fields[] = { { "domain", &cfg_type_astring, 0 }, { "trust-anchor", &cfg_type_optional_trustanchor, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_lookaside = { "lookaside", cfg_parse_tuple, print_lookaside, doc_lookaside, &cfg_rep_tuple, lookaside_fields }; /* * DNS64. */ static cfg_clausedef_t dns64_clauses[] = { { "clients", &cfg_type_bracketed_aml, 0 }, { "mapped", &cfg_type_bracketed_aml, 0 }, { "exclude", &cfg_type_bracketed_aml, 0 }, { "suffix", &cfg_type_netaddr6, 0 }, { "recursive-only", &cfg_type_boolean, 0 }, { "break-dnssec", &cfg_type_boolean, 0 }, { NULL, NULL, 0 }, }; static cfg_clausedef_t * dns64_clausesets[] = { dns64_clauses, NULL }; static cfg_type_t cfg_type_dns64 = { "dns64", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, dns64_clausesets }; /*% * Clauses that can be found within the 'view' statement, * with defaults in the 'options' statement. */ static cfg_clausedef_t view_clauses[] = { { "acache-cleaning-interval", &cfg_type_uint32, 0 }, { "acache-enable", &cfg_type_boolean, 0 }, { "additional-from-auth", &cfg_type_boolean, 0 }, { "additional-from-cache", &cfg_type_boolean, 0 }, { "allow-new-zones", &cfg_type_boolean, 0 }, { "allow-query-cache", &cfg_type_bracketed_aml, 0 }, { "allow-query-cache-on", &cfg_type_bracketed_aml, 0 }, { "allow-recursion", &cfg_type_bracketed_aml, 0 }, { "allow-recursion-on", &cfg_type_bracketed_aml, 0 }, { "allow-v6-synthesis", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_OBSOLETE }, { "attach-cache", &cfg_type_astring, 0 }, { "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT }, { "cache-file", &cfg_type_qstring, 0 }, { "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI }, { "cleaning-interval", &cfg_type_uint32, 0 }, { "clients-per-query", &cfg_type_uint32, 0 }, { "deny-answer-addresses", &cfg_type_denyaddresses, 0 }, { "deny-answer-aliases", &cfg_type_denyaliases, 0 }, { "disable-algorithms", &cfg_type_disablealgorithm, CFG_CLAUSEFLAG_MULTI }, { "disable-empty-zone", &cfg_type_astring, CFG_CLAUSEFLAG_MULTI }, { "dns64", &cfg_type_dns64, CFG_CLAUSEFLAG_MULTI }, { "dns64-server", &cfg_type_astring, 0 }, { "dns64-contact", &cfg_type_astring, 0 }, { "dnssec-accept-expired", &cfg_type_boolean, 0 }, { "dnssec-enable", &cfg_type_boolean, 0 }, { "dnssec-lookaside", &cfg_type_lookaside, CFG_CLAUSEFLAG_MULTI }, { "dnssec-must-be-secure", &cfg_type_mustbesecure, CFG_CLAUSEFLAG_MULTI }, { "dnssec-validation", &cfg_type_boolorauto, 0 }, { "dual-stack-servers", &cfg_type_nameportiplist, 0 }, { "edns-udp-size", &cfg_type_uint32, 0 }, { "empty-contact", &cfg_type_astring, 0 }, { "empty-server", &cfg_type_astring, 0 }, { "empty-zones-enable", &cfg_type_boolean, 0 }, { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 }, { "lame-ttl", &cfg_type_uint32, 0 }, { "max-acache-size", &cfg_type_sizenodefault, 0 }, { "max-cache-size", &cfg_type_sizenodefault, 0 }, { "max-cache-ttl", &cfg_type_uint32, 0 }, { "max-clients-per-query", &cfg_type_uint32, 0 }, { "max-ncache-ttl", &cfg_type_uint32, 0 }, + { "max-recursion-depth", &cfg_type_uint32, 0 }, + { "max-recursion-queries", &cfg_type_uint32, 0 }, { "max-udp-size", &cfg_type_uint32, 0 }, { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP }, { "minimal-responses", &cfg_type_boolean, 0 }, { "preferred-glue", &cfg_type_astring, 0 }, { "provide-ixfr", &cfg_type_boolean, 0 }, /* * Note that the query-source option syntax is different * from the other -source options. */ { "query-source", &cfg_type_querysource4, 0 }, { "query-source-v6", &cfg_type_querysource6, 0 }, { "queryport-pool-ports", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE }, { "queryport-pool-updateinterval", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE }, { "recursion", &cfg_type_boolean, 0 }, { "request-ixfr", &cfg_type_boolean, 0 }, { "request-nsid", &cfg_type_boolean, 0 }, { "resolver-query-timeout", &cfg_type_uint32, 0 }, { "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI }, { "root-delegation-only", &cfg_type_optional_exclude, 0 }, { "rrset-order", &cfg_type_rrsetorder, 0 }, { "sortlist", &cfg_type_bracketed_aml, 0 }, { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI }, { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP }, { "transfer-format", &cfg_type_transferformat, 0 }, { "use-queryport-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "zero-no-soa-ttl-cache", &cfg_type_boolean, 0 }, #ifdef ALLOW_FILTER_AAAA_ON_V4 { "filter-aaaa", &cfg_type_bracketed_aml, 0 }, { "filter-aaaa-on-v4", &cfg_type_v4_aaaa, 0 }, #else { "filter-aaaa", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTCONFIGURED }, { "filter-aaaa-on-v4", &cfg_type_v4_aaaa, CFG_CLAUSEFLAG_NOTCONFIGURED }, #endif { "response-policy", &cfg_type_rpz, 0 }, { NULL, NULL, 0 } }; /*% * Clauses that can be found within the 'view' statement only. */ static cfg_clausedef_t view_only_clauses[] = { { "match-clients", &cfg_type_bracketed_aml, 0 }, { "match-destinations", &cfg_type_bracketed_aml, 0 }, { "match-recursive-only", &cfg_type_boolean, 0 }, { NULL, NULL, 0 } }; /*% * Sig-validity-interval. */ static isc_result_t parse_optional_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; UNUSED(type); CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER)); if (pctx->token.type == isc_tokentype_number) { CHECK(cfg_parse_obj(pctx, &cfg_type_uint32, ret)); } else { CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret)); } cleanup: return (result); } static void doc_optional_uint32(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, "[ ]"); } static cfg_type_t cfg_type_optional_uint32 = { "optional_uint32", parse_optional_uint32, NULL, doc_optional_uint32, NULL, NULL }; static cfg_tuplefielddef_t validityinterval_fields[] = { { "validity", &cfg_type_uint32, 0 }, { "re-sign", &cfg_type_optional_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_validityinterval = { "validityinterval", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, validityinterval_fields }; /*% * Clauses that can be found in a 'zone' statement, * with defaults in the 'view' or 'options' statement. */ static cfg_clausedef_t zone_clauses[] = { { "allow-notify", &cfg_type_bracketed_aml, 0 }, { "allow-query", &cfg_type_bracketed_aml, 0 }, { "allow-query-on", &cfg_type_bracketed_aml, 0 }, { "allow-transfer", &cfg_type_bracketed_aml, 0 }, { "allow-update", &cfg_type_bracketed_aml, 0 }, { "allow-update-forwarding", &cfg_type_bracketed_aml, 0 }, { "also-notify", &cfg_type_portiplist, 0 }, { "alt-transfer-source", &cfg_type_sockaddr4wild, 0 }, { "alt-transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, { "auto-dnssec", &cfg_type_autodnssec, 0 }, { "check-dup-records", &cfg_type_checkmode, 0 }, { "check-integrity", &cfg_type_boolean, 0 }, { "check-mx", &cfg_type_checkmode, 0 }, { "check-mx-cname", &cfg_type_checkmode, 0 }, { "check-sibling", &cfg_type_boolean, 0 }, { "check-spf", &cfg_type_warn, 0 }, { "check-srv-cname", &cfg_type_checkmode, 0 }, { "check-wildcard", &cfg_type_boolean, 0 }, { "dialup", &cfg_type_dialuptype, 0 }, { "dnssec-dnskey-kskonly", &cfg_type_boolean, 0 }, { "dnssec-secure-to-insecure", &cfg_type_boolean, 0 }, { "forward", &cfg_type_forwardtype, 0 }, { "forwarders", &cfg_type_portiplist, 0 }, { "key-directory", &cfg_type_qstring, 0 }, { "maintain-ixfr-base", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "masterfile-format", &cfg_type_masterformat, 0 }, { "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE }, { "max-journal-size", &cfg_type_sizenodefault, 0 }, { "max-refresh-time", &cfg_type_uint32, 0 }, { "max-retry-time", &cfg_type_uint32, 0 }, { "max-transfer-idle-in", &cfg_type_uint32, 0 }, { "max-transfer-idle-out", &cfg_type_uint32, 0 }, { "max-transfer-time-in", &cfg_type_uint32, 0 }, { "max-transfer-time-out", &cfg_type_uint32, 0 }, { "min-refresh-time", &cfg_type_uint32, 0 }, { "min-retry-time", &cfg_type_uint32, 0 }, { "multi-master", &cfg_type_boolean, 0 }, { "notify", &cfg_type_notifytype, 0 }, { "notify-delay", &cfg_type_uint32, 0 }, { "notify-source", &cfg_type_sockaddr4wild, 0 }, { "notify-source-v6", &cfg_type_sockaddr6wild, 0 }, { "notify-to-soa", &cfg_type_boolean, 0 }, { "nsec3-test-zone", &cfg_type_boolean, CFG_CLAUSEFLAG_TESTONLY }, { "sig-signing-nodes", &cfg_type_uint32, 0 }, { "sig-signing-signatures", &cfg_type_uint32, 0 }, { "sig-signing-type", &cfg_type_uint32, 0 }, { "sig-validity-interval", &cfg_type_validityinterval, 0 }, { "transfer-source", &cfg_type_sockaddr4wild, 0 }, { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, { "try-tcp-refresh", &cfg_type_boolean, 0 }, { "update-check-ksk", &cfg_type_boolean, 0 }, { "use-alt-transfer-source", &cfg_type_boolean, 0 }, { "zero-no-soa-ttl", &cfg_type_boolean, 0 }, { "zone-statistics", &cfg_type_boolean, 0 }, { NULL, NULL, 0 } }; /*% * Clauses that can be found in a 'zone' statement * only. */ static cfg_clausedef_t zone_only_clauses[] = { { "type", &cfg_type_zonetype, 0 }, { "file", &cfg_type_qstring, 0 }, { "journal", &cfg_type_qstring, 0 }, { "ixfr-base", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, { "ixfr-tmp-file", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, { "masters", &cfg_type_namesockaddrkeylist, 0 }, { "pubkey", &cfg_type_pubkey, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE }, { "update-policy", &cfg_type_updatepolicy, 0 }, { "database", &cfg_type_astring, 0 }, { "delegation-only", &cfg_type_boolean, 0 }, /* * Note that the format of the check-names option is different between * the zone options and the global/view options. Ugh. */ { "check-names", &cfg_type_checkmode, 0 }, { "ixfr-from-differences", &cfg_type_boolean, 0 }, { "server-addresses", &cfg_type_bracketed_sockaddrlist, 0 }, { "server-names", &cfg_type_namelist, 0 }, { NULL, NULL, 0 } }; /*% The top-level named.conf syntax. */ static cfg_clausedef_t * namedconf_clausesets[] = { namedconf_clauses, namedconf_or_view_clauses, NULL }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = { "namedconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, namedconf_clausesets }; /*% The bind.keys syntax (trusted-keys/managed-keys only). */ static cfg_clausedef_t * bindkeys_clausesets[] = { bindkeys_clauses, NULL }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_bindkeys = { "bindkeys", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, bindkeys_clausesets }; /*% The new-zone-file syntax (for zones added by 'rndc addzone') */ static cfg_clausedef_t newzones_clauses[] = { { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; static cfg_clausedef_t * newzones_clausesets[] = { newzones_clauses, NULL }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_newzones = { "newzones", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, newzones_clausesets }; /*% The "options" statement syntax. */ static cfg_clausedef_t * options_clausesets[] = { options_clauses, view_clauses, zone_clauses, NULL }; static cfg_type_t cfg_type_options = { "options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, options_clausesets }; /*% The "view" statement syntax. */ static cfg_clausedef_t * view_clausesets[] = { view_only_clauses, namedconf_or_view_clauses, view_clauses, zone_clauses, dynamically_loadable_zones_clauses, NULL }; static cfg_type_t cfg_type_viewopts = { "view", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, view_clausesets }; /*% The "zone" statement syntax. */ static cfg_clausedef_t * zone_clausesets[] = { zone_only_clauses, zone_clauses, NULL }; static cfg_type_t cfg_type_zoneopts = { "zoneopts", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, zone_clausesets }; /*% The "dynamically loadable zones" statement syntax. */ static cfg_clausedef_t * dynamically_loadable_zones_clausesets[] = { dynamically_loadable_zones_clauses, NULL }; static cfg_type_t cfg_type_dynamically_loadable_zones_opts = { "dynamically_loadable_zones_opts", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, dynamically_loadable_zones_clausesets }; /*% * Clauses that can be found within the 'key' statement. */ static cfg_clausedef_t key_clauses[] = { { "algorithm", &cfg_type_astring, 0 }, { "secret", &cfg_type_sstring, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * key_clausesets[] = { key_clauses, NULL }; static cfg_type_t cfg_type_key = { "key", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, key_clausesets }; /*% * Clauses that can be found in a 'server' statement. */ static cfg_clausedef_t server_clauses[] = { { "bogus", &cfg_type_boolean, 0 }, { "provide-ixfr", &cfg_type_boolean, 0 }, { "request-ixfr", &cfg_type_boolean, 0 }, { "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "transfers", &cfg_type_uint32, 0 }, { "transfer-format", &cfg_type_transferformat, 0 }, { "keys", &cfg_type_server_key_kludge, 0 }, { "edns", &cfg_type_boolean, 0 }, { "edns-udp-size", &cfg_type_uint32, 0 }, { "max-udp-size", &cfg_type_uint32, 0 }, { "notify-source", &cfg_type_sockaddr4wild, 0 }, { "notify-source-v6", &cfg_type_sockaddr6wild, 0 }, { "query-source", &cfg_type_querysource4, 0 }, { "query-source-v6", &cfg_type_querysource6, 0 }, { "transfer-source", &cfg_type_sockaddr4wild, 0 }, { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * server_clausesets[] = { server_clauses, NULL }; static cfg_type_t cfg_type_server = { "server", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, server_clausesets }; /*% * Clauses that can be found in a 'channel' clause in the * 'logging' statement. * * These have some additional constraints that need to be * checked after parsing: * - There must exactly one of file/syslog/null/stderr * */ static cfg_clausedef_t channel_clauses[] = { /* Destinations. We no longer require these to be first. */ { "file", &cfg_type_logfile, 0 }, { "syslog", &cfg_type_optional_facility, 0 }, { "null", &cfg_type_void, 0 }, { "stderr", &cfg_type_void, 0 }, /* Options. We now accept these for the null channel, too. */ { "severity", &cfg_type_logseverity, 0 }, { "print-time", &cfg_type_boolean, 0 }, { "print-severity", &cfg_type_boolean, 0 }, { "print-category", &cfg_type_boolean, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * channel_clausesets[] = { channel_clauses, NULL }; static cfg_type_t cfg_type_channel = { "channel", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, channel_clausesets }; /*% A list of log destination, used in the "category" clause. */ static cfg_type_t cfg_type_destinationlist = { "destinationlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring }; /*% * Clauses that can be found in a 'logging' statement. */ static cfg_clausedef_t logging_clauses[] = { { "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI }, { "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; static cfg_clausedef_t * logging_clausesets[] = { logging_clauses, NULL }; static cfg_type_t cfg_type_logging = { "logging", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, logging_clausesets }; /*% * For parsing an 'addzone' statement */ static cfg_tuplefielddef_t addzone_fields[] = { { "name", &cfg_type_astring, 0 }, { "class", &cfg_type_optional_class, 0 }, { "view", &cfg_type_optional_class, 0 }, { "options", &cfg_type_zoneopts, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_addzone = { "addzone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, addzone_fields }; static cfg_clausedef_t addzoneconf_clauses[] = { { "addzone", &cfg_type_addzone, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * addzoneconf_clausesets[] = { addzoneconf_clauses, NULL }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_addzoneconf = { "addzoneconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, addzoneconf_clausesets }; static isc_result_t parse_unitstring(char *str, isc_resourcevalue_t *valuep) { char *endp; unsigned int len; isc_uint64_t value; isc_uint64_t unit; value = isc_string_touint64(str, &endp, 10); if (*endp == 0) { *valuep = value; return (ISC_R_SUCCESS); } len = strlen(str); if (len < 2 || endp[1] != '\0') return (ISC_R_FAILURE); switch (str[len - 1]) { case 'k': case 'K': unit = 1024; break; case 'm': case 'M': unit = 1024 * 1024; break; case 'g': case 'G': unit = 1024 * 1024 * 1024; break; default: return (ISC_R_FAILURE); } if (value > ISC_UINT64_MAX / unit) return (ISC_R_FAILURE); *valuep = value * unit; return (ISC_R_SUCCESS); } static isc_result_t parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; isc_uint64_t val; UNUSED(type); CHECK(cfg_gettoken(pctx, 0)); if (pctx->token.type != isc_tokentype_string) { result = ISC_R_UNEXPECTEDTOKEN; goto cleanup; } CHECK(parse_unitstring(TOKEN_STRING(pctx), &val)); CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj)); obj->value.uint64 = val; *ret = obj; return (ISC_R_SUCCESS); cleanup: cfg_parser_error(pctx, CFG_LOG_NEAR, "expected integer and optional unit"); return (result); } /*% * A size value (number + optional unit). */ static cfg_type_t cfg_type_sizeval = { "sizeval", parse_sizeval, cfg_print_uint64, cfg_doc_terminal, &cfg_rep_uint64, NULL }; /*% * A size, "unlimited", or "default". */ static isc_result_t parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret)); } static const char *size_enums[] = { "unlimited", "default", NULL }; static cfg_type_t cfg_type_size = { "size", parse_size, cfg_print_ustring, cfg_doc_terminal, &cfg_rep_string, size_enums }; /*% * A size or "unlimited", but not "default". */ static const char *sizenodefault_enums[] = { "unlimited", NULL }; static cfg_type_t cfg_type_sizenodefault = { "size_no_default", parse_size, cfg_print_ustring, cfg_doc_terminal, &cfg_rep_string, sizenodefault_enums }; /*% * optional_keyvalue */ static isc_result_t parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, isc_boolean_t optional, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; const keyword_type_t *kw = type->of; CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), kw->name) == 0) { CHECK(cfg_gettoken(pctx, 0)); CHECK(kw->type->parse(pctx, kw->type, &obj)); obj->type = type; /* XXX kludge */ } else { if (optional) { CHECK(cfg_parse_void(pctx, NULL, &obj)); } else { cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '%s'", kw->name); result = ISC_R_UNEXPECTEDTOKEN; goto cleanup; } } *ret = obj; cleanup: return (result); } static isc_result_t parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype, const cfg_type_t *othertype, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string && cfg_is_enum(TOKEN_STRING(pctx), enumtype->of)) { CHECK(cfg_parse_enum(pctx, enumtype, ret)); } else { CHECK(cfg_parse_obj(pctx, othertype, ret)); } cleanup: return (result); } static void doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *type) { cfg_doc_terminal(pctx, type); #if 0 /* XXX */ cfg_print_chars(pctx, "( ", 2);... #endif } static isc_result_t parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_maybe_optional_keyvalue(pctx, type, ISC_FALSE, ret)); } static isc_result_t parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_maybe_optional_keyvalue(pctx, type, ISC_TRUE, ret)); } static void print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj) { const keyword_type_t *kw = obj->type->of; cfg_print_cstr(pctx, kw->name); cfg_print_chars(pctx, " ", 1); kw->type->print(pctx, obj); } static void doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) { const keyword_type_t *kw = type->of; cfg_print_cstr(pctx, kw->name); cfg_print_chars(pctx, " ", 1); cfg_doc_obj(pctx, kw->type); } static void doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) { const keyword_type_t *kw = type->of; cfg_print_chars(pctx, "[ ", 2); cfg_print_cstr(pctx, kw->name); cfg_print_chars(pctx, " ", 1); cfg_doc_obj(pctx, kw->type); cfg_print_chars(pctx, " ]", 2); } static const char *dialup_enums[] = { "notify", "notify-passive", "refresh", "passive", NULL }; static isc_result_t parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); } static cfg_type_t cfg_type_dialuptype = { "dialuptype", parse_dialup_type, cfg_print_ustring, doc_enum_or_other, &cfg_rep_string, dialup_enums }; static const char *notify_enums[] = { "explicit", "master-only", NULL }; static isc_result_t parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); } static cfg_type_t cfg_type_notifytype = { "notifytype", parse_notify_type, cfg_print_ustring, doc_enum_or_other, &cfg_rep_string, notify_enums, }; static const char *ixfrdiff_enums[] = { "master", "slave", NULL }; static isc_result_t parse_ixfrdiff_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); } static cfg_type_t cfg_type_ixfrdifftype = { "ixfrdiff", parse_ixfrdiff_type, cfg_print_ustring, doc_enum_or_other, &cfg_rep_string, ixfrdiff_enums, }; static const char *v4_aaaa_enums[] = { "break-dnssec", NULL }; static isc_result_t parse_v4_aaaa(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); } static cfg_type_t cfg_type_v4_aaaa = { "v4_aaaa", parse_v4_aaaa, cfg_print_ustring, doc_enum_or_other, &cfg_rep_string, v4_aaaa_enums, }; static keyword_type_t key_kw = { "key", &cfg_type_astring }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = { "keyref", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_string, &key_kw }; static cfg_type_t cfg_type_optional_keyref = { "optional_keyref", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &key_kw }; /*% * A "controls" statement is represented as a map with the multivalued * "inet" and "unix" clauses. */ static keyword_type_t controls_allow_kw = { "allow", &cfg_type_bracketed_aml }; static cfg_type_t cfg_type_controls_allow = { "controls_allow", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_list, &controls_allow_kw }; static keyword_type_t controls_keys_kw = { "keys", &cfg_type_keylist }; static cfg_type_t cfg_type_controls_keys = { "controls_keys", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_list, &controls_keys_kw }; static cfg_tuplefielddef_t inetcontrol_fields[] = { { "address", &cfg_type_controls_sockaddr, 0 }, { "allow", &cfg_type_controls_allow, 0 }, { "keys", &cfg_type_controls_keys, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_inetcontrol = { "inetcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, inetcontrol_fields }; static keyword_type_t controls_perm_kw = { "perm", &cfg_type_uint32 }; static cfg_type_t cfg_type_controls_perm = { "controls_perm", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_uint32, &controls_perm_kw }; static keyword_type_t controls_owner_kw = { "owner", &cfg_type_uint32 }; static cfg_type_t cfg_type_controls_owner = { "controls_owner", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_uint32, &controls_owner_kw }; static keyword_type_t controls_group_kw = { "group", &cfg_type_uint32 }; static cfg_type_t cfg_type_controls_group = { "controls_allow", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_uint32, &controls_group_kw }; static cfg_tuplefielddef_t unixcontrol_fields[] = { { "path", &cfg_type_qstring, 0 }, { "perm", &cfg_type_controls_perm, 0 }, { "owner", &cfg_type_controls_owner, 0 }, { "group", &cfg_type_controls_group, 0 }, { "keys", &cfg_type_controls_keys, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_unixcontrol = { "unixcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, unixcontrol_fields }; static cfg_clausedef_t controls_clauses[] = { { "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI }, { "unix", &cfg_type_unixcontrol, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; static cfg_clausedef_t * controls_clausesets[] = { controls_clauses, NULL }; static cfg_type_t cfg_type_controls = { "controls", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, &controls_clausesets }; /*% * A "statistics-channels" statement is represented as a map with the * multivalued "inet" clauses. */ static void doc_optional_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) { const keyword_type_t *kw = type->of; cfg_print_chars(pctx, "[ ", 2); cfg_print_cstr(pctx, kw->name); cfg_print_chars(pctx, " ", 1); cfg_doc_obj(pctx, kw->type); cfg_print_chars(pctx, " ]", 2); } static cfg_type_t cfg_type_optional_allow = { "optional_allow", parse_optional_keyvalue, print_keyvalue, doc_optional_bracketed_list, &cfg_rep_list, &controls_allow_kw }; static cfg_tuplefielddef_t statserver_fields[] = { { "address", &cfg_type_controls_sockaddr, 0 }, /* reuse controls def */ { "allow", &cfg_type_optional_allow, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_statschannel = { "statschannel", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, statserver_fields }; static cfg_clausedef_t statservers_clauses[] = { { "inet", &cfg_type_statschannel, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; static cfg_clausedef_t * statservers_clausesets[] = { statservers_clauses, NULL }; static cfg_type_t cfg_type_statschannels = { "statistics-channels", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, &statservers_clausesets }; /*% * An optional class, as used in view and zone statements. */ static isc_result_t parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; UNUSED(type); CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string) CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, ret)); else CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret)); cleanup: return (result); } static cfg_type_t cfg_type_optional_class = { "optional_class", parse_optional_class, NULL, cfg_doc_terminal, NULL, NULL }; static isc_result_t parse_querysource(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; isc_netaddr_t netaddr; in_port_t port; unsigned int have_address = 0; unsigned int have_port = 0; const unsigned int *flagp = type->of; if ((*flagp & CFG_ADDR_V4OK) != 0) isc_netaddr_any(&netaddr); else if ((*flagp & CFG_ADDR_V6OK) != 0) isc_netaddr_any6(&netaddr); else INSIST(0); port = 0; for (;;) { CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string) { if (strcasecmp(TOKEN_STRING(pctx), "address") == 0) { /* read "address" */ CHECK(cfg_gettoken(pctx, 0)); CHECK(cfg_parse_rawaddr(pctx, *flagp, &netaddr)); have_address++; } else if (strcasecmp(TOKEN_STRING(pctx), "port") == 0) { /* read "port" */ CHECK(cfg_gettoken(pctx, 0)); CHECK(cfg_parse_rawport(pctx, CFG_ADDR_WILDOK, &port)); have_port++; } else if (have_port == 0 && have_address == 0) { return (cfg_parse_sockaddr(pctx, type, ret)); } else { cfg_parser_error(pctx, CFG_LOG_NEAR, "expected 'address' or 'port'"); return (ISC_R_UNEXPECTEDTOKEN); } } else break; } if (have_address > 1 || have_port > 1 || have_address + have_port == 0) { cfg_parser_error(pctx, 0, "expected one address and/or port"); return (ISC_R_UNEXPECTEDTOKEN); } CHECK(cfg_create_obj(pctx, &cfg_type_querysource, &obj)); isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port); *ret = obj; return (ISC_R_SUCCESS); cleanup: cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source"); CLEANUP_OBJ(obj); return (result); } static void print_querysource(cfg_printer_t *pctx, const cfg_obj_t *obj) { isc_netaddr_t na; isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr); cfg_print_cstr(pctx, "address "); cfg_print_rawaddr(pctx, &na); cfg_print_cstr(pctx, " port "); cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr)); } static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK; static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK; static cfg_type_t cfg_type_querysource4 = { "querysource4", parse_querysource, NULL, cfg_doc_terminal, NULL, &sockaddr4wild_flags }; static cfg_type_t cfg_type_querysource6 = { "querysource6", parse_querysource, NULL, cfg_doc_terminal, NULL, &sockaddr6wild_flags }; static cfg_type_t cfg_type_querysource = { "querysource", NULL, print_querysource, NULL, &cfg_rep_sockaddr, NULL }; /*% addrmatchelt */ static isc_result_t parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; UNUSED(type); CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string || pctx->token.type == isc_tokentype_qstring) { if (pctx->token.type == isc_tokentype_string && (strcasecmp(TOKEN_STRING(pctx), "key") == 0)) { CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret)); } else { if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK | CFG_ADDR_V6OK)) { CHECK(cfg_parse_netprefix(pctx, NULL, ret)); } else { CHECK(cfg_parse_astring(pctx, NULL, ret)); } } } else if (pctx->token.type == isc_tokentype_special) { if (pctx->token.value.as_char == '{') { /* Nested match list. */ CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_aml, ret)); } else if (pctx->token.value.as_char == '!') { CHECK(cfg_gettoken(pctx, 0)); /* read "!" */ CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret)); } else { goto bad; } } else { bad: cfg_parser_error(pctx, CFG_LOG_NEAR, "expected IP match list element"); return (ISC_R_UNEXPECTEDTOKEN); } cleanup: return (result); } /*% * A negated address match list element (like "! 10.0.0.1"). * Somewhat sneakily, the caller is expected to parse the * "!", but not to print it. */ static cfg_tuplefielddef_t negated_fields[] = { { "value", &cfg_type_addrmatchelt, 0 }, { NULL, NULL, 0 } }; static void print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) { cfg_print_chars(pctx, "!", 1); cfg_print_tuple(pctx, obj); } static cfg_type_t cfg_type_negated = { "negated", cfg_parse_tuple, print_negated, NULL, &cfg_rep_tuple, &negated_fields }; /*% An address match list element */ static cfg_type_t cfg_type_addrmatchelt = { "address_match_element", parse_addrmatchelt, NULL, cfg_doc_terminal, NULL, NULL }; /*% A bracketed address match list */ static cfg_type_t cfg_type_bracketed_aml = { "bracketed_aml", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_addrmatchelt }; /*% * The socket address syntax in the "controls" statement is silly. * It allows both socket address families, but also allows "*", * whis is gratuitously interpreted as the IPv4 wildcard address. */ static unsigned int controls_sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK | CFG_ADDR_WILDOK; static cfg_type_t cfg_type_controls_sockaddr = { "controls_sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr, &cfg_rep_sockaddr, &controls_sockaddr_flags }; /*% * Handle the special kludge syntax of the "keys" clause in the "server" * statement, which takes a single key with or without braces and semicolon. */ static isc_result_t parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; isc_boolean_t braces = ISC_FALSE; UNUSED(type); /* Allow opening brace. */ CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_special && pctx->token.value.as_char == '{') { CHECK(cfg_gettoken(pctx, 0)); braces = ISC_TRUE; } CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret)); if (braces) { /* Skip semicolon if present. */ CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_special && pctx->token.value.as_char == ';') CHECK(cfg_gettoken(pctx, 0)); CHECK(cfg_parse_special(pctx, '}')); } cleanup: return (result); } static cfg_type_t cfg_type_server_key_kludge = { "server_key", parse_server_key_kludge, NULL, cfg_doc_terminal, NULL, NULL }; /*% * An optional logging facility. */ static isc_result_t parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; UNUSED(type); CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string || pctx->token.type == isc_tokentype_qstring) { CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret)); } else { CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret)); } cleanup: return (result); } static cfg_type_t cfg_type_optional_facility = { "optional_facility", parse_optional_facility, NULL, cfg_doc_terminal, NULL, NULL }; /*% * A log severity. Return as a string, except "debug N", * which is returned as a keyword object. */ static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 }; static cfg_type_t cfg_type_debuglevel = { "debuglevel", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_uint32, &debug_kw }; static isc_result_t parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; UNUSED(type); CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "debug") == 0) { CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */ CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER)); if (pctx->token.type == isc_tokentype_number) { CHECK(cfg_parse_uint32(pctx, NULL, ret)); } else { /* * The debug level is optional and defaults to 1. * This makes little sense, but we support it for * compatibility with BIND 8. */ CHECK(cfg_create_obj(pctx, &cfg_type_uint32, ret)); (*ret)->value.uint32 = 1; } (*ret)->type = &cfg_type_debuglevel; /* XXX kludge */ } else { CHECK(cfg_parse_obj(pctx, &cfg_type_loglevel, ret)); } cleanup: return (result); } static cfg_type_t cfg_type_logseverity = { "log_severity", parse_logseverity, NULL, cfg_doc_terminal, NULL, NULL }; /*% * The "file" clause of the "channel" statement. * This is yet another special case. */ static const char *logversions_enums[] = { "unlimited", NULL }; static isc_result_t parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_uint32, ret)); } static cfg_type_t cfg_type_logversions = { "logversions", parse_logversions, cfg_print_ustring, cfg_doc_terminal, &cfg_rep_string, logversions_enums }; static cfg_tuplefielddef_t logfile_fields[] = { { "file", &cfg_type_qstring, 0 }, { "versions", &cfg_type_logversions, 0 }, { "size", &cfg_type_size, 0 }, { NULL, NULL, 0 } }; static isc_result_t parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; const cfg_tuplefielddef_t *fields = type->of; CHECK(cfg_create_tuple(pctx, type, &obj)); /* Parse the mandatory "file" field */ CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0])); /* Parse "versions" and "size" fields in any order. */ for (;;) { CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string) { CHECK(cfg_gettoken(pctx, 0)); if (strcasecmp(TOKEN_STRING(pctx), "versions") == 0 && obj->value.tuple[1] == NULL) { CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1])); } else if (strcasecmp(TOKEN_STRING(pctx), "size") == 0 && obj->value.tuple[2] == NULL) { CHECK(cfg_parse_obj(pctx, fields[2].type, &obj->value.tuple[2])); } else { break; } } else { break; } } /* Create void objects for missing optional values. */ if (obj->value.tuple[1] == NULL) CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1])); if (obj->value.tuple[2] == NULL) CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2])); *ret = obj; return (ISC_R_SUCCESS); cleanup: CLEANUP_OBJ(obj); return (result); } static void print_logfile(cfg_printer_t *pctx, const cfg_obj_t *obj) { cfg_print_obj(pctx, obj->value.tuple[0]); /* file */ if (obj->value.tuple[1]->type->print != cfg_print_void) { cfg_print_cstr(pctx, " versions "); cfg_print_obj(pctx, obj->value.tuple[1]); } if (obj->value.tuple[2]->type->print != cfg_print_void) { cfg_print_cstr(pctx, " size "); cfg_print_obj(pctx, obj->value.tuple[2]); } } static void doc_logfile(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ versions ( \"unlimited\" | ) ]"); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ size ]"); } static cfg_type_t cfg_type_logfile = { "log_file", parse_logfile, print_logfile, doc_logfile, &cfg_rep_tuple, logfile_fields }; /*% An IPv4 address with optional port, "*" accepted as wildcard. */ static cfg_type_t cfg_type_sockaddr4wild = { "sockaddr4wild", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr4wild_flags }; /*% An IPv6 address with optional port, "*" accepted as wildcard. */ static cfg_type_t cfg_type_sockaddr6wild = { "v6addrportwild", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr6wild_flags }; /*% * lwres */ static cfg_tuplefielddef_t lwres_view_fields[] = { { "name", &cfg_type_astring, 0 }, { "class", &cfg_type_optional_class, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_lwres_view = { "lwres_view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, lwres_view_fields }; static cfg_type_t cfg_type_lwres_searchlist = { "lwres_searchlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring }; static cfg_clausedef_t lwres_clauses[] = { { "listen-on", &cfg_type_portiplist, 0 }, { "view", &cfg_type_lwres_view, 0 }, { "search", &cfg_type_lwres_searchlist, 0 }, { "ndots", &cfg_type_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * lwres_clausesets[] = { lwres_clauses, NULL }; static cfg_type_t cfg_type_lwres = { "lwres", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, lwres_clausesets }; /*% * rndc */ static cfg_clausedef_t rndcconf_options_clauses[] = { { "default-key", &cfg_type_astring, 0 }, { "default-port", &cfg_type_uint32, 0 }, { "default-server", &cfg_type_astring, 0 }, { "default-source-address", &cfg_type_netaddr4wild, 0 }, { "default-source-address-v6", &cfg_type_netaddr6wild, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * rndcconf_options_clausesets[] = { rndcconf_options_clauses, NULL }; static cfg_type_t cfg_type_rndcconf_options = { "rndcconf_options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, rndcconf_options_clausesets }; static cfg_clausedef_t rndcconf_server_clauses[] = { { "key", &cfg_type_astring, 0 }, { "port", &cfg_type_uint32, 0 }, { "source-address", &cfg_type_netaddr4wild, 0 }, { "source-address-v6", &cfg_type_netaddr6wild, 0 }, { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * rndcconf_server_clausesets[] = { rndcconf_server_clauses, NULL }; static cfg_type_t cfg_type_rndcconf_server = { "rndcconf_server", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, rndcconf_server_clausesets }; static cfg_clausedef_t rndcconf_clauses[] = { { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, { "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI }, { "options", &cfg_type_rndcconf_options, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * rndcconf_clausesets[] = { rndcconf_clauses, NULL }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = { "rndcconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, rndcconf_clausesets }; static cfg_clausedef_t rndckey_clauses[] = { { "key", &cfg_type_key, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * rndckey_clausesets[] = { rndckey_clauses, NULL }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = { "rndckey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, rndckey_clausesets }; /* * session.key has exactly the same syntax as rndc.key, but it's defined * separately for clarity (and so we can extend it someday, if needed). */ LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sessionkey = { "sessionkey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, rndckey_clausesets }; static cfg_tuplefielddef_t nameport_fields[] = { { "name", &cfg_type_astring, 0 }, { "port", &cfg_type_optional_port, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_nameport = { "nameport", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, nameport_fields }; static void doc_sockaddrnameport(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_chars(pctx, "( ", 2); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ port ]"); cfg_print_chars(pctx, " | ", 3); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ port ]"); cfg_print_chars(pctx, " | ", 3); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ port ]"); cfg_print_chars(pctx, " )", 2); } static isc_result_t parse_sockaddrnameport(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; UNUSED(type); CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string || pctx->token.type == isc_tokentype_qstring) { if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK)) CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret)); else { const cfg_tuplefielddef_t *fields = cfg_type_nameport.of; CHECK(cfg_create_tuple(pctx, &cfg_type_nameport, &obj)); CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0])); CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1])); *ret = obj; obj = NULL; } } else { cfg_parser_error(pctx, CFG_LOG_NEAR, "expected IP address or hostname"); return (ISC_R_UNEXPECTEDTOKEN); } cleanup: CLEANUP_OBJ(obj); return (result); } static cfg_type_t cfg_type_sockaddrnameport = { "sockaddrnameport_element", parse_sockaddrnameport, NULL, doc_sockaddrnameport, NULL, NULL }; static cfg_type_t cfg_type_bracketed_sockaddrnameportlist = { "bracketed_sockaddrnameportlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_sockaddrnameport }; /*% * A list of socket addresses or name with an optional default port, * as used in the dual-stack-servers option. E.g., * "port 1234 { dual-stack-servers.net; 10.0.0.1; 1::2 port 69; }" */ static cfg_tuplefielddef_t nameportiplist_fields[] = { { "port", &cfg_type_optional_port, 0 }, { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_nameportiplist = { "nameportiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, nameportiplist_fields }; /*% * masters element. */ static void doc_masterselement(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_chars(pctx, "( ", 2); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " | ", 3); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ port ]"); cfg_print_chars(pctx, " | ", 3); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ port ]"); cfg_print_chars(pctx, " )", 2); } static isc_result_t parse_masterselement(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; UNUSED(type); CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string || pctx->token.type == isc_tokentype_qstring) { if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK)) CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret)); else CHECK(cfg_parse_astring(pctx, &cfg_type_astring, ret)); } else { cfg_parser_error(pctx, CFG_LOG_NEAR, "expected IP address or masters name"); return (ISC_R_UNEXPECTEDTOKEN); } cleanup: CLEANUP_OBJ(obj); return (result); } static cfg_type_t cfg_type_masterselement = { "masters_element", parse_masterselement, NULL, doc_masterselement, NULL, NULL }; Index: stable/8/contrib/file/elfclass.h =================================================================== --- stable/8/contrib/file/elfclass.h (revision 275668) +++ stable/8/contrib/file/elfclass.h (revision 275669) @@ -1,69 +1,77 @@ /* * Copyright (c) Christos Zoulas 2008. * 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 immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ if (nbytes <= sizeof(elfhdr)) return 0; u.l = 1; (void)memcpy(&elfhdr, buf, sizeof elfhdr); swap = (u.c[sizeof(int32_t) - 1] + 1) != elfhdr.e_ident[EI_DATA]; type = elf_getu16(swap, elfhdr.e_type); switch (type) { #ifdef ELFCORE case ET_CORE: + phnum = elf_getu16(swap, elfhdr.e_phnum); + if (phnum > MAX_PHNUM) + return toomany(ms, "program", phnum); if (dophn_core(ms, clazz, swap, fd, - (off_t)elf_getu(swap, elfhdr.e_phoff), - elf_getu16(swap, elfhdr.e_phnum), + (off_t)elf_getu(swap, elfhdr.e_phoff), phnum, (size_t)elf_getu16(swap, elfhdr.e_phentsize), fsize, &flags) == -1) return -1; break; #endif case ET_EXEC: case ET_DYN: + phnum = elf_getu16(swap, elfhdr.e_phnum); + if (phnum > MAX_PHNUM) + return toomany(ms, "program", phnum); + shnum = elf_getu16(swap, elfhdr.e_shnum); + if (shnum > MAX_SHNUM) + return toomany(ms, "section", shnum); if (dophn_exec(ms, clazz, swap, fd, - (off_t)elf_getu(swap, elfhdr.e_phoff), - elf_getu16(swap, elfhdr.e_phnum), + (off_t)elf_getu(swap, elfhdr.e_phoff), phnum, (size_t)elf_getu16(swap, elfhdr.e_phentsize), - fsize, &flags, elf_getu16(swap, elfhdr.e_shnum)) - == -1) + fsize, &flags, shnum) == -1) return -1; /*FALLTHROUGH*/ case ET_REL: + shnum = elf_getu16(swap, elfhdr.e_shnum); + if (shnum > MAX_SHNUM) + return toomany(ms, "section", shnum); if (doshn(ms, clazz, swap, fd, - (off_t)elf_getu(swap, elfhdr.e_shoff), - elf_getu16(swap, elfhdr.e_shnum), + (off_t)elf_getu(swap, elfhdr.e_shoff), shnum, (size_t)elf_getu16(swap, elfhdr.e_shentsize), &flags, elf_getu16(swap, elfhdr.e_machine)) == -1) return -1; break; default: break; } return 1; Index: stable/8/contrib/file/readelf.c =================================================================== --- stable/8/contrib/file/readelf.c (revision 275668) +++ stable/8/contrib/file/readelf.c (revision 275669) @@ -1,1192 +1,1209 @@ /* * Copyright (c) Christos Zoulas 2003. * 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 immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "file.h" #ifndef lint FILE_RCSID("@(#)$File: readelf.c,v 1.81 2008/11/04 16:38:28 christos Exp $") #endif #ifdef BUILTIN_ELF #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "readelf.h" #include "magic.h" #ifdef ELFCORE private int dophn_core(struct magic_set *, int, int, int, off_t, int, size_t, off_t, int *); #endif private int dophn_exec(struct magic_set *, int, int, int, off_t, int, size_t, off_t, int *, int); private int doshn(struct magic_set *, int, int, int, off_t, int, size_t, int *, int); private size_t donote(struct magic_set *, void *, size_t, size_t, int, int, size_t, int *); #define ELF_ALIGN(a) ((((a) + align - 1) / align) * align) #define isquote(c) (strchr("'\"`", (c)) != NULL) private uint16_t getu16(int, uint16_t); private uint32_t getu32(int, uint32_t); private uint64_t getu64(int, uint64_t); +#define MAX_PHNUM 256 +#define MAX_SHNUM 1024 + +private int +toomany(struct magic_set *ms, const char *name, uint16_t num) +{ + if (file_printf(ms, ", too many %s header sections (%u)", name, num + ) == -1) + return -1; + return 0; +} + private uint16_t getu16(int swap, uint16_t value) { union { uint16_t ui; char c[2]; } retval, tmpval; if (swap) { tmpval.ui = value; retval.c[0] = tmpval.c[1]; retval.c[1] = tmpval.c[0]; return retval.ui; } else return value; } private uint32_t getu32(int swap, uint32_t value) { union { uint32_t ui; char c[4]; } retval, tmpval; if (swap) { tmpval.ui = value; retval.c[0] = tmpval.c[3]; retval.c[1] = tmpval.c[2]; retval.c[2] = tmpval.c[1]; retval.c[3] = tmpval.c[0]; return retval.ui; } else return value; } private uint64_t getu64(int swap, uint64_t value) { union { uint64_t ui; char c[8]; } retval, tmpval; if (swap) { tmpval.ui = value; retval.c[0] = tmpval.c[7]; retval.c[1] = tmpval.c[6]; retval.c[2] = tmpval.c[5]; retval.c[3] = tmpval.c[4]; retval.c[4] = tmpval.c[3]; retval.c[5] = tmpval.c[2]; retval.c[6] = tmpval.c[1]; retval.c[7] = tmpval.c[0]; return retval.ui; } else return value; } #define elf_getu16(swap, value) getu16(swap, value) #define elf_getu32(swap, value) getu32(swap, value) #ifdef USE_ARRAY_FOR_64BIT_TYPES # define elf_getu64(swap, array) \ ((swap ? ((uint64_t)elf_getu32(swap, array[0])) << 32 : elf_getu32(swap, array[0])) + \ (swap ? elf_getu32(swap, array[1]) : ((uint64_t)elf_getu32(swap, array[1]) << 32))) #else # define elf_getu64(swap, value) getu64(swap, value) #endif #define xsh_addr (clazz == ELFCLASS32 \ ? (void *) &sh32 \ : (void *) &sh64) #define xsh_sizeof (clazz == ELFCLASS32 \ ? sizeof sh32 \ : sizeof sh64) #define xsh_size (clazz == ELFCLASS32 \ ? elf_getu32(swap, sh32.sh_size) \ : elf_getu64(swap, sh64.sh_size)) #define xsh_offset (clazz == ELFCLASS32 \ ? elf_getu32(swap, sh32.sh_offset) \ : elf_getu64(swap, sh64.sh_offset)) #define xsh_type (clazz == ELFCLASS32 \ ? elf_getu32(swap, sh32.sh_type) \ : elf_getu32(swap, sh64.sh_type)) #define xph_addr (clazz == ELFCLASS32 \ ? (void *) &ph32 \ : (void *) &ph64) #define xph_sizeof (clazz == ELFCLASS32 \ ? sizeof ph32 \ : sizeof ph64) #define xph_type (clazz == ELFCLASS32 \ ? elf_getu32(swap, ph32.p_type) \ : elf_getu32(swap, ph64.p_type)) #define xph_offset (off_t)(clazz == ELFCLASS32 \ ? elf_getu32(swap, ph32.p_offset) \ : elf_getu64(swap, ph64.p_offset)) #define xph_align (size_t)((clazz == ELFCLASS32 \ ? (off_t) (ph32.p_align ? \ elf_getu32(swap, ph32.p_align) : 4) \ : (off_t) (ph64.p_align ? \ elf_getu64(swap, ph64.p_align) : 4))) #define xph_filesz (size_t)((clazz == ELFCLASS32 \ ? elf_getu32(swap, ph32.p_filesz) \ : elf_getu64(swap, ph64.p_filesz))) #define xnh_addr (clazz == ELFCLASS32 \ ? (void *) &nh32 \ : (void *) &nh64) #define xph_memsz (size_t)((clazz == ELFCLASS32 \ ? elf_getu32(swap, ph32.p_memsz) \ : elf_getu64(swap, ph64.p_memsz))) #define xnh_sizeof (clazz == ELFCLASS32 \ ? sizeof nh32 \ : sizeof nh64) #define xnh_type (clazz == ELFCLASS32 \ ? elf_getu32(swap, nh32.n_type) \ : elf_getu32(swap, nh64.n_type)) #define xnh_namesz (clazz == ELFCLASS32 \ ? elf_getu32(swap, nh32.n_namesz) \ : elf_getu32(swap, nh64.n_namesz)) #define xnh_descsz (clazz == ELFCLASS32 \ ? elf_getu32(swap, nh32.n_descsz) \ : elf_getu32(swap, nh64.n_descsz)) #define prpsoffsets(i) (clazz == ELFCLASS32 \ ? prpsoffsets32[i] \ : prpsoffsets64[i]) #define xcap_addr (clazz == ELFCLASS32 \ ? (void *) &cap32 \ : (void *) &cap64) #define xcap_sizeof (clazz == ELFCLASS32 \ ? sizeof cap32 \ : sizeof cap64) #define xcap_tag (clazz == ELFCLASS32 \ ? elf_getu32(swap, cap32.c_tag) \ : elf_getu64(swap, cap64.c_tag)) #define xcap_val (clazz == ELFCLASS32 \ ? elf_getu32(swap, cap32.c_un.c_val) \ : elf_getu64(swap, cap64.c_un.c_val)) #ifdef ELFCORE /* * Try larger offsets first to avoid false matches * from earlier data that happen to look like strings. */ static const size_t prpsoffsets32[] = { #ifdef USE_NT_PSINFO 104, /* SunOS 5.x (command line) */ 88, /* SunOS 5.x (short name) */ #endif /* USE_NT_PSINFO */ 100, /* SunOS 5.x (command line) */ 84, /* SunOS 5.x (short name) */ 44, /* Linux (command line) */ 28, /* Linux 2.0.36 (short name) */ 8, /* FreeBSD */ }; static const size_t prpsoffsets64[] = { #ifdef USE_NT_PSINFO 152, /* SunOS 5.x (command line) */ 136, /* SunOS 5.x (short name) */ #endif /* USE_NT_PSINFO */ 136, /* SunOS 5.x, 64-bit (command line) */ 120, /* SunOS 5.x, 64-bit (short name) */ 56, /* Linux (command line) */ 40, /* Linux (tested on core from 2.4.x, short name) */ 16, /* FreeBSD, 64-bit */ }; #define NOFFSETS32 (sizeof prpsoffsets32 / sizeof prpsoffsets32[0]) #define NOFFSETS64 (sizeof prpsoffsets64 / sizeof prpsoffsets64[0]) #define NOFFSETS (clazz == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64) /* * Look through the program headers of an executable image, searching * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or * "FreeBSD"; if one is found, try looking in various places in its * contents for a 16-character string containing only printable * characters - if found, that string should be the name of the program * that dropped core. Note: right after that 16-character string is, * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and * Linux, a longer string (80 characters, in 5.x, probably other * SVR4-flavored systems, and Linux) containing the start of the * command line for that program. * * SunOS 5.x core files contain two PT_NOTE sections, with the types * NT_PRPSINFO (old) and NT_PSINFO (new). These structs contain the * same info about the command name and command line, so it probably * isn't worthwhile to look for NT_PSINFO, but the offsets are provided * above (see USE_NT_PSINFO), in case we ever decide to do so. The * NT_PRPSINFO and NT_PSINFO sections are always in order and adjacent; * the SunOS 5.x file command relies on this (and prefers the latter). * * The signal number probably appears in a section of type NT_PRSTATUS, * but that's also rather OS-dependent, in ways that are harder to * dissect with heuristics, so I'm not bothering with the signal number. * (I suppose the signal number could be of interest in situations where * you don't have the binary of the program that dropped core; if you * *do* have that binary, the debugger will probably tell you what * signal it was.) */ #define OS_STYLE_SVR4 0 #define OS_STYLE_FREEBSD 1 #define OS_STYLE_NETBSD 2 private const char os_style_names[][8] = { "SVR4", "FreeBSD", "NetBSD", }; #define FLAGS_DID_CORE 1 #define FLAGS_DID_NOTE 2 #define FLAGS_DID_CORE_STYLE 4 private int dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, size_t size, off_t fsize, int *flags) { Elf32_Phdr ph32; Elf64_Phdr ph64; size_t offset; unsigned char nbuf[BUFSIZ]; ssize_t bufsize; off_t savedoffset; struct stat st; if (fstat(fd, &st) < 0) { file_badread(ms); return -1; } if (size != xph_sizeof) { if (file_printf(ms, ", corrupted program header size") == -1) return -1; return 0; } /* * Loop through all the program headers. */ for ( ; num; num--) { if ((savedoffset = lseek(fd, off, SEEK_SET)) == (off_t)-1) { file_badseek(ms); return -1; } if (read(fd, xph_addr, xph_sizeof) == -1) { file_badread(ms); return -1; } if (xph_offset > fsize) { if (lseek(fd, savedoffset, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } continue; } off += size; if (xph_type != PT_NOTE) continue; /* * This is a PT_NOTE section; loop through all the notes * in the section. */ if (lseek(fd, xph_offset, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } bufsize = read(fd, nbuf, ((xph_filesz < sizeof(nbuf)) ? xph_filesz : sizeof(nbuf))); if (bufsize == -1) { file_badread(ms); return -1; } offset = 0; for (;;) { if (offset >= (size_t)bufsize) break; offset = donote(ms, nbuf, offset, (size_t)bufsize, clazz, swap, 4, flags); if (offset == 0) break; } } return 0; } #endif private size_t donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size, int clazz, int swap, size_t align, int *flags) { Elf32_Nhdr nh32; Elf64_Nhdr nh64; size_t noff, doff; #ifdef ELFCORE int os_style = -1; #endif uint32_t namesz, descsz; unsigned char *nbuf = CAST(unsigned char *, vbuf); (void)memcpy(xnh_addr, &nbuf[offset], xnh_sizeof); offset += xnh_sizeof; namesz = xnh_namesz; descsz = xnh_descsz; if ((namesz == 0) && (descsz == 0)) { /* * We're out of note headers. */ return (offset >= size) ? offset : size; } if (namesz & 0x80000000) { (void)file_printf(ms, ", bad note name size 0x%lx", (unsigned long)namesz); - return offset; + return 0; } if (descsz & 0x80000000) { (void)file_printf(ms, ", bad note description size 0x%lx", (unsigned long)descsz); - return offset; + return 0; } noff = offset; doff = ELF_ALIGN(offset + namesz); if (offset + namesz > size) { /* * We're past the end of the buffer. */ return doff; } offset = ELF_ALIGN(doff + descsz); if (doff + descsz > size) { /* * We're past the end of the buffer. */ return (offset >= size) ? offset : size; } if (*flags & FLAGS_DID_NOTE) goto core; if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 && xnh_type == NT_GNU_VERSION && descsz == 16) { uint32_t desc[4]; (void)memcpy(desc, &nbuf[doff], sizeof(desc)); if (file_printf(ms, ", for GNU/") == -1) return size; switch (elf_getu32(swap, desc[0])) { case GNU_OS_LINUX: if (file_printf(ms, "Linux") == -1) return size; break; case GNU_OS_HURD: if (file_printf(ms, "Hurd") == -1) return size; break; case GNU_OS_SOLARIS: if (file_printf(ms, "Solaris") == -1) return size; break; case GNU_OS_KFREEBSD: if (file_printf(ms, "kFreeBSD") == -1) return size; break; case GNU_OS_KNETBSD: if (file_printf(ms, "kNetBSD") == -1) return size; break; default: if (file_printf(ms, "") == -1) return size; } if (file_printf(ms, " %d.%d.%d", elf_getu32(swap, desc[1]), elf_getu32(swap, desc[2]), elf_getu32(swap, desc[3])) == -1) return size; *flags |= FLAGS_DID_NOTE; return size; } if (namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0 && xnh_type == NT_NETBSD_VERSION && descsz == 4) { uint32_t desc; (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); desc = elf_getu32(swap, desc); if (file_printf(ms, ", for NetBSD") == -1) return size; /* * The version number used to be stuck as 199905, and was thus * basically content-free. Newer versions of NetBSD have fixed * this and now use the encoding of __NetBSD_Version__: * * MMmmrrpp00 * * M = major version * m = minor version * r = release ["",A-Z,Z[A-Z] but numeric] * p = patchlevel */ if (desc > 100000000U) { uint32_t ver_patch = (desc / 100) % 100; uint32_t ver_rel = (desc / 10000) % 100; uint32_t ver_min = (desc / 1000000) % 100; uint32_t ver_maj = desc / 100000000; if (file_printf(ms, " %u.%u", ver_maj, ver_min) == -1) return size; if (ver_rel == 0 && ver_patch != 0) { if (file_printf(ms, ".%u", ver_patch) == -1) return size; } else if (ver_rel != 0) { while (ver_rel > 26) { if (file_printf(ms, "Z") == -1) return size; ver_rel -= 26; } if (file_printf(ms, "%c", 'A' + ver_rel - 1) == -1) return size; } } *flags |= FLAGS_DID_NOTE; return size; } if (namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0 && xnh_type == NT_FREEBSD_VERSION && descsz == 4) { uint32_t desc; (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); desc = elf_getu32(swap, desc); if (file_printf(ms, ", for FreeBSD") == -1) return size; /* * Contents is __FreeBSD_version, whose relation to OS * versions is defined by a huge table in the Porter's * Handbook. This is the general scheme: * * Releases: * Mmp000 (before 4.10) * Mmi0p0 (before 5.0) * Mmm0p0 * * Development branches: * Mmpxxx (before 4.6) * Mmp1xx (before 4.10) * Mmi1xx (before 5.0) * M000xx (pre-M.0) * Mmm1xx * * M = major version * m = minor version * i = minor version increment (491000 -> 4.10) * p = patchlevel * x = revision * * The first release of FreeBSD to use ELF by default * was version 3.0. */ if (desc == 460002) { if (file_printf(ms, " 4.6.2") == -1) return size; } else if (desc < 460100) { if (file_printf(ms, " %d.%d", desc / 100000, desc / 10000 % 10) == -1) return size; if (desc / 1000 % 10 > 0) if (file_printf(ms, ".%d", desc / 1000 % 10) == -1) return size; if ((desc % 1000 > 0) || (desc % 100000 == 0)) if (file_printf(ms, " (%d)", desc) == -1) return size; } else if (desc < 500000) { if (file_printf(ms, " %d.%d", desc / 100000, desc / 10000 % 10 + desc / 1000 % 10) == -1) return size; if (desc / 100 % 10 > 0) { if (file_printf(ms, " (%d)", desc) == -1) return size; } else if (desc / 10 % 10 > 0) { if (file_printf(ms, ".%d", desc / 10 % 10) == -1) return size; } } else { if (file_printf(ms, " %d.%d", desc / 100000, desc / 1000 % 100) == -1) return size; if ((desc / 100 % 10 > 0) || (desc % 100000 / 100 == 0)) { if (file_printf(ms, " (%d)", desc) == -1) return size; } else if (desc / 10 % 10 > 0) { if (file_printf(ms, ".%d", desc / 10 % 10) == -1) return size; } } *flags |= FLAGS_DID_NOTE; return size; } if (namesz == 8 && strcmp((char *)&nbuf[noff], "OpenBSD") == 0 && xnh_type == NT_OPENBSD_VERSION && descsz == 4) { if (file_printf(ms, ", for OpenBSD") == -1) return size; /* Content of note is always 0 */ *flags |= FLAGS_DID_NOTE; return size; } if (namesz == 10 && strcmp((char *)&nbuf[noff], "DragonFly") == 0 && xnh_type == NT_DRAGONFLY_VERSION && descsz == 4) { uint32_t desc; if (file_printf(ms, ", for DragonFly") == -1) return size; (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); desc = elf_getu32(swap, desc); if (file_printf(ms, " %d.%d.%d", desc / 100000, desc / 10000 % 10, desc % 10000) == -1) return size; *flags |= FLAGS_DID_NOTE; return size; } core: /* * Sigh. The 2.0.36 kernel in Debian 2.1, at * least, doesn't correctly implement name * sections, in core dumps, as specified by * the "Program Linking" section of "UNIX(R) System * V Release 4 Programmer's Guide: ANSI C and * Programming Support Tools", because my copy * clearly says "The first 'namesz' bytes in 'name' * contain a *null-terminated* [emphasis mine] * character representation of the entry's owner * or originator", but the 2.0.36 kernel code * doesn't include the terminating null in the * name.... */ if ((namesz == 4 && strncmp((char *)&nbuf[noff], "CORE", 4) == 0) || (namesz == 5 && strcmp((char *)&nbuf[noff], "CORE") == 0)) { os_style = OS_STYLE_SVR4; } if ((namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0)) { os_style = OS_STYLE_FREEBSD; } if ((namesz >= 11 && strncmp((char *)&nbuf[noff], "NetBSD-CORE", 11) == 0)) { os_style = OS_STYLE_NETBSD; } #ifdef ELFCORE if ((*flags & FLAGS_DID_CORE) != 0) return size; if (os_style != -1 && (*flags & FLAGS_DID_CORE_STYLE) == 0) { if (file_printf(ms, ", %s-style", os_style_names[os_style]) == -1) return size; *flags |= FLAGS_DID_CORE_STYLE; } switch (os_style) { case OS_STYLE_NETBSD: if (xnh_type == NT_NETBSD_CORE_PROCINFO) { uint32_t signo; /* * Extract the program name. It is at * offset 0x7c, and is up to 32-bytes, * including the terminating NUL. */ if (file_printf(ms, ", from '%.31s'", &nbuf[doff + 0x7c]) == -1) return size; /* * Extract the signal number. It is at * offset 0x08. */ (void)memcpy(&signo, &nbuf[doff + 0x08], sizeof(signo)); if (file_printf(ms, " (signal %u)", elf_getu32(swap, signo)) == -1) return size; *flags |= FLAGS_DID_CORE; return size; } break; default: if (xnh_type == NT_PRPSINFO) { size_t i, j; unsigned char c; /* * Extract the program name. We assume * it to be 16 characters (that's what it * is in SunOS 5.x and Linux). * * Unfortunately, it's at a different offset * in various OSes, so try multiple offsets. * If the characters aren't all printable, * reject it. */ for (i = 0; i < NOFFSETS; i++) { unsigned char *cname, *cp; size_t reloffset = prpsoffsets(i); size_t noffset = doff + reloffset; for (j = 0; j < 16; j++, noffset++, reloffset++) { /* * Make sure we're not past * the end of the buffer; if * we are, just give up. */ if (noffset >= size) goto tryanother; /* * Make sure we're not past * the end of the contents; * if we are, this obviously * isn't the right offset. */ if (reloffset >= descsz) goto tryanother; c = nbuf[noffset]; if (c == '\0') { /* * A '\0' at the * beginning is * obviously wrong. * Any other '\0' * means we're done. */ if (j == 0) goto tryanother; else break; } else { /* * A nonprintable * character is also * wrong. */ if (!isprint(c) || isquote(c)) goto tryanother; } } /* * Well, that worked. */ cname = (unsigned char *) &nbuf[doff + prpsoffsets(i)]; for (cp = cname; *cp && isprint(*cp); cp++) continue; /* * Linux apparently appends a space at the end * of the command line: remove it. */ while (cp > cname && isspace(cp[-1])) cp--; if (file_printf(ms, ", from '%.*s'", (int)(cp - cname), cname) == -1) return size; *flags |= FLAGS_DID_CORE; return size; tryanother: ; } } break; } #endif return offset; } /* SunOS 5.x hardware capability descriptions */ typedef struct cap_desc { uint64_t cd_mask; const char *cd_name; } cap_desc_t; static const cap_desc_t cap_desc_sparc[] = { { AV_SPARC_MUL32, "MUL32" }, { AV_SPARC_DIV32, "DIV32" }, { AV_SPARC_FSMULD, "FSMULD" }, { AV_SPARC_V8PLUS, "V8PLUS" }, { AV_SPARC_POPC, "POPC" }, { AV_SPARC_VIS, "VIS" }, { AV_SPARC_VIS2, "VIS2" }, { AV_SPARC_ASI_BLK_INIT, "ASI_BLK_INIT" }, { AV_SPARC_FMAF, "FMAF" }, { AV_SPARC_FJFMAU, "FJFMAU" }, { AV_SPARC_IMA, "IMA" }, { 0, NULL } }; static const cap_desc_t cap_desc_386[] = { { AV_386_FPU, "FPU" }, { AV_386_TSC, "TSC" }, { AV_386_CX8, "CX8" }, { AV_386_SEP, "SEP" }, { AV_386_AMD_SYSC, "AMD_SYSC" }, { AV_386_CMOV, "CMOV" }, { AV_386_MMX, "MMX" }, { AV_386_AMD_MMX, "AMD_MMX" }, { AV_386_AMD_3DNow, "AMD_3DNow" }, { AV_386_AMD_3DNowx, "AMD_3DNowx" }, { AV_386_FXSR, "FXSR" }, { AV_386_SSE, "SSE" }, { AV_386_SSE2, "SSE2" }, { AV_386_PAUSE, "PAUSE" }, { AV_386_SSE3, "SSE3" }, { AV_386_MON, "MON" }, { AV_386_CX16, "CX16" }, { AV_386_AHF, "AHF" }, { AV_386_TSCP, "TSCP" }, { AV_386_AMD_SSE4A, "AMD_SSE4A" }, { AV_386_POPCNT, "POPCNT" }, { AV_386_AMD_LZCNT, "AMD_LZCNT" }, { AV_386_SSSE3, "SSSE3" }, { AV_386_SSE4_1, "SSE4.1" }, { AV_386_SSE4_2, "SSE4.2" }, { 0, NULL } }; private int doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, size_t size, int *flags, int mach) { Elf32_Shdr sh32; Elf64_Shdr sh64; int stripped = 1; + size_t nbadcap = 0; void *nbuf; off_t noff; uint64_t cap_hw1 = 0; /* SunOS 5.x hardware capabilites */ uint64_t cap_sf1 = 0; /* SunOS 5.x software capabilites */ if (size != xsh_sizeof) { if (file_printf(ms, ", corrupted section header size") == -1) return -1; return 0; } if (lseek(fd, off, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } for ( ; num; num--) { if (read(fd, xsh_addr, xsh_sizeof) == -1) { file_badread(ms); return -1; } switch (xsh_type) { case SHT_SYMTAB: #if 0 case SHT_DYNSYM: #endif stripped = 0; break; case SHT_NOTE: if ((off = lseek(fd, (off_t)0, SEEK_CUR)) == (off_t)-1) { file_badread(ms); return -1; } if ((nbuf = malloc((size_t)xsh_size)) == NULL) { file_error(ms, errno, "Cannot allocate memory" " for note"); return -1; } if ((noff = lseek(fd, (off_t)xsh_offset, SEEK_SET)) == (off_t)-1) { file_badread(ms); free(nbuf); return -1; } if (read(fd, nbuf, (size_t)xsh_size) != (ssize_t)xsh_size) { free(nbuf); file_badread(ms); return -1; } noff = 0; for (;;) { if (noff >= (off_t)xsh_size) break; noff = donote(ms, nbuf, (size_t)noff, (size_t)xsh_size, clazz, swap, 4, flags); if (noff == 0) break; } if ((lseek(fd, off, SEEK_SET)) == (off_t)-1) { free(nbuf); file_badread(ms); return -1; } free(nbuf); break; case SHT_SUNW_cap: { off_t coff; + if (nbadcap > 5) + break; if ((off = lseek(fd, (off_t)0, SEEK_CUR)) == (off_t)-1) { file_badread(ms); return -1; } if (lseek(fd, (off_t)xsh_offset, SEEK_SET) == (off_t)-1) { file_badread(ms); return -1; } coff = 0; for (;;) { Elf32_Cap cap32; Elf64_Cap cap64; char cbuf[/*CONSTCOND*/ MAX(sizeof cap32, sizeof cap64)]; if ((coff += xcap_sizeof) >= (off_t)xsh_size) break; if (read(fd, cbuf, (size_t)xcap_sizeof) != (ssize_t)xcap_sizeof) { file_badread(ms); return -1; } (void)memcpy(xcap_addr, cbuf, xcap_sizeof); switch (xcap_tag) { case CA_SUNW_NULL: break; case CA_SUNW_HW_1: cap_hw1 |= xcap_val; break; case CA_SUNW_SF_1: cap_sf1 |= xcap_val; break; default: if (file_printf(ms, ", with unknown capability " "0x%llx = 0x%llx", (unsigned long long)xcap_tag, (unsigned long long)xcap_val) == -1) return -1; + if (nbadcap++ > 2) + coff = xsh_size; break; } } if (lseek(fd, off, SEEK_SET) == (off_t)-1) { file_badread(ms); return -1; } break; } } } if (file_printf(ms, ", %sstripped", stripped ? "" : "not ") == -1) return -1; if (cap_hw1) { const cap_desc_t *cdp; switch (mach) { case EM_SPARC: case EM_SPARC32PLUS: case EM_SPARCV9: cdp = cap_desc_sparc; break; case EM_386: case EM_IA_64: case EM_AMD64: cdp = cap_desc_386; break; default: cdp = NULL; break; } if (file_printf(ms, ", uses") == -1) return -1; if (cdp) { while (cdp->cd_name) { if (cap_hw1 & cdp->cd_mask) { if (file_printf(ms, " %s", cdp->cd_name) == -1) return -1; cap_hw1 &= ~cdp->cd_mask; } ++cdp; } if (cap_hw1) if (file_printf(ms, " unknown hardware capability 0x%llx", (unsigned long long)cap_hw1) == -1) return -1; } else { if (file_printf(ms, " hardware capability 0x%llx", (unsigned long long)cap_hw1) == -1) return -1; } } if (cap_sf1) { if (cap_sf1 & SF1_SUNW_FPUSED) { if (file_printf(ms, (cap_sf1 & SF1_SUNW_FPKNWN) ? ", uses frame pointer" : ", not known to use frame pointer") == -1) return -1; } cap_sf1 &= ~SF1_SUNW_MASK; if (cap_sf1) if (file_printf(ms, ", with unknown software capability 0x%llx", (unsigned long long)cap_sf1) == -1) return -1; } return 0; } /* * Look through the program headers of an executable image, searching * for a PT_INTERP section; if one is found, it's dynamically linked, * otherwise it's statically linked. */ private int dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, size_t size, off_t fsize, int *flags, int sh_num) { Elf32_Phdr ph32; Elf64_Phdr ph64; const char *linking_style = "statically"; const char *shared_libraries = ""; unsigned char nbuf[BUFSIZ]; int bufsize; size_t offset, align; off_t savedoffset = (off_t)-1; struct stat st; if (fstat(fd, &st) < 0) { file_badread(ms); return -1; } if (size != xph_sizeof) { if (file_printf(ms, ", corrupted program header size") == -1) return -1; return 0; } if (lseek(fd, off, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } for ( ; num; num--) { if (read(fd, xph_addr, xph_sizeof) == -1) { file_badread(ms); return -1; } if (xph_offset > st.st_size && savedoffset != (off_t)-1) { if (lseek(fd, savedoffset, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } continue; } if ((savedoffset = lseek(fd, (off_t)0, SEEK_CUR)) == (off_t)-1) { file_badseek(ms); return -1; } if (xph_offset > fsize) { if (lseek(fd, savedoffset, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } continue; } switch (xph_type) { case PT_DYNAMIC: linking_style = "dynamically"; break; case PT_INTERP: shared_libraries = " (uses shared libs)"; break; case PT_NOTE: if ((align = xph_align) & 0x80000000) { if (file_printf(ms, ", invalid note alignment 0x%lx", (unsigned long)align) == -1) return -1; align = 4; } if (sh_num) break; /* * This is a PT_NOTE section; loop through all the notes * in the section. */ if (lseek(fd, xph_offset, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } bufsize = read(fd, nbuf, ((xph_filesz < sizeof(nbuf)) ? xph_filesz : sizeof(nbuf))); if (bufsize == -1) { file_badread(ms); return -1; } offset = 0; for (;;) { if (offset >= (size_t)bufsize) break; offset = donote(ms, nbuf, offset, (size_t)bufsize, clazz, swap, align, flags); if (offset == 0) break; } if (lseek(fd, savedoffset, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } break; default: break; } } if (file_printf(ms, ", %s linked%s", linking_style, shared_libraries) == -1) return -1; return 0; } protected int file_tryelf(struct magic_set *ms, int fd, const unsigned char *buf, size_t nbytes) { union { int32_t l; char c[sizeof (int32_t)]; } u; int clazz; int swap; struct stat st; off_t fsize; int flags = 0; Elf32_Ehdr elf32hdr; Elf64_Ehdr elf64hdr; - uint16_t type; + uint16_t type, phnum, shnum; if (ms->flags & (MAGIC_MIME|MAGIC_APPLE)) return 0; /* * ELF executables have multiple section headers in arbitrary * file locations and thus file(1) cannot determine it from easily. * Instead we traverse thru all section headers until a symbol table * one is found or else the binary is stripped. * Return immediately if it's not ELF (so we avoid pipe2file unless needed). */ if (buf[EI_MAG0] != ELFMAG0 || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1) || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) return 0; /* * If we cannot seek, it must be a pipe, socket or fifo. */ if((lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE)) fd = file_pipe2file(ms, fd, buf, nbytes); if (fstat(fd, &st) == -1) { file_badread(ms); return -1; } fsize = st.st_size; clazz = buf[EI_CLASS]; switch (clazz) { case ELFCLASS32: #undef elf_getu #define elf_getu(a, b) elf_getu32(a, b) #undef elfhdr #define elfhdr elf32hdr #include "elfclass.h" case ELFCLASS64: #undef elf_getu #define elf_getu(a, b) elf_getu64(a, b) #undef elfhdr #define elfhdr elf64hdr #include "elfclass.h" default: if (file_printf(ms, ", unknown class %d", clazz) == -1) return -1; break; } return 0; } #endif Index: stable/8/contrib/file/softmagic.c =================================================================== --- stable/8/contrib/file/softmagic.c (revision 275668) +++ stable/8/contrib/file/softmagic.c (revision 275669) @@ -1,2018 +1,2021 @@ /* * Copyright (c) Ian F. Darwin 1986-1995. * Software written by Ian F. Darwin and others; * maintained 1995-present by Christos Zoulas and others. * * 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 immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * softmagic - interpret variable magic from MAGIC */ #include "file.h" #ifndef lint FILE_RCSID("@(#)$File: softmagic.c,v 1.135 2009/03/27 22:42:49 christos Exp $") #endif /* lint */ #include "magic.h" #include #include #include #include private int match(struct magic_set *, struct magic *, uint32_t, const unsigned char *, size_t, int, int); private int mget(struct magic_set *, const unsigned char *, struct magic *, size_t, unsigned int, int); private int magiccheck(struct magic_set *, struct magic *); private int32_t mprint(struct magic_set *, struct magic *); private int32_t moffset(struct magic_set *, struct magic *); private void mdebug(uint32_t, const char *, size_t); private int mcopy(struct magic_set *, union VALUETYPE *, int, int, const unsigned char *, uint32_t, size_t, size_t); private int mconvert(struct magic_set *, struct magic *); private int print_sep(struct magic_set *, int); private int handle_annotation(struct magic_set *, struct magic *); private void cvt_8(union VALUETYPE *, const struct magic *); private void cvt_16(union VALUETYPE *, const struct magic *); private void cvt_32(union VALUETYPE *, const struct magic *); private void cvt_64(union VALUETYPE *, const struct magic *); #define OFFSET_OOB(n, o, i) ((n) < (o) || (i) > ((n) - (o))) + +#define MAX_RECURSION_LEVEL 10 + /* * softmagic - lookup one file in parsed, in-memory copy of database * Passed the name and FILE * of one file to be typed. */ /*ARGSUSED1*/ /* nbytes passed for regularity, maybe need later */ protected int file_softmagic(struct magic_set *ms, const unsigned char *buf, size_t nbytes, size_t level, int mode) { struct mlist *ml; int rv; for (ml = ms->mlist->next; ml != ms->mlist; ml = ml->next) if ((rv = match(ms, ml->magic, ml->nmagic, buf, nbytes, mode, level)) != 0) return rv; return 0; } /* * Go through the whole list, stopping if you find a match. Process all * the continuations of that match before returning. * * We support multi-level continuations: * * At any time when processing a successful top-level match, there is a * current continuation level; it represents the level of the last * successfully matched continuation. * * Continuations above that level are skipped as, if we see one, it * means that the continuation that controls them - i.e, the * lower-level continuation preceding them - failed to match. * * Continuations below that level are processed as, if we see one, * it means we've finished processing or skipping higher-level * continuations under the control of a successful or unsuccessful * lower-level continuation, and are now seeing the next lower-level * continuation and should process it. The current continuation * level reverts to the level of the one we're seeing. * * Continuations at the current level are processed as, if we see * one, there's no lower-level continuation that may have failed. * * If a continuation matches, we bump the current continuation level * so that higher-level continuations are processed. */ private int match(struct magic_set *ms, struct magic *magic, uint32_t nmagic, const unsigned char *s, size_t nbytes, int mode, int recursion_level) { uint32_t magindex = 0; unsigned int cont_level = 0; int need_separator = 0; int returnval = 0, e; /* if a match is found it is set to 1*/ int firstline = 1; /* a flag to print X\n X\n- X */ int printed_something = 0; int print = (ms->flags & (MAGIC_MIME|MAGIC_APPLE)) == 0; if (file_check_mem(ms, cont_level) == -1) return -1; for (magindex = 0; magindex < nmagic; magindex++) { int flush = 0; struct magic *m = &magic[magindex]; if ((m->flag & BINTEST) != mode) { /* Skip sub-tests */ while (magic[magindex + 1].cont_level != 0 && ++magindex < nmagic) continue; continue; /* Skip to next top-level test*/ } ms->offset = m->offset; ms->line = m->lineno; /* if main entry matches, print it... */ switch (mget(ms, s, m, nbytes, cont_level, recursion_level + 1)) { case -1: return -1; case 0: flush = m->reln != '!'; break; default: if (m->type == FILE_INDIRECT) returnval = 1; switch (magiccheck(ms, m)) { case -1: return -1; case 0: flush++; break; default: flush = 0; break; } break; } if (flush) { /* * main entry didn't match, * flush its continuations */ while (magindex < nmagic - 1 && magic[magindex + 1].cont_level != 0) magindex++; continue; } /* * If we are going to print something, we'll need to print * a blank before we print something else. */ if (*m->desc) { need_separator = 1; printed_something = 1; if ((e = handle_annotation(ms, m)) != 0) return e; if (print_sep(ms, firstline) == -1) return -1; } if (print && mprint(ms, m) == -1) return -1; ms->c.li[cont_level].off = moffset(ms, m); /* and any continuations that match */ if (file_check_mem(ms, ++cont_level) == -1) return -1; while (magic[magindex+1].cont_level != 0 && ++magindex < nmagic) { m = &magic[magindex]; ms->line = m->lineno; /* for messages */ if (cont_level < m->cont_level) continue; if (cont_level > m->cont_level) { /* * We're at the end of the level * "cont_level" continuations. */ cont_level = m->cont_level; } ms->offset = m->offset; if (m->flag & OFFADD) { ms->offset += ms->c.li[cont_level - 1].off; } #ifdef ENABLE_CONDITIONALS if (m->cond == COND_ELSE || m->cond == COND_ELIF) { if (ms->c.li[cont_level].last_match == 1) continue; } #endif switch (mget(ms, s, m, nbytes, cont_level, recursion_level + 1)) { case -1: return -1; case 0: if (m->reln != '!') continue; flush = 1; break; default: if (m->type == FILE_INDIRECT) returnval = 1; flush = 0; break; } switch (flush ? 1 : magiccheck(ms, m)) { case -1: return -1; case 0: #ifdef ENABLE_CONDITIONALS ms->c.li[cont_level].last_match = 0; #endif break; default: #ifdef ENABLE_CONDITIONALS ms->c.li[cont_level].last_match = 1; #endif if (m->type != FILE_DEFAULT) ms->c.li[cont_level].got_match = 1; else if (ms->c.li[cont_level].got_match) { ms->c.li[cont_level].got_match = 0; break; } /* * If we are going to print something, * make sure that we have a separator first. */ if (*m->desc) { if ((e = handle_annotation(ms, m)) != 0) return e; if (!printed_something) { printed_something = 1; if (print_sep(ms, firstline) == -1) return -1; } } /* * This continuation matched. Print * its message, with a blank before it * if the previous item printed and * this item isn't empty. */ /* space if previous printed */ if (need_separator && ((m->flag & NOSPACE) == 0) && *m->desc) { if (print && file_printf(ms, " ") == -1) return -1; need_separator = 0; } if (print && mprint(ms, m) == -1) return -1; ms->c.li[cont_level].off = moffset(ms, m); if (*m->desc) need_separator = 1; /* * If we see any continuations * at a higher level, * process them. */ if (file_check_mem(ms, ++cont_level) == -1) return -1; break; } } if (printed_something) { firstline = 0; if (print) returnval = 1; } if ((ms->flags & MAGIC_CONTINUE) == 0 && printed_something) { return returnval; /* don't keep searching */ } } return returnval; /* This is hit if -k is set or there is no match */ } private int check_fmt(struct magic_set *ms, struct magic *m) { regex_t rx; int rc; if (strchr(m->desc, '%') == NULL) return 0; rc = regcomp(&rx, "%[-0-9\\.]*s", REG_EXTENDED|REG_NOSUB); if (rc) { char errmsg[512]; (void)regerror(rc, &rx, errmsg, sizeof(errmsg)); file_magerror(ms, "regex error %d, (%s)", rc, errmsg); return -1; } else { rc = regexec(&rx, m->desc, 0, 0, 0); regfree(&rx); return !rc; } } #ifndef HAVE_STRNDUP char * strndup(const char *, size_t); char * strndup(const char *str, size_t n) { size_t len; char *copy; for (len = 0; len < n && str[len]; len++) continue; if ((copy = malloc(len + 1)) == NULL) return NULL; (void)memcpy(copy, str, len); copy[len] = '\0'; return copy; } #endif /* HAVE_STRNDUP */ private int32_t mprint(struct magic_set *ms, struct magic *m) { uint64_t v; float vf; double vd; int64_t t = 0; char buf[128]; union VALUETYPE *p = &ms->ms_value; switch (m->type) { case FILE_BYTE: v = file_signextend(ms, m, (uint64_t)p->b); switch (check_fmt(ms, m)) { case -1: return -1; case 1: (void)snprintf(buf, sizeof(buf), "%c", (unsigned char)v); if (file_printf(ms, m->desc, buf) == -1) return -1; break; default: if (file_printf(ms, m->desc, (unsigned char) v) == -1) return -1; break; } t = ms->offset + sizeof(char); break; case FILE_SHORT: case FILE_BESHORT: case FILE_LESHORT: v = file_signextend(ms, m, (uint64_t)p->h); switch (check_fmt(ms, m)) { case -1: return -1; case 1: (void)snprintf(buf, sizeof(buf), "%hu", (unsigned short)v); if (file_printf(ms, m->desc, buf) == -1) return -1; break; default: if ( file_printf(ms, m->desc, (unsigned short) v) == -1) return -1; break; } t = ms->offset + sizeof(short); break; case FILE_LONG: case FILE_BELONG: case FILE_LELONG: case FILE_MELONG: v = file_signextend(ms, m, (uint64_t)p->l); switch (check_fmt(ms, m)) { case -1: return -1; case 1: (void)snprintf(buf, sizeof(buf), "%u", (uint32_t)v); if (file_printf(ms, m->desc, buf) == -1) return -1; break; default: if (file_printf(ms, m->desc, (uint32_t) v) == -1) return -1; break; } t = ms->offset + sizeof(int32_t); break; case FILE_QUAD: case FILE_BEQUAD: case FILE_LEQUAD: v = file_signextend(ms, m, p->q); if (file_printf(ms, m->desc, (uint64_t) v) == -1) return -1; t = ms->offset + sizeof(int64_t); break; case FILE_STRING: case FILE_PSTRING: case FILE_BESTRING16: case FILE_LESTRING16: if (m->reln == '=' || m->reln == '!') { if (file_printf(ms, m->desc, m->value.s) == -1) return -1; t = ms->offset + m->vallen; } else { if (*m->value.s == '\0') p->s[strcspn(p->s, "\n")] = '\0'; if (file_printf(ms, m->desc, p->s) == -1) return -1; t = ms->offset + strlen(p->s); if (m->type == FILE_PSTRING) t++; } break; case FILE_DATE: case FILE_BEDATE: case FILE_LEDATE: case FILE_MEDATE: if (file_printf(ms, m->desc, file_fmttime(p->l, 1)) == -1) return -1; t = ms->offset + sizeof(time_t); break; case FILE_LDATE: case FILE_BELDATE: case FILE_LELDATE: case FILE_MELDATE: if (file_printf(ms, m->desc, file_fmttime(p->l, 0)) == -1) return -1; t = ms->offset + sizeof(time_t); break; case FILE_QDATE: case FILE_BEQDATE: case FILE_LEQDATE: if (file_printf(ms, m->desc, file_fmttime((uint32_t)p->q, 1)) == -1) return -1; t = ms->offset + sizeof(uint64_t); break; case FILE_QLDATE: case FILE_BEQLDATE: case FILE_LEQLDATE: if (file_printf(ms, m->desc, file_fmttime((uint32_t)p->q, 0)) == -1) return -1; t = ms->offset + sizeof(uint64_t); break; case FILE_FLOAT: case FILE_BEFLOAT: case FILE_LEFLOAT: vf = p->f; switch (check_fmt(ms, m)) { case -1: return -1; case 1: (void)snprintf(buf, sizeof(buf), "%g", vf); if (file_printf(ms, m->desc, buf) == -1) return -1; break; default: if (file_printf(ms, m->desc, vf) == -1) return -1; break; } t = ms->offset + sizeof(float); break; case FILE_DOUBLE: case FILE_BEDOUBLE: case FILE_LEDOUBLE: vd = p->d; switch (check_fmt(ms, m)) { case -1: return -1; case 1: (void)snprintf(buf, sizeof(buf), "%g", vd); if (file_printf(ms, m->desc, buf) == -1) return -1; break; default: if (file_printf(ms, m->desc, vd) == -1) return -1; break; } t = ms->offset + sizeof(double); break; case FILE_REGEX: { char *cp; int rval; cp = strndup((const char *)ms->search.s, ms->search.rm_len); if (cp == NULL) { file_oomem(ms, ms->search.rm_len); return -1; } rval = file_printf(ms, m->desc, cp); free(cp); if (rval == -1) return -1; if ((m->str_flags & REGEX_OFFSET_START)) t = ms->search.offset; else t = ms->search.offset + ms->search.rm_len; break; } case FILE_SEARCH: if (file_printf(ms, m->desc, m->value.s) == -1) return -1; if ((m->str_flags & REGEX_OFFSET_START)) t = ms->search.offset; else t = ms->search.offset + m->vallen; break; case FILE_DEFAULT: if (file_printf(ms, m->desc, m->value.s) == -1) return -1; t = ms->offset; break; case FILE_INDIRECT: t = ms->offset; break; default: file_magerror(ms, "invalid m->type (%d) in mprint()", m->type); return -1; } return (int32_t)t; } private int32_t moffset(struct magic_set *ms, struct magic *m) { switch (m->type) { case FILE_BYTE: return ms->offset + sizeof(char); case FILE_SHORT: case FILE_BESHORT: case FILE_LESHORT: return ms->offset + sizeof(short); case FILE_LONG: case FILE_BELONG: case FILE_LELONG: case FILE_MELONG: return ms->offset + sizeof(int32_t); case FILE_QUAD: case FILE_BEQUAD: case FILE_LEQUAD: return ms->offset + sizeof(int64_t); case FILE_STRING: case FILE_PSTRING: case FILE_BESTRING16: case FILE_LESTRING16: if (m->reln == '=' || m->reln == '!') return ms->offset + m->vallen; else { union VALUETYPE *p = &ms->ms_value; uint32_t t; if (*m->value.s == '\0') p->s[strcspn(p->s, "\n")] = '\0'; t = ms->offset + strlen(p->s); if (m->type == FILE_PSTRING) t++; return t; } case FILE_DATE: case FILE_BEDATE: case FILE_LEDATE: case FILE_MEDATE: return ms->offset + sizeof(time_t); case FILE_LDATE: case FILE_BELDATE: case FILE_LELDATE: case FILE_MELDATE: return ms->offset + sizeof(time_t); case FILE_QDATE: case FILE_BEQDATE: case FILE_LEQDATE: return ms->offset + sizeof(uint64_t); case FILE_QLDATE: case FILE_BEQLDATE: case FILE_LEQLDATE: return ms->offset + sizeof(uint64_t); case FILE_FLOAT: case FILE_BEFLOAT: case FILE_LEFLOAT: return ms->offset + sizeof(float); case FILE_DOUBLE: case FILE_BEDOUBLE: case FILE_LEDOUBLE: return ms->offset + sizeof(double); break; case FILE_REGEX: if ((m->str_flags & REGEX_OFFSET_START) != 0) return ms->search.offset; else return ms->search.offset + ms->search.rm_len; case FILE_SEARCH: if ((m->str_flags & REGEX_OFFSET_START) != 0) return ms->search.offset; else return ms->search.offset + m->vallen; case FILE_DEFAULT: return ms->offset; case FILE_INDIRECT: return ms->offset; default: return 0; } } #define DO_CVT(fld, cast) \ if (m->num_mask) \ switch (m->mask_op & FILE_OPS_MASK) { \ case FILE_OPAND: \ p->fld &= cast m->num_mask; \ break; \ case FILE_OPOR: \ p->fld |= cast m->num_mask; \ break; \ case FILE_OPXOR: \ p->fld ^= cast m->num_mask; \ break; \ case FILE_OPADD: \ p->fld += cast m->num_mask; \ break; \ case FILE_OPMINUS: \ p->fld -= cast m->num_mask; \ break; \ case FILE_OPMULTIPLY: \ p->fld *= cast m->num_mask; \ break; \ case FILE_OPDIVIDE: \ p->fld /= cast m->num_mask; \ break; \ case FILE_OPMODULO: \ p->fld %= cast m->num_mask; \ break; \ } \ if (m->mask_op & FILE_OPINVERSE) \ p->fld = ~p->fld \ private void cvt_8(union VALUETYPE *p, const struct magic *m) { DO_CVT(b, (uint8_t)); } private void cvt_16(union VALUETYPE *p, const struct magic *m) { DO_CVT(h, (uint16_t)); } private void cvt_32(union VALUETYPE *p, const struct magic *m) { DO_CVT(l, (uint32_t)); } private void cvt_64(union VALUETYPE *p, const struct magic *m) { DO_CVT(q, (uint64_t)); } #define DO_CVT2(fld, cast) \ if (m->num_mask) \ switch (m->mask_op & FILE_OPS_MASK) { \ case FILE_OPADD: \ p->fld += cast m->num_mask; \ break; \ case FILE_OPMINUS: \ p->fld -= cast m->num_mask; \ break; \ case FILE_OPMULTIPLY: \ p->fld *= cast m->num_mask; \ break; \ case FILE_OPDIVIDE: \ p->fld /= cast m->num_mask; \ break; \ } \ private void cvt_float(union VALUETYPE *p, const struct magic *m) { DO_CVT2(f, (float)); } private void cvt_double(union VALUETYPE *p, const struct magic *m) { DO_CVT2(d, (double)); } /* * Convert the byte order of the data we are looking at * While we're here, let's apply the mask operation * (unless you have a better idea) */ private int mconvert(struct magic_set *ms, struct magic *m) { union VALUETYPE *p = &ms->ms_value; switch (m->type) { case FILE_BYTE: cvt_8(p, m); return 1; case FILE_SHORT: cvt_16(p, m); return 1; case FILE_LONG: case FILE_DATE: case FILE_LDATE: cvt_32(p, m); return 1; case FILE_QUAD: case FILE_QDATE: case FILE_QLDATE: cvt_64(p, m); return 1; case FILE_STRING: case FILE_BESTRING16: case FILE_LESTRING16: { /* Null terminate and eat *trailing* return */ p->s[sizeof(p->s) - 1] = '\0'; #if 0 /* Why? breaks magic numbers that end with \xa */ len = strlen(p->s); if (len-- && p->s[len] == '\n') p->s[len] = '\0'; #endif return 1; } case FILE_PSTRING: { char *ptr1 = p->s, *ptr2 = ptr1 + 1; size_t len = *p->s; if (len >= sizeof(p->s)) len = sizeof(p->s) - 1; while (len--) *ptr1++ = *ptr2++; *ptr1 = '\0'; #if 0 /* Why? breaks magic numbers that end with \xa */ len = strlen(p->s); if (len-- && p->s[len] == '\n') p->s[len] = '\0'; #endif return 1; } case FILE_BESHORT: p->h = (short)((p->hs[0]<<8)|(p->hs[1])); cvt_16(p, m); return 1; case FILE_BELONG: case FILE_BEDATE: case FILE_BELDATE: p->l = (int32_t) ((p->hl[0]<<24)|(p->hl[1]<<16)|(p->hl[2]<<8)|(p->hl[3])); cvt_32(p, m); return 1; case FILE_BEQUAD: case FILE_BEQDATE: case FILE_BEQLDATE: p->q = (uint64_t) (((uint64_t)p->hq[0]<<56)|((uint64_t)p->hq[1]<<48)| ((uint64_t)p->hq[2]<<40)|((uint64_t)p->hq[3]<<32)| ((uint64_t)p->hq[4]<<24)|((uint64_t)p->hq[5]<<16)| ((uint64_t)p->hq[6]<<8)|((uint64_t)p->hq[7])); cvt_64(p, m); return 1; case FILE_LESHORT: p->h = (short)((p->hs[1]<<8)|(p->hs[0])); cvt_16(p, m); return 1; case FILE_LELONG: case FILE_LEDATE: case FILE_LELDATE: p->l = (int32_t) ((p->hl[3]<<24)|(p->hl[2]<<16)|(p->hl[1]<<8)|(p->hl[0])); cvt_32(p, m); return 1; case FILE_LEQUAD: case FILE_LEQDATE: case FILE_LEQLDATE: p->q = (uint64_t) (((uint64_t)p->hq[7]<<56)|((uint64_t)p->hq[6]<<48)| ((uint64_t)p->hq[5]<<40)|((uint64_t)p->hq[4]<<32)| ((uint64_t)p->hq[3]<<24)|((uint64_t)p->hq[2]<<16)| ((uint64_t)p->hq[1]<<8)|((uint64_t)p->hq[0])); cvt_64(p, m); return 1; case FILE_MELONG: case FILE_MEDATE: case FILE_MELDATE: p->l = (int32_t) ((p->hl[1]<<24)|(p->hl[0]<<16)|(p->hl[3]<<8)|(p->hl[2])); cvt_32(p, m); return 1; case FILE_FLOAT: cvt_float(p, m); return 1; case FILE_BEFLOAT: p->l = ((uint32_t)p->hl[0]<<24)|((uint32_t)p->hl[1]<<16)| ((uint32_t)p->hl[2]<<8) |((uint32_t)p->hl[3]); cvt_float(p, m); return 1; case FILE_LEFLOAT: p->l = ((uint32_t)p->hl[3]<<24)|((uint32_t)p->hl[2]<<16)| ((uint32_t)p->hl[1]<<8) |((uint32_t)p->hl[0]); cvt_float(p, m); return 1; case FILE_DOUBLE: cvt_double(p, m); return 1; case FILE_BEDOUBLE: p->q = ((uint64_t)p->hq[0]<<56)|((uint64_t)p->hq[1]<<48)| ((uint64_t)p->hq[2]<<40)|((uint64_t)p->hq[3]<<32)| ((uint64_t)p->hq[4]<<24)|((uint64_t)p->hq[5]<<16)| ((uint64_t)p->hq[6]<<8) |((uint64_t)p->hq[7]); cvt_double(p, m); return 1; case FILE_LEDOUBLE: p->q = ((uint64_t)p->hq[7]<<56)|((uint64_t)p->hq[6]<<48)| ((uint64_t)p->hq[5]<<40)|((uint64_t)p->hq[4]<<32)| ((uint64_t)p->hq[3]<<24)|((uint64_t)p->hq[2]<<16)| ((uint64_t)p->hq[1]<<8) |((uint64_t)p->hq[0]); cvt_double(p, m); return 1; case FILE_REGEX: case FILE_SEARCH: case FILE_DEFAULT: return 1; default: file_magerror(ms, "invalid type %d in mconvert()", m->type); return 0; } } private void mdebug(uint32_t offset, const char *str, size_t len) { (void) fprintf(stderr, "mget @%d: ", offset); file_showstr(stderr, str, len); (void) fputc('\n', stderr); (void) fputc('\n', stderr); } private int mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir, const unsigned char *s, uint32_t offset, size_t nbytes, size_t linecnt) { /* * Note: FILE_SEARCH and FILE_REGEX do not actually copy * anything, but setup pointers into the source */ if (indir == 0) { switch (type) { case FILE_SEARCH: ms->search.s = (const char *)s + offset; ms->search.s_len = nbytes - offset; ms->search.offset = offset; return 0; case FILE_REGEX: { const char *b; const char *c; const char *last; /* end of search region */ const char *buf; /* start of search region */ const char *end; size_t lines; if (s == NULL) { ms->search.s_len = 0; ms->search.s = NULL; return 0; } buf = (const char *)s + offset; end = last = (const char *)s + nbytes; /* mget() guarantees buf <= last */ for (lines = linecnt, b = buf; lines && ((b = memchr(c = b, '\n', end - b)) || (b = memchr(c, '\r', end - c))); lines--, b++) { last = b; if (b[0] == '\r' && b[1] == '\n') b++; } if (lines) last = (const char *)s + nbytes; ms->search.s = buf; ms->search.s_len = last - buf; ms->search.offset = offset; ms->search.rm_len = 0; return 0; } case FILE_BESTRING16: case FILE_LESTRING16: { const unsigned char *src = s + offset; const unsigned char *esrc = s + nbytes; char *dst = p->s; char *edst = &p->s[sizeof(p->s) - 1]; if (type == FILE_BESTRING16) src++; /* check for pointer overflow */ if (src < s) { file_magerror(ms, "invalid offset %u in mcopy()", offset); return -1; } for (/*EMPTY*/; src < esrc; src += 2, dst++) { if (dst < edst) *dst = *src; else break; if (*dst == '\0') { if (type == FILE_BESTRING16 ? *(src - 1) != '\0' : *(src + 1) != '\0') *dst = ' '; } } *edst = '\0'; return 0; } case FILE_STRING: /* XXX - these two should not need */ case FILE_PSTRING: /* to copy anything, but do anyway. */ default: break; } } if (offset >= nbytes) { (void)memset(p, '\0', sizeof(*p)); return 0; } if (nbytes - offset < sizeof(*p)) nbytes = nbytes - offset; else nbytes = sizeof(*p); (void)memcpy(p, s + offset, nbytes); /* * the usefulness of padding with zeroes eludes me, it * might even cause problems */ if (nbytes < sizeof(*p)) (void)memset(((char *)(void *)p) + nbytes, '\0', sizeof(*p) - nbytes); return 0; } private int mget(struct magic_set *ms, const unsigned char *s, struct magic *m, size_t nbytes, unsigned int cont_level, int recursion_level) { uint32_t offset = ms->offset; uint32_t count = m->str_range; union VALUETYPE *p = &ms->ms_value; - if (recursion_level >= 20) { + if (recursion_level >= MAX_RECURSION_LEVEL) { file_error(ms, 0, "recursion nesting exceeded"); return -1; } if (mcopy(ms, p, m->type, m->flag & INDIR, s, offset, nbytes, count) == -1) return -1; if ((ms->flags & MAGIC_DEBUG) != 0) { mdebug(offset, (char *)(void *)p, sizeof(union VALUETYPE)); #ifndef COMPILE_ONLY file_mdump(m); #endif } if (m->flag & INDIR) { int off = m->in_offset; if (m->in_op & FILE_OPINDIRECT) { const union VALUETYPE *q = CAST(const union VALUETYPE *, ((const void *)(s + offset + off))); switch (m->in_type) { case FILE_BYTE: off = q->b; break; case FILE_SHORT: off = q->h; break; case FILE_BESHORT: off = (short)((q->hs[0]<<8)|(q->hs[1])); break; case FILE_LESHORT: off = (short)((q->hs[1]<<8)|(q->hs[0])); break; case FILE_LONG: off = q->l; break; case FILE_BELONG: case FILE_BEID3: off = (int32_t)((q->hl[0]<<24)|(q->hl[1]<<16)| (q->hl[2]<<8)|(q->hl[3])); break; case FILE_LEID3: case FILE_LELONG: off = (int32_t)((q->hl[3]<<24)|(q->hl[2]<<16)| (q->hl[1]<<8)|(q->hl[0])); break; case FILE_MELONG: off = (int32_t)((q->hl[1]<<24)|(q->hl[0]<<16)| (q->hl[3]<<8)|(q->hl[2])); break; } } switch (m->in_type) { case FILE_BYTE: if (OFFSET_OOB(nbytes, offset, 1)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = p->b & off; break; case FILE_OPOR: offset = p->b | off; break; case FILE_OPXOR: offset = p->b ^ off; break; case FILE_OPADD: offset = p->b + off; break; case FILE_OPMINUS: offset = p->b - off; break; case FILE_OPMULTIPLY: offset = p->b * off; break; case FILE_OPDIVIDE: offset = p->b / off; break; case FILE_OPMODULO: offset = p->b % off; break; } } else offset = p->b; if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_BESHORT: if (OFFSET_OOB(nbytes, offset, 2)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = (short)((p->hs[0]<<8)| (p->hs[1])) & off; break; case FILE_OPOR: offset = (short)((p->hs[0]<<8)| (p->hs[1])) | off; break; case FILE_OPXOR: offset = (short)((p->hs[0]<<8)| (p->hs[1])) ^ off; break; case FILE_OPADD: offset = (short)((p->hs[0]<<8)| (p->hs[1])) + off; break; case FILE_OPMINUS: offset = (short)((p->hs[0]<<8)| (p->hs[1])) - off; break; case FILE_OPMULTIPLY: offset = (short)((p->hs[0]<<8)| (p->hs[1])) * off; break; case FILE_OPDIVIDE: offset = (short)((p->hs[0]<<8)| (p->hs[1])) / off; break; case FILE_OPMODULO: offset = (short)((p->hs[0]<<8)| (p->hs[1])) % off; break; } } else offset = (short)((p->hs[0]<<8)| (p->hs[1])); if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_LESHORT: if (OFFSET_OOB(nbytes, offset, 2)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = (short)((p->hs[1]<<8)| (p->hs[0])) & off; break; case FILE_OPOR: offset = (short)((p->hs[1]<<8)| (p->hs[0])) | off; break; case FILE_OPXOR: offset = (short)((p->hs[1]<<8)| (p->hs[0])) ^ off; break; case FILE_OPADD: offset = (short)((p->hs[1]<<8)| (p->hs[0])) + off; break; case FILE_OPMINUS: offset = (short)((p->hs[1]<<8)| (p->hs[0])) - off; break; case FILE_OPMULTIPLY: offset = (short)((p->hs[1]<<8)| (p->hs[0])) * off; break; case FILE_OPDIVIDE: offset = (short)((p->hs[1]<<8)| (p->hs[0])) / off; break; case FILE_OPMODULO: offset = (short)((p->hs[1]<<8)| (p->hs[0])) % off; break; } } else offset = (short)((p->hs[1]<<8)| (p->hs[0])); if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_SHORT: if (OFFSET_OOB(nbytes, offset, 2)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = p->h & off; break; case FILE_OPOR: offset = p->h | off; break; case FILE_OPXOR: offset = p->h ^ off; break; case FILE_OPADD: offset = p->h + off; break; case FILE_OPMINUS: offset = p->h - off; break; case FILE_OPMULTIPLY: offset = p->h * off; break; case FILE_OPDIVIDE: offset = p->h / off; break; case FILE_OPMODULO: offset = p->h % off; break; } } else offset = p->h; if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_BELONG: case FILE_BEID3: if (OFFSET_OOB(nbytes, offset, 4)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) & off; break; case FILE_OPOR: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) | off; break; case FILE_OPXOR: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) ^ off; break; case FILE_OPADD: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) + off; break; case FILE_OPMINUS: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) - off; break; case FILE_OPMULTIPLY: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) * off; break; case FILE_OPDIVIDE: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) / off; break; case FILE_OPMODULO: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) % off; break; } } else offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])); if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_LELONG: case FILE_LEID3: if (OFFSET_OOB(nbytes, offset, 4)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) & off; break; case FILE_OPOR: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) | off; break; case FILE_OPXOR: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) ^ off; break; case FILE_OPADD: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) + off; break; case FILE_OPMINUS: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) - off; break; case FILE_OPMULTIPLY: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) * off; break; case FILE_OPDIVIDE: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) / off; break; case FILE_OPMODULO: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) % off; break; } } else offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])); if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_MELONG: if (OFFSET_OOB(nbytes, offset, 4)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) & off; break; case FILE_OPOR: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) | off; break; case FILE_OPXOR: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) ^ off; break; case FILE_OPADD: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) + off; break; case FILE_OPMINUS: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) - off; break; case FILE_OPMULTIPLY: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) * off; break; case FILE_OPDIVIDE: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) / off; break; case FILE_OPMODULO: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) % off; break; } } else offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])); if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_LONG: if (OFFSET_OOB(nbytes, offset, 4)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = p->l & off; break; case FILE_OPOR: offset = p->l | off; break; case FILE_OPXOR: offset = p->l ^ off; break; case FILE_OPADD: offset = p->l + off; break; case FILE_OPMINUS: offset = p->l - off; break; case FILE_OPMULTIPLY: offset = p->l * off; break; case FILE_OPDIVIDE: offset = p->l / off; break; case FILE_OPMODULO: offset = p->l % off; break; } } else offset = p->l; if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; } switch (m->in_type) { case FILE_LEID3: case FILE_BEID3: offset = ((((offset >> 0) & 0x7f) << 0) | (((offset >> 8) & 0x7f) << 7) | (((offset >> 16) & 0x7f) << 14) | (((offset >> 24) & 0x7f) << 21)) + 10; break; default: break; } if (m->flag & INDIROFFADD) { offset += ms->c.li[cont_level-1].off; } if (mcopy(ms, p, m->type, 0, s, offset, nbytes, count) == -1) return -1; ms->offset = offset; if ((ms->flags & MAGIC_DEBUG) != 0) { mdebug(offset, (char *)(void *)p, sizeof(union VALUETYPE)); #ifndef COMPILE_ONLY file_mdump(m); #endif } } /* Verify we have enough data to match magic type */ switch (m->type) { case FILE_BYTE: if (OFFSET_OOB(nbytes, offset, 1)) return 0; break; case FILE_SHORT: case FILE_BESHORT: case FILE_LESHORT: if (OFFSET_OOB(nbytes, offset, 2)) return 0; break; case FILE_LONG: case FILE_BELONG: case FILE_LELONG: case FILE_MELONG: case FILE_DATE: case FILE_BEDATE: case FILE_LEDATE: case FILE_MEDATE: case FILE_LDATE: case FILE_BELDATE: case FILE_LELDATE: case FILE_MELDATE: case FILE_FLOAT: case FILE_BEFLOAT: case FILE_LEFLOAT: if (OFFSET_OOB(nbytes, offset, 4)) return 0; break; case FILE_DOUBLE: case FILE_BEDOUBLE: case FILE_LEDOUBLE: if (OFFSET_OOB(nbytes, offset, 8)) return 0; break; case FILE_STRING: case FILE_PSTRING: case FILE_SEARCH: if (OFFSET_OOB(nbytes, offset, m->vallen)) return 0; break; case FILE_REGEX: if (OFFSET_OOB(nbytes, offset, 0)) return 0; break; case FILE_INDIRECT: if (offset == 0) return 0; if ((ms->flags & (MAGIC_MIME|MAGIC_APPLE)) == 0 && file_printf(ms, m->desc) == -1) return -1; if (OFFSET_OOB(nbytes, offset, 0)) return 0; return file_softmagic(ms, s + offset, nbytes - offset, recursion_level, BINTEST); case FILE_DEFAULT: /* nothing to check */ default: break; } if (!mconvert(ms, m)) return 0; return 1; } private uint64_t file_strncmp(const char *s1, const char *s2, size_t len, uint32_t flags) { /* * Convert the source args to unsigned here so that (1) the * compare will be unsigned as it is in strncmp() and (2) so * the ctype functions will work correctly without extra * casting. */ const unsigned char *a = (const unsigned char *)s1; const unsigned char *b = (const unsigned char *)s2; uint64_t v; /* * What we want here is v = strncmp(s1, s2, len), * but ignoring any nulls. */ v = 0; if (0L == flags) { /* normal string: do it fast */ while (len-- > 0) if ((v = *b++ - *a++) != '\0') break; } else { /* combine the others */ while (len-- > 0) { if ((flags & STRING_IGNORE_LOWERCASE) && islower(*a)) { if ((v = tolower(*b++) - *a++) != '\0') break; } else if ((flags & STRING_IGNORE_UPPERCASE) && isupper(*a)) { if ((v = toupper(*b++) - *a++) != '\0') break; } else if ((flags & STRING_COMPACT_BLANK) && isspace(*a)) { a++; if (isspace(*b++)) { while (isspace(*b)) b++; } else { v = 1; break; } } else if ((flags & STRING_COMPACT_OPTIONAL_BLANK) && isspace(*a)) { a++; while (isspace(*b)) b++; } else { if ((v = *b++ - *a++) != '\0') break; } } } return v; } private uint64_t file_strncmp16(const char *a, const char *b, size_t len, uint32_t flags) { /* * XXX - The 16-bit string compare probably needs to be done * differently, especially if the flags are to be supported. * At the moment, I am unsure. */ flags = 0; return file_strncmp(a, b, len, flags); } private int magiccheck(struct magic_set *ms, struct magic *m) { uint64_t l = m->value.q; uint64_t v; float fl, fv; double dl, dv; int matched; union VALUETYPE *p = &ms->ms_value; switch (m->type) { case FILE_BYTE: v = p->b; break; case FILE_SHORT: case FILE_BESHORT: case FILE_LESHORT: v = p->h; break; case FILE_LONG: case FILE_BELONG: case FILE_LELONG: case FILE_MELONG: case FILE_DATE: case FILE_BEDATE: case FILE_LEDATE: case FILE_MEDATE: case FILE_LDATE: case FILE_BELDATE: case FILE_LELDATE: case FILE_MELDATE: v = p->l; break; case FILE_QUAD: case FILE_LEQUAD: case FILE_BEQUAD: case FILE_QDATE: case FILE_BEQDATE: case FILE_LEQDATE: case FILE_QLDATE: case FILE_BEQLDATE: case FILE_LEQLDATE: v = p->q; break; case FILE_FLOAT: case FILE_BEFLOAT: case FILE_LEFLOAT: fl = m->value.f; fv = p->f; switch (m->reln) { case 'x': matched = 1; break; case '!': matched = fv != fl; break; case '=': matched = fv == fl; break; case '>': matched = fv > fl; break; case '<': matched = fv < fl; break; default: matched = 0; file_magerror(ms, "cannot happen with float: invalid relation `%c'", m->reln); return -1; } return matched; case FILE_DOUBLE: case FILE_BEDOUBLE: case FILE_LEDOUBLE: dl = m->value.d; dv = p->d; switch (m->reln) { case 'x': matched = 1; break; case '!': matched = dv != dl; break; case '=': matched = dv == dl; break; case '>': matched = dv > dl; break; case '<': matched = dv < dl; break; default: matched = 0; file_magerror(ms, "cannot happen with double: invalid relation `%c'", m->reln); return -1; } return matched; case FILE_DEFAULT: l = 0; v = 0; break; case FILE_STRING: case FILE_PSTRING: l = 0; v = file_strncmp(m->value.s, p->s, (size_t)m->vallen, m->str_flags); break; case FILE_BESTRING16: case FILE_LESTRING16: l = 0; v = file_strncmp16(m->value.s, p->s, (size_t)m->vallen, m->str_flags); break; case FILE_SEARCH: { /* search ms->search.s for the string m->value.s */ size_t slen; size_t idx; if (ms->search.s == NULL) return 0; slen = MIN(m->vallen, sizeof(m->value.s)); l = 0; v = 0; for (idx = 0; m->str_range == 0 || idx < m->str_range; idx++) { if (slen + idx > ms->search.s_len) break; v = file_strncmp(m->value.s, ms->search.s + idx, slen, m->str_flags); if (v == 0) { /* found match */ ms->search.offset += idx; break; } } break; } case FILE_REGEX: { int rc; regex_t rx; char errmsg[512]; if (ms->search.s == NULL) return 0; l = 0; rc = regcomp(&rx, m->value.s, REG_EXTENDED|REG_NEWLINE| ((m->str_flags & STRING_IGNORE_CASE) ? REG_ICASE : 0)); if (rc) { (void)regerror(rc, &rx, errmsg, sizeof(errmsg)); file_magerror(ms, "regex error %d, (%s)", rc, errmsg); v = (uint64_t)-1; } else { regmatch_t pmatch[1]; #ifndef REG_STARTEND #define REG_STARTEND 0 size_t l = ms->search.s_len - 1; char c = ms->search.s[l]; ((char *)(intptr_t)ms->search.s)[l] = '\0'; #else pmatch[0].rm_so = 0; pmatch[0].rm_eo = ms->search.s_len; #endif rc = regexec(&rx, (const char *)ms->search.s, 1, pmatch, REG_STARTEND); #if REG_STARTEND == 0 ((char *)(intptr_t)ms->search.s)[l] = c; #endif switch (rc) { case 0: ms->search.s += (int)pmatch[0].rm_so; ms->search.offset += (size_t)pmatch[0].rm_so; ms->search.rm_len = (size_t)(pmatch[0].rm_eo - pmatch[0].rm_so); v = 0; break; case REG_NOMATCH: v = 1; break; default: (void)regerror(rc, &rx, errmsg, sizeof(errmsg)); file_magerror(ms, "regexec error %d, (%s)", rc, errmsg); v = (uint64_t)-1; break; } regfree(&rx); } if (v == (uint64_t)-1) return -1; break; } case FILE_INDIRECT: return 1; default: file_magerror(ms, "invalid type %d in magiccheck()", m->type); return -1; } v = file_signextend(ms, m, v); switch (m->reln) { case 'x': if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%llu == *any* = 1\n", (unsigned long long)v); matched = 1; break; case '!': matched = v != l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%llu != %llu = %d\n", (unsigned long long)v, (unsigned long long)l, matched); break; case '=': matched = v == l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%llu == %llu = %d\n", (unsigned long long)v, (unsigned long long)l, matched); break; case '>': if (m->flag & UNSIGNED) { matched = v > l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%llu > %llu = %d\n", (unsigned long long)v, (unsigned long long)l, matched); } else { matched = (int64_t) v > (int64_t) l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%lld > %lld = %d\n", (long long)v, (long long)l, matched); } break; case '<': if (m->flag & UNSIGNED) { matched = v < l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%llu < %llu = %d\n", (unsigned long long)v, (unsigned long long)l, matched); } else { matched = (int64_t) v < (int64_t) l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%lld < %lld = %d\n", (long long)v, (long long)l, matched); } break; case '&': matched = (v & l) == l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "((%llx & %llx) == %llx) = %d\n", (unsigned long long)v, (unsigned long long)l, (unsigned long long)l, matched); break; case '^': matched = (v & l) != l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "((%llx & %llx) != %llx) = %d\n", (unsigned long long)v, (unsigned long long)l, (unsigned long long)l, matched); break; default: matched = 0; file_magerror(ms, "cannot happen: invalid relation `%c'", m->reln); return -1; } return matched; } private int handle_annotation(struct magic_set *ms, struct magic *m) { if (ms->flags & MAGIC_APPLE) { if (file_printf(ms, "%.8s", m->apple) == -1) return -1; return 1; } if ((ms->flags & MAGIC_MIME_TYPE) && m->mimetype[0]) { if (file_printf(ms, "%s", m->mimetype) == -1) return -1; return 1; } return 0; } private int print_sep(struct magic_set *ms, int firstline) { if (ms->flags & MAGIC_MIME) return 0; if (firstline) return 0; /* * we found another match * put a newline and '-' to do some simple formatting */ return file_printf(ms, "\n- "); } Index: stable/8/lib/bind/isc/Makefile =================================================================== --- stable/8/lib/bind/isc/Makefile (revision 275668) +++ stable/8/lib/bind/isc/Makefile (revision 275669) @@ -1,155 +1,156 @@ # $FreeBSD$ .include BIND_DIR= ${.CURDIR}/../../../contrib/bind9 LIB_BIND_REL= .. LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} SRCDIR= ${BIND_DIR}/lib/isc .include "${LIB_BIND_DIR}/config.mk" LIB= isc .PATH: ${SRCDIR}/unix SRCS+= app.c dir.c entropy.c \ errno2result.c file.c fsaccess.c \ interfaceiter.c keyboard.c net.c \ os.c resource.c socket.c stdio.c \ stdtime.c strerror.c syslog.c time.c \ .PATH: ${SRCDIR}/nls SRCS+= msgcat.c \ .PATH: ${SRCDIR}/pthreads SRCS+= condition.c mutex.c \ thread.c .PATH: ${SRCDIR} SRCS+= inet_pton.c \ assertions.c backtrace.c base32.c base64.c bitstring.c \ - buffer.c bufferlist.c commandline.c error.c event.c \ + buffer.c bufferlist.c commandline.c counter.c error.c event.c \ fsaccess.c hash.c \ heap.c hex.c hmacmd5.c hmacsha.c \ httpd.c inet_aton.c \ inet_ntop.c \ iterated_hash.c \ lex.c lfsr.c lib.c log.c \ md5.c mem.c mutexblock.c \ netaddr.c netscope.c ondestroy.c \ parseint.c portset.c \ print.c \ quota.c radix.c random.c \ ratelimiter.c refcount.c region.c regex.c result.c rwlock.c \ safe.c \ serial.c sha1.c sha2.c sockaddr.c stats.c string.c strtoul.c \ symtab.c task.c taskpool.c timer.c version.c .PATH: ${.CURDIR} SRCS+= backtrace-emptytbl.c CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/pthreads/include CFLAGS+= -I${SRCDIR}/include -I${.CURDIR} CFLAGS+= -I${SRCDIR}/${ISC_ATOMIC_ARCH}/include DPADD= ${PTHREAD_DPADD} LDADD= ${PTHREAD_LDADD} .if ${MK_BIND_LIBS} != "no" INCS= ${SRCDIR}/include/isc/app.h \ ${SRCDIR}/include/isc/assertions.h \ ${SRCDIR}/include/isc/base32.h \ ${SRCDIR}/include/isc/base64.h \ ${SRCDIR}/include/isc/bind9.h \ ${SRCDIR}/include/isc/bitstring.h \ ${SRCDIR}/include/isc/boolean.h \ ${SRCDIR}/include/isc/buffer.h \ ${SRCDIR}/include/isc/bufferlist.h \ ${SRCDIR}/include/isc/commandline.h \ + ${SRCDIR}/include/isc/counter.h \ ${SRCDIR}/include/isc/entropy.h \ ${SRCDIR}/include/isc/error.h \ ${SRCDIR}/include/isc/event.h \ ${SRCDIR}/include/isc/eventclass.h \ ${SRCDIR}/include/isc/file.h \ ${SRCDIR}/include/isc/formatcheck.h \ ${SRCDIR}/include/isc/fsaccess.h \ ${SRCDIR}/include/isc/hash.h \ ${SRCDIR}/include/isc/heap.h \ ${SRCDIR}/include/isc/hex.h \ ${SRCDIR}/include/isc/hmacmd5.h \ ${SRCDIR}/include/isc/hmacsha.h \ ${SRCDIR}/include/isc/httpd.h \ ${SRCDIR}/include/isc/iterated_hash.h \ ${SRCDIR}/include/isc/interfaceiter.h \ ${SRCDIR}/include/isc/ipv6.h \ ${SRCDIR}/include/isc/lang.h \ ${SRCDIR}/include/isc/lex.h \ ${SRCDIR}/include/isc/lfsr.h \ ${SRCDIR}/include/isc/lib.h \ ${SRCDIR}/include/isc/list.h \ ${SRCDIR}/include/isc/log.h \ ${SRCDIR}/include/isc/magic.h \ ${SRCDIR}/include/isc/md5.h \ ${SRCDIR}/include/isc/mem.h \ ${SRCDIR}/include/isc/msgcat.h \ ${SRCDIR}/include/isc/msgs.h \ ${SRCDIR}/include/isc/mutexblock.h \ ${SRCDIR}/include/isc/namespace.h \ ${SRCDIR}/include/isc/netaddr.h \ ${SRCDIR}/include/isc/netscope.h \ ${SRCDIR}/include/isc/ondestroy.h \ ${SRCDIR}/include/isc/os.h \ ${SRCDIR}/include/isc/parseint.h \ ${SRCDIR}/include/isc/portset.h \ ${SRCDIR}/include/isc/print.h \ ${SRCDIR}/include/isc/quota.h \ ${SRCDIR}/include/isc/radix.h \ ${SRCDIR}/include/isc/random.h \ ${SRCDIR}/include/isc/ratelimiter.h \ ${SRCDIR}/include/isc/refcount.h \ ${SRCDIR}/include/isc/region.h \ ${SRCDIR}/include/isc/regex.h \ ${SRCDIR}/include/isc/resource.h \ ${SRCDIR}/include/isc/result.h \ ${SRCDIR}/include/isc/resultclass.h \ ${SRCDIR}/include/isc/rwlock.h \ ${SRCDIR}/include/isc/safe.h \ ${SRCDIR}/include/isc/serial.h \ ${SRCDIR}/include/isc/sha1.h \ ${SRCDIR}/include/isc/sha2.h \ ${SRCDIR}/include/isc/sockaddr.h \ ${SRCDIR}/include/isc/socket.h \ ${SRCDIR}/include/isc/stats.h \ ${SRCDIR}/include/isc/stdio.h \ ${SRCDIR}/include/isc/stdlib.h \ ${SRCDIR}/include/isc/string.h \ ${SRCDIR}/include/isc/symtab.h \ ${SRCDIR}/include/isc/task.h \ ${SRCDIR}/include/isc/taskpool.h \ ${SRCDIR}/include/isc/timer.h \ ${SRCDIR}/include/isc/types.h \ ${SRCDIR}/include/isc/util.h \ ${SRCDIR}/include/isc/version.h \ ${SRCDIR}/include/isc/xml.h \ ${SRCDIR}/pthreads/include/isc/condition.h \ ${SRCDIR}/pthreads/include/isc/mutex.h \ ${SRCDIR}/pthreads/include/isc/once.h \ ${SRCDIR}/pthreads/include/isc/thread.h \ ${SRCDIR}/unix/include/isc/dir.h \ ${SRCDIR}/unix/include/isc/int.h \ ${SRCDIR}/unix/include/isc/keyboard.h \ ${SRCDIR}/unix/include/isc/net.h \ ${SRCDIR}/unix/include/isc/netdb.h \ ${SRCDIR}/unix/include/isc/offset.h \ ${SRCDIR}/unix/include/isc/stat.h \ ${SRCDIR}/unix/include/isc/stdtime.h \ ${SRCDIR}/unix/include/isc/strerror.h \ ${SRCDIR}/unix/include/isc/syslog.h \ ${SRCDIR}/unix/include/isc/time.h \ ${SRCDIR}/${ISC_ATOMIC_ARCH}/include/isc/atomic.h \ isc/platform.h INCSDIR= ${INCLUDEDIR}/isc .endif .include Index: stable/9/contrib/bind9/CHANGES =================================================================== --- stable/9/contrib/bind9/CHANGES (revision 275668) +++ stable/9/contrib/bind9/CHANGES (revision 275669) @@ -1,11794 +1,11805 @@ +4006. [security] A flaw in delegation handling could be exploited + to put named into an infinite loop. This has + been addressed by placing limits on the number + of levels of recursion named will allow (default 7), + and the number of iterative queries that it will + send (default 50) before terminating a recursive + query (CVE-2014-8500). + + The recursion depth limit is configured via the + "max-recursion-depth" option. [RT #35780] + --- 9.9.5 released --- --- 9.9.5rc2 released --- 3710. [bug] Address double dns_zone_detach when switching to using automatic empty zones from regular zones. [RT #35177] 3709. [port] Use built-in versions of strptime() and timegm() on all platforms to avoid portability issues. [RT #35183] 3708. [bug] Address a portentry locking issue in dispatch.c. [RT #35128] 3707. [bug] irs_resconf_load now returns ISC_R_FILENOTFOUND on a missing resolv.conf file and initializes the structure as if it had been configured with: nameserver ::1 nameserver 127.0.0.1 Note: Callers will need to be updated to treat ISC_R_FILENOTFOUND as a qualified success or else they will leak memory. The following code fragment will work with both old and new versions without changing the behaviour of the existing code. resconf = NULL; result = irs_resconf_load(mctx, "/etc/resolv.conf", &resconf); if (result != ISC_SUCCESS) { if (resconf != NULL) irs_resconf_destroy(&resconf); .... } [RT #35194] 3706. [contrib] queryperf: Fixed a possible integer overflow when printing results. [RT #35182] 3704. [protocol] Accept integer timestamps in RRSIG records. [RT #35185] --- 9.9.5rc1 released --- 3701. [func] named-checkconf can now obscure shared secrets when printing by specifying '-x'. [RT #34465] 3699. [bug] Improvements to statistics channel XSL stylesheet: the stylesheet can now be cached by the browser; section headers are omitted from the stats display when there is no data in those sections to be displayed; counters are now right-justified for easier readability. (Only available with configure --enable-newstats.) [RT #35117] 3698. [cleanup] Replaced all uses of memcpy() with memmove(). [RT #35120] 3697. [bug] Handle "." as a search list element when IDN support is enabled. [RT #35133] 3696. [bug] dig failed to handle AXFR style IXFR responses which span multiple messages. [RT #35137] 3695. [bug] Address a possible race in dispatch.c. [RT #35107] 3694. [bug] Warn when a key-directory is configured for a zone, but does not exist or is not a directory. [RT #35108] 3693. [security] memcpy was incorrectly called with overlapping ranges resulting in malformed names being generated on some platforms. This could cause INSIST failures when serving NSEC3 signed zones (CVE-2014-0591). [RT #35120] 3692. [bug] Two calls to dns_db_getoriginnode were fatal if there was no data at the node. [RT #35080] 3690. [bug] Iterative responses could be missed when the source port for an upstream query was the same as the listener port (53). [RT #34925] 3689. [bug] Fixed a bug causing an insecure delegation from one static-stub zone to another to fail with a broken trust chain. [RT #35081] --- 9.9.5b1 released --- 3688. [bug] loadnode could return a freed node on out of memory. [RT #35106] 3687. [bug] Address null pointer dereference in zone_xfrdone. [RT #35042] 3686. [func] "dnssec-signzone -Q" drops signatures from keys that are still published but no longer active. [RT #34990] 3685. [bug] "rndc refresh" didn't work correctly with slave zones using inline-signing. [RT #35105] 3683. [cleanup] Add a more detailed "not found" message to rndc commands which specify a zone name. [RT #35059] 3682. [bug] Correct the behavior of rndc retransfer to allow inline-signing slave zones to retain NSEC3 parameters instead of reverting to NSEC. [RT #34745] 3681. [port] Update the Windows build system to support feature selection and WIN64 builds. This is a work in progress. [RT #34160] 3679. [bug] dig could fail to clean up TCP sockets still waiting on connect(). [RT #35074] 3678. [port] Update config.guess and config.sub. [RT #35060] 3677. [bug] 'nsupdate' leaked memory if 'realm' was used multiple times. [RT #35073] 3676. [bug] "named-checkconf -z" now checks zones of type hint and redirect as well as master. [RT #35046] 3675. [misc] Provide a place for third parties to add version information for their extensions in the version file by setting the EXTENSIONS variable. 3674. [bug] RPZ zeroed ttls if the query type was '*'. [RT #35026] 3672. [func] Local address can now be specified when using dns_client API. [RT #34811] 3671. [bug] Don't allow dnssec-importkey overwrite a existing non-imported private key. 3670. [bug] Address read after free in server side of lwres_getrrsetbyname. [RT #29075] 3669. [port] freebsd: --with-gssapi needs -lhx509. [RT #35001] 3668. [bug] Fix cast in lex.c which could see 0xff treated as eof. [RT #34993] 3667. [test] dig: add support to keep the TCP socket open between successive queries (+[no]keepopen). [RT #34918] 3665. [bug] Failure to release lock on error in receive_secure_db. [RT #34944] 3664. [bug] Updated OpenSSL PKCS#11 patches to fix active list locking and other bugs. [RT #34855] 3663. [bug] Address bugs in dns_rdata_fromstruct and dns_rdata_tostruct for WKS and ISDN types. [RT #34910] 3662. [bug] 'host' could die if a UDP query timed out. [RT #34870] 3661. [bug] Address lock order reversal deadlock with inline zones. [RT #34856] 3660. [cleanup] Changed the name of "isc-config.sh" to "bind9-config". [RT #23825] 3659. [port] solaris: don't add explict dependancies/rules for python programs as make won't use the implicit rules. [RT #34835] 3658. [port] linux: Address platform specific compilation issue when libcap-devel is installed. [RT #34838] 3657. [port] Some readline clones don't accept NULL pointers when calling add_history. [RT #34842] 3656. [security] Treat an all zero netmask as invalid when generating the localnets acl. (The prior behavior could allow unexpected matches when using some versions of Winsock: CVE-2013-6320.) [RT #34687] 3655. [cleanup] Simplify TCP message processing when requesting a zone transfer. [RT #34825] 3654. [bug] Address race condition with manual notify requests. [RT #34806] 3653. [func] Create delegations for all "children" of empty zones except "forward first". [RT #34826] 3651. [tuning] Adjust when a master server is deemed unreachable. [RT #27075] 3650. [tuning] Use separate rate limiting queues for refresh and notify requests. [RT #30589] 3649. [cleanup] Include a comment in .nzf files, giving the name of the associated view. [RT #34765] 3648. [test] Updated the ATF test framework to version 0.17. [RT #25627] 3647. [bug] Address a race condition when shutting down a zone. [RT #34750] 3646. [bug] Journal filename string could be set incorrectly, causing garbage in log messages. [RT #34738] 3645. [protocol] Use case sensitive compression when responding to queries. [RT #34737] 3644. [protocol] Check that EDNS subnet client options are well formed. [RT #34718] 3642. [func] Allow externally generated DNSKEY to be imported into the DNSKEY management framework. A new tool dnssec-importkey is used to do this. [RT #34698] 3641. [bug] Handle changes to sig-validity-interval settings better. [RT #34625] 3640. [bug] ndots was not being checked when searching. Only continue searching on NXDOMAIN responses. Add the ability to specify ndots to nslookup. [RT #34711] 3639. [bug] Treat type 65533 (KEYDATA) as opaque except when used in a key zone. [RT #34238] --- 9.9.4 released --- 3643. [doc] Clarify RRL "slip" documentation. 3638. [cleanup] Add the ability to handle ENOPROTOOPT in case it is encountered. [RT #34668] --- 9.9.4rc2 released --- 3637. [bug] 'allow-query-on' was checking the source address rather than the destination address. [RT #34590] 3636. [bug] Automatic empty zones now behave better with forward only "zones" beneath them. [RT #34583] 3635. [bug] Signatures were not being removed from a zone with only KSK keys for a algorithm. [RT #34439] 3634. [func] Report build-id in rndc status. Report build-id when building from a git repository. [RT #20422] 3633. [cleanup] Refactor OPT processing in named to make it easier to support new EDNS options. [RT #34414] 3632. [bug] Signature from newly inactive keys were not being removed. [RT #32178] 3631. [bug] Remove spurious warning about missing signatures when qtype is SIG. [RT #34600] 3630. [bug] Ensure correct ID computation for MD5 keys. [RT #33033] 3627. [bug] RPZ changes were not effective on slaves. [RT #34450] 3625. [bug] Don't send notify messages to machines outside of the test setup. 3623. [bug] zone-statistics was only effective in new statistics. [RT #34466] --- 9.9.4rc1 released --- 3621. [security] Incorrect bounds checking on private type 'keydata' can lead to a remotely triggerable REQUIRE failure (CVE-2013-4854). [RT #34238] 3617. [bug] Named was failing to answer queries during "rndc reload" [RT #34098] 3616. [bug] Change #3613 was incomplete. [RT #34177] 3615. [cleanup] "configure" now finishes by printing a summary of optional BIND features and whether they are active or inactive. ("configure --enable-full-report" increases the verbosity of the summary.) [RT #31777] 3614. [port] Check for . [RT #34162] 3613. [bug] named could crash when deleting inline-signing zones with "rndc delzone". [RT #34066] 3611. [bug] Improved resistance to a theoretical authentication attack based on differential timing. [RT #33939] 3610. [cleanup] win32: Some executables had been omitted from the installer. [RT #34116] 3608. [port] win32: added todos.pl script to ensure all text files the win32 build depends on are converted to DOS newline format. [RT #22067] 3607. [bug] dnssec-keygen had broken 'Invalid keyfile' error message. [RT #34045] --- 9.9.4b1 released --- 3605. [port] win32: Addressed several compatibility issues with newer versions of Visual Studio. [RT #33916] 3603. [bug] Install . [RT #33956] 3601. [bug] Added to PKCS#11 openssl patches a value len attribute in DH derive key. [RT #33928] 3600. [cleanup] dig: Fixed a typo in the warning output when receiving an oversized response. [RT #33910] 3599. [tuning] Check for pointer equivalence in name comparisons. [RT #18125] 3596. [port] Updated win32 build documentation, added dnssec-verify. [RT #22067] 3594. [maint] Update config.guess and config.sub. [RT #33816] 3592. [doc] Moved documentation of rndc command options to the rndc man page. [RT #33506] 3590. [bug] When using RRL on recursive servers, defer rate-limiting until after recursion is complete; also, use correct rcode for slipped NXDOMAIN responses. [RT #33604] 3588. [bug] dig: addressed a memory leak in the sigchase code that could cause a shutdown crash. [RT #33733] 3587. [func] 'named -g' now checks the logging configuration but does not use it. [RT #33473] 3586. [bug] Handle errors in xmlDocDumpFormatMemoryEnc. [RT #33706] 3584. [security] Caching data from an incompletely signed zone could trigger an assertion failure in resolver.c (CVE-2013-3919). [RT #33690] 3583. [bug] Address memory leak in GSS-API processing [RT #33574] 3582. [bug] Silence false positive warning regarding missing file directive for inline slave zones. [RT #33662] 3581. [bug] Changed the tcp-listen-queue default to 10. [RT #33029] 3580. [bug] Addressed a possible race in acache.c [RT #33602] 3579. [maint] Updates to PKCS#11 openssl patches, supporting versions 0.9.8y, 1.0.0k, 1.0.1e [RT #33463] 3578. [bug] 'rndc -c file' now fails if 'file' does not exist. [RT #33571] 3577. [bug] Handle zero TTL values better. [RT #33411] 3576. [bug] Address a shutdown race when validating. [RT #33573] 3575. [func] Changed the logging category for RRL events from 'queries' to 'query-errors'. [RT #33540] 3574. [doc] The 'hostname' keyword was missing from server-id description in the named.conf man page. [RT #33476] 3573. [bug] "rndc addzone" and "rndc delzone" incorrectly handled zone names containing punctuation marks and other nonstandard characters. [RT #33419] 3571. [bug] Address race condition in dns_client_startresolve(). [RT #33234] 3566. [func] Log when forwarding updates to master. [RT #33240] 3554. [bug] RRL failed to correctly rate-limit upward referrals and failed to count dropped error responses in the statistics. [RT #33225] 3545. [bug] RRL slip behavior was incorrect when set to 1. [RT #33111] 3518. [bug] Increase the size of dns_rrl_key.s.rtype by one bit so that all dns_rrl_rtype_t enum values fit regardless of whether it is teated as signed or unsigned by the compiler. [RT #32792] 3494. [func] DNS RRL: Blunt the impact of DNS reflection and amplification attacks by rate-limiting substantially- identical responses. To enable, use "configure --enable-rrl". [RT #28130] --- 9.9.3 released --- 3568. [cleanup] Add a product description line to the version file, to be reported by named -v/-V. [RT #33366] 3567. [bug] Silence clang static analyzer warnings. [RT #33365] 3563. [contrib] zone2sqlite failed with some table names. [RT #33375] 3561. [bug] dig: issue a warning if an EDNS query returns FORMERR or NOTIMP. Adjust usage message. [RT #33363] --- 9.9.3rc2 released --- 3560. [bug] isc-config.sh did not honor includedir and libdir when set via configure. [RT #33345] 3559. [func] Check that both forms of Sender Policy Framework records exist or do not exist. [RT #33355] 3558. [bug] IXFR of a DLZ stored zone was broken. [RT #33331] 3557. [bug] Reloading redirect zones was broken. [RT #33292] 3556. [maint] Added AAAA for D.ROOT-SERVERS.NET. 3555. [bug] Address theoretical race conditions in acache.c (change #3553 was incomplete). [RT #33252] 3553. [bug] Address suspected double free in acache. [RT #33252] 3552. [bug] Wrong getopt option string for 'nsupdate -r'. [RT #33280] 3549. [doc] Documentation for "request-nsid" was missing. [RT #33153] 3548. [bug] The NSID request code in resolver.c was broken resulting in invalid EDNS options being sent. [RT #33153] 3547. [bug] Some malformed unknown rdata records were not properly detected and rejected. [RT #33129] --- 9.9.3rc1 released --- 3546. [func] Add EUI48 and EUI64 types. [RT #33082] 3544. [contrib] check5011.pl: Script to report the status of managed keys as recorded in managed-keys.bind. Contributed by Tony Finch 3543. [bug] Update socket structure before attaching to socket manager after accept. [RT #33084] 3541. [bug] Parts of libdns were not properly initialized when built in libexport mode. [RT #33028] 3540. [test] libt_api: t_info and t_assert were not thread safe. 3539. [port] win32: timestamp format didn't match other platforms. 3538. [test] Running "make test" now requires loopback interfaces to be set up. [RT #32452] 3537. [tuning] Slave zones, when updated, now send NOTIFY messages to peers before being dumped to disk rather than after. [RT #27242] 3535. [bug] Minor win32 cleanups. [RT #32962] 3534. [bug] Extra text after an embedded NULL was ignored when parsing zone files. [RT #32699] 3533. [contrib] query-loc-0.4.0: memory leaks. [RT #32960] 3532. [contrib] zkt: fixed buffer overrun, resource leaks. [RT #32960] 3531. [bug] win32: A uninitialized value could be returned on out of memory. [RT #32960] 3530. [contrib] Better RTT tracking in queryperf. [RT #30128] 3528. [func] New "dnssec-coverage" command scans the timing metadata for a set of DNSSEC keys and reports if a lapse in signing coverage has been scheduled inadvertently. (Note: This tool depends on python; it will not be built or installed on systems that do not have a python interpreter.) [RT #28098] 3527. [compat] Add a URI to allow applications to explicitly request a particular XML schema from the statistics channel, returning 404 if not supported. [RT #32481] 3526. [cleanup] Set up dependencies for unit tests correctly during build. [RT #32803] 3521. [bug] Address memory leak in opensslecdsa_link.c. [RT #32249] 3520. [bug] 'mctx' was not being referenced counted in some places where it should have been. [RT #32794] --- 9.9.3b2 released --- 3517. [bug] Reorder destruction to avoid shutdown race. [RT #32777] 3515. [port] '%T' is not portable in strftime(). [RT #32763] 3514. [bug] The ranges for valid key sizes in ddns-confgen and rndc-confgen were too constrained. Keys up to 512 bits are now allowed for most algorithms, and up to 1024 bits for hmac-sha384 and hmac-sha512. [RT #32753] 3511. [doc] Improve documentation of redirect zones. [RT #32756] 3509. [cleanup] Added a product line to version file to allow for easy naming of different products (BIND vs BIND ESV, for example). [RT #32755] 3508. [contrib] queryperf was incorrectly rejecting the -T option. [RT #32338] 3507. [bug] Statistics channel XSL (when built with --enable-newstats) had a glitch when attempting to chart query data before any queries had been received. [RT #32620] 3505. [bug] When setting "max-cache-size" and "max-acache-size", larger values than 4 gigabytes could not be set explicitly, though larger sizes were available when setting cache size to 0. This has been corrected; the full range is now available. [RT #32358] 3503. [doc] Clarify size_spec syntax. [RT #32449] 3501. [func] zone-statistics now takes three options: full, terse, and none. "yes" and "no" are retained as synonyms for full and terse, respectively. [RT #29165] 3500. [security] Support NAPTR regular expression validation on all platforms without using libregex, which can be vulnerable to memory exhaustion attack (CVE-2013-2266). [RT #32688] 3499. [doc] Corrected ARM documentation of built-in zones. [RT #32694] 3498. [bug] zone statistics for zones which matched a potential empty zone could have their zone-statistics setting overridden. 3496. [func] Improvements to RPZ performance. The "response-policy" syntax now includes a "min-ns-dots" clause, with default 1, to exclude top-level domains from NSIP and NSDNAME checking. --enable-rpz-nsip and --enable-rpz-nsdname are now the default. [RT #32251] 3493. [contrib] Added BDBHPT dynamically-lodable DLZ module, contributed by Mark Goldfinch. [RT #32549] 3492. [bug] Fixed a regression in zone loading performance due to lock contention. [RT #30399] 3491. [bug] Slave zones using inline-signing must specify a file name. [RT #31946] 3489. [bug] --enable-developer now turns on ISC_LIST_CHECKINIT. When cloning a rdataset do not copy the link contents. [RT #32651] 3488. [bug] Use after free error with DH generated keys. [RT #32649] 3487. [bug] Change 3444 was not complete. There was a additional place where the NOQNAME proof needed to be saved. [RT #32629] 3486. [bug] named could crash when using TKEY-negotiated keys that had been deleted and then recreated. [RT #32506] 3485. [cleanup] Only compile openssl_gostlink.c if we support GOST. 3483. [bug] Corrected XSL code in use with --enable-newstats. [RT #32587] 3481. [cleanup] Removed use of const const in atf. 3480. [bug] Silence logging noise when setting up zone statistics. [RT #32525] 3479. [bug] Address potential memory leaks in gssapi support code. [RT #32405] 3478. [port] Fix a build failure in strict C99 environments [RT #32475] 3474. [bug] nsupdate could assert when the local and remote address families didn't match. [RT #22897] 3473. [bug] dnssec-signzone/verify could incorrectly report an error condition due to an empty node above an opt-out delegation lacking an NSEC3. [RT #32072] 3471. [bug] The number of UDP dispatches now defaults to the number of CPUs even if -n has been set to a higher value. [RT #30964] 3470. [bug] Slave zones could fail to dump when successfully refreshing after an initial failure. [RT #31276] --- 9.9.3b1 released --- 3468. [security] RPZ rules to generate A records (but not AAAA records) could trigger an assertion failure when used in conjunction with DNS64 (CVE-2012-5689). [RT #32141] 3467. [bug] Added checks in dnssec-keygen and dnssec-settime to check for delete date < inactive date. [RT #31719] 3466. [contrib] Corrected the DNS_CLIENTINFOMETHODS_VERSION check in DLZ example driver. [RT #32275] 3465. [bug] Handle isolated reserved ports. [RT #31778] 3464. [maint] Updates to PKCS#11 openssl patches, supporting versions 0.9.8x, 1.0.0j, 1.0.1c [RT #29749] 3463. [doc] Clarify managed-keys syntax in ARM. [RT #32232] 3462. [doc] Clarify server selection behavior of dig when using -4 or -6 options. [RT #32181] 3461. [bug] Negative responses could incorrectly have AD=1 set. [RT #32237] 3460. [bug] Only link against readline where needed. [RT #29810] 3458. [bug] Return FORMERR when presented with a overly long domain named in a request. [RT #29682] 3457. [protocol] Add ILNP records (NID, LP, L32, L64). [RT #31836] 3456. [port] g++47: ATF failed to compile. [RT #32012] 3455. [contrib] queryperf: fix getopt option list. [RT #32338] 3454. [port] sparc64: improve atomic support. [RT #25182] 3453. [bug] 'rndc addzone' of a zone with 'inline-signing yes;' failed. [RT #31960] 3452. [bug] Accept duplicate singleton records. [RT #32329] 3451. [port] Increase per thread stack size from 64K to 1M. [RT #32230] 3450. [bug] Stop logfileconfig system test spam system logs. [RT #32315] 3449. [bug] gen.c: use the pre-processor to construct format strings so that compiler can perform sanity checks; check the snprintf results. [RT #17576] 3448. [bug] The allow-query-on ACL was not processed correctly. [RT #29486] 3447. [port] Add support for libxml2-2.9.x [RT #32231] 3446. [port] win32: Add source ID (see change #3400) to build. [RT #31683] 3445. [bug] Warn about zone files with blank owner names immediately after $ORIGIN directives. [RT #31848] 3444. [bug] The NOQNAME proof was not being returned from cached insecure responses. [RT #21409] 3443. [bug] ddns-confgen: Some TSIG algorithms were incorrectly rejected when generating keys. [RT #31927] 3442. [port] Net::DNS 0.69 introduced a non backwards compatible change. [RT #32216] 3441. [maint] D.ROOT-SERVERS.NET is now 199.7.91.13. 3440. [bug] Reorder get_key_struct to not trigger a assertion when cleaning up due to out of memory error. [RT #32131] 3439. [bug] contrib/dlz error checking fixes. [RT #32102] 3438. [bug] Don't accept unknown data escape in quotes. [RT #32031] 3437. [bug] isc_buffer_init -> isc_buffer_constinit to initialize buffers with constant data. [RT #32064] 3436. [bug] Check malloc/calloc return values. [RT #32088] 3435. [bug] Cross compilation support in configure was broken. [RT #32078] 3431. [bug] ddns-confgen: Some valid key algorithms were not accepted. [RT #31927] 3430. [bug] win32: isc_time_formatISO8601 was missing the 'T' between the date and time. [RT #32044] 3429. [bug] dns_zone_getserial2 could a return success without returning a valid serial. [RT #32007] 3428. [cleanup] dig: Add timezone to date output. [RT #2269] 3427. [bug] dig +trace incorrectly displayed name server addresses instead of names. [RT #31641] 3426. [bug] dnssec-checkds: Clearer output when records are not found. [RT #31968] 3425. [bug] "acacheentry" reference counting was broken resulting in use after free. [RT #31908] 3424. [func] dnssec-dsfromkey now emits the hash without spaces. [RT #31951] 3423. [bug] "rndc signing -nsec3param" didn't accept the full range of possible values. Address portability issues. [RT #31938] 3422. [bug] Added a clear error message for when the SOA does not match the referral. [RT #31281] 3421. [bug] Named loops when re-signing if all keys are offline. [RT #31916] 3420. [bug] Address VPATH compilation issues. [RT #31879] 3419. [bug] Memory leak on validation cancel. [RT #31869] 3417. [func] Optional new XML schema (version 3.0) for the statistics channel adds query type statistics at the zone level, and flattens the XML tree and uses compressed format to optimize parsing. Includes new XSL that permits charting via the Google Charts API on browsers that support javascript in XSL. To enable, build with "configure --enable-newstats". [RT #30023] 3416. [bug] Named could die on shutdown if running with 128 UDP dispatches per interface. [RT #31743] 3415. [bug] named could die with a REQUIRE failure if a validation was canceled. [RT #31804] 3414. [bug] Address locking issues found by Coverity. [RT #31626] 3412. [bug] Copy timeval structure from control message data. [RT #31548] 3411. [tuning] Use IPV6_USE_MIN_MTU or equivalent with TCP in addition to UDP. [RT #31690] 3410. [bug] Addressed Coverity warnings. [RT #31626] 3409. [contrib] contrib/dane/mkdane.sh: Tool to generate TLSA RR's from X.509 certificates, for use with DANE (DNS-based Authentication of Named Entities). [RT #30513] 3408. [bug] Some DNSSEC-related options (update-check-ksk, dnssec-loadkeys-interval, dnssec-dnskey-kskonly) are now legal in slave zones as long as inline-signing is in use. [RT #31078] 3406. [bug] mem.c: Fix compilation errors when building with ISC_MEM_TRACKLINES or ISC_MEMPOOL_NAMES disabled. Also, ISC_MEM_DEBUG is no longer optional. [RT #31559] 3405. [bug] Handle time going backwards in acache. [RT #31253] 3404. [bug] dnssec-signzone: When re-signing a zone, remove RRSIG and NSEC records from nodes that used to be in-zone but are now below a zone cut. [RT #31556] 3403. [bug] Silence noisy OpenSSL logging. [RT #31497] 3402. [test] The IPv6 interface numbers used for system tests were incorrect on some platforms. [RT #25085] 3401. [bug] Addressed Coverity warnings. [RT #31484] 3400. [cleanup] "named -V" can now report a source ID string, defined in the "srcid" file in the build tree and normally set to the most recent git hash. [RT #31494] 3399. [port] netbsd: rename 'bool' parameter to avoid namespace clash. [RT #31515] 3398. [bug] SOA parameters were not being updated with inline signed zones if the zone was modified while the server was offline. [RT #29272] 3397. [bug] dig crashed when using +nssearch with +tcp. [RT #25298] 3396. [bug] OPT records were incorrectly removed from signed, truncated responses. [RT #31439] 3395. [protocol] Add RFC 6598 reverse zones to built in empty zones list, 64.100.IN-ADDR.ARPA ... 127.100.IN-ADDR.ARPA. [RT #31336] 3394. [bug] Adjust 'successfully validated after lower casing signer' log level and category. [RT #31414] 3393. [bug] 'host -C' could core dump if REFUSED was received. [RT #31381] 3391. [bug] A DNSKEY lookup that encountered a CNAME failed. [RT #31262] 3390. [bug] Silence clang compiler warnings. [RT #30417] 3389. [bug] Always return NOERROR (not 0) in TSIG. [RT #31275] 3388. [bug] Fixed several Coverity warnings. Note: This change includes a fix for a bug that was subsequently determined to be an exploitable security vulnerability, CVE-2012-5688: named could die on specific queries with dns64 enabled. [RT #30996] 3386. [bug] Address locking violation when generating new NSEC / NSEC3 chains. [RT #31224] 3385. [bug] named-checkconf didn't detect missing master lists in also-notify clauses. [RT #30810] 3384. [bug] Improved logging of crypto errors. [RT #30963] 3382. [bug] SOA query from slave used use-v6-udp-ports range, if set, regardless of the address family in use. [RT #24173] 3381. [contrib] Update queryperf to support more RR types. [RT #30762] 3380. [bug] named could die if a nonexistent master list was referenced in a also-notify. [RT #31004] 3379. [bug] isc_interval_zero and isc_time_epoch should be "const (type)* const". [RT #31069] 3378. [bug] Handle missing 'managed-keys-directory' better. [RT #30625] 3377. [bug] Removed spurious newline from NSEC3 multiline output. [RT #31044] 3376. [bug] Lack of EDNS support was being recorded without a successful response. [RT #30811] 3375. [func] Check that 'rndc dumpdb' works on a empty cache. [RT #30808] 3374. [bug] isc_parse_uint32 failed to return a range error on systems with 64 bit longs. [RT #30232] 3372. [bug] Silence spurious "deleted from unreachable cache" messages. [RT #30501] 3371. [bug] AD=1 should behave like DO=1 when deciding whether to add NS RRsets to the additional section or not. [RT #30479] 3316. [tuning] Improved locking performance when recursing. [RT #28836] 3315. [tuning] Use multiple dispatch objects for sending upstream queries; this can improve performance on busy multiprocessor systems by reducing lock contention. [RT #28605] --- 9.9.2 released --- 3383. [security] A certain combination of records in the RBT could cause named to hang while populating the additional section of a response. [RT #31090] 3373. [bug] win32: open raw files in binary mode. [RT #30944] 3364. [security] Named could die on specially crafted record. [RT #30416] --- 9.9.2rc1 released --- 3370. [bug] Address use after free while shutting down. [RT #30241] 3369. [bug] nsupdate terminated unexpectedly in interactive mode if built with readline support. [RT #29550] 3368. [bug] , and were not C++ safe. 3367. [bug] dns_dnsseckey_create() result was not being checked. [RT #30685] 3366. [bug] Fixed Read-After-Write dependency violation for IA64 atomic operations. [RT #25181] 3365. [bug] Removed spurious newlines from log messages in zone.c [RT #30675] 3363. [bug] Need to allow "forward" and "fowarders" options in static-stub zones; this had been overlooked. [RT #30482] 3362. [bug] Setting some option values to 0 in named.conf could trigger an assertion failure on startup. [RT #27730] 3361. [bug] "rndc signing -nsec3param" didn't work correctly when salt was set to '-' (no salt). [RT #30099] 3360. [bug] 'host -w' could die. [RT #18723] 3359. [bug] An improperly-formed TSIG secret could cause a memory leak. [RT #30607] 3357. [port] Add support for libxml2-2.8.x [RT #30440] 3356. [bug] Cap the TTL of signed RRsets when RRSIGs are approaching their expiry, so they don't remain in caches after expiry. [RT #26429] 3355. [port] Use more portable awk in verify system test. 3354. [func] Improve OpenSSL error logging. [RT #29932] --- 9.9.2b1 released --- 3353. [bug] Use a single task for task exclusive operations. [RT #29872] 3352. [bug] Ensure that learned server attributes timeout of the adb cache. [RT #29856] 3351. [bug] isc_mem_put and isc_mem_putanddetach didn't report caller if either ISC_MEM_DEBUGSIZE or ISC_MEM_DEBUGCTX memory debugging flags are set. [RT #30243] 3350. [bug] Memory read overrun in isc___mem_reallocate if ISC_MEM_DEBUGCTX memory debugging flag is set. [RT #30240] 3349. [bug] Change #3345 was incomplete. [RT #30233] 3348. [bug] Prevent RRSIG data from being cached if a negative record matching the covering type exists at a higher trust level. Such data already can't be retrieved from the cache since change 3218 -- this prevents it being inserted into the cache as well. [RT #26809] 3347. [bug] dnssec-settime: Issue a warning when writing a new private key file would cause a change in the permissions of the existing file. [RT #27724] 3346. [security] Bad-cache data could be used before it was initialized, causing an assert. [RT #30025] 3345. [bug] Addressed race condition when removing the last item or inserting the first item in an ISC_QUEUE. [RT #29539] 3344. [func] New "dnssec-checkds" command checks a zone to determine which DS records should be published in the parent zone, or which DLV records should be published in a DLV zone, and queries the DNS to ensure that it exists. (Note: This tool depends on python; it will not be built or installed on systems that do not have a python interpreter.) [RT #28099] 3342. [bug] Change #3314 broke saving of stub zones to disk resulting in excessive cpu usage in some cases. [RT #29952] 3341. [func] New "dnssec-verify" command checks a signed zone to ensure correctness of signatures and of NSEC/NSEC3 chains. [RT #23673] 3339. [func] Allow the maximum supported rsa exponent size to be specified: "max-rsa-exponent-size ;" [RT #29228] 3338. [bug] Address race condition in units tests: asyncload_zone and asyncload_zt. [RT #26100] 3337. [bug] Change #3294 broke support for the multiple keys in controls. [RT #29694] 3335. [func] nslookup: return a nonzero exit code when unable to get an answer. [RT #29492] 3334. [bug] Hold a zone table reference while performing a asynchronous load of a zone. [RT #28326] 3333. [bug] Setting resolver-query-timeout too low can cause named to not recover if it loses connectivity. [RT #29623] 3332. [bug] Re-use cached DS rrsets if possible. [RT #29446] 3331. [security] dns_rdataslab_fromrdataset could produce bad rdataslabs. [RT #29644] 3330. [func] Fix missing signatures on NOERROR results despite RPZ rewriting. Also - add optional "recursive-only yes|no" to the response-policy statement - add optional "max-policy-ttl" to the response-policy statement to limit the false data that "recursive-only no" can introduce into resolvers' caches - add a RPZ performance test to bin/tests/system/rpz when queryperf is available. - the encoding of PASSTHRU action to "rpz-passthru". (The old encoding is still accepted.) [RT #26172] 3329. [bug] Handle RRSIG signer-name case consistently: We generate RRSIG records with the signer-name in lower case. We accept them with any case, but if they fail to validate, we try again in lower case. [RT #27451] 3328. [bug] Fixed inconsistent data checking in dst_parse.c. [RT #29401] 3317. [func] Add ECDSA support (RFC 6605). [RT #21918] --- 9.9.1 released --- 3318. [tuning] Reduce the amount of work performed while holding a bucket lock when finished with a fetch context. [RT #29239] 3314. [bug] The masters list could be updated while stub_callback or refresh_callback were using it. [RT #26732] 3313. [protocol] Add TLSA record type. [RT #28989] 3312. [bug] named-checkconf didn't detect a bad dns64 clients acl. [RT #27631] 3311. [bug] Abort the zone dump if zone->db is NULL in zone.c:zone_gotwritehandle. [RT #29028] 3310. [test] Increase table size for mutex profiling. [RT #28809] 3309. [bug] resolver.c:fctx_finddone() was not thread safe. [RT #27995] 3307. [bug] Add missing ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS. [RT #28956] 3306. [bug] Improve DNS64 reverse zone performance. [RT #28563] 3305. [func] Add wire format lookup method to sdb. [RT #28563] 3304. [bug] Use hmctx, not mctx when freeing rbtdb->heaps. [RT #28571] 3303. [bug] named could die when reloading. [RT #28606] 3302. [bug] dns_dnssec_findmatchingkeys could fail to find keys if the zone name contained character that required special mappings. [RT #28600] 3301. [contrib] Update queryperf to build on darwin. Add -R flag for non-recursive queries. [RT #28565] 3300. [bug] Named could die if gssapi was enabled in named.conf but was not compiled in. [RT #28338] 3299. [bug] Make SDB handle errors from database drivers better. [RT #28534] 3298. [bug] Named could dereference a NULL pointer in zmgr_start_xfrin_ifquota if the zone was being removed. [RT #28419] 3297. [bug] Named could die on a malformed master file. [RT #28467] 3296. [bug] Named could die with a INSIST failure in client.c:exit_check. [RT #28346] 3295. [bug] Adjust isc_time_secondsastimet range check to be more portable. [RT # 26542] 3294. [bug] isccc/cc.c:table_fromwire failed to free alist on error. [RT #28265] 3291. [port] Fixed a build error on systems without ENOTSUP. [RT #28200] 3290. [bug] was not being installed. [RT #28169] 3273. [bug] AAAA responses could be returned in the additional section even when filter-aaaa-on-v4 was in use. [RT #27292] --- 9.9.0 released --- --- 9.9.0rc4 released --- 3289. [bug] 'rndc retransfer' failed for inline zones. [RT #28036] 3288. [bug] dlz_destroy() function wasn't correctly registered by the DLZ dlopen driver. [RT #28056] 3287. [port] Update ans.pl to work with Net::DNS 0.68. [RT #28028] 3286. [bug] Managed key maintenance timer could fail to start after 'rndc reconfig'. [RT #26786] --- 9.9.0rc3 released --- 3285. [bug] val-frdataset was incorrectly disassociated in proveunsecure after calling startfinddlvsep. [RT #27928] 3284. [bug] Address race conditions with the handling of rbtnode.deadlink. [RT #27738] 3283. [bug] Raw zones with with more than 512 records in a RRset failed to load. [RT #27863] 3282. [bug] Restrict the TTL of NS RRset to no more than that of the old NS RRset when replacing it. [RT #27792] [RT #27884] 3281. [bug] SOA refresh queries could be treated as cancelled despite succeeding over the loopback interface. [RT #27782] 3280. [bug] Potential double free of a rdataset on out of memory with DNS64. [RT #27762] 3279. [bug] Hold a internal reference to the zone while performing a asynchronous load. Address potential memory leak if the asynchronous is cancelled. [RT #27750] 3278. [bug] Make sure automatic key maintenance is started when "auto-dnssec maintain" is turned on during "rndc reconfig". [RT #26805] 3277. [bug] win32: isc_socket_dup is not implemented. [RT #27696] 3276. [bug] win32: ns_os_openfile failed to return NULL on safe_open failure. [RT #27696] 3275. [bug] Corrected rndc -h output; the 'rndc sync -clean' option had been misspelled as '-clear'. (To avoid future confusion, both options now work.) [RT #27173] 3271. [port] darwin: mksymtbl is not always stable, loop several times before giving up. mksymtbl was using non portable perl to covert 64 bit hex strings. [RT #27653] --- 9.9.0rc2 released --- 3270. [bug] "rndc reload" didn't reuse existing zones correctly when inline-signing was in use. [RT #27650] 3269. [port] darwin 11 and later now built threaded by default. 3268. [bug] Convert RRSIG expiry times to 64 timestamps to work out the earliest expiry time. [RT #23311] 3267. [bug] Memory allocation failures could be mis-reported as unexpected error. New ISC_R_UNSET result code. [RT #27336] 3266. [bug] The maximum number of NSEC3 iterations for a DNSKEY RRset was not being properly computed. [RT #26543] 3265. [bug] Corrected a problem with lock ordering in the inline-signing code. [RT #27557] 3264. [bug] Automatic regeneration of signatures in an inline-signing zone could stall when the server was restarted. [RT #27344] 3263. [bug] "rndc sync" did not affect the unsigned side of an inline-signing zone. [RT #27337] 3262. [bug] Signed responses were handled incorrectly by RPZ. [RT #27316] 3261. [func] RRset ordering now defaults to random. [RT #27174] 3260. [bug] "rrset-order cyclic" could appear not to rotate for some query patterns. [RT #27170/27185] --- 9.9.0rc1 released --- 3259. [bug] named-compilezone: Suppress "dump zone to " message when writing to stdout. [RT #27109] 3258. [test] Add "forcing full sign with unreadable keys" test. [RT #27153] 3257. [bug] Do not generate a error message when calling fsync() in a pipe or socket. [RT #27109] 3256. [bug] Disable empty zones for lwresd -C. [RT #27139] 3255. [func] No longer require that a empty zones be explicitly enabled or that a empty zone is disabled for RFC 1918 empty zones to be configured. [RT #27139] 3254. [bug] Set isc_socket_ipv6only() on the IPv6 control channels. [RT #22249] 3253. [bug] Return DNS_R_SYNTAX when the input to a text field is too long. [RT #26956] 3252. [bug] When master zones using inline-signing were updated while the server was offline, the source zone could fall out of sync with the signed copy. They can now resynchronize. [RT #26676] 3251. [bug] Enforce a upper bound (65535 bytes) on the amount of memory dns_sdlz_putrr() can allocate per record to prevent run away memory consumption on ISC_R_NOSPACE. [RT #26956] 3250. [func] 'configure --enable-developer'; turn on various configure options, normally off by default, that we want developers to build and test with. [RT #27103] 3249. [bug] Update log message when saving slave zones files for analysis after load failures. [RT #27087] 3248. [bug] Configure options --enable-fixed-rrset and --enable-exportlib were incompatible with each other. [RT #27087] 3247. [bug] 'raw' format zones failed to preserve load order breaking 'fixed' sort order. [RT #27087] 3246. [bug] Named failed to start with a empty also-notify list. [RT #27087] 3245. [bug] Don't report a error unchanged serials unless there were other changes when thawing a zone with ixfr-fromdifferences. [RT #26845] 3244. [func] Added readline support to nslookup and nsupdate. Also simplified nsupdate syntax to make "update" and "prereq" optional. [RT #24659] 3243. [port] freebsd,netbsd,bsdi: the thread defaults were not being properly set. 3242. [func] Extended the header of raw-format master files to include the serial number of the zone from which they were generated, if different (as in the case of inline-signing zones). This is to be used in inline-signing zones, to track changes between the unsigned and signed versions of the zone, which may have different serial numbers. (Note: raw zonefiles generated by this version of BIND are no longer compatible with prior versions. To generate a backward-compatible raw zonefile using dnssec-signzone or named-compilezone, specify output format "raw=0" instead of simply "raw".) [RT #26587] 3241. [bug] Address race conditions in the resolver code. [RT #26889] 3240. [bug] DNSKEY state change events could be missed. [RT #26874] 3239. [bug] dns_dnssec_findmatchingkeys needs to use a consistent timestamp. [RT #26883] 3238. [bug] keyrdata was not being reinitialized in lib/dns/rbtdb.c:iszonesecure. [RT#26913] 3237. [bug] dig -6 didn't work with +trace. [RT #26906] 3236. [bug] Backed out changes #3182 and #3202, related to EDNS(0) fallback behavior. [RT #26416] 3235. [func] dns_db_diffx, a extended dns_db_diff which returns the generated diff and optionally writes it to a journal. [RT #26386] 3234. [bug] 'make depend' produced invalid makefiles. [RT #26830] 3233. [bug] 'rndc freeze/thaw' didn't work for inline zones. [RT #26632] 3232. [bug] Zero zone->curmaster before return in dns_zone_setmasterswithkeys(). [RT #26732] 3231. [bug] named could fail to send a incompressible zone. [RT #26796] 3230. [bug] 'dig axfr' failed to properly handle a multi-message axfr with a serial of 0. [RT #26796] 3229. [bug] Fix local variable to struct var assignment found by CLANG warning. 3228. [tuning] Dynamically grow symbol table to improve zone loading performance. [RT #26523] 3227. [bug] Interim fix to make WKS's use of getprotobyname() and getservbyname() self thread safe. [RT #26232] 3226. [bug] Address minor resource leakages. [RT #26624] 3225. [bug] Silence spurious "setsockopt(517, IPV6_V6ONLY) failed" messages. [RT #26507] 3224. [bug] 'rndc signing' argument parsing was broken. [RT #26684] 3223. [bug] 'task_test privilege_drop' generated false positives. [RT #26766] 3222. [cleanup] Replace dns_journal_{get,set}_bitws with dns_journal_{get,set}_sourceserial. [RT #26634] 3221. [bug] Fixed a potential core dump on shutdown due to referencing fetch context after it's been freed. [RT #26720] --- 9.9.0b2 released --- 3220. [bug] Change #3186 was incomplete; dns_db_rpz_findips() could fail to set the database version correctly, causing an assertion failure. [RT #26180] 3219. [bug] Disable NOEDNS caching following a timeout. 3218. [security] Cache lookup could return RRSIG data associated with nonexistent records, leading to an assertion failure. [RT #26590] 3217. [cleanup] Fix build problem with --disable-static. [RT #26476] 3216. [bug] resolver.c:validated() was not thread-safe. [RT #26478] 3215. [bug] 'rndc recursing' could cause a core dump. [RT #26495] 3214. [func] Add 'named -U' option to set the number of UDP listener threads per interface. [RT #26485] 3213. [doc] Clarify ixfr-from-differences behavior. [RT #25188] 3212. [bug] rbtdb.c: failed to remove a node from the deadnodes list prior to adding a reference to it leading a possible assertion failure. [RT #23219] 3211. [func] dnssec-signzone: "-f -" prints to stdout; "-O full" option prints in single-line-per-record format. [RT #20287] 3210. [bug] Canceling the oldest query due to recursive-client overload could trigger an assertion failure. [RT #26463] 3209. [func] Add "dnssec-lookaside 'no'". [RT #24858] 3208. [bug] 'dig -y' handle unknown tsig algorithm better. [RT #25522] 3207. [contrib] Fixed build error in Berkeley DB DLZ module. [RT #26444] 3206. [cleanup] Add ISC information to log at start time. [RT #25484] 3205. [func] Upgrade dig's defaults to better reflect modern nameserver behavior. Enable "dig +adflag" and "dig +edns=0" by default. Enable "+dnssec" when running "dig +trace". [RT #23497] 3204. [bug] When a master server that has been marked as unreachable sends a NOTIFY, mark it reachable again. [RT #25960] 3203. [bug] Increase log level to 'info' for validation failures from expired or not-yet-valid RRSIGs. [RT #21796] 3202. [bug] NOEDNS caching on timeout was too aggressive. [RT #26416] 3201. [func] 'rndc querylog' can now be given an on/off parameter instead of only being used as a toggle. [RT #18351] 3200. [doc] Some rndc functions were undocumented or were missing from 'rndc -h' output. [RT #25555] 3199. [func] When logging client information, include the name being queried. [RT #25944] 3198. [doc] Clarified that dnssec-settime can alter keyfile permissions. [RT #24866] 3197. [bug] Don't try to log the filename and line number when the config parser can't open a file. [RT #22263] 3196. [bug] nsupdate: return nonzero exit code when target zone doesn't exist. [RT #25783] 3195. [cleanup] Silence "file not found" warnings when loading managed-keys zone. [RT #26340] 3194. [doc] Updated RFC references in the 'empty-zones-enable' documentation. [RT #25203] 3193. [cleanup] Changed MAXZONEKEYS to DNS_MAXZONEKEYS, moved to dnssec.h. [RT #26415] 3192. [bug] A query structure could be used after being freed. [RT #22208] 3191. [bug] Print NULL records using "unknown" format. [RT #26392] 3190. [bug] Underflow in error handling in isc_mutexblock_init. [RT #26397] 3189. [test] Added a summary report after system tests. [RT #25517] 3188. [bug] zone.c:zone_refreshkeys() could fail to detach references correctly when errors occurred, causing a hang on shutdown. [RT #26372] 3187. [port] win32: support for Visual Studio 2008. [RT #26356] --- 9.9.0b1 released --- 3186. [bug] Version/db mis-match in rpz code. [RT #26180] 3185. [func] New 'rndc signing' option for auto-dnssec zones: - 'rndc signing -list' displays the current state of signing operations - 'rndc signing -clear' clears the signing state records for keys that have fully signed the zone - 'rndc signing -nsec3param' sets the NSEC3 parameters for the zone The 'rndc keydone' syntax is removed. [RT #23729] 3184. [bug] named had excessive cpu usage when a redirect zone was configured. [RT #26013] 3183. [bug] Added RTLD_GLOBAL flag to dlopen call. [RT #26301] 3182. [bug] Auth servers behind firewalls which block packets greater than 512 bytes may cause other servers to perform poorly. Now, adb retains edns information and caches noedns servers. [RT #23392/24964] 3181. [func] Inline-signing is now supported for master zones. [RT #26224] 3180. [func] Local copies of slave zones are now saved in raw format by default, to improve startup performance. 'masterfile-format text;' can be used to override the default, if desired. [RT #25867] 3179. [port] kfreebsd: build issues. [RT #26273] 3178. [bug] A race condition introduced by change #3163 could cause an assertion failure on shutdown. [RT #26271] 3177. [func] 'rndc keydone', remove the indicator record that named has finished signing the zone with the corresponding key. [RT #26206] 3176. [doc] Corrected example code and added a README to the sample external DLZ module in contrib/dlz/example. [RT #26215] 3175. [bug] Fix how DNSSEC positive wildcard responses from a NSEC3 signed zone are validated. Stop sending a unnecessary NSEC3 record when generating such responses. [RT #26200] 3174. [bug] Always compute to revoked key tag from scratch. [RT #26186] 3173. [port] Correctly validate root DS responses. [RT #25726] 3172. [port] darwin 10.* and freebsd [89] are now built threaded by default. 3171. [bug] Exclusively lock the task when adding a zone using 'rndc addzone'. [RT #25600] --- 9.9.0a3 released --- 3170. [func] RPZ update: - fix precedence among competing rules - improve ARM text including documenting rule precedence - try to rewrite CNAME chains until first hit - new "rpz" logging channel - RDATA for CNAME rules can include wildcards - replace "NO-OP" named.conf policy override with "PASSTHRU" and add "DISABLED" override ("NO-OP" is still recognized) [RT #25172] 3169. [func] Catch db/version mis-matches when calling dns_db_*(). [RT #26017] 3168. [bug] Nxdomain redirection could trigger an assert with a ANY query. [RT #26017] 3167. [bug] Negative answers from forwarders were not being correctly tagged making them appear to not be cached. [RT #25380] 3166. [bug] Upgrading a zone to support inline-signing failed. [RT #26014] 3165. [bug] dnssec-signzone could generate new signatures when resigning, even when valid signatures were already present. [RT #26025] 3164. [func] Enable DLZ modules to retrieve client information, so that responses can be changed depending on the source address of the query. [RT #25768] 3163. [bug] Use finer-grained locking in client.c to address concurrency problems with large numbers of threads. [RT #26044] 3162. [test] start.pl: modified to allow for "named.args" in ns*/ subdirectory to override stock arguments to named. Largely from RT#26044, but no separate ticket. 3161. [bug] zone.c:del_sigs failed to always reset rdata leading assertion failures. [RT #25880] 3160. [bug] When printing out a NSEC3 record in multiline form the newline was not being printed causing type codes to be run together. [RT #25873] 3159. [bug] On some platforms, named could assert on startup when running in a chrooted environment without /proc. [RT #25863] 3158. [bug] Recursive servers would prefer a particular UDP socket instead of using all available sockets. [RT #26038] 3157. [tuning] Reduce the time spent in "rndc reconfig" by parsing the config file before pausing the server. [RT #21373] 3156. [placeholder] --- 9.9.0a2 released --- 3155. [bug] Fixed a build failure when using contrib DLZ drivers (e.g., mysql, postgresql, etc). [RT #25710] 3154. [bug] Attempting to print an empty rdataset could trigger an assert. [RT #25452] 3153. [func] Extend request-ixfr to zone level and remove the side effect of forcing an AXFR. [RT #25156] 3152. [cleanup] Some versions of gcc and clang failed due to incorrect use of __builtin_expect. [RT #25183] 3151. [bug] Queries for type RRSIG or SIG could be handled incorrectly. [RT #21050] 3150. [func] Improved startup and reconfiguration time by enabling zones to load in multiple threads. [RT #25333] 3149. [placeholder] 3148. [bug] Processing of normal queries could be stalled when forwarding a UPDATE message. [RT #24711] 3147. [func] Initial inline signing support. [RT #23657] --- 9.9.0a1 released --- 3146. [test] Fixed gcc4.6.0 errors in ATF. [RT #25598] 3145. [test] Capture output of ATF unit tests in "./atf.out" if there were any errors while running them. [RT #25527] 3144. [bug] dns_dbiterator_seek() could trigger an assert when used with a nonexistent database node. [RT #25358] 3143. [bug] Silence clang compiler warnings. [RT #25174] 3142. [bug] NAPTR is class agnostic. [RT #25429] 3141. [bug] Silence spurious "zone serial (0) unchanged" messages associated with empty zones. [RT #25079] 3140. [func] New command "rndc flushtree " clears the specified name from the server cache along with all names under it. [RT #19970] 3139. [test] Added tests from RFC 6234, RFC 2202, and RFC 1321 for the hashing algorithms (md5, sha1 - sha512, and their hmac counterparts). [RT #25067] 3138. [bug] Address memory leaks and out-of-order operations when shutting named down. [RT #25210] 3137. [func] Improve hardware scalability by allowing multiple worker threads to process incoming UDP packets. This can significantly increase query throughput on some systems. [RT #22992] 3136. [func] Add RFC 1918 reverse zones to the list of built-in empty zones switched on by the 'empty-zones-enable' option. [RT #24990] 3135. [port] FreeBSD: workaround broken IPV6_USE_MIN_MTU processing. See http://www.freebsd.org/cgi/query-pr.cgi?pr=158307 [RT #24950] 3134. [bug] Improve the accuracy of dnssec-signzone's signing statistics. [RT #16030] 3133. [bug] Change #3114 was incomplete. [RT #24577] 3132. [placeholder] 3131. [tuning] Improve scalability by allocating one zone task per 100 zones at startup time, rather than using a fixed-size task table. [RT #24406] 3130. [func] Support alternate methods for managing a dynamic zone's serial number. Two methods are currently defined using serial-update-method, "increment" (default) and "unixtime". [RT #23849] 3129. [bug] Named could crash on 'rndc reconfig' when allow-new-zones was set to yes and named ACLs were used. [RT #22739] 3128. [func] Inserting an NSEC3PARAM via dynamic update in an auto-dnssec zone that has not been signed yet will cause it to be signed with the specified NSEC3 parameters when keys are activated. The NSEC3PARAM record will not appear in the zone until it is signed, but the parameters will be stored. [RT #23684] 3127. [bug] 'rndc thaw' will now remove a zone's journal file if the zone serial number has been changed and ixfr-from-differences is not in use. [RT #24687] 3126. [security] Using DNAME record to generate replacements caused RPZ to exit with a assertion failure. [RT #24766] 3125. [security] Using wildcard CNAME records as a replacement with RPZ caused named to exit with a assertion failure. [RT #24715] 3124. [bug] Use an rdataset attribute flag to indicate negative-cache records rather than using rrtype 0; this will prevent problems when that rrtype is used in actual DNS packets. [RT #24777] 3123. [security] Change #2912 exposed a latent flaw in dns_rdataset_totext() that could cause named to crash with an assertion failure. [RT #24777] 3122. [cleanup] dnssec-settime: corrected usage message. [RT #24664] 3121. [security] An authoritative name server sending a negative response containing a very large RRset could trigger an off-by-one error in the ncache code and crash named. [RT #24650] 3120. [bug] Named could fail to validate zones listed in a DLV that validated insecure without using DLV and had DS records in the parent zone. [RT #24631] 3119. [bug] When rolling to a new DNSSEC key, a private-type record could be created and never marked complete. [RT #23253] 3118. [bug] nsupdate could dump core on shutdown when using SIG(0) keys. [RT #24604] 3117. [cleanup] Remove doc and parser references to the never-implemented 'auto-dnssec create' option. [RT #24533] 3116. [func] New 'dnssec-update-mode' option controls updates of DNSSEC records in signed dynamic zones. Set to 'no-resign' to disable automatic RRSIG regeneration while retaining the ability to sign new or changed data. [RT #24533] 3115. [bug] Named could fail to return requested data when following a CNAME that points into the same zone. [RT #24455] 3114. [bug] Retain expired RRSIGs in dynamic zones if key is inactive and there is no replacement key. [RT #23136] 3113. [doc] Document the relationship between serial-query-rate and NOTIFY messages. 3112. [doc] Add missing descriptions of the update policy name types "ms-self", "ms-subdomain", "krb5-self" and "krb5-subdomain", which allow machines to update their own records, to the BIND 9 ARM. 3111. [bug] Improved consistency checks for dnssec-enable and dnssec-validation, added test cases to the checkconf system test. [RT #24398] 3110. [bug] dnssec-signzone: Wrong error message could appear when attempting to sign with no KSK. [RT #24369] 3109. [func] The also-notify option now uses the same syntax as a zone's masters clause. This means it is now possible to specify a TSIG key to use when sending notifies to a given server, or to include an explicit named masters list in an also-notfiy statement. [RT #23508] 3108. [cleanup] dnssec-signzone: Clarified some error and warning messages; removed #ifdef ALLOW_KSKLESS_ZONES code (use -P instead). [RT #20852] 3107. [bug] dnssec-signzone: Report the correct number of ZSKs when using -x. [RT #20852] 3106. [func] When logging client requests, include the name of the TSIG key if any. [RT #23619] 3105. [bug] GOST support can be suppressed by "configure --without-gost" [RT #24367] 3104. [bug] Better support for cross-compiling. [RT #24367] 3103. [bug] Configuring 'dnssec-validation auto' in a view instead of in the options statement could trigger an assertion failure in named-checkconf. [RT #24382] 3102. [func] New 'dnssec-loadkeys-interval' option configures how often, in minutes, to check the key repository for updates when using automatic key maintenance. Default is every 60 minutes (formerly hard-coded to 12 hours). [RT #23744] 3101. [bug] Zones using automatic key maintenance could fail to check the key repository for updates. [RT #23744] 3100. [security] Certain response policy zone configurations could trigger an INSIST when receiving a query of type RRSIG. [RT #24280] 3099. [test] "dlz" system test now runs but gives R:SKIPPED if not compiled with --with-dlz-filesystem. [RT #24146] 3098. [bug] DLZ zones were answering without setting the AA bit. [RT #24146] 3097. [test] Add a tool to test handling of malformed packets. [RT #24096] 3096. [bug] Set KRB5_KTNAME before calling log_cred() in dst_gssapi_acceptctx(). [RT #24004] 3095. [bug] Handle isolated reserved ports in the port range. [RT #23957] 3094. [doc] Expand dns64 documentation. 3093. [bug] Fix gssapi/kerberos dependencies [RT #23836] 3092. [bug] Signatures for records at the zone apex could go stale due to an incorrect timer setting. [RT #23769] 3091. [bug] Fixed a bug in which zone keys that were published and then subsequently activated could fail to trigger automatic signing. [RT #22911] 3090. [func] Make --with-gssapi default [RT #23738] 3089. [func] dnssec-dsfromkey now supports reading keys from standard input "dnssec-dsfromkey -f -". [RT# 20662] 3088. [bug] Remove bin/tests/system/logfileconfig/ns1/named.conf and add setup.sh in order to resolve changing named.conf issue. [RT #23687] 3087. [bug] DDNS updates using SIG(0) with update-policy match type "external" could cause a crash. [RT #23735] 3086. [bug] Running dnssec-settime -f on an old-style key will now force an update to the new key format even if no other change has been specified, using "-P now -A now" as default values. [RT #22474] 3085. [func] New '-R' option in dnssec-signzone forces removal of signatures which have not yet expired but were generated by a key that no longer exists. [RT #22471] 3084. [func] A new command "rndc sync" dumps pending changes in a dynamic zone to disk; "rndc sync -clean" also removes the journal file after syncing. Also, "rndc freeze" no longer removes journal files. [RT #22473] 3083. [bug] NOTIFY messages were not being sent when generating a NSEC3 chain incrementally. [RT #23702] 3082. [port] strtok_r is threads only. [RT #23747] 3081. [bug] Failure of DNAME substitution did not return YXDOMAIN. [RT #23591] 3080. [cleanup] Replaced compile time constant by STDTIME_ON_32BITS. [RT #23587] 3079. [bug] Handle isc_event_allocate failures in t_tasks. [RT #23572] 3078. [func] Added a new include file with function typedefs for the DLZ "dlopen" driver. [RT #23629] 3077. [bug] zone.c:zone_refreshkeys() incorrectly called dns_zone_attach(), use zone->irefs instead. [RT #23303] 3076. [func] New '-L' option in dnssec-keygen, dnsset-settime, and dnssec-keyfromlabel sets the default TTL of the key. When possible, automatic signing will use that TTL when the key is published. [RT #23304] 3075. [bug] dns_dnssec_findzonekeys{2} used a inconsistent timestamp when determining which keys are active. [RT #23642] 3074. [bug] Make the adb cache read through for zone data and glue learn for zone named is authoritative for. [RT #22842] 3073. [bug] managed-keys changes were not properly being recorded. [RT #20256] 3072. [bug] dns_dns64_aaaaok() potential NULL pointer dereference. [RT #20256] 3071. [bug] has_nsec could be used uninitialized in update.c:next_active. [RT #20256] 3070. [bug] dnssec-signzone potential NULL pointer dereference. [RT #20256] 3069. [cleanup] Silence warnings messages from clang static analysis. [RT #20256] 3068. [bug] Named failed to build with a OpenSSL without engine support. [RT #23473] 3067. [bug] ixfr-from-differences {master|slave}; failed to select the master/slave zones. [RT #23580] 3066. [func] The DLZ "dlopen" driver is now built by default, no longer requiring a configure option. To disable it, use "configure --without-dlopen". Driver also supported on win32. [RT #23467] 3065. [bug] RRSIG could have time stamps too far in the future. [RT #23356] 3064. [bug] powerpc: add sync instructions to the end of atomic operations. [RT #23469] 3063. [contrib] More verbose error reporting from DLZ LDAP. [RT #23402] 3062. [func] Made several changes to enhance human readability of DNSSEC data in dig output and in generated zone files: - DNSKEY record comments are more verbose, no longer used in multiline mode only - multiline RRSIG records reformatted - multiline output mode for NSEC3PARAM records - "dig +norrcomments" suppresses DNSKEY comments - "dig +split=X" breaks hex/base64 records into fields of width X; "dig +nosplit" disables this. [RT #22820] 3061. [func] New option "dnssec-signzone -D", only write out generated DNSSEC records. [RT #22896] 3060. [func] New option "dnssec-signzone -X " allows specification of a separate expiration date for DNSKEY RRSIGs and other RRSIGs. [RT #22141] 3059. [test] Added a regression test for change #3023. 3058. [bug] Cause named to terminate at startup or rndc reconfig/ reload to fail, if a log file specified in the conf file isn't a plain file. [RT #22771] 3057. [bug] "rndc secroots" would abort after the first error and so could miss some views. [RT #23488] 3056. [func] Added support for URI resource record. [RT #23386] 3055. [placeholder] 3054. [bug] Added elliptic curve support check in GOST OpenSSL engine detection. [RT #23485] 3053. [bug] Under a sustained high query load with a finite max-cache-size, it was possible for cache memory to be exhausted and not recovered. [RT #23371] 3052. [test] Fixed last autosign test report. [RT #23256] 3051. [bug] NS records obscure DNAME records at the bottom of the zone if both are present. [RT #23035] 3050. [bug] The autosign system test was timing dependent. Wait for the initial autosigning to complete before running the rest of the test. [RT #23035] 3049. [bug] Save and restore the gid when creating creating named.pid at startup. [RT #23290] 3048. [bug] Fully separate view key management. [RT #23419] 3047. [bug] DNSKEY NODATA responses not cached fixed in validator.c. Tests added to dnssec system test. [RT #22908] 3046. [bug] Use RRSIG original TTL to compute validated RRset and RRSIG TTL. [RT #23332] 3045. [removed] Replaced by change #3050. 3044. [bug] Hold the socket manager lock while freeing the socket. [RT #23333] 3043. [test] Merged in the NetBSD ATF test framework (currently version 0.12) for development of future unit tests. Use configure --with-atf to build ATF internally or configure --with-atf=prefix to use an external copy. [RT #23209] 3042. [bug] dig +trace could fail attempting to use IPv6 addresses on systems with only IPv4 connectivity. [RT #23297] 3041. [bug] dnssec-signzone failed to generate new signatures on ttl changes. [RT #23330] 3040. [bug] Named failed to validate insecure zones where a node with a CNAME existed between the trust anchor and the top of the zone. [RT #23338] 3039. [func] Redirect on NXDOMAIN support. [RT #23146] 3038. [bug] Install . [RT #23342] 3037. [doc] Update COPYRIGHT to contain all the individual copyright notices that cover various parts. 3036. [bug] Check built-in zone arguments to see if the zone is re-usable or not. [RT #21914] 3035. [cleanup] Simplify by using strlcpy. [RT #22521] 3034. [cleanup] nslookup: use strlcpy instead of safecopy. [RT #22521] 3033. [cleanup] Add two INSIST(bucket != DNS_ADB_INVALIDBUCKET). [RT #22521] 3032. [bug] rdatalist.c: add missing REQUIREs. [RT #22521] 3031. [bug] dns_rdataclass_format() handle a zero sized buffer. [RT #22521] 3030. [bug] dns_rdatatype_format() handle a zero sized buffer. [RT #22521] 3029. [bug] isc_netaddr_format() handle a zero sized buffer. [RT #22521] 3028. [bug] isc_sockaddr_format() handle a zero sized buffer. [RT #22521] 3027. [bug] Add documented REQUIREs to cfg_obj_asnetprefix() to catch NULL pointer dereferences before they happen. [RT #22521] 3026. [bug] lib/isc/httpd.c: check that we have enough space after calling grow_headerspace() and if not re-call grow_headerspace() until we do. [RT #22521] 3025. [bug] Fixed a possible deadlock due to zone resigning. [RT #22964] 3024. [func] RTT Banding removed due to minor security increase but major impact on resolver latency. [RT #23310] 3023. [bug] Named could be left in an inconsistent state when receiving multiple AXFR response messages that were not all TSIG-signed. [RT #23254] 3022. [bug] Fixed rpz SERVFAILs after failed zone transfers [RT #23246] 3021. [bug] Change #3010 was incomplete. [RT #22296] 3020. [bug] auto-dnssec failed to correctly update the zone when changing the DNSKEY RRset. [RT #23232] 3019. [test] Test: check apex NSEC3 records after adding DNSKEY record via UPDATE. [RT #23229] 3018. [bug] Named failed to check for the "none;" acl when deciding if a zone may need to be re-signed. [RT #23120] 3017. [doc] dnssec-keyfromlabel -I was not properly documented. [RT #22887] 3016. [bug] rndc usage missing '-b'. [RT #22937] 3015. [port] win32: fix IN6_IS_ADDR_LINKLOCAL and IN6_IS_ADDR_SITELOCAL macros. [RT #22724] 3014. [placeholder] 3013. [bug] The DNS64 ttl was not always being set as expected. [RT #23034] 3012. [bug] Remove DNSKEY TTL change pairs before generating signing records for any remaining DNSKEY changes. [RT #22590] 3011. [func] Change the default query timeout from 30 seconds to 10. Allow setting this in named.conf using the new 'resolver-query-timeout' option, which specifies a max time in seconds. 0 means 'default' and anything longer than 30 will be silently set to 30. [RT #22852] 3010. [bug] Fixed a bug where "rndc reconfig" stopped the timer for refreshing managed-keys. [RT #22296] 3009. [bug] clients-per-query code didn't work as expected with particular query patterns. [RT #22972] --- 9.8.0b1 released --- 3008. [func] Response policy zones (RPZ) support. [RT #21726] 3007. [bug] Named failed to preserve the case of domain names in rdata which is not compressible when writing master files. [RT #22863] 3006. [func] Allow dynamically generated TSIG keys to be preserved across restarts of named. Initially this is for TSIG keys generated using GSSAPI. [RT #22639] 3005. [port] Solaris: Work around the lack of gsskrb5_register_acceptor_identity() by setting the KRB5_KTNAME environment variable to the contents of tkey-gssapi-keytab. Also fixed test errors on MacOSX. [RT #22853] 3004. [func] DNS64 reverse support. [RT #22769] 3003. [experimental] Added update-policy match type "external", enabling named to defer the decision of whether to allow a dynamic update to an external daemon. (Contributed by Andrew Tridgell.) [RT #22758] 3002. [bug] isc_mutex_init_errcheck() failed to destroy attr. [RT #22766] 3001. [func] Added a default trust anchor for the root zone, which can be switched on by setting "dnssec-validation auto;" in the named.conf options. [RT #21727] 3000. [bug] More TKEY/GSS fixes: - nsupdate can now get the default realm from the user's Kerberos principal - corrected gsstest compilation flags - improved documentation - fixed some NULL dereferences [RT #22795] 2999. [func] Add GOST support (RFC 5933). [RT #20639] 2998. [func] Add isc_task_beginexclusive and isc_task_endexclusive to the task api. [RT #22776] 2997. [func] named -V now reports the OpenSSL and libxml2 verions it was compiled against. [RT #22687] 2996. [security] Temporarily disable SO_ACCEPTFILTER support. [RT #22589] 2995. [bug] The Kerberos realm was not being correctly extracted from the signer's identity. [RT #22770] 2994. [port] NetBSD: use pthreads by default on NetBSD >= 5.0, and do not use threads on earlier versions. Also kill the unproven-pthreads, mit-pthreads, and ptl2 support. 2993. [func] Dynamically grow adb hash tables. [RT #21186] 2992. [contrib] contrib/check-secure-delegation.pl: A simple tool for looking at a secure delegation. [RT #22059] 2991. [contrib] contrib/zone-edit.sh: A simple zone editing tool for dynamic zones. [RT #22365] 2990. [bug] 'dnssec-settime -S' no longer tests prepublication interval validity when the interval is set to 0. [RT #22761] 2989. [func] Added support for writable DLZ zones. (Contributed by Andrew Tridgell of the Samba project.) [RT #22629] 2988. [experimental] Added a "dlopen" DLZ driver, allowing the creation of external DLZ drivers that can be loaded as shared objects at runtime rather than linked with named. Currently this is switched on via a compile-time option, "configure --with-dlz-dlopen". Note: the syntax for configuring DLZ zones is likely to be refined in future releases. (Contributed by Andrew Tridgell of the Samba project.) [RT #22629] 2987. [func] Improve ease of configuring TKEY/GSS updates by adding a "tkey-gssapi-keytab" option. If set, updates will be allowed with any key matching a principal in the specified keytab file. "tkey-gssapi-credential" is no longer required and is expected to be deprecated. (Contributed by Andrew Tridgell of the Samba project.) [RT #22629] 2986. [func] Add new zone type "static-stub". It's like a stub zone, but the nameserver names and/or their IP addresses are statically configured. [RT #21474] 2985. [bug] Add a regression test for change #2896. [RT #21324] 2984. [bug] Don't run MX checks when the target of the MX record is ".". [RT #22645] 2983. [bug] Include "loadkeys" in rndc help output. [RT #22493] --- 9.8.0a1 released --- 2982. [bug] Reference count dst keys. dst_key_attach() can be used increment the reference count. Note: dns_tsigkey_createfromkey() callers should now always call dst_key_free() rather than setting it to NULL on success. [RT #22672] 2981. [func] Partial DNS64 support (AAAA synthesis). [RT #21991] 2980. [bug] named didn't properly handle UPDATES that changed the TTL of the NSEC3PARAM RRset. [RT #22363] 2979. [bug] named could deadlock during shutdown if two "rndc stop" commands were issued at the same time. [RT #22108] 2978. [port] hpux: look for [RT #21919] 2977. [bug] 'nsupdate -l' report if the session key is missing. [RT #21670] 2976. [bug] named could die on exit after negotiating a GSS-TSIG key. [RT #22573] 2975. [bug] rbtdb.c:cleanup_dead_nodes_callback() acquired the wrong lock which could lead to server deadlock. [RT #22614] 2974. [bug] Some valid UPDATE requests could fail due to a consistency check examining the existing version of the zone rather than the new version resulting from the UPDATE. [RT #22413] 2973. [bug] bind.keys.h was being removed by the "make clean" at the end of configure resulting in build failures where there is very old version of perl installed. Move it to "make maintainer-clean". [RT #22230] 2972. [bug] win32: address windows socket errors. [RT #21906] 2971. [bug] Fixed a bug that caused journal files not to be compacted on Windows systems as a result of non-POSIX-compliant rename() semantics. [RT #22434] 2970. [security] Adding a NO DATA negative cache entry failed to clear any matching RRSIG records. A subsequent lookup of of NO DATA cache entry could trigger a INSIST when the unexpected RRSIG was also returned with the NO DATA cache entry. CVE-2010-3613, VU#706148. [RT #22288] 2969. [security] Fix acl type processing so that allow-query works in options and view statements. Also add a new set of tests to verify proper functioning. CVE-2010-3615, VU#510208. [RT #22418] 2968. [security] Named could fail to prove a data set was insecure before marking it as insecure. One set of conditions that can trigger this occurs naturally when rolling DNSKEY algorithms. CVE-2010-3614, VU#837744. [RT #22309] 2967. [bug] 'host -D' now turns on debugging messages earlier. [RT #22361] 2966. [bug] isc_print_vsnprintf() failed to check if there was space available in the buffer when adding a left justified character with a non zero width, (e.g. "%-1c"). [RT #22270] 2965. [func] Test HMAC functions using test data from RFC 2104 and RFC 4634. [RT #21702] 2964. [placeholder] 2963. [security] The allow-query acl was being applied instead of the allow-query-cache acl to cache lookups. [RT #22114] 2962. [port] win32: add more dependencies to BINDBuild.dsw. [RT #22062] 2961. [bug] Be still more selective about the non-authoritative answers we apply change 2748 to. [RT #22074] 2960. [func] Check that named accepts non-authoritative answers. [RT #21594] 2959. [func] Check that named starts with a missing masterfile. [RT #22076] 2958. [bug] named failed to start with a missing master file. [RT #22076] 2957. [bug] entropy_get() and entropy_getpseudo() failed to match the API for RAND_bytes() and RAND_pseudo_bytes() respectively. [RT #21962] 2956. [port] Enable atomic operations on the PowerPC64. [RT #21899] 2955. [func] Provide more detail in the recursing log. [RT #22043] 2954. [bug] contrib: dlz_mysql_driver.c bad error handling on build_sqldbinstance failure. [RT #21623] 2953. [bug] Silence spurious "expected covering NSEC3, got an exact match" message when returning a wildcard no data response. [RT #21744] 2952. [port] win32: named-checkzone and named-checkconf failed to initialize winsock. [RT #21932] 2951. [bug] named failed to generate a correct signed response in a optout, delegation only zone with no secure delegations. [RT #22007] 2950. [bug] named failed to perform a SOA up to date check when falling back to TCP on UDP timeouts when ixfr-from-differences was set. [RT #21595] 2949. [bug] dns_view_setnewzones() contained a memory leak if it was called multiple times. [RT #21942] 2948. [port] MacOS: provide a mechanism to configure the test interfaces at reboot. See bin/tests/system/README for details. 2947. [placeholder] 2946. [doc] Document the default values for the minimum and maximum zone refresh and retry values in the ARM. [RT #21886] 2945. [doc] Update empty-zones list in ARM. [RT #21772] 2944. [maint] Remove ORCHID prefix from built in empty zones. [RT #21772] 2943. [func] Add support to load new keys into managed zones without signing immediately with "rndc loadkeys". Add support to link keys with "dnssec-keygen -S" and "dnssec-settime -S". [RT #21351] 2942. [contrib] zone2sqlite failed to setup the entropy sources. [RT #21610] 2941. [bug] sdb and sdlz (dlz's zone database) failed to support DNAME at the zone apex. [RT #21610] 2940. [port] Remove connection aborted error message on Windows. [RT #21549] 2939. [func] Check that named successfully skips NSEC3 records that fail to match the NSEC3PARAM record currently in use. [RT# 21868] 2938. [bug] When generating signed responses, from a signed zone that uses NSEC3, named would use a uninitialized pointer if it needed to skip a NSEC3 record because it didn't match the selected NSEC3PARAM record for zone. [RT# 21868] 2937. [bug] Worked around an apparent race condition in over memory conditions. Without this fix a DNS cache DB or ADB could incorrectly stay in an over memory state, effectively refusing further caching, which subsequently made a BIND 9 caching server unworkable. This fix prevents this problem from happening by polling the state of the memory context, rather than making a copy of the state, which appeared to cause a race. This is a "workaround" in that it doesn't solve the possible race per se, but several experiments proved this change solves the symptom. Also, the polling overhead hasn't been reported to be an issue. This bug should only affect a caching server that specifies a finite max-cache-size. It's also quite likely that the bug happens only when enabling threads, but it's not confirmed yet. [RT #21818] 2936. [func] Improved configuration syntax and multiple-view support for addzone/delzone feature (see change #2930). Removed "new-zone-file" option, replaced with "allow-new-zones (yes|no)". The new-zone-file for each view is now created automatically, with a filename generated from a hash of the view name. It is no longer necessary to "include" the new-zone-file in named.conf; this happens automatically. Zones that were not added via "rndc addzone" can no longer be removed with "rndc delzone". [RT #19447] 2935. [bug] nsupdate: improve 'file not found' error message. [RT #21871] 2934. [bug] Use ANSI C compliant shift range in lib/isc/entropy.c. [RT #21871] 2933. [bug] 'dig +nsid' used stack memory after it went out of scope. This could potentially result in a unknown, potentially malformed, EDNS option being sent instead of the desired NSID option. [RT #21781] 2932. [cleanup] Corrected a numbering error in the "dnssec" test. [RT #21597] 2931. [bug] Temporarily and partially disable change 2864 because it would cause infinite attempts of RRSIG queries. This is an urgent care fix; we'll revisit the issue and complete the fix later. [RT #21710] 2930. [experimental] New "rndc addzone" and "rndc delzone" commands allow dynamic addition and deletion of zones. To enable this feature, specify a "new-zone-file" option at the view or options level in named.conf. Zone configuration information for the new zones will be written into that file. To make the new zones persist after a restart, "include" the file into named.conf in the appropriate view. (Note: This feature is not yet documented, and its syntax is expected to change.) [RT #19447] 2929. [bug] Improved handling of GSS security contexts: - added LRU expiration for generated TSIGs - added the ability to use a non-default realm - added new "realm" keyword in nsupdate - limited lifetime of generated keys to 1 hour or the lifetime of the context (whichever is smaller) [RT #19737] 2928. [bug] Be more selective about the non-authoritative answer we apply change 2748 to. [RT #21594] 2927. [placeholder] 2926. [placeholder] 2925. [bug] Named failed to accept uncachable negative responses from insecure zones. [RT# 21555] 2924. [func] 'rndc secroots' dump a combined summary of the current managed keys combined with trusted keys. [RT #20904] 2923. [bug] 'dig +trace' could drop core after "connection timeout". [RT #21514] 2922. [contrib] Update zkt to version 1.0. 2921. [bug] The resolver could attempt to destroy a fetch context too soon. [RT #19878] 2920. [func] Allow 'filter-aaaa-on-v4' to be applied selectively to IPv4 clients. New acl 'filter-aaaa' (default any). 2919. [func] Add autosign-ksk and autosign-zsk virtual time tests. [RT #20840] 2918. [maint] Add AAAA address for I.ROOT-SERVERS.NET. 2917. [func] Virtual time test framework. [RT #20801] 2916. [func] Add framework to use IPv6 in tests. fd92:7065:b8e:ffff::1 ... fd92:7065:b8e:ffff::7 2915. [cleanup] Be smarter about which objects we attempt to compile based on configure options. [RT #21444] 2914. [bug] Make the "autosign" system test more portable. [RT #20997] 2913. [func] Add pkcs#11 system tests. [RT #20784] 2912. [func] Windows clients don't like UPDATE responses that clear the zone section. [RT #20986] 2911. [bug] dnssec-signzone didn't handle out of zone records well. [RT #21367] 2910. [func] Sanity check Kerberos credentials. [RT #20986] 2909. [bug] named-checkconf -p could die if "update-policy local;" was specified in named.conf. [RT #21416] 2908. [bug] It was possible for re-signing to stop after removing a DNSKEY. [RT #21384] 2907. [bug] The export version of libdns had undefined references. [RT #21444] 2906. [bug] Address RFC 5011 implementation issues. [RT #20903] 2905. [port] aix: set use_atomic=yes with native compiler. [RT #21402] 2904. [bug] When using DLV, sub-zones of the zones in the DLV, could be incorrectly marked as insecure instead of secure leading to negative proofs failing. This was a unintended outcome from change 2890. [RT# 21392] 2903. [bug] managed-keys-directory missing from namedconf.c. [RT #21370] 2902. [func] Add regression test for change 2897. [RT #21040] 2901. [port] Use AC_C_FLEXIBLE_ARRAY_MEMBER. [RT #21316] 2900. [bug] The placeholder negative caching element was not properly constructed triggering a INSIST in dns_ncache_towire(). [RT #21346] 2899. [port] win32: Support linking against OpenSSL 1.0.0. 2898. [bug] nslookup leaked memory when -domain=value was specified. [RT #21301] 2897. [bug] NSEC3 chains could be left behind when transitioning to insecure. [RT #21040] 2896. [bug] "rndc sign" failed to properly update the zone when adding a DNSKEY for publication only. [RT #21045] 2895. [func] genrandom: add support for the generation of multiple files. [RT #20917] 2894. [contrib] DLZ LDAP support now use '$' not '%'. [RT #21294] 2893. [bug] Improve managed keys support. New named.conf option managed-keys-directory. [RT #20924] 2892. [bug] Handle REVOKED keys better. [RT #20961] 2891. [maint] Update empty-zones list to match draft-ietf-dnsop-default-local-zones-13. [RT# 21099] 2890. [bug] Handle the introduction of new trusted-keys and DS, DLV RRsets better. [RT #21097] 2889. [bug] Elements of the grammar where not properly reported. [RT #21046] 2888. [bug] Only the first EDNS option was displayed. [RT #21273] 2887. [bug] Report the keytag times in UTC in the .key file, local time is presented as a comment within the comment. [RT #21223] 2886. [bug] ctime() is not thread safe. [RT #21223] 2885. [bug] Improve -fno-strict-aliasing support probing in configure. [RT #21080] 2884. [bug] Insufficient validation in dns_name_getlabelsequence(). [RT #21283] 2883. [bug] 'dig +short' failed to handle really large datasets. [RT #21113] 2882. [bug] Remove memory context from list of active contexts before clearing 'magic'. [RT #21274] 2881. [bug] Reduce the amount of time the rbtdb write lock is held when closing a version. [RT #21198] 2880. [cleanup] Make the output of dnssec-keygen and dnssec-revoke consistent. [RT #21078] 2879. [contrib] DLZ bdbhpt driver fails to close correct cursor. [RT #21106] 2878. [func] Incrementally write the master file after performing a AXFR. [RT #21010] 2877. [bug] The validator failed to skip obviously mismatching RRSIGs. [RT #21138] 2876. [bug] Named could return SERVFAIL for negative responses from unsigned zones. [RT #21131] 2875. [bug] dns_time64_fromtext() could accept non digits. [RT #21033] 2874. [bug] Cache lack of EDNS support only after the server successfully responds to the query using plain DNS. [RT #20930] 2873. [bug] Canceling a dynamic update via the dns/client module could trigger an assertion failure. [RT #21133] 2872. [bug] Modify dns/client.c:dns_client_createx() to only require one of IPv4 or IPv6 rather than both. [RT #21122] 2871. [bug] Type mismatch in mem_api.c between the definition and the header file, causing build failure with --enable-exportlib. [RT #21138] 2870. [maint] Add AAAA address for L.ROOT-SERVERS.NET. 2869. [bug] Fix arguments to dns_keytable_findnextkeynode() call. [RT #20877] 2868. [cleanup] Run "make clean" at the end of configure to ensure any changes made by configure are integrated. Use --with-make-clean=no to disable. [RT #20994] 2867. [bug] Don't set GSS_C_SEQUENCE_FLAG as Windows DNS servers don't like it. [RT #20986] 2866. [bug] Windows does not like the TSIG name being compressed. [RT #20986] 2865. [bug] memset to zero event.data. [RT #20986] 2864. [bug] Direct SIG/RRSIG queries were not handled correctly. [RT #21050] 2863. [port] linux: disable IPv6 PMTUD and use network minimum MTU. [RT #21056] 2862. [bug] nsupdate didn't default to the parent zone when updating DS records. [RT #20896] 2861. [doc] dnssec-settime man pages didn't correctly document the inactivation time. [RT #21039] 2860. [bug] named-checkconf's usage was out of date. [RT #21039] 2859. [bug] When canceling validation it was possible to leak memory. [RT #20800] 2858. [bug] RTT estimates were not being adjusted on ICMP errors. [RT #20772] 2857. [bug] named-checkconf did not fail on a bad trusted key. [RT #20705] 2856. [bug] The size of a memory allocation was not always properly recorded. [RT #20927] 2855. [func] nsupdate will now preserve the entered case of domain names in update requests it sends. [RT #20928] 2854. [func] dig: allow the final soa record in a axfr response to be suppressed, dig +onesoa. [RT #20929] 2853. [bug] add_sigs() could run out of scratch space. [RT #21015] 2852. [bug] Handle broken DNSSEC trust chains better. [RT #15619] 2851. [doc] nslookup.1, removed from the docbook source as it produced bad nroff. [RT #21007] 2850. [bug] If isc_heap_insert() failed due to memory shortage the heap would have corrupted entries. [RT #20951] 2849. [bug] Don't treat errors from the xml2 library as fatal. [RT #20945] 2848. [doc] Moved README.dnssec, README.libdns, README.pkcs11 and README.rfc5011 into the ARM. [RT #20899] 2847. [cleanup] Corrected usage message in dnssec-settime. [RT #20921] 2846. [bug] EOF on unix domain sockets was not being handled correctly. [RT #20731] 2845. [bug] RFC 5011 client could crash on shutdown. [RT #20903] 2844. [doc] notify-delay default in ARM was wrong. It should have been five (5) seconds. 2843. [func] Prevent dnssec-keygen and dnssec-keyfromlabel from creating key files if there is a chance that the new key ID will collide with an existing one after either of the keys has been revoked. (To override this in the case of dnssec-keyfromlabel, use the -y option. dnssec-keygen will simply create a different, non-colliding key, so an override is not necessary.) [RT #20838] 2842. [func] Added "smartsign" and improved "autosign" and "dnssec" regression tests. [RT #20865] 2841. [bug] Change 2836 was not complete. [RT #20883] 2840. [bug] Temporary fixed pkcs11-destroy usage check. [RT #20760] 2839. [bug] A KSK revoked by named could not be deleted. [RT #20881] 2838. [placeholder] 2837. [port] Prevent Linux spurious warnings about fwrite(). [RT #20812] 2836. [bug] Keys that were scheduled to become active could be delayed. [RT #20874] 2835. [bug] Key inactivity dates were inadvertently stored in the private key file with the outdated tag "Unpublish" rather than "Inactive". This has been fixed; however, any existing keys that had Inactive dates set will now need to have them reset, using 'dnssec-settime -I'. [RT #20868] 2834. [bug] HMAC-SHA* keys that were longer than the algorithm digest length were used incorrectly, leading to interoperability problems with other DNS implementations. This has been corrected. (Note: If an oversize key is in use, and compatibility is needed with an older release of BIND, the new tool "isc-hmac-fixup" can convert the key secret to a form that will work with all versions.) [RT #20751] 2833. [cleanup] Fix usage messages in dnssec-keygen and dnssec-settime. [RT #20851] 2832. [bug] Modify "struct stat" in lib/export/samples/nsprobe.c to avoid redefinition in some OSs [RT 20831] 2831. [security] Do not attempt to validate or cache out-of-bailiwick data returned with a secure answer; it must be re-fetched from its original source and validated in that context. [RT #20819] 2830. [bug] Changing the OPTOUT setting could take multiple passes. [RT #20813] 2829. [bug] Fixed potential node inconsistency in rbtdb.c. [RT #20808] 2828. [security] Cached CNAME or DNAME RR could be returned to clients without DNSSEC validation. [RT #20737] 2827. [security] Bogus NXDOMAIN could be cached as if valid. [RT #20712] 2826. [bug] NSEC3->NSEC transitions could fail due to a lock not being released. [RT #20740] 2825. [bug] Changing the setting of OPTOUT in a NSEC3 chain that was in the process of being created was not properly recorded in the zone. [RT #20786] 2824. [bug] "rndc sign" was not being run by the correct task. [RT #20759] 2823. [bug] rbtdb.c:getsigningtime() was missing locks. [RT #20781] 2822. [bug] rbtdb.c:loadnode() could return the wrong result. [RT #20802] 2821. [doc] Add note that named-checkconf doesn't automatically read rndc.key and bind.keys [RT #20758] 2820. [func] Handle read access failure of OpenSSL configuration file more user friendly (PKCS#11 engine patch). [RT #20668] 2819. [cleanup] Removed unnecessary DNS_POINTER_MAXHOPS define. [RT #20771] 2818. [cleanup] rndc could return an incorrect error code when a zone was not found. [RT #20767] 2817. [cleanup] Removed unnecessary isc_task_endexclusive() calls. [RT #20768] 2816. [bug] previous_closest_nsec() could fail to return data for NSEC3 nodes [RT #29730] 2815. [bug] Exclusively lock the task when freezing a zone. [RT #19838] 2814. [func] Provide a definitive error message when a master zone is not loaded. [RT #20757] 2813. [bug] Better handling of unreadable DNSSEC key files. [RT #20710] 2812. [bug] Make sure updates can't result in a zone with NSEC-only keys and NSEC3 records. [RT #20748] 2811. [cleanup] Add "rndc sign" to list of commands in rndc usage output. [RT #20733] 2810. [doc] Clarified the process of transitioning an NSEC3 zone to insecure. [RT #20746] 2809. [cleanup] Restored accidentally-deleted text in usage output in dnssec-settime and dnssec-revoke [RT #20739] 2808. [bug] Remove the attempt to install atomic.h from lib/isc. atomic.h is correctly installed by the architecture specific subdirectories. [RT #20722] 2807. [bug] Fixed a possible ASSERT when reconfiguring zone keys. [RT #20720] --- 9.7.0rc1 released --- 2806. [bug] "rdnc sign" could delay re-signing the DNSKEY when it had changed. [RT #20703] 2805. [bug] Fixed namespace problems encountered when building external programs using non-exported BIND9 libraries (i.e., built without --enable-exportlib). [RT #20679] 2804. [bug] Send notifies when a zone is signed with "rndc sign" or as a result of a scheduled key change. [RT #20700] 2803. [port] win32: Install named-journalprint, nsec3hash, arpaname and genrandom under windows. [RT #20670] 2802. [cleanup] Rename journalprint to named-journalprint. [RT #20670] 2801. [func] Detect and report records that are different according to DNSSEC but are semantically equal according to plain DNS. Apply plain DNS comparisons rather than DNSSEC comparisons when processing UPDATE requests. dnssec-signzone now removes such semantically duplicate records prior to signing the RRset. named-checkzone -r {ignore|warn|fail} (default warn) named-compilezone -r {ignore|warn|fail} (default warn) named.conf: check-dup-records {ignore|warn|fail}; 2800. [func] Reject zones which have NS records which refer to CNAMEs, DNAMEs or don't have address record (class IN only). Reject UPDATEs which would cause the zone to fail the above checks if committed. [RT #20678] 2799. [cleanup] Changed the "secure-to-insecure" option to "dnssec-secure-to-insecure", and "dnskey-ksk-only" to "dnssec-dnskey-kskonly", for clarity. [RT #20586] 2798. [bug] Addressed bugs in managed-keys initialization and rollover. [RT #20683] 2797. [bug] Don't decrement the dispatch manager's maxbuffers. [RT #20613] 2796. [bug] Missing dns_rdataset_disassociate() call in dns_nsec3_delnsec3sx(). [RT #20681] 2795. [cleanup] Add text to differentiate "update with no effect" log messages. [RT #18889] 2794. [bug] Install . [RT #20677] 2793. [func] Add "autosign" and "metadata" tests to the automatic tests. [RT #19946] 2792. [func] "filter-aaaa-on-v4" can now be set in view options (if compiled in). [RT #20635] 2791. [bug] The installation of isc-config.sh was broken. [RT #20667] 2790. [bug] Handle DS queries to stub zones. [RT #20440] 2789. [bug] Fixed an INSIST in dispatch.c [RT #20576] 2788. [bug] dnssec-signzone could sign with keys that were not requested [RT #20625] 2787. [bug] Spurious log message when zone keys were dynamically reconfigured. [RT #20659] 2786. [bug] Additional could be promoted to answer. [RT #20663] --- 9.7.0b3 released --- 2785. [bug] Revoked keys could fail to self-sign [RT #20652] 2784. [bug] TC was not always being set when required glue was dropped. [RT #20655] 2783. [func] Return minimal responses to EDNS/UDP queries with a UDP buffer size of 512 or less. [RT #20654] 2782. [port] win32: use getaddrinfo() for hostname lookups. [RT #20650] 2781. [bug] Inactive keys could be used for signing. [RT #20649] 2780. [bug] dnssec-keygen -A none didn't properly unset the activation date in all cases. [RT #20648] 2779. [bug] Dynamic key revocation could fail. [RT #20644] 2778. [bug] dnssec-signzone could fail when a key was revoked without deleting the unrevoked version. [RT #20638] 2777. [contrib] DLZ MYSQL auto reconnect support discovery was wrong. 2776. [bug] Change #2762 was not correct. [RT #20647] 2775. [bug] Accept RSASHA256 and RSASHA512 as NSEC3 compatible in dnssec-keyfromlabel. [RT #20643] 2774. [bug] Existing cache DB wasn't being reused after reconfiguration. [RT #20629] 2773. [bug] In autosigned zones, the SOA could be signed with the KSK. [RT #20628] 2772. [security] When validating, track whether pending data was from the additional section or not and only return it if validates as secure. [RT #20438] 2771. [bug] dnssec-signzone: DNSKEY records could be corrupted when importing from key files [RT #20624] 2770. [cleanup] Add log messages to resolver.c to indicate events causing FORMERR responses. [RT #20526] 2769. [cleanup] Change #2742 was incomplete. [RT #19589] 2768. [bug] dnssec-signzone: -S no longer implies -g [RT #20568] 2767. [bug] named could crash on startup if a zone was configured with auto-dnssec and there was no key-directory. [RT #20615] 2766. [bug] isc_socket_fdwatchpoke() should only update the socketmgr state if the socket is not pending on a read or write. [RT #20603] 2765. [bug] Skip masters for which the TSIG key cannot be found. [RT #20595] 2764. [bug] "rndc-confgen -a" could trigger a REQUIRE. [RT #20610] 2763. [bug] "rndc sign" didn't create an NSEC chain. [RT #20591] 2762. [bug] DLV validation failed with a local slave DLV zone. [RT #20577] 2761. [cleanup] Enable internal symbol table for backtrace only for systems that are known to work. Currently, BSD variants, Linux and Solaris are supported. [RT# 20202] 2760. [cleanup] Corrected named-compilezone usage summary. [RT #20533] 2759. [doc] Add information about .jbk/.jnw files to the ARM. [RT #20303] 2758. [bug] win32: Added a workaround for a windows 2008 bug that could cause the UDP client handler to shut down. [RT #19176] 2757. [bug] dig: assertion failure could occur in connect timeout. [RT #20599] 2756. [bug] Fixed corrupt logfile message in update.c. [RT# 20597] 2755. [placeholder] 2754. [bug] Secure-to-insecure transitions failed when zone was signed with NSEC3. [RT #20587] 2753. [bug] Removed an unnecessary warning that could appear when building an NSEC chain. [RT #20589] 2752. [bug] Locking violation. [RT #20587] 2751. [bug] Fixed a memory leak in dnssec-keyfromlabel. [RT #20588] 2750. [bug] dig: assertion failure could occur when a server didn't have an address. [RT #20579] 2749. [bug] ixfr-from-differences generated a non-minimal ixfr for NSEC3 signed zones. [RT #20452] 2748. [func] Identify bad answers from GTLD servers and treat them as referrals. [RT #18884] 2747. [bug] Journal roll forwards failed to set the re-signing time of RRSIGs correctly. [RT #20541] 2746. [port] hpux: address signed/unsigned expansion mismatch of dns_rbtnode_t.nsec. [RT #20542] 2745. [bug] configure script didn't probe the return type of gai_strerror(3) correctly. [RT #20573] 2744. [func] Log if a query was over TCP. [RT #19961] 2743. [bug] RRSIG could be incorrectly set in the NSEC3 record for a insecure delegation. --- 9.7.0b2 released --- 2742. [cleanup] Clarify some DNSSEC-related log messages in validator.c. [RT #19589] 2741. [func] Allow the dnssec-keygen progress messages to be suppressed (dnssec-keygen -q). Automatically suppress the progress messages when stdin is not a tty. [RT #20474] 2740. [placeholder] 2739. [cleanup] Clean up API for initializing and clearing trust anchors for a view. [RT #20211] 2738. [func] Add RSASHA256 and RSASHA512 tests to the dnssec system test. [RT #20453] 2737. [func] UPDATE requests can leak existence information. [RT #17261] 2736. [func] Improve the performance of NSEC signed zones with more than a normal amount of glue below a delegation. [RT #20191] 2735. [bug] dnssec-signzone could fail to read keys that were specified on the command line with full paths, but weren't in the current directory. [RT #20421] 2734. [port] cygwin: arpaname did not compile. [RT #20473] 2733. [cleanup] Clean up coding style in pkcs11-* tools. [RT #20355] 2732. [func] Add optional filter-aaaa-on-v4 option, available if built with './configure --enable-filter-aaaa'. Filters out AAAA answers to clients connecting via IPv4. (This is NOT recommended for general use.) [RT #20339] 2731. [func] Additional work on change 2709. The key parser will now ignore unrecognized fields when the minor version number of the private key format has been increased. It will reject any key with the major version number increased. [RT #20310] 2730. [func] Have dnssec-keygen display a progress indication a la 'openssl genrsa' on standard error. Note when the first '.' is followed by a long stop one has the choice between slow generation vs. poor random quality, i.e., '-r /dev/urandom'. [RT #20284] 2729. [func] When constructing a CNAME from a DNAME use the DNAME TTL. [RT #20451] 2728. [bug] dnssec-keygen, dnssec-keyfromlabel and dnssec-signzone now warn immediately if asked to write into a nonexistent directory. [RT #20278] 2727. [func] The 'key-directory' option can now specify a relative path. [RT #20154] 2726. [func] Added support for SHA-2 DNSSEC algorithms, RSASHA256 and RSASHA512. [RT #20023] 2725. [doc] Added information about the file "managed-keys.bind" to the ARM. [RT #20235] 2724. [bug] Updates to a existing node in secure zone using NSEC were failing. [RT #20448] 2723. [bug] isc_base32_totext(), isc_base32hex_totext(), and isc_base64_totext(), didn't always mark regions of memory as fully consumed after conversion. [RT #20445] 2722. [bug] Ensure that the memory associated with the name of a node in a rbt tree is not altered during the life of the node. [RT #20431] 2721. [port] Have dst__entropy_status() prime the random number generator. [RT #20369] 2720. [bug] RFC 5011 trust anchor updates could trigger an assert if the DNSKEY record was unsigned. [RT #20406] 2719. [func] Skip trusted/managed keys for unsupported algorithms. [RT #20392] 2718. [bug] The space calculations in opensslrsa_todns() were incorrect. [RT #20394] 2717. [bug] named failed to update the NSEC/NSEC3 record when the last private type record was removed as a result of completing the signing the zone with a key. [RT #20399] 2716. [bug] nslookup debug mode didn't return the ttl. [RT #20414] --- 9.7.0b1 released --- 2715. [bug] Require OpenSSL support to be explicitly disabled. [RT #20288] 2714. [port] aix/powerpc: 'asm("ics");' needs non standard assembler flags. 2713. [bug] powerpc: atomic operations missing asm("ics") / __isync() calls. 2712. [func] New 'auto-dnssec' zone option allows zone signing to be fully automated in zones configured for dynamic DNS. 'auto-dnssec allow;' permits a zone to be signed by creating keys for it in the key-directory and using 'rndc sign '. 'auto-dnssec maintain;' allows that too, plus it also keeps the zone's DNSSEC keys up to date according to their timing metadata. [RT #19943] 2711. [port] win32: Add the bin/pkcs11 tools into the full build. [RT #20372] 2710. [func] New 'dnssec-signzone -x' flag and 'dnskey-ksk-only' zone option cause a zone to be signed with only KSKs signing the DNSKEY RRset, not ZSKs. This reduces the size of a DNSKEY answer. [RT #20340] 2709. [func] Added some data fields, currently unused, to the private key file format, to allow implementation of explicit key rollover in a future release without impairing backward or forward compatibility. [RT #20310] 2708. [func] Insecure to secure and NSEC3 parameter changes via update are now fully supported and no longer require defines to enable. We now no longer overload the NSEC3PARAM flag field, nor the NSEC OPT bit at the apex. Secure to insecure changes are controlled by by the named.conf option 'secure-to-insecure'. Warning: If you had previously enabled support by adding defines at compile time to BIND 9.6 you should ensure that all changes that are in progress have completed prior to upgrading to BIND 9.7. BIND 9.7 is not backwards compatible. 2707. [func] dnssec-keyfromlabel no longer require engine name to be specified in the label if there is a default engine or the -E option has been used. Also, it now uses default algorithms as dnssec-keygen does (i.e., RSASHA1, or NSEC3RSASHA1 if -3 is used). [RT #20371] 2706. [bug] Loading a zone with a very large NSEC3 salt could trigger an assert. [RT #20368] 2705. [placeholder] 2704. [bug] Serial of dynamic and stub zones could be inconsistent with their SOA serial. [RT #19387] 2703. [func] Introduce an OpenSSL "engine" argument with -E for all binaries which can take benefit of crypto hardware. [RT #20230] 2702. [func] Update PKCS#11 tools (bin/pkcs11) [RT #20225 & all] 2701. [doc] Correction to ARM: hmac-md5 is no longer the only supported TSIG key algorithm. [RT #18046] 2700. [doc] The match-mapped-addresses option is discouraged. [RT #12252] 2699. [bug] Missing lock in rbtdb.c. [RT #20037] 2698. [placeholder] 2697. [port] win32: ensure that S_IFMT, S_IFDIR, S_IFCHR and S_IFREG are defined after including . [RT #20309] 2696. [bug] named failed to successfully process some valid acl constructs. [RT #20308] 2695. [func] DHCP/DDNS - update fdwatch code for use by DHCP. Modify the api to isc_sockfdwatch_t (the callback function for isc_socket_fdwatchcreate) to include information about the direction (read or write) and add isc_socket_fdwatchpoke. [RT #20253] 2694. [bug] Reduce default NSEC3 iterations from 100 to 10. [RT #19970] 2693. [port] Add some noreturn attributes. [RT #20257] 2692. [port] win32: 32/64 bit cleanups. [RT #20335] 2691. [func] dnssec-signzone: retain the existing NSEC or NSEC3 chain when re-signing a previously-signed zone. Use -u to modify NSEC3 parameters or switch between NSEC and NSEC3. [RT #20304] 2690. [bug] win32: fix isc_thread_key_getspecific() prototype. [RT #20315] 2689. [bug] Correctly handle snprintf result. [RT #20306] 2688. [bug] Use INTERFACE_F_POINTTOPOINT, not IFF_POINTOPOINT, to decide to fetch the destination address. [RT #20305] 2687. [bug] Fixed dnssec-signzone -S handling of revoked keys. Also, added warnings when revoking a ZSK, as this is not defined by protocol (but is legal). [RT #19943] 2686. [bug] dnssec-signzone should clean the old NSEC chain when signing with NSEC3 and vice versa. [RT #20301] 2685. [contrib] Update contrib/zkt to version 0.99c. [RT #20054] 2684. [cleanup] dig: formalize +ad and +cd as synonyms for +adflag and +cdflag. [RT #19305] 2683. [bug] dnssec-signzone should clean out old NSEC3 chains when the NSEC3 parameters used to sign the zone change. [RT #20246] 2682. [bug] "configure --enable-symtable=all" failed to build. [RT #20282] 2681. [bug] IPSECKEY RR of gateway type 3 was not correctly decoded. [RT #20269] 2680. [func] Move contrib/pkcs11-keygen to bin/pkcs11. [RT #20067] 2679. [func] dig -k can now accept TSIG keys in named.conf format. [RT #20031] 2678. [func] Treat DS queries as if "minimal-response yes;" was set. [RT #20258] 2677. [func] Changes to key metadata behavior: - Keys without "publish" or "active" dates set will no longer be used for smart signing. However, those dates will be set to "now" by default when a key is created; to generate a key but not use it yet, use dnssec-keygen -G. - New "inactive" date (dnssec-keygen/settime -I) sets the time when a key is no longer used for signing but is still published. - The "unpublished" date (-U) is deprecated in favor of "deleted" (-D). [RT #20247] 2676. [bug] --with-export-installdir should have been --with-export-includedir. [RT #20252] 2675. [bug] dnssec-signzone could crash if the key directory did not exist. [RT #20232] --- 9.7.0a3 released --- 2674. [bug] "dnssec-lookaside auto;" crashed if named was built without openssl. [RT #20231] 2673. [bug] The managed-keys.bind zone file could fail to load due to a spurious result from sync_keyzone() [RT #20045] 2672. [bug] Don't enable searching in 'host' when doing reverse lookups. [RT #20218] 2671. [bug] Add support for PKCS#11 providers not returning the public exponent in RSA private keys (OpenCryptoki for instance) in dnssec-keyfromlabel. [RT #19294] 2670. [bug] Unexpected connect failures failed to log enough information to be useful. [RT #20205] 2669. [func] Update PKCS#11 support to support Keyper HSM. Update PKCS#11 patch to be against openssl-0.9.8i. 2668. [func] Several improvements to dnssec-* tools, including: - dnssec-keygen and dnssec-settime can now set key metadata fields 0 (to unset a value, use "none") - dnssec-revoke sets the revocation date in addition to the revoke bit - dnssec-settime can now print individual metadata fields instead of always printing all of them, and can print them in unix epoch time format for use by scripts [RT #19942] 2667. [func] Add support for logging stack backtrace on assertion failure (not available for all platforms). [RT #19780] 2666. [func] Added an 'options' argument to dns_name_fromstring() (API change from 9.7.0a2). [RT #20196] 2665. [func] Clarify syntax for managed-keys {} statement, add ARM documentation about RFC 5011 support. [RT #19874] 2664. [bug] create_keydata() and minimal_update() in zone.c didn't properly check return values for some functions. [RT #19956] 2663. [func] win32: allow named to run as a service using "NT AUTHORITY\LocalService" as the account. [RT #19977] 2662. [bug] lwres_getipnodebyname() and lwres_getipnodebyaddr() returned a misleading error code when lwresd was down. [RT #20028] 2661. [bug] Check whether socket fd exceeds FD_SETSIZE when creating lwres context. [RT #20029] 2660. [func] Add a new set of DNS libraries for non-BIND9 applications. See README.libdns. [RT #19369] 2659. [doc] Clarify dnssec-keygen doc: key name must match zone name for DNSSEC keys. [RT #19938] 2658. [bug] dnssec-settime and dnssec-revoke didn't process key file paths correctly. [RT #20078] 2657. [cleanup] Lower "journal file does not exist, creating it" log level to debug 1. [RT #20058] 2656. [func] win32: add a "tools only" check box to the installer which causes it to only install dig, host, nslookup, nsupdate and relevant DLLs. [RT #19998] 2655. [doc] Document that key-directory does not affect bind.keys, rndc.key or session.key. [RT #20155] 2654. [bug] Improve error reporting on duplicated names for deny-answer-xxx. [RT #20164] 2653. [bug] Treat ENGINE_load_private_key() failures as key not found rather than out of memory. [RT #18033] 2652. [func] Provide more detail about what record is being deleted. [RT #20061] 2651. [bug] Dates could print incorrectly in K*.key files on 64-bit systems. [RT #20076] 2650. [bug] Assertion failure in dnssec-signzone when trying to read keyset-* files. [RT #20075] 2649. [bug] Set the domain for forward only zones. [RT #19944] 2648. [port] win32: isc_time_seconds() was broken. [RT #19900] 2647. [bug] Remove unnecessary SOA updates when a new KSK is added. [RT #19913] 2646. [bug] Incorrect cleanup on error in socket.c. [RT #19987] 2645. [port] "gcc -m32" didn't work on amd64 and x86_64 platforms which default to 64 bits. [RT #19927] --- 9.7.0a2 released --- 2644. [bug] Change #2628 caused a regression on some systems; named was unable to write the PID file and would fail on startup. [RT #20001] 2643. [bug] Stub zones interacted badly with NSEC3 support. [RT #19777] 2642. [bug] nsupdate could dump core on solaris when reading improperly formatted key files. [RT #20015] 2641. [bug] Fixed an error in parsing update-policy syntax, added a regression test to check it. [RT #20007] 2640. [security] A specially crafted update packet will cause named to exit. [RT #20000] 2639. [bug] Silence compiler warnings in gssapi code. [RT #19954] 2638. [bug] Install arpaname. [RT #19957] 2637. [func] Rationalize dnssec-signzone's signwithkey() calling. [RT #19959] 2636. [func] Simplify zone signing and key maintenance with the dnssec-* tools. Major changes: - all dnssec-* tools now take a -K option to specify a directory in which key files will be stored - DNSSEC can now store metadata indicating when they are scheduled to be published, activated, revoked or removed; these values can be set by dnssec-keygen or overwritten by the new dnssec-settime command - dnssec-signzone -S (for "smart") option reads key metadata and uses it to determine automatically which keys to publish to the zone, use for signing, revoke, or remove from the zone [RT #19816] 2635. [bug] isc_inet_ntop() incorrectly handled 0.0/16 addresses. [RT #19716] 2634. [port] win32: Add support for libxml2, enable statschannel. [RT #19773] 2633. [bug] Handle 15 bit rand() functions. [RT #19783] 2632. [func] util/kit.sh: warn if documentation appears to be out of date. [RT #19922] 2631. [bug] Handle "//", "/./" and "/../" in mkdirpath(). [RT #19926 ] 2630. [func] Improved syntax for DDNS autoconfiguration: use "update-policy local;" to switch on local DDNS in a zone. (The "ddns-autoconf" option has been removed.) [RT #19875] 2629. [port] Check for seteuid()/setegid(), use setresuid()/ setresgid() if not present. [RT #19932] 2628. [port] linux: Allow /var/run/named/named.pid to be opened at startup with reduced capabilities in operation. [RT #19884] 2627. [bug] Named aborted if the same key was included in trusted-keys more than once. [RT #19918] 2626. [bug] Multiple trusted-keys could trigger an assertion failure. [RT #19914] 2625. [bug] Missing UNLOCK in rbtdb.c. [RT #19865] 2624. [func] 'named-checkconf -p' will print out the parsed configuration. [RT #18871] 2623. [bug] Named started searches for DS non-optimally. [RT #19915] 2622. [bug] Printing of named.conf grammar was broken. [RT #19919] 2621. [doc] Made copyright boilerplate consistent. [RT #19833] 2620. [bug] Delay thawing the zone until the reload of it has completed successfully. [RT #19750] 2619. [func] Add support for RFC 5011, automatic trust anchor maintenance. The new "managed-keys" statement can be used in place of "trusted-keys" for zones which support this protocol. (Note: this syntax is expected to change prior to 9.7.0 final.) [RT #19248] 2618. [bug] The sdb and sdlz db_interator_seek() methods could loop infinitely. [RT #19847] 2617. [bug] ifconfig.sh failed to emit an error message when run from the wrong location. [RT #19375] 2616. [bug] 'host' used the nameservers from resolv.conf even when a explicit nameserver was specified. [RT #19852] 2615. [bug] "__attribute__((unused))" was in the wrong place for ia64 gcc builds. [RT #19854] 2614. [port] win32: 'named -v' should automatically be executed in the foreground. [RT #19844] 2613. [placeholder] --- 9.7.0a1 released --- 2612. [func] Add default values for the arguments to dnssec-keygen. Without arguments, it will now generate a 1024-bit RSASHA1 zone-signing key, or with the -f KSK option, a 2048-bit RSASHA1 key-signing key. [RT #19300] 2611. [func] Add -l option to dnssec-dsfromkey to generate DLV records instead of DS records. [RT #19300] 2610. [port] sunos: Change #2363 was not complete. [RT #19796] 2609. [func] Simplify the configuration of dynamic zones: - add ddns-confgen command to generate configuration text for named.conf - add zone option "ddns-autoconf yes;", which causes named to generate a TSIG session key and allow updates to the zone using that key - add '-l' (localhost) option to nsupdate, which causes nsupdate to connect to a locally-running named process using the session key generated by named [RT #19284] 2608. [func] Perform post signing verification checks in dnssec-signzone. These can be disabled with -P. The post sign verification test ensures that for each algorithm in use there is at least one non revoked self signed KSK key. That all revoked KSK keys are self signed. That all records in the zone are signed by the algorithm. [RT #19653] 2607. [bug] named could incorrectly delete NSEC3 records for empty nodes when processing a update request. [RT #19749] 2606. [bug] "delegation-only" was not being accepted in delegation-only type zones. [RT #19717] 2605. [bug] Accept DS responses from delegation only zones. [RT # 19296] 2604. [func] Add support for DNS rebinding attack prevention through new options, deny-answer-addresses and deny-answer-aliases. Based on contributed code from JD Nurmi, Google. [RT #18192] 2603. [port] win32: handle .exe extension of named-checkzone and named-comilezone argv[0] names under windows. [RT #19767] 2602. [port] win32: fix debugging command line build of libisccfg. [RT #19767] 2601. [doc] Mention file creation mode mask in the named manual page. 2600. [doc] ARM: miscellaneous reformatting for different page widths. [RT #19574] 2599. [bug] Address rapid memory growth when validation fails. [RT #19654] 2598. [func] Reserve the -F flag. [RT #19657] 2597. [bug] Handle a validation failure with a insecure delegation from a NSEC3 signed master/slave zone. [RT #19464] 2596. [bug] Stale tree nodes of cache/dynamic rbtdb could stay long, leading to inefficient memory usage or rejecting newer cache entries in the worst case. [RT #19563] 2595. [bug] Fix unknown extended rcodes in dig. [RT #19625] 2594. [func] Have rndc warn if using its default configuration file when the key file also exists. [RT #19424] 2593. [bug] Improve a corner source of SERVFAILs [RT #19632] 2592. [bug] Treat "any" as a type in nsupdate. [RT #19455] 2591. [bug] named could die when processing a update in removed_orphaned_ds(). [RT #19507] 2590. [func] Report zone/class of "update with no effect". [RT #19542] 2589. [bug] dns_db_unregister() failed to clear '*dbimp'. [RT #19626] 2588. [bug] SO_REUSEADDR could be set unconditionally after failure of bind(2) call. This should be rare and mostly harmless, but may cause interference with other processes that happen to use the same port. [RT #19642] 2587. [func] Improve logging by reporting serial numbers for when zone serial has gone backwards or unchanged. [RT #19506] 2586. [bug] Missing cleanup of SIG rdataset in searching a DLZ DB or SDB. [RT #19577] 2585. [bug] Uninitialized socket name could be referenced via a statistics channel, triggering an assertion failure in XML rendering. [RT #19427] 2584. [bug] alpha: gcc optimization could break atomic operations. [RT #19227] 2583. [port] netbsd: provide a control to not add the compile date to the version string, -DNO_VERSION_DATE. 2582. [bug] Don't emit warning log message when we attempt to remove non-existent journal. [RT #19516] 2581. [contrib] dlz/mysql set MYSQL_OPT_RECONNECT option on connection. Requires MySQL 5.0.19 or later. [RT #19084] 2580. [bug] UpdateRej statistics counter could be incremented twice for one rejection. [RT #19476] 2579. [bug] DNSSEC lookaside validation failed to handle unknown algorithms. [RT #19479] 2578. [bug] Changed default sig-signing-type to 65534, because 65535 turns out to be reserved. [RT #19477] 2577. [doc] Clarified some statistics counters. [RT #19454] 2576. [bug] NSEC record were not being correctly signed when a zone transitions from insecure to secure. Handle such incorrectly signed zones. [RT #19114] 2575. [func] New functions dns_name_fromstring() and dns_name_tostring(), to simplify conversion of a string to a dns_name structure and vice versa. [RT #19451] 2574. [doc] Document nsupdate -g and -o. [RT #19351] 2573. [bug] Replacing a non-CNAME record with a CNAME record in a single transaction in a signed zone failed. [RT #19397] 2572. [func] Simplify DLV configuration, with a new option "dnssec-lookaside auto;" This is the equivalent of "dnssec-lookaside . trust-anchor dlv.isc.org;" plus setting a trusted-key for dlv.isc.org. Note: The trusted key is hard-coded into named, but is also stored in (and can be overridden by) $sysconfdir/bind.keys. As the ISC DLV key rolls over it can be kept up to date by replacing the bind.keys file with a key downloaded from https://www.isc.org/solutions/dlv. [RT #18685] 2571. [func] Add a new tool "arpaname" which translates IP addresses to the corresponding IN-ADDR.ARPA or IP6.ARPA name. [RT #18976] 2570. [func] Log the destination address the query was sent to. [RT #19209] 2569. [func] Move journalprint, nsec3hash, and genrandom commands from bin/tests into bin/tools; "make install" will put them in $sbindir. [RT #19301] 2568. [bug] Report when the write to indicate a otherwise successful start fails. [RT #19360] 2567. [bug] dst__privstruct_writefile() could miss write errors. write_public_key() could miss write errors. dnssec-dsfromkey could miss write errors. [RT #19360] 2566. [cleanup] Clarify logged message when an insecure DNSSEC response arrives from a zone thought to be secure: "insecurity proof failed" instead of "not insecure". [RT #19400] 2565. [func] Add support for HIP record. Includes new functions dns_rdata_hip_first(), dns_rdata_hip_next() and dns_rdata_hip_current(). [RT #19384] 2564. [bug] Only take EDNS fallback steps when processing timeouts. [RT #19405] 2563. [bug] Dig could leak a socket causing it to wait forever to exit. [RT #19359] 2562. [doc] ARM: miscellaneous improvements, reorganization, and some new content. 2561. [doc] Add isc-config.sh(1) man page. [RT #16378] 2560. [bug] Add #include to iptable.c. [RT #18258] 2559. [bug] dnssec-dsfromkey could compute bad DS records when reading from a K* files. [RT #19357] 2558. [func] Set the ownership of missing directories created for pid-file if -u has been specified on the command line. [RT #19328] 2557. [cleanup] PCI compliance: * new libisc log module file * isc_dir_chroot() now also changes the working directory to "/". * additional INSISTs * additional logging when files can't be removed. 2556. [port] Solaris: mkdir(2) on tmpfs filesystems does not do the error checks in the correct order resulting in the wrong error code sometimes being returned. [RT #19249] 2555. [func] dig: when emitting a hex dump also display the corresponding characters. [RT #19258] 2554. [bug] Validation of uppercase queries from NSEC3 zones could fail. [RT #19297] 2553. [bug] Reference leak on DNSSEC validation errors. [RT #19291] 2552. [bug] zero-no-soa-ttl-cache was not being honored. [RT #19340] 2551. [bug] Potential Reference leak on return. [RT #19341] 2550. [bug] Check --with-openssl= finds . [RT #19343] 2549. [port] linux: define NR_OPEN if not currently defined. [RT #19344] 2548. [bug] Install iterated_hash.h. [RT #19335] 2547. [bug] openssl_link.c:mem_realloc() could reference an out-of-range area of the source buffer. New public function isc_mem_reallocate() was introduced to address this bug. [RT #19313] 2546. [func] Add --enable-openssl-hash configure flag to use OpenSSL (in place of internal routine) for hash functions (MD5, SHA[12] and HMAC). [RT #18815] 2545. [doc] ARM: Legal hostname checking (check-names) is for SRV RDATA too. [RT #19304] 2544. [cleanup] Removed unused structure members in adb.c. [RT #19225] 2543. [contrib] Update contrib/zkt to version 0.98. [RT #19113] 2542. [doc] Update the description of dig +adflag. [RT #19290] 2541. [bug] Conditionally update dispatch manager statistics. [RT #19247] 2540. [func] Add a nibble mode to $GENERATE. [RT #18872] 2539. [security] Update the interaction between recursion, allow-query, allow-query-cache and allow-recursion. [RT #19198] 2538. [bug] cache/ADB memory could grow over max-cache-size, especially with threads and smaller max-cache-size values. [RT #19240] 2537. [func] Added more statistics counters including those on socket I/O events and query RTT histograms. [RT #18802] 2536. [cleanup] Silence some warnings when -Werror=format-security is specified. [RT #19083] 2535. [bug] dig +showsearch and +trace interacted badly. [RT #19091] 2534. [func] Check NAPTR records regular expressions and replacement strings to ensure they are syntactically valid and consistent. [RT #18168] 2533. [doc] ARM: document @ (at-sign). [RT #17144] 2532. [bug] dig: check the question section of the response to see if it matches the asked question. [RT #18495] 2531. [bug] Change #2207 was incomplete. [RT #19098] 2530. [bug] named failed to reject insecure to secure transitions via UPDATE. [RT #19101] 2529. [cleanup] Upgrade libtool to silence complaints from recent version of autoconf. [RT #18657] 2528. [cleanup] Silence spurious configure warning about --datarootdir [RT #19096] 2527. [placeholder] 2526. [func] New named option "attach-cache" that allows multiple views to share a single cache to save memory and improve lookup efficiency. Based on contributed code from Barclay Osborn, Google. [RT #18905] 2525. [func] New logging category "query-errors" to provide detailed internal information about query failures, especially about server failures. [RT #19027] 2524. [port] sunos: dnssec-signzone needs strtoul(). [RT #19129] 2523. [bug] Random type rdata freed by dns_nsec_typepresent(). [RT #19112] 2522. [security] Handle -1 from DSA_do_verify() and EVP_VerifyFinal(). 2521. [bug] Improve epoll cross compilation support. [RT #19047] 2520. [bug] Update xml statistics version number to 2.0 as change #2388 made the schema incompatible to the previous version. [RT #19080] 2519. [bug] dig/host with -4 or -6 didn't work if more than two nameserver addresses of the excluded address family preceded in resolv.conf. [RT #19081] 2518. [func] Add support for the new CERT types from RFC 4398. [RT #19077] 2517. [bug] dig +trace with -4 or -6 failed when it chose a nameserver address of the excluded address type. [RT #18843] 2516. [bug] glue sort for responses was performed even when not needed. [RT #19039] 2515. [port] win32: build dnssec-dsfromkey and dnssec-keyfromlabel. [RT #19063] 2514. [bug] dig/host failed with -4 or -6 when resolv.conf contains a nameserver of the excluded address family. [RT #18848] 2513. [bug] Fix windows cli build. [RT #19062] 2512. [func] Print a summary of the cached records which make up the negative response. [RT #18885] 2511. [cleanup] dns_rdata_tofmttext() add const to linebreak. [RT #18885] 2510. [bug] "dig +sigchase" could trigger REQUIRE failures. [RT #19033] 2509. [bug] Specifying a fixed query source port was broken. [RT #19051] 2508. [placeholder] 2507. [func] Log the recursion quota values when killing the oldest query or refusing to recurse due to quota. [RT #19022] 2506. [port] solaris: Check at configure time if hack_shutup_pthreadonceinit is needed. [RT #19037] 2505. [port] Treat amd64 similarly to x86_64 when determining atomic operation support. [RT #19031] 2504. [bug] Address race condition in the socket code. [RT #18899] 2503. [port] linux: improve compatibility with Linux Standard Base. [RT #18793] 2502. [cleanup] isc_radix: Improve compliance with coding style, document function in . [RT #18534] 2501. [func] $GENERATE now supports all rdata types. Multi-field rdata types need to be quoted. See the ARM for details. [RT #18368] 2500. [contrib] contrib/sdb/pgsql/zonetodb.c called non-existent function. [RT #18582] 2499. [port] solaris: lib/lwres/getaddrinfo.c namespace clash. [RT #18837] --- 9.6.0rc1 released --- 2498. [bug] Removed a bogus function argument used with ISC_SOCKET_USE_POLLWATCH: it could cause compiler warning or crash named with the debug 1 level of logging. [RT #18917] 2497. [bug] Don't add RRSIG bit to NSEC3 bit map for insecure delegation. 2496. [bug] Add sanity length checks to NSID option. [RT #18813] 2495. [bug] Tighten RRSIG checks. [RT #18795] 2494. [bug] isc/radix.h, dns/sdlz.h and dns/dlz.h were not being installed. [RT #18826] 2493. [bug] The linux capabilities code was not correctly cleaning up after itself. [RT #18767] 2492. [func] Rndc status now reports the number of cpus discovered and the number of worker threads when running multi-threaded. [RT #18273] 2491. [func] Attempt to re-use a local port if we are already using the port. [RT #18548] 2490. [port] aix: work around a kernel bug where IPV6_RECVPKTINFO is cleared when IPV6_V6ONLY is set. [RT #18785] 2489. [port] solaris: Workaround Solaris's kernel bug about /dev/poll: http://bugs.opensolaris.org/view_bug.do?bug_id=6724237 Define ISC_SOCKET_USE_POLLWATCH at build time to enable this workaround. [RT #18870] 2488. [func] Added a tool, dnssec-dsfromkey, to generate DS records from keyset and .key files. [RT #18694] 2487. [bug] Give TCP connections longer to complete. [RT #18675] 2486. [func] The default locations for named.pid and lwresd.pid are now /var/run/named/named.pid and /var/run/lwresd/lwresd.pid respectively. This allows the owner of the containing directory to be set, for "named -u" support, and allows there to be a permanent symbolic link in the path, for "named -t" support. [RT #18306] 2485. [bug] Change update's the handling of obscured RRSIG records. Not all orphaned DS records were being removed. [RT #18828] 2484. [bug] It was possible to trigger a REQUIRE failure when adding NSEC3 proofs to the response in query_addwildcardproof(). [RT #18828] 2483. [port] win32: chroot() is not supported. [RT #18805] 2482. [port] libxml2: support versions 2.7.* in addition to 2.6.*. [RT #18806] --- 9.6.0b1 released --- 2481. [bug] rbtdb.c:matchparams() failed to handle NSEC3 chain collisions. [RT #18812] 2480. [bug] named could fail to emit all the required NSEC3 records. [RT #18812] 2479. [bug] xfrout:covers was not properly initialized. [RT #18801] 2478. [bug] 'addresses' could be used uninitialized in configure_forward(). [RT #18800] 2477. [bug] dig: the global option to print the command line is +cmd not print_cmd. Update the output to reflect this. [RT #17008] 2476. [doc] ARM: improve documentation for max-journal-size and ixfr-from-differences. [RT #15909] [RT #18541] 2475. [bug] LRU cache cleanup under overmem condition could purge particular entries more aggressively. [RT #17628] 2474. [bug] ACL structures could be allocated with insufficient space, causing an array overrun. [RT #18765] 2473. [port] linux: raise the limit on open files to the possible maximum value before spawning threads; 'files' specified in named.conf doesn't seem to work with threads as expected. [RT #18784] 2472. [port] linux: check the number of available cpu's before calling chroot as it depends on "/proc". [RT #16923] 2471. [bug] named-checkzone was not reporting missing mandatory glue when sibling checks were disabled. [RT #18768] 2470. [bug] Elements of the isc_radix_node_t could be incorrectly overwritten. [RT# 18719] 2469. [port] solaris: Work around Solaris's select() limitations. [RT #18769] 2468. [bug] Resolver could try unreachable servers multiple times. [RT #18739] 2467. [bug] Failure of fcntl(F_DUPFD) wasn't logged. [RT #18740] 2466. [doc] ARM: explain max-cache-ttl 0 SERVFAIL issue. [RT #18302] 2465. [bug] Adb's handling of lame addresses was different for IPv4 and IPv6. [RT #18738] 2464. [port] linux: check that a capability is present before trying to set it. [RT #18135] 2463. [port] linux: POSIX doesn't include the IPv6 Advanced Socket API and glibc hides parts of the IPv6 Advanced Socket API as a result. This is stupid as it breaks how the two halves (Basic and Advanced) of the IPv6 Socket API were designed to be used but we have to live with it. Define _GNU_SOURCE to pull in the IPv6 Advanced Socket API. [RT #18388] 2462. [doc] Document -m (enable memory usage debugging) option for dig. [RT #18757] 2461. [port] sunos: Change #2363 was not complete. [RT #17513] --- 9.6.0a1 released --- 2460. [bug] Don't call dns_db_getnsec3parameters() on the cache. [RT #18697] 2459. [contrib] Import dnssec-zkt to contrib/zkt. [RT #18448] 2458. [doc] ARM: update and correction for max-cache-size. [RT #18294] 2457. [tuning] max-cache-size is reverted to 0, the previous default. It should be safe because expired cache entries are also purged. [RT #18684] 2456. [bug] In ACLs, ::/0 and 0.0.0.0/0 would both match any address, regardless of family. They now correctly distinguish IPv4 from IPv6. [RT #18559] 2455. [bug] Stop metadata being transferred via axfr/ixfr. [RT #18639] 2454. [func] nsupdate: you can now set a default ttl. [RT #18317] 2453. [bug] Remove NULL pointer dereference in dns_journal_print(). [RT #18316] 2452. [func] Improve bin/test/journalprint. [RT #18316] 2451. [port] solaris: handle runtime linking better. [RT #18356] 2450. [doc] Fix lwresd docbook problem for manual page. [RT #18672] 2449. [placeholder] 2448. [func] Add NSEC3 support. [RT #15452] 2447. [cleanup] libbind has been split out as a separate product. 2446. [func] Add a new log message about build options on startup. A new command-line option '-V' for named is also provided to show this information. [RT# 18645] 2445. [doc] ARM out-of-date on empty reverse zones (list includes RFC1918 address, but these are not yet compiled in). [RT #18578] 2444. [port] Linux, FreeBSD, AIX: Turn off path mtu discovery (clear DF) for UDP responses and requests. 2443. [bug] win32: UDP connect() would not generate an event, and so connected UDP sockets would never clean up. Fix this by doing an immediate WSAConnect() rather than an io completion port type for UDP. 2442. [bug] A lock could be destroyed twice. [RT# 18626] 2441. [bug] isc_radix_insert() could copy radix tree nodes incompletely. [RT #18573] 2440. [bug] named-checkconf used an incorrect test to determine if an ACL was set to none. 2439. [bug] Potential NULL dereference in dns_acl_isanyornone(). [RT #18559] 2438. [bug] Timeouts could be logged incorrectly under win32. 2437. [bug] Sockets could be closed too early, leading to inconsistent states in the socket module. [RT #18298] 2436. [security] win32: UDP client handler can be shutdown. [RT #18576] 2435. [bug] Fixed an ACL memory leak affecting win32. 2434. [bug] Fixed a minor error-reporting bug in lib/isc/win32/socket.c. 2433. [tuning] Set initial timeout to 800ms. 2432. [bug] More Windows socket handling improvements. Stop using I/O events and use IO Completion Ports throughout. Rewrite the receive path logic to make it easier to support multiple simultaneous requesters in the future. Add stricter consistency checking as a compile-time option (define ISC_SOCKET_CONSISTENCY_CHECKS; defaults to off). 2431. [bug] Acl processing could leak memory. [RT #18323] 2430. [bug] win32: isc_interval_set() could round down to zero if the input was less than NS_INTERVAL nanoseconds. Round up instead. [RT #18549] 2429. [doc] nsupdate should be in section 1 of the man pages. [RT #18283] 2428. [bug] dns_iptable_merge() mishandled merges of negative tables. [RT #18409] 2427. [func] Treat DNSKEY queries as if "minimal-response yes;" was set. [RT #18528] 2426. [bug] libbind: inet_net_pton() can sometimes return the wrong value if excessively large net masks are supplied. [RT #18512] 2425. [bug] named didn't detect unavailable query source addresses at load time. [RT #18536] 2424. [port] configure now probes for a working epoll implementation. Allow the use of kqueue, epoll and /dev/poll to be selected at compile time. [RT #18277] 2423. [security] Randomize server selection on queries, so as to make forgery a little more difficult. Instead of always preferring the server with the lowest RTT, pick a server with RTT within the same 128 millisecond band. [RT #18441] 2422. [bug] Handle the special return value of a empty node as if it was a NXRRSET in the validator. [RT #18447] 2421. [func] Add new command line option '-S' for named to specify the max number of sockets. [RT #18493] Use caution: this option may not work for some operating systems without rebuilding named. 2420. [bug] Windows socket handling cleanup. Let the io completion event send out canceled read/write done events, which keeps us from writing to memory we no longer have ownership of. Add debugging socket_log() function. Rework TCP socket handling to not leak sockets. 2419. [cleanup] Document that isc_socket_create() and isc_socket_open() should not be used for isc_sockettype_fdwatch sockets. [RT #18521] 2418. [bug] AXFR request on a DLZ could trigger a REQUIRE failure [RT #18430] 2417. [bug] Connecting UDP sockets for outgoing queries could unexpectedly fail with an 'address already in use' error. [RT #18411] 2416. [func] Log file descriptors that cause exceeding the internal maximum. [RT #18460] 2415. [bug] 'rndc dumpdb' could trigger various assertion failures in rbtdb.c. [RT #18455] 2414. [bug] A masterdump context held the database lock too long, causing various troubles such as dead lock and recursive lock acquisition. [RT #18311, #18456] 2413. [bug] Fixed an unreachable code path in socket.c. [RT #18442] 2412. [bug] win32: address a resource leak. [RT #18374] 2411. [bug] Allow using a larger number of sockets than FD_SETSIZE for select(). To enable this, set ISC_SOCKET_MAXSOCKETS at compilation time. [RT #18433] Note: with changes #2469 and #2421 above, there is no need to tweak ISC_SOCKET_MAXSOCKETS at compilation time any more. 2410. [bug] Correctly delete m_versionInfo. [RT #18432] 2409. [bug] Only log that we disabled EDNS processing if we were subsequently successful. [RT #18029] 2408. [bug] A duplicate TCP dispatch event could be sent, which could then trigger an assertion failure in resquery_response(). [RT #18275] 2407. [port] hpux: test for sys/dyntune.h. [RT #18421] 2406. [placeholder] 2405. [cleanup] The default value for dnssec-validation was changed to "yes" in 9.5.0-P1 and all subsequent releases; this was inadvertently omitted from CHANGES at the time. 2404. [port] hpux: files unlimited support. 2403. [bug] TSIG context leak. [RT #18341] 2402. [port] Support Solaris 2.11 and over. [RT #18362] 2401. [bug] Expect to get E[MN]FILE errno internal_accept() (from accept() or fcntl() system calls). [RT #18358] 2400. [bug] Log if kqueue()/epoll_create()/open(/dev/poll) fails. [RT #18297] 2399. [placeholder] 2398. [bug] Improve file descriptor management. New, temporary, named.conf option reserved-sockets, default 512. [RT #18344] 2397. [bug] gssapi_functions had too many elements. [RT #18355] 2396. [bug] Don't set SO_REUSEADDR for randomized ports. [RT #18336] 2395. [port] Avoid warning and no effect from "files unlimited" on Linux when running as root. [RT #18335] 2394. [bug] Default configuration options set the limit for open files to 'unlimited' as described in the documentation. [RT #18331] 2393. [bug] nested acls containing keys could trigger an assertion in acl.c. [RT #18166] 2392. [bug] remove 'grep -q' from acl test script, some platforms don't support it. [RT #18253] 2391. [port] hpux: cover additional recvmsg() error codes. [RT #18301] 2390. [bug] dispatch.c could make a false warning on 'odd socket'. [RT #18301]. 2389. [bug] Move the "working directory writable" check to after the ns_os_changeuser() call. [RT #18326] 2388. [bug] Avoid using tables for layout purposes in statistics XSL [RT #18159]. 2387. [bug] Silence compiler warnings in lib/isc/radix.c. [RT #18147] [RT #18258] 2386. [func] Add warning about too small 'open files' limit. [RT #18269] 2385. [bug] A condition variable in socket.c could leak in rare error handling [RT #17968]. 2384. [security] Fully randomize UDP query ports to improve forgery resilience. [RT #17949, #18098] 2383. [bug] named could double queries when they resulted in SERVFAIL due to overkilling EDNS0 failure detection. [RT #18182] 2382. [doc] Add descriptions of DHCID, IPSECKEY, SPF and SSHFP to ARM. 2381. [port] dlz/mysql: support multiple install layouts for mysql. /include/{,mysql/}mysql.h and /lib/{,mysql/}. [RT #18152] 2380. [bug] dns_view_find() was not returning NXDOMAIN/NXRRSET proofs which, in turn, caused validation failures for insecure zones immediately below a secure zone the server was authoritative for. [RT #18112] 2379. [contrib] queryperf/gen-data-queryperf.py: removed redundant TLDs and supported RRs with TTLs [RT #17972] 2378. [bug] gssapi_functions{} had a redundant member in BIND 9.5. [RT #18169] 2377. [bug] Address race condition in dnssec-signzone. [RT #18142] 2376. [bug] Change #2144 was not complete. 2375. [placeholder] 2374. [bug] "blackhole" ACLs could cause named to segfault due to some uninitialized memory. [RT #18095] 2373. [bug] Default values of zone ACLs were re-parsed each time a new zone was configured, causing an overconsumption of memory. [RT #18092] 2372. [bug] Fixed incorrect TAG_HMACSHA256_BITS value [RT #18047] 2371. [doc] Add +nsid option to dig man page. [RT #18039] 2370. [bug] "rndc freeze" could trigger an assertion in named when called on a nonexistent zone. [RT #18050] 2369. [bug] libbind: Array bounds overrun on read in bitncmp(). [RT #18054] 2368. [port] Linux: use libcap for capability management if possible. [RT# 18026] 2367. [bug] Improve counting of dns_resstatscounter_retry [RT #18030] 2366. [bug] Adb shutdown race. [RT #18021] 2365. [bug] Fix a bug that caused dns_acl_isany() to return spurious results. [RT #18000] 2364. [bug] named could trigger a assertion when serving a malformed signed zone. [RT #17828] 2363. [port] sunos: pre-set "lt_cv_sys_max_cmd_len=4096;". [RT #17513] 2362. [cleanup] Make "rrset-order fixed" a compile-time option. settable by "./configure --enable-fixed-rrset". Disabled by default. [RT #17977] 2361. [bug] "recursion" statistics counter could be counted multiple times for a single query. [RT #17990] 2360. [bug] Fix a condition where we release a database version (which may acquire a lock) while holding the lock. 2359. [bug] Fix NSID bug. [RT #17942] 2358. [doc] Update host's default query description. [RT #17934] 2357. [port] Don't use OpenSSL's engine support in versions before OpenSSL 0.9.7f. [RT #17922] 2356. [bug] Built in mutex profiler was not scalable enough. [RT #17436] 2355. [func] Extend the number statistics counters available. [RT #17590] 2354. [bug] Failed to initialize some rdatasetheader_t elements. [RT #17927] 2353. [func] Add support for Name Server ID (RFC 5001). 'dig +nsid' requests NSID from server. 'request-nsid yes;' causes recursive server to send NSID requests to upstream servers. Server responds to NSID requests with the string configured by 'server-id' option. [RT #17091] 2352. [bug] Various GSS_API fixups. [RT #17729] 2351. [bug] convertxsl.pl generated very long lines. [RT #17906] 2350. [port] win32: IPv6 support. [RT #17797] 2349. [func] Provide incremental re-signing support for secure dynamic zones. [RT #1091] 2348. [func] Use the EVP interface to OpenSSL. Add PKCS#11 support. Documentation is in the new README.pkcs11 file. New tool, dnssec-keyfromlabel, which takes the label of a key pair in a HSM and constructs a DNS key pair for use by named and dnssec-signzone. [RT #16844] 2347. [bug] Delete now traverses the RB tree in the canonical order. [RT #17451] 2346. [func] Memory statistics now cover all active memory contexts in increased detail. [RT #17580] 2345. [bug] named-checkconf failed to detect when forwarders were set at both the options/view level and in a root zone. [RT #17671] 2344. [bug] Improve "logging{ file ...; };" documentation. [RT #17888] 2343. [bug] (Seemingly) duplicate IPv6 entries could be created in ADB. [RT #17837] 2342. [func] Use getifaddrs() if available under Linux. [RT #17224] 2341. [bug] libbind: add missing -I../include for off source tree builds. [RT #17606] 2340. [port] openbsd: interface configuration. [RT #17700] 2339. [port] tru64: support for libbind. [RT #17589] 2338. [bug] check_ds() could be called with a non DS rdataset. [RT #17598] 2337. [bug] BUILD_LDFLAGS was not being correctly set. [RT #17614] 2336. [func] If "named -6" is specified then listen on all IPv6 interfaces if there are not listen-on-v6 clauses in named.conf. [RT #17581] 2335. [port] sunos: libbind and *printf() support for long long. [RT #17513] 2334. [bug] Bad REQUIRES in fromstruct_in_naptr(), off by one bug in fromstruct_txt(). [RT #17609] 2333. [bug] Fix off by one error in isc_time_nowplusinterval(). [RT #17608] 2332. [contrib] query-loc-0.4.0. [RT #17602] 2331. [bug] Failure to regenerate any signatures was not being reported nor being past back to the UPDATE client. [RT #17570] 2330. [bug] Remove potential race condition when handling over memory events. [RT #17572] WARNING: API CHANGE: over memory callback function now needs to call isc_mem_waterack(). See for details. 2329. [bug] Clearer help text for dig's '-x' and '-i' options. 2328. [maint] Add AAAA addresses for A.ROOT-SERVERS.NET, F.ROOT-SERVERS.NET, H.ROOT-SERVERS.NET, J.ROOT-SERVERS.NET, K.ROOT-SERVERS.NET and M.ROOT-SERVERS.NET. 2327. [bug] It was possible to dereference a NULL pointer in rbtdb.c. Implement dead node processing in zones as we do for caches. [RT #17312] 2326. [bug] It was possible to trigger a INSIST in the acache processing. 2325. [port] Linux: use capset() function if available. [RT #17557] 2324. [bug] Fix IPv6 matching against "any;". [RT #17533] 2323. [port] tru64: namespace clash. [RT #17547] 2322. [port] MacOS: work around the limitation of setrlimit() for RLIMIT_NOFILE. [RT #17526] 2321. [placeholder] 2320. [func] Make statistics counters thread-safe for platforms that support certain atomic operations. [RT #17466] 2319. [bug] Silence Coverity warnings in lib/dns/rdata/in_1/apl_42.c. [RT #17469] 2318. [port] sunos fixes for libbind. [RT #17514] 2317. [bug] "make distclean" removed bind9.xsl.h. [RT #17518] 2316. [port] Missing #include in lib/dns/gssapictx.c. [RT #17513] 2315. [bug] Used incorrect address family for mapped IPv4 addresses in acl.c. [RT #17519] 2314. [bug] Uninitialized memory use on error path in bin/named/lwdnoop.c. [RT #17476] 2313. [cleanup] Silence Coverity warnings. Handle private stacks. [RT #17447] [RT #17478] 2312. [cleanup] Silence Coverity warning in lib/isc/unix/socket.c. [RT #17458] 2311. [bug] IPv6 addresses could match IPv4 ACL entries and vice versa. [RT #17462] 2310. [bug] dig, host, nslookup: flush stdout before emitting debug/fatal messages. [RT #17501] 2309. [cleanup] Fix Coverity warnings in lib/dns/acl.c and iptable.c. [RT #17455] 2308. [cleanup] Silence Coverity warning in bin/named/controlconf.c. [RT #17495] 2307. [bug] Remove infinite loop from lib/dns/sdb.c. [RT #17496] 2306. [bug] Remove potential race from lib/dns/resolver.c. [RT #17470] 2305. [security] inet_network() buffer overflow. CVE-2008-0122. 2304. [bug] Check returns from all dns_rdata_tostruct() calls. [RT #17460] 2303. [bug] Remove unnecessary code from bin/named/lwdgnba.c. [RT #17471] 2302. [bug] Fix memset() calls in lib/tests/t_api.c. [RT #17472] 2301. [bug] Remove resource leak and fix error messages in bin/tests/system/lwresd/lwtest.c. [RT #17474] 2300. [bug] Fixed failure to close open file in bin/tests/names/t_names.c. [RT #17473] 2299. [bug] Remove unnecessary NULL check in bin/nsupdate/nsupdate.c. [RT #17475] 2298. [bug] isc_mutex_lock() failure not caught in bin/tests/timers/t_timers.c. [RT #17468] 2297. [bug] isc_entropy_createfilesource() failure not caught in bin/tests/dst/t_dst.c. [RT #17467] 2296. [port] Allow docbook stylesheet location to be specified to configure. [RT #17457] 2295. [bug] Silence static overrun error in bin/named/lwaddr.c. [RT #17459] 2294. [func] Allow the experimental statistics channels to have multiple connections and ACL. Note: the stats-server and stats-server-v6 options available in the previous beta releases are replaced with the generic statistics-channels statement. 2293. [func] Add ACL regression test. [RT #17375] 2292. [bug] Log if the working directory is not writable. [RT #17312] 2291. [bug] PR_SET_DUMPABLE may be set too late. Also report failure to set PR_SET_DUMPABLE. [RT #17312] 2290. [bug] Let AD in the query signal that the client wants AD set in the response. [RT #17301] 2289. [func] named-checkzone now reports the out-of-zone CNAME found. [RT #17309] 2288. [port] win32: mark service as running when we have finished loading. [RT #17441] 2287. [bug] Use 'volatile' if the compiler supports it. [RT #17413] 2286. [func] Allow a TCP connection to be used as a weak authentication method for reverse zones. New update-policy methods tcp-self and 6to4-self. [RT #17378] 2285. [func] Test framework for client memory context management. [RT #17377] 2284. [bug] Memory leak in UPDATE prerequisite processing. [RT #17377] 2283. [bug] TSIG keys were not attaching to the memory context. TSIG keys should use the rings memory context rather than the clients memory context. [RT #17377] 2282. [bug] Acl code fixups. [RT #17346] [RT #17374] 2281. [bug] Attempts to use undefined acls were not being logged. [RT #17307] 2280. [func] Allow the experimental http server to be reached over IPv6 as well as IPv4. [RT #17332] 2279. [bug] Use setsockopt(SO_NOSIGPIPE), when available, to protect applications from receiving spurious SIGPIPE signals when using the resolver. 2278. [bug] win32: handle the case where Windows returns no search list or DNS suffix. [RT #17354] 2277. [bug] Empty zone names were not correctly being caught at in the post parse checks. [RT #17357] 2276. [bug] Install . [RT# 17359] 2275. [func] Add support to dig to perform IXFR queries over UDP. [RT #17235] 2274. [func] Log zone transfer statistics. [RT #17336] 2273. [bug] Adjust log level to WARNING when saving inconsistent stub/slave master and journal files. [RT# 17279] 2272. [bug] Handle illegal dnssec-lookaside trust-anchor names. [RT #17262] 2271. [bug] Fix a memory leak in http server code [RT #17100] 2270. [bug] dns_db_closeversion() version->writer could be reset before it is tested. [RT #17290] 2269. [contrib] dbus memory leaks and missing va_end calls. [RT #17232] 2268. [bug] 0.IN-ADDR.ARPA was missing from the empty zones list. --- 9.5.0b1 released --- 2267. [bug] Radix tree node_num value could be set incorrectly, causing positive ACL matches to look like negative ones. [RT #17311] 2266. [bug] client.c:get_clientmctx() returned the same mctx once the pool of mctx's was filled. [RT #17218] 2265. [bug] Test that the memory context's basic_table is non NULL before freeing. [RT #17265] 2264. [bug] Server prefix length was being ignored. [RT #17308] 2263. [bug] "named-checkconf -z" failed to set default value for "check-integrity". [RT #17306] 2262. [bug] Error status from all but the last view could be lost. [RT #17292] 2261. [bug] Fix memory leak with "any" and "none" ACLs [RT #17272] 2260. [bug] Reported wrong clients-per-query when increasing the value. [RT #17236] 2259. [placeholder] --- 9.5.0a7 released --- 2258. [bug] Fallback from IXFR/TSIG to SOA/AXFR/TSIG broken. [RT #17241] 2257. [bug] win32: Use the full path to vcredist_x86.exe when calling it. [RT #17222] 2256. [bug] win32: Correctly register the installation location of bindevt.dll. [RT #17159] 2255. [maint] L.ROOT-SERVERS.NET is now 199.7.83.42. 2254. [bug] timer.c:dispatch() failed to lock timer->lock when reading timer->idle allowing it to see intermediate values as timer->idle was reset by isc_timer_touch(). [RT #17243] 2253. [func] "max-cache-size" defaults to 32M. "max-acache-size" defaults to 16M. 2252. [bug] Fixed errors in sortlist code [RT #17216] 2251. [placeholder] 2250. [func] New flag 'memstatistics' to state whether the memory statistics file should be written or not. Additionally named's -m option will cause the statistics file to be written. [RT #17113] 2249. [bug] Only set Authentic Data bit if client requested DNSSEC, per RFC 3655 [RT #17175] 2248. [cleanup] Fix several errors reported by Coverity. [RT #17160] 2247. [doc] Sort doc/misc/options. [RT #17067] 2246. [bug] Make the startup of test servers (ans.pl) more robust. [RT #17147] 2245. [bug] Validating lack of DS records at trust anchors wasn't working. [RT #17151] 2244. [func] Allow the check of nameserver names against the SOA MNAME field to be disabled by specifying 'notify-to-soa yes;'. [RT #17073] 2243. [func] Configuration files without a newline at the end now parse without error. [RT #17120] 2242. [bug] nsupdate: GSS-TSIG support using the Heimdal Kerberos library could require a source of random data. [RT #17127] 2241. [func] nsupdate: add a interactive 'help' command. [RT #17099] 2240. [bug] Cleanup nsupdates GSS-TSIG support. Convert a number of INSIST()s into plain fatal() errors which report the triggering result code. The 'key' command wasn't disabling GSS-TSIG. [RT #17099] 2239. [func] Ship a pre built bin/named/bind9.xsl.h. [RT #17114] 2238. [bug] It was possible to trigger a REQUIRE when a validation was canceled. [RT #17106] 2237. [bug] libbind: res_init() was not thread aware. [RT #17123] 2236. [bug] dnssec-signzone failed to preserve the case of of wildcard owner names. [RT #17085] 2235. [bug] was not being installed. [RT #17135] 2234. [port] Correct some compiler warnings on SCO OSr5 [RT #17134] 2233. [func] Add support for O(1) ACL processing, based on radix tree code originally written by Kevin Brintnall. [RT #16288] 2232. [bug] dns_adb_findaddrinfo() could fail and return ISC_R_SUCCESS. [RT #17137] 2231. [bug] Building dlzbdb (contrib/dlz/bin/dlzbdb) was broken. [RT #17088] 2230. [bug] We could INSIST reading a corrupted journal. [RT #17132] 2229. [bug] Null pointer dereference on query pool creation failure. [RT #17133] 2228. [contrib] contrib: Change 2188 was incomplete. 2227. [cleanup] Tidied up the FAQ. [RT #17121] 2226. [placeholder] 2225. [bug] More support for systems with no IPv4 addresses. [RT #17111] 2224. [bug] Defer journal compaction if a xfrin is in progress. [RT #17119] 2223. [bug] Make a new journal when compacting. [RT #17119] 2222. [func] named-checkconf now checks server key references. [RT #17097] 2221. [bug] Set the event result code to reflect the actual record turned to caller when a cache update is rejected due to a more credible answer existing. [RT #17017] 2220. [bug] win32: Address a race condition in final shutdown of the Windows socket code. [RT #17028] 2219. [bug] Apply zone consistency checks to additions, not removals, when updating. [RT #17049] 2218. [bug] Remove unnecessary REQUIRE from dns_validator_create(). [RT #16976] 2217. [func] Adjust update log levels. [RT #17092] 2216. [cleanup] Fix a number of errors reported by Coverity. [RT #17094] 2215. [bug] Bad REQUIRE check isc_hmacsha1_verify(). [RT #17094] 2214. [bug] Deregister OpenSSL lock callback when cleaning up. Reorder OpenSSL cleanup so that RAND_cleanup() is called before the locks are destroyed. [RT #17098] 2213. [bug] SIG0 diagnostic failure messages were looking at the wrong status code. [RT #17101] 2212. [func] 'host -m' now causes memory statistics and active memory to be printed at exit. [RT 17028] 2211. [func] Update "dynamic update temporarily disabled" message. [RT #17065] 2210. [bug] Deleting class specific records via UPDATE could fail. [RT #17074] 2209. [port] osx: linking against user supplied static OpenSSL libraries failed as the system ones were still being found. [RT #17078] 2208. [port] win32: make sure both build methods produce the same output. [RT #17058] 2207. [port] Some implementations of getaddrinfo() fail to set ai_canonname correctly. [RT #17061] --- 9.5.0a6 released --- 2206. [security] "allow-query-cache" and "allow-recursion" now cross inherit from each other. If allow-query-cache is not set in named.conf then allow-recursion is used if set, otherwise allow-query is used if set, otherwise the default (localnets; localhost;) is used. If allow-recursion is not set in named.conf then allow-query-cache is used if set, otherwise allow-query is used if set, otherwise the default (localnets; localhost;) is used. [RT #16987] 2205. [bug] libbind: change #2119 broke thread support. [RT #16982] 2204. [bug] "rndc flushanme name unknown-view" caused named to crash. [RT #16984] 2203. [security] Query id generation was cryptographically weak. [RT # 16915] 2202. [security] The default acls for allow-query-cache and allow-recursion were not being applied. [RT #16960] 2201. [bug] The build failed in a separate object directory. [RT #16943] 2200. [bug] The search for cached NSEC records was stopping to early leading to excessive DLV queries. [RT #16930] 2199. [bug] win32: don't call WSAStartup() while loading dlls. [RT #16911] 2198. [bug] win32: RegCloseKey() could be called when RegOpenKeyEx() failed. [RT #16911] 2197. [bug] Add INSIST to catch negative responses which are not setting the event result code appropriately. [RT #16909] 2196. [port] win32: yield processor while waiting for once to to complete. [RT #16958] 2195. [func] dnssec-keygen now defaults to nametype "ZONE" when generating DNSKEYs. [RT #16954] 2194. [bug] Close journal before calling 'done' in xfrin.c. --- 9.5.0a5 released --- 2193. [port] win32: BINDInstall.exe is now linked statically. [RT #16906] 2192. [port] win32: use vcredist_x86.exe to install Visual Studio's redistributable dlls if building with Visual Stdio 2005 or later. 2191. [func] named-checkzone now allows dumping to stdout (-). named-checkconf now has -h for help. named-checkzone now has -h for help. rndc now has -h for help. Better handling of '-?' for usage summaries. [RT #16707] 2190. [func] Make fallback to plain DNS from EDNS due to timeouts more visible. New logging category "edns-disabled". [RT #16871] 2189. [bug] Handle socket() returning EINTR. [RT #15949] 2188. [contrib] queryperf: autoconf changes to make the search for libresolv or libbind more robust. [RT #16299] 2187. [bug] query_addds(), query_addwildcardproof() and query_addnxrrsetnsec() should take a version argument. [RT #16368] 2186. [port] cygwin: libbind: check for struct sockaddr_storage independently of IPv6. [RT #16482] 2185. [port] sunos: libbind: check for ssize_t, memmove() and memchr(). [RT #16463] 2184. [bug] bind9.xsl.h didn't build out of the source tree. [RT #16830] 2183. [bug] dnssec-signzone didn't handle offline private keys well. [RT #16832] 2182. [bug] dns_dispatch_createtcp() and dispatch_createudp() could return ISC_R_SUCCESS when they ran out of memory. [RT #16365] 2181. [port] sunos: libbind: add paths.h from BIND 8. [RT #16462] 2180. [cleanup] Remove bit test from 'compress_test' as they are no longer needed. [RT #16497] 2179. [func] 'rndc command zone' will now find 'zone' if it is unique to all the views. [RT #16821] 2178. [bug] 'rndc reload' of a slave or stub zone resulted in a reference leak. [RT #16867] 2177. [bug] Array bounds overrun on read (rcodetext) at debug level 10+. [RT #16798] 2176. [contrib] dbus update to handle race condition during initialization (Bugzilla 235809). [RT #16842] 2175. [bug] win32: windows broadcast condition variable support was broken. [RT #16592] 2174. [bug] I/O errors should always be fatal when reading master files. [RT #16825] 2173. [port] win32: When compiling with MSVS 2005 SP1 we also need to ship Microsoft.VC80.MFCLOC. --- 9.5.0a4 released --- 2172. [bug] query_addsoa() was being called with a non zone db. [RT #16834] 2171. [bug] Handle breaks in DNSSEC trust chains where the parent servers are not DS aware (DS queries to the parent return a referral to the child). 2170. [func] Add acache processing to test suite. [RT #16711] 2169. [bug] host, nslookup: when reporting NXDOMAIN report the given name and not the last name searched for. [RT #16763] 2168. [bug] nsupdate: in non-interactive mode treat syntax errors as fatal errors. [RT #16785] 2167. [bug] When re-using a automatic zone named failed to attach it to the new view. [RT #16786] --- 9.5.0a3 released --- 2166. [bug] When running in batch mode, dig could misinterpret a server address as a name to be looked up, causing unexpected output. [RT #16743] 2165. [func] Allow the destination address of a query to determine if we will answer the query or recurse. allow-query-on, allow-recursion-on and allow-query-cache-on. [RT #16291] 2164. [bug] The code to determine how named-checkzone / named-compilezone was called failed under windows. [RT #16764] 2163. [bug] If only one of query-source and query-source-v6 specified a port the query pools code broke (change 2129). [RT #16768] 2162. [func] Allow "rrset-order fixed" to be disabled at compile time. [RT #16665] 2161. [bug] Fix which log messages are emitted for 'rndc flush'. [RT #16698] 2160. [bug] libisc wasn't handling NULL ifa_addr pointers returned from getifaddrs(). [RT #16708] --- 9.5.0a2 released --- 2159. [bug] Array bounds overrun in acache processing. [RT #16710] 2158. [bug] ns_client_isself() failed to initialize key leading to a REQUIRE failure. [RT #16688] 2157. [func] dns_db_transfernode() created. [RT #16685] 2156. [bug] Fix node reference leaks in lookup.c:lookup_find(), resolver.c:validated() and resolver.c:cache_name(). Fix a memory leak in rbtdb.c:free_noqname(). Make lookup.c:lookup_find() robust against event leaks. [RT #16685] 2155. [contrib] SQLite sdb module from jaboydjr@netwalk.com. [RT #16694] 2154. [func] Scoped (e.g. IPv6 link-local) addresses may now be matched in acls by omitting the scope. [RT #16599] 2153. [bug] nsupdate could leak memory. [RT #16691] 2152. [cleanup] Use sizeof(buf) instead of fixed number in dighost.c:get_trusted_key(). [RT #16678] 2151. [bug] Missing newline in usage message for journalprint. [RT #16679] 2150. [bug] 'rrset-order cyclic' uniformly distribute the starting point for the first response for a given RRset. [RT #16655] 2149. [bug] isc_mem_checkdestroyed() failed to abort on if there were still active memory contexts. [RT #16672] 2148. [func] Add positive logging for rndc commands. [RT #14623] 2147. [bug] libbind: remove potential buffer overflow from hmac_link.c. [RT #16437] 2146. [cleanup] Silence Linux's spurious "obsolete setsockopt SO_BSDCOMPAT" message. [RT #16641] 2145. [bug] Check DS/DLV digest lengths for known digests. [RT #16622] 2144. [cleanup] Suppress logging of SERVFAIL from forwarders. [RT #16619] 2143. [bug] We failed to restart the IPv6 client when the kernel failed to return the destination the packet was sent to. [RT #16613] 2142. [bug] Handle master files with a modification time that matches the epoch. [RT# 16612] 2141. [bug] dig/host should not be setting IDN_ASCCHECK (IDN equivalent of LDH checks). [RT #16609] 2140. [bug] libbind: missing unlock on pthread_key_create() failures. [RT #16654] 2139. [bug] dns_view_find() was being called with wrong type in adb.c. [RT #16670] 2138. [bug] Lock order reversal in resolver.c. [RT #16653] 2137. [port] Mips little endian and/or mips 64 bit are now supported for atomic operations. [RT#16648] 2136. [bug] nslookup/host looped if there was no search list and the host didn't exist. [RT #16657] 2135. [bug] Uninitialized rdataset in sdlz.c. [RT# 16656] 2134. [func] Additional statistics support. [RT #16666] 2133. [port] powerpc: Support both IBM and MacOS Power PC assembler syntaxes. [RT #16647] 2132. [bug] Missing unlock on out of memory in dns_dispatchmgr_setudp(). 2131. [contrib] dlz/mysql: AXFR was broken. [RT #16630] 2130. [func] Log if CD or DO were set. [RT #16640] 2129. [func] Provide a pool of UDP sockets for queries to be made over. See use-queryport-pool, queryport-pool-ports and queryport-pool-updateinterval. [RT #16415] 2128. [doc] xsltproc --nonet, update DTD versions. [RT #16635] 2127. [port] Improved OpenSSL 0.9.8 support. [RT #16563] 2126. [security] Serialize validation of type ANY responses. [RT #16555] 2125. [bug] dns_zone_getzeronosoattl() REQUIRE failure if DLZ was defined. [RT #16574] 2124. [security] It was possible to dereference a freed fetch context. [RT #16584] --- 9.5.0a1 released --- 2123. [func] Use Doxygen to generate internal documentation. [RT #11398] 2122. [func] Experimental http server and statistics support for named via xml. 2121. [func] Add a 10 slot dead masters cache (LRU) with a 600 second timeout. [RT #16553] 2120. [doc] Fix markup on nsupdate man page. [RT #16556] 2119. [compat] libbind: allow res_init() to succeed enough to return the default domain even if it was unable to allocate memory. 2118. [bug] Handle response with long chains of domain name compression pointers which point to other compression pointers. [RT #16427] 2117. [bug] DNSSEC fixes: named could fail to cache NSEC records which could lead to validation failures. named didn't handle negative DS responses that were in the process of being validated. Check CNAME bit before accepting NODATA proof. To be able to ignore a child NSEC there must be SOA (and NS) set in the bitmap. [RT #16399] 2116. [bug] 'rndc reload' could cause the cache to continually be cleaned. [RT #16401] 2115. [bug] 'rndc reconfig' could trigger a INSIST if the number of masters for a zone was reduced. [RT #16444] 2114. [bug] dig/host/nslookup: searches for names with multiple labels were failing. [RT #16447] 2113. [bug] nsupdate: if a zone is specified it should be used for server discover. [RT# 16455] 2112. [security] Warn if weak RSA exponent is used. [RT #16460] 2111. [bug] Fix a number of errors reported by Coverity. [RT #16507] 2110. [bug] "minimal-responses yes;" interacted badly with BIND 8 priming queries. [RT #16491] 2109. [port] libbind: silence aix 5.3 compiler warnings. [RT #16502] 2108. [func] DHCID support. [RT #16456] 2107. [bug] dighost.c: more cleanup of buffers. [RT #16499] 2106. [func] 'rndc status' now reports named's version. [RT #16426] 2105. [func] GSS-TSIG support (RFC 3645). 2104. [port] Fix Solaris SMF error message. 2103. [port] Add /usr/sfw to list of locations for OpenSSL under Solaris. 2102. [port] Silence Solaris 10 warnings. 2101. [bug] OpenSSL version checks were not quite right. [RT #16476] 2100. [port] win32: copy libeay32.dll to Build\Debug. Copy Debug\named-checkzone to Debug\named-compilezone. 2099. [port] win32: more manifest issues. 2098. [bug] Race in rbtdb.c:no_references(), which occasionally triggered an INSIST failure about the node lock reference. [RT #16411] 2097. [bug] named could reference a destroyed memory context after being reloaded / reconfigured. [RT #16428] 2096. [bug] libbind: handle applications that fail to detect res_init() failures better. 2095. [port] libbind: alway prototype inet_cidr_ntop_ipv6() and net_cidr_ntop_ipv6(). [RT #16388] 2094. [contrib] Update named-bootconf. [RT# 16404] 2093. [bug] named-checkzone -s was broken. 2092. [bug] win32: dig, host, nslookup. Use registry config if resolv.conf does not exist or no nameservers listed. [RT #15877] 2091. [port] dighost.c: race condition on cleanup. [RT #16417] 2090. [port] win32: Visual C++ 2005 command line manifest support. [RT #16417] 2089. [security] Raise the minimum safe OpenSSL versions to OpenSSL 0.9.7l and OpenSSL 0.9.8d. Versions prior to these have known security flaws which are (potentially) exploitable in named. [RT #16391] 2088. [security] Change the default RSA exponent from 3 to 65537. [RT #16391] 2087. [port] libisc failed to compile on OS's w/o a vsnprintf. [RT #16382] 2086. [port] libbind: FreeBSD now has get*by*_r() functions. [RT #16403] 2085. [doc] win32: added index.html and README to zip. [RT #16201] 2084. [contrib] dbus update for 9.3.3rc2. 2083. [port] win32: Visual C++ 2005 support. 2082. [doc] Document 'cache-file' as a test only option. 2081. [port] libbind: minor 64-bit portability fix in memcluster.c. [RT #16360] 2080. [port] libbind: res_init.c did not compile on older versions of Solaris. [RT #16363] 2079. [bug] The lame cache was not handling multiple types correctly. [RT #16361] 2078. [bug] dnssec-checkzone output style "default" was badly named. It is now called "relative". [RT #16326] 2077. [bug] 'dnssec-signzone -O raw' wasn't outputting the complete signed zone. [RT #16326] 2076. [bug] Several files were missing #include causing build failures on OSF. [RT #16341] 2075. [bug] The spillat timer event hander could leak memory. [RT #16357] 2074. [bug] dns_request_createvia2(), dns_request_createvia3(), dns_request_createraw2() and dns_request_createraw3() failed to send multiple UDP requests. [RT #16349] 2073. [bug] Incorrect semantics check for update policy "wildcard". [RT #16353] 2072. [bug] We were not generating valid HMAC SHA digests. [RT #16320] 2071. [port] Test whether gcc accepts -fno-strict-aliasing. [RT #16324] 2070. [bug] The remote address was not always displayed when reporting dispatch failures. [RT #16315] 2069. [bug] Cross compiling was not working. [RT #16330] 2068. [cleanup] Lower incremental tuning message to debug 1. [RT #16319] 2067. [bug] 'rndc' could close the socket too early triggering a INSIST under Windows. [RT #16317] 2066. [security] Handle SIG queries gracefully. [RT #16300] 2065. [bug] libbind: probe for HPUX prototypes for endprotoent_r() and endservent_r(). [RT 16313] 2064. [bug] libbind: silence AIX compiler warnings. [RT #16218] 2063. [bug] Change #1955 introduced a bug which caused the first 'rndc flush' call to not free memory. [RT #16244] 2062. [bug] 'dig +nssearch' was reusing a buffer before it had been returned by the socket code. [RT #16307] 2061. [bug] Accept expired wildcard message reversed. [RT #16296] 2060. [bug] Enabling DLZ support could leave views partially configured. [RT #16295] 2059. [bug] Search into cache rbtdb could trigger an INSIST failure while cleaning up a stale rdataset. [RT #16292] 2058. [bug] Adjust how we calculate rtt estimates in the presence of authoritative servers that drop EDNS and/or CD requests. Also fallback to EDNS/512 and plain DNS faster for zones with less than 3 servers. [RT #16187] 2057. [bug] Make setting "ra" dependent on both allow-query-cache and allow-recursion. [RT #16290] 2056. [bug] dig: ixfr= was not being treated case insensitively at all times. [RT #15955] 2055. [bug] Missing goto after dropping multicast query. [RT #15944] 2054. [port] freebsd: do not explicitly link against -lpthread. [RT #16170] 2053. [port] netbsd:libbind: silence compiler warnings. [RT #16220] 2052. [bug] 'rndc' improve connect failed message to report the failing address. [RT #15978] 2051. [port] More strtol() fixes. [RT #16249] 2050. [bug] Parsing of NSAP records was not case insensitive. [RT #16287] 2049. [bug] Restore SOA before AXFR when falling back from a attempted IXFR when transferring in a zone. Allow a initial SOA query before attempting a AXFR to be requested. [RT #16156] 2048. [bug] It was possible to loop forever when using avoid-v4-udp-ports / avoid-v6-udp-ports when the OS always returned the same local port. [RT #16182] 2047. [bug] Failed to initialize the interface flags to zero. [RT #16245] 2046. [bug] rbtdb.c:rdataset_setadditional() could cause duplicate cleanup [RT #16247]. 2045. [func] Use lock buckets for acache entries to limit memory consumption. [RT #16183] 2044. [port] Add support for atomic operations for Itanium. [RT #16179] 2043. [port] nsupdate/nslookup: Force the flushing of the prompt for interactive sessions. [RT#16148] 2042. [bug] named-checkconf was incorrectly rejecting the logging category "config". [RT #16117] 2041. [bug] "configure --with-dlz-bdb=yes" produced a bad set of libraries to be linked. [RT #16129] 2040. [bug] rbtdb no_references() could trigger an INSIST failure with --enable-atomic. [RT #16022] 2039. [func] Check that all buffers passed to the socket code have been retrieved when the socket event is freed. [RT #16122] 2038. [bug] dig/nslookup/host was unlinking from wrong list when handling errors. [RT #16122] 2037. [func] When unlinking the first or last element in a list check that the list head points to the element to be unlinked. [RT #15959] 2036. [bug] 'rndc recursing' could cause trigger a REQUIRE. [RT #16075] 2035. [func] Make falling back to TCP on UDP refresh failure optional. Default "try-tcp-refresh yes;" for BIND 8 compatibility. [RT #16123] 2034. [bug] gcc: set -fno-strict-aliasing. [RT #16124] 2033. [bug] We weren't creating multiple client memory contexts on demand as expected. [RT #16095] 2032. [bug] Remove a INSIST in query_addadditional2(). [RT #16074] 2031. [bug] Emit a error message when "rndc refresh" is called on a non slave/stub zone. [RT # 16073] 2030. [bug] We were being overly conservative when disabling openssl engine support. [RT #16030] 2029. [bug] host printed out the server multiple times when specified on the command line. [RT #15992] 2028. [port] linux: socket.c compatibility for old systems. [RT #16015] 2027. [port] libbind: Solaris x86 support. [RT #16020] 2026. [bug] Rate limit the two recursive client exceeded messages. [RT #16044] 2025. [func] Update "zone serial unchanged" message. [RT #16026] 2024. [bug] named emitted spurious "zone serial unchanged" messages on reload. [RT #16027] 2023. [bug] "make install" should create ${localstatedir}/run and ${sysconfdir} if they do not exist. [RT #16033] 2022. [bug] If dnssec validation is disabled only assert CD if CD was requested. [RT #16037] 2021. [bug] dnssec-enable no; triggered a REQUIRE. [RT #16037] 2020. [bug] rdataset_setadditional() could leak memory. [RT #16034] 2019. [tuning] Reduce the amount of work performed per quantum when cleaning the cache. [RT #15986] 2018. [bug] Checking if the HMAC MD5 private file was broken. [RT #15960] 2017. [bug] allow-query default was not correct. [RT #15946] 2016. [bug] Return a partial answer if recursion is not allowed but requested and we had the answer to the original qname. [RT #15945] 2015. [cleanup] use-additional-cache is now acache-enable for consistency. Default acache-enable off in BIND 9.4 as it requires memory usage to be configured. It may be enabled by default in BIND 9.5 once we have more experience with it. 2014. [func] Statistics about acache now recorded and sent to log. [RT #15976] 2013. [bug] Handle unexpected TSIGs on unsigned AXFR/IXFR responses more gracefully. [RT #15941] 2012. [func] Don't insert new acache entries if acache is full. [RT #15970] 2011. [func] dnssec-signzone can now update the SOA record of the signed zone, either as an increment or as the system time(). [RT #15633] 2010. [placeholder] rt15958 2009. [bug] libbind: Coverity fixes. [RT #15808] 2008. [func] It is now possible to enable/disable DNSSEC validation from rndc. This is useful for the mobile hosts where the current connection point breaks DNSSEC (firewall/proxy). [RT #15592] rndc validation newstate [view] 2007. [func] It is now possible to explicitly enable DNSSEC validation. default dnssec-validation no; to be changed to yes in 9.5.0. [RT #15674] 2006. [security] Allow-query-cache and allow-recursion now default to the built in acls "localnets" and "localhost". This is being done to make caching servers less attractive as reflective amplifying targets for spoofed traffic. This still leave authoritative servers exposed. The best fix is for full BCP 38 deployment to remove spoofed traffic. 2005. [bug] libbind: Retransmission timeouts should be based on which attempt it is to the nameserver and not the nameserver itself. [RT #13548] 2004. [bug] dns_tsig_sign() could pass a NULL pointer to dst_context_destroy() when cleaning up after a error. [RT #15835] 2003. [bug] libbind: The DNS name/address lookup functions could occasionally follow a random pointer due to structures not being completely zeroed. [RT #15806] 2002. [bug] libbind: tighten the constraints on when struct addrinfo._ai_pad exists. [RT #15783] 2001. [func] Check the KSK flag when updating a secure dynamic zone. New zone option "update-check-ksk yes;". [RT #15817] 2000. [bug] memmove()/strtol() fix was incomplete. [RT #15812] 1999. [func] Implement "rrset-order fixed". [RT #13662] 1998. [bug] Restrict handling of fifos as sockets to just SunOS. This allows named to connect to entropy gathering daemons that use fifos instead of sockets. [RT #15840] 1997. [bug] Named was failing to replace negative cache entries when a positive one for the type was learnt. [RT #15818] 1996. [bug] nsupdate: if a zone has been specified it should appear in the output of 'show'. [RT #15797] 1995. [bug] 'host' was reporting multiple "is an alias" messages. [RT #15702] 1994. [port] OpenSSL 0.9.8 support. [RT #15694] 1993. [bug] Log messages, via syslog, were missing the space after the timestamp if "print-time yes" was specified. [RT #15844] 1992. [bug] Not all incoming zone transfer messages included the view. [RT #15825] 1991. [cleanup] The configuration data, once read, should be treated as read only. Expand the use of const to enforce this at compile time. [RT #15813] 1990. [bug] libbind: isc's override of broken gettimeofday() implementations was not always effective. [RT #15709] 1989. [bug] win32: don't check the service password when re-installing. [RT #15882] 1988. [bug] Remove a bus error from the SHA256/SHA512 support. [RT #15878] 1987. [func] DS/DLV SHA256 digest algorithm support. [RT #15608] 1986. [func] Report when a zone is removed. [RT #15849] 1985. [protocol] DLV has now been assigned a official type code of 32769. [RT #15807] Note: care should be taken to ensure you upgrade both named and dnssec-signzone at the same time for zones with DLV records where named is the master server for the zone. Also any zones that contain DLV records should be removed when upgrading a slave zone. You do not however have to upgrade all servers for a zone with DLV records simultaneously. 1984. [func] dig, nslookup and host now advertise a 4096 byte EDNS UDP buffer size by default. [RT #15855] 1983. [func] Two new update policies. "selfsub" and "selfwild". [RT #12895] 1982. [bug] DNSKEY was being accepted on the parent side of a delegation. KEY is still accepted there for RFC 3007 validated updates. [RT #15620] 1981. [bug] win32: condition.c:wait() could fail to reattain the mutex lock. 1980. [func] dnssec-signzone: output the SOA record as the first record in the signed zone. [RT #15758] 1979. [port] linux: allow named to drop core after changing user ids. [RT #15753] 1978. [port] Handle systems which have a broken recvmsg(). [RT #15742] 1977. [bug] Silence noisy log message. [RT #15704] 1976. [bug] Handle systems with no IPv4 addresses. [RT #15695] 1975. [bug] libbind: isc_gethexstring() could misparse multi-line hex strings with comments. [RT #15814] 1974. [doc] List each of the zone types and associated zone options separately in the ARM. 1973. [func] TSIG HMACSHA1, HMACSHA224, HMACSHA256, HMACSHA384 and HMACSHA512 support. [RT #13606] 1972. [contrib] DBUS dynamic forwarders integration from Jason Vas Dias . 1971. [port] linux: make detection of missing IF_NAMESIZE more robust. [RT #15443] 1970. [bug] nsupdate: adjust UDP timeout when falling back to unsigned SOA query. [RT #15775] 1969. [bug] win32: the socket code was freeing the socket structure too early. [RT #15776] 1968. [bug] Missing lock in resolver.c:validated(). [RT #15739] 1967. [func] dig/nslookup/host: warn about missing "QR". [RT #15779] 1966. [bug] Don't set CD when we have fallen back to plain DNS. [RT #15727] 1965. [func] Suppress spurious "recursion requested but not available" warning with 'dig +qr'. [RT #15780]. 1964. [func] Separate out MX and SRV to CNAME checks. [RT #15723] 1963. [port] Tru64 4.0E doesn't support send() and recv(). [RT #15586] 1962. [bug] Named failed to clear old update-policy when it was removed. [RT #15491] 1961. [bug] Check the port and address of responses forwarded to dispatch. [RT #15474] 1960. [bug] Update code should set NSEC ttls from SOA MINIMUM. [RT #15465] 1959. [func] Control the zeroing of the negative response TTL to a soa query. Defaults "zero-no-soa-ttl yes;" and "zero-no-soa-ttl-cache no;". [RT #15460] 1958. [bug] Named failed to update the zone's secure state until the zone was reloaded. [RT #15412] 1957. [bug] Dig mishandled responses to class ANY queries. [RT #15402] 1956. [bug] Improve cross compile support, 'gen' is now built by native compiler. See README for additional cross compile support information. [RT #15148] 1955. [bug] Pre-allocate the cache cleaning iterator. [RT #14998] 1954. [func] Named now falls back to advertising EDNS with a 512 byte receive buffer if the initial EDNS queries fail. [RT #14852] 1953. [func] The maximum EDNS UDP response named will send can now be set in named.conf (max-udp-size). This is independent of the advertised receive buffer (edns-udp-size). [RT #14852] 1952. [port] hpux: tell the linker to build a runtime link path "-Wl,+b:". [RT #14816]. 1951. [security] Drop queries from particular well known ports. Don't return FORMERR to queries from particular well known ports. [RT #15636] 1950. [port] Solaris 2.5.1 and earlier cannot bind() then connect() a TCP socket. This prevents the source address being set for TCP connections. [RT #15628] 1949. [func] Addition memory leakage checks. [RT #15544] 1948. [bug] If was possible to trigger a REQUIRE failure in xfrin.c:maybe_free() if named ran out of memory. [RT #15568] 1947. [func] It is now possible to configure named to accept expired RRSIGs. Default "dnssec-accept-expired no;". Setting "dnssec-accept-expired yes;" leaves named vulnerable to replay attacks. [RT #14685] 1946. [bug] resume_dslookup() could trigger a REQUIRE failure when using forwarders. [RT #15549] 1945. [cleanup] dnssec-keygen: RSA (RSAMD5) is no longer recommended. To generate a RSAMD5 key you must explicitly request RSAMD5. [RT #13780] 1944. [cleanup] isc_hash_create() does not need a read/write lock. [RT #15522] 1943. [bug] Set the loadtime after rolling forward the journal. [RT #15647] 1942. [bug] If the name of a DNSKEY match that of one in trusted-keys do not attempt to validate the DNSKEY using the parents DS RRset. [RT #15649] 1941. [bug] ncache_adderesult() should set eresult even if no rdataset is passed to it. [RT #15642] 1940. [bug] Fixed a number of error conditions reported by Coverity. 1939. [bug] The resolver could dereference a null pointer after validation if all the queries have timed out. [RT #15528] 1938. [bug] The validator was not correctly handling unsecure negative responses at or below a SEP. [RT #15528] 1937. [bug] sdlz doesn't handle RRSIG records. [RT #15564] 1936. [bug] The validator could leak memory. [RT #15544] 1935. [bug] 'acache' was DO sensitive. [RT #15430] 1934. [func] Validate pending NS RRsets, in the authority section, prior to returning them if it can be done without requiring DNSKEYs to be fetched. [RT #15430] 1933. [bug] dump_rdataset_raw() had a incorrect INSIST. [RT #15534] 1932. [bug] hpux: LDFLAGS was getting corrupted. [RT #15530] 1931. [bug] Per-client mctx could require a huge amount of memory, particularly for a busy caching server. [RT #15519] 1930. [port] HPUX: ia64 support. [RT #15473] 1929. [port] FreeBSD: extend use of PTHREAD_SCOPE_SYSTEM. 1928. [bug] Race in rbtdb.c:currentversion(). [RT #15517] 1927. [bug] Access to soanode or nsnode in rbtdb violated the lock order rule and could cause a dead lock. [RT# 15518] 1926. [bug] The Windows installer did not check for empty passwords. BINDinstall was being installed in the wrong place. [RT #15483] 1925. [port] All outer level AC_TRY_RUNs need cross compiling defaults. [RT #15469] 1924. [port] libbind: hpux ia64 support. [RT #15473] 1923. [bug] ns_client_detach() called too early. [RT #15499] 1922. [bug] check-tool.c:setup_logging() missing call to dns_log_setcontext(). 1921. [bug] Client memory contexts were not using internal malloc. [RT# 15434] 1920. [bug] The cache rbtdb lock array was too small to have the desired performance characteristics. [RT #15454] 1919. [contrib] queryperf: a set of new features: collecting/printing response delays, printing intermediate results, and adjusting query rate for the "target" qps. 1918. [bug] Memory leak when checking acls. [RT #15391] 1917. [doc] funcsynopsisinfo wasn't being treated as verbatim when generating man pages. [RT #15385] 1916. [func] Integrate contributed IDN code from JPNIC. [RT #15383] 1915. [bug] dig +ndots was broken. [RT #15215] 1914. [protocol] DS is required to accept mnemonic algorithms (RFC 4034). Still emit numeric algorithms for compatibility with RFC 3658. [RT #15354] 1913. [func] Integrate contributed DLZ code into named. [RT #11382] 1912. [port] aix: atomic locking for powerpc. [RT #15020] 1911. [bug] Update windows socket code. [RT #14965] 1910. [bug] dig's +sigchase code overhauled. [RT #14933] 1909. [bug] The DLV code has been re-worked to make no longer query order sensitive. [RT #14933] 1908. [func] dig now warns if 'RA' is not set in the answer when 'RD' was set in the query. host/nslookup skip servers that fail to set 'RA' when 'RD' is set unless a server is explicitly set. [RT #15005] 1907. [func] host/nslookup now continue (default)/fail on SERVFAIL. [RT #15006] 1906. [func] dig now has a '-q queryname' and '+showsearch' options. [RT #15034] 1905. [bug] Strings returned from cfg_obj_asstring() should be treated as read-only. The prototype for cfg_obj_asstring() has been updated to reflect this. [RT #15256] 1904. [func] Automatic empty zone creation for D.F.IP6.ARPA and friends. Note: RFC 1918 zones are not yet covered by this but are likely to be in a future release. New options: empty-server, empty-contact, empty-zones-enable and disable-empty-zone. 1903. [func] ISC string copy API. 1902. [func] Attempt to make the amount of work performed in a iteration self tuning. The covers nodes clean from the cache per iteration, nodes written to disk when rewriting a master file and nodes destroyed per iteration when destroying a zone or a cache. [RT #14996] 1901. [cleanup] Don't add DNSKEY records to the additional section. 1900. [bug] ixfr-from-differences failed to ensure that the serial number increased. [RT #15036] 1899. [func] named-checkconf now validates update-policy entries. [RT #14963] 1898. [bug] Extend ISC_SOCKADDR_FORMATSIZE and ISC_NETADDR_FORMATSIZE to allow for scope details. 1897. [func] x86 and x86_64 now have separate atomic locking implementations. 1896. [bug] Recursive clients soft quota support wasn't working as expected. [RT #15103] 1895. [bug] A escaped character is, potentially, converted to the output character set too early. [RT #14666] 1894. [doc] Review ARM for BIND 9.4. 1893. [port] Use uintptr_t if available. [RT #14606] 1892. [func] Support for SPF rdata type. [RT #15033] 1891. [port] freebsd: pthread_mutex_init can fail if it runs out of memory. [RT #14995] 1890. [func] Raise the UDP receive buffer size to 32k if it is less than 32k. [RT #14953] 1889. [port] sunos: non blocking i/o support. [RT #14951] 1888. [func] Support for IPSECKEY rdata type. [RT #14967] 1887. [bug] The cache could delete expired records too fast for clients with a virtual time in the past. [RT #14991] 1886. [bug] fctx_create() could return success even though it failed. [RT #14993] 1885. [func] dig: report the number of extra bytes still left in the packet after processing all the records. 1884. [cleanup] dighost.c: move external declarations into . 1883. [bug] dnssec-signzone, dnssec-keygen: handle negative debug levels. [RT #14962] 1882. [func] Limit the number of recursive clients that can be waiting for a single query () to resolve. New options clients-per-query and max-clients-per-query. 1881. [func] Add a system test for named-checkconf. [RT #14931] 1880. [func] The lame cache is now done on a basis as some servers only appear to be lame for certain query types. [RT #14916] 1879. [func] "USE INTERNAL MALLOC" is now runtime selectable. [RT #14892] 1878. [func] Detect duplicates of UDP queries we are recursing on and drop them. New stats category "duplicate". [RT #2471] 1877. [bug] Fix unreasonably low quantum on call to dns_rbt_destroy2(). Remove unnecessary unhash_node() call. [RT #14919] 1876. [func] Additional memory debugging support to track size and mctx arguments. [RT #14814] 1875. [bug] process_dhtkey() was using the wrong memory context to free some memory. [RT #14890] 1874. [port] sunos: portability fixes. [RT #14814] 1873. [port] win32: isc__errno2result() now reports its caller. [RT #13753] 1872. [port] win32: Handle ERROR_NETNAME_DELETED. [RT #13753] 1871. [placeholder] 1870. [func] Added framework for handling multiple EDNS versions. [RT #14873] 1869. [func] dig can now specify the EDNS version when making a query. [RT #14873] 1868. [func] edns-udp-size can now be overridden on a per server basis. [RT #14851] 1867. [bug] It was possible to trigger a INSIST in dlv_validatezonekey(). [RT #14846] 1866. [bug] resolv.conf parse errors were being ignored by dig/host/nslookup. [RT #14841] 1865. [bug] Silently ignore nameservers in /etc/resolv.conf with bad addresses. [RT #14841] 1864. [bug] Don't try the alternative transfer source if you got a answer / transfer with the main source address. [RT #14802] 1863. [bug] rrset-order "fixed" error messages not complete. 1862. [func] Add additional zone data constancy checks. named-checkzone has extended checking of NS, MX and SRV record and the hosts they reference. named has extended post zone load checks. New zone options: check-mx and integrity-check. [RT #4940] 1861. [bug] dig could trigger a INSIST on certain malformed responses. [RT #14801] 1860. [port] solaris 2.8: hack_shutup_pthreadmutexinit was incorrectly set. [RT #14775] 1859. [func] Add support for CH A record. [RT #14695] 1858. [bug] The flush-zones-on-shutdown option wasn't being parsed. [RT #14686] 1857. [bug] named could trigger a INSIST() if reconfigured / reloaded too fast. [RT #14673] 1856. [doc] Switch Docbook toolchain from DSSSL to XSL. [RT #11398] 1855. [bug] ixfr-from-differences was failing to detect changes of ttl due to dns_diff_subtract() was ignoring the ttl of records. [RT #14616] 1854. [bug] lwres also needs to know the print format for (long long). [RT #13754] 1853. [bug] Rework how DLV interacts with proveunsecure(). [RT #13605] 1852. [cleanup] Remove last vestiges of dnssec-signkey and dnssec-makekeyset (removed from Makefile years ago). 1851. [doc] Doxygen comment markup. [RT #11398] 1850. [bug] Memory leak in lwres_getipnodebyaddr(). [RT #14591] 1849. [doc] All forms of the man pages (docbook, man, html) should have consistent copyright dates. 1848. [bug] Improve SMF integration. [RT #13238] 1847. [bug] isc_ondestroy_init() is called too late in dns_rbtdb_create()/dns_rbtdb64_create(). [RT #13661] 1846. [contrib] query-loc-0.3.0 from Stephane Bortzmeyer . 1845. [bug] Improve error reporting to distinguish between accept()/fcntl() and socket()/fcntl() errors. [RT #13745] 1844. [bug] inet_pton() accepted more that 4 hexadecimal digits for each 16 bit piece of the IPv6 address. The text representation of a IPv6 address has been tightened to disallow this (draft-ietf-ipv6-addr-arch-v4-02.txt). [RT #5662] 1843. [cleanup] CINCLUDES takes precedence over CFLAGS. This helps when CFLAGS contains "-I /usr/local/include" resulting in old header files being used. 1842. [port] cmsg_len() could produce incorrect results on some platform. [RT #13744] 1841. [bug] "dig +nssearch" now makes a recursive query to find the list of nameservers to query. [RT #13694] 1840. [func] dnssec-signzone can now randomize signature end times (dnssec-signzone -j jitter). [RT #13609] 1839. [bug] was not being installed. 1838. [cleanup] Don't allow Linux capabilities to be inherited. [RT #13707] 1837. [bug] Compile time option ISC_FACILITY was not effective for 'named -u '. [RT #13714] 1836. [cleanup] Silence compiler warnings in hash_test.c. 1835. [bug] Update dnssec-signzone's usage message. [RT #13657] 1834. [bug] Bad memset in rdata_test.c. [RT #13658] 1833. [bug] Race condition in isc_mutex_lock_profile(). [RT #13660] 1832. [bug] named fails to return BADKEY on unknown TSIG algorithm. [RT #13620] 1831. [doc] Update named-checkzone documentation. [RT#13604] 1830. [bug] adb lame cache has sence of test reversed. [RT #13600] 1829. [bug] win32: "pid-file none;" broken. [RT #13563] 1828. [bug] isc_rwlock_init() failed to properly cleanup if it encountered a error. [RT #13549] 1827. [bug] host: update usage message for '-a'. [RT #37116] 1826. [bug] Missing DESTROYLOCK() in isc_mem_createx() on out of memory error. [RT #13537] 1825. [bug] Missing UNLOCK() on out of memory error from in rbtdb.c:subtractrdataset(). [RT #13519] 1824. [bug] Memory leak on dns_zone_setdbtype() failure. [RT #13510] 1823. [bug] Wrong macro used to check for point to point interface. [RT#13418] 1822. [bug] check-names test for RT was reversed. [RT #13382] 1821. [placeholder] 1820. [bug] Gracefully handle acl loops. [RT #13659] 1819. [bug] The validator needed to check both the algorithm and digest types of the DS to determine if it could be used to introduce a secure zone. [RT #13593] 1818. [bug] 'named-checkconf -z' triggered an INSIST. [RT #13599] 1817. [func] Add support for additional zone file formats for improving loading performance. The masterfile-format option in named.conf can be used to specify a non-default format. A separate command named-compilezone was provided to generate zone files in the new format. Additionally, the -I and -O options for dnssec-signzone specify the input and output formats. 1816. [port] UnixWare: failed to compile lib/isc/unix/net.c. [RT #13597] 1815. [bug] nsupdate triggered a REQUIRE if the server was set without also setting the zone and it encountered a CNAME and was using TSIG. [RT #13086] 1814. [func] UNIX domain controls are now supported. 1813. [func] Restructured the data locking framework using architecture dependent atomic operations (when available), improving response performance on multi-processor machines significantly. x86, x86_64, alpha, powerpc, and mips are currently supported. 1812. [port] win32: IN6_IS_ADDR_UNSPECIFIED macro is incorrect. [RT #13453] 1811. [func] Preserve the case of domain names in rdata during zone transfers. [RT #13547] 1810. [bug] configure, lib/bind/configure make different default decisions about whether to do a threaded build. [RT #13212] 1809. [bug] "make distclean" failed for libbind if the platform is not supported. 1808. [bug] zone.c:notify_zone() contained a race condition, zone->db could change underneath it. [RT #13511] 1807. [bug] When forwarding (forward only) set the active domain from the forward zone name. [RT #13526] 1806. [bug] The resolver returned the wrong result when a CNAME / DNAME was encountered when fetching glue from a secure namespace. [RT #13501] 1805. [bug] Pending status was not being cleared when DLV was active. [RT #13501] 1804. [bug] Ensure that if we are queried for glue that it fits in the additional section or TC is set to tell the client to retry using TCP. [RT #10114] 1803. [bug] dnssec-signzone sometimes failed to remove old RRSIGs. [RT #13483] 1802. [bug] Handle connection resets better. [RT #11280] 1801. [func] Report differences between hints and real NS rrset and associated address records. 1800. [bug] Changes #1719 allowed a INSIST to be triggered. [RT #13428] 1799. [bug] 'rndc flushname' failed to flush negative cache entries. [RT #13438] 1798. [func] The server syntax has been extended to support a range of servers. [RT #11132] 1797. [func] named-checkconf now check acls to verify that they only refer to existing acls. [RT #13101] 1796. [func] "rndc freeze/thaw" now freezes/thaws all zones. 1795. [bug] "rndc dumpdb" was not fully documented. Minor formating issues with "rndc dumpdb -all". [RT #13396] 1794. [func] Named and named-checkzone can now both check for non-terminal wildcard records. 1793. [func] Extend adjusting TTL warning messages. [RT #13378] 1792. [func] New zone option "notify-delay". Specify a minimum delay between sets of NOTIFY messages. 1791. [bug] 'host -t a' still printed out AAAA and MX records. [RT #13230] 1790. [cleanup] Move lib/dns/sec/dst up into lib/dns. This should allow parallel make to succeed. 1789. [bug] Prerequisite test for tkey and dnssec could fail with "configure --with-libtool". 1788. [bug] libbind9.la/libbind9.so needs to link against libisccfg.la/libisccfg.so. 1787. [port] HPUX: both "cc" and "gcc" need -Wl,+vnocompatwarnings. 1786. [port] AIX: libt_api needs to be taught to look for T_testlist in the main executable (--with-libtool). [RT #13239] 1785. [bug] libbind9.la/libbind9.so needs to link against libisc.la/libisc.so. 1784. [cleanup] "libtool -allow-undefined" is the default. Leave hooks in configure to allow it to be set if needed in the future. 1783. [cleanup] We only need one copy of libtool.m4, ltmain.sh in the source tree. 1782. [port] OSX: --with-libtool + --enable-libbind broke on __evOptMonoTime. [RT #13219] 1781. [port] FreeBSD 5.3: set PTHREAD_SCOPE_SYSTEM. [RT #12810] 1780. [bug] Update libtool to 1.5.10. 1779. [port] OSF 5.1: libtool didn't handle -pthread correctly. 1778. [port] HUX 11.11: fix broken IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT macros. 1777. [port] OSF 5.1: fix broken IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT macros. 1776. [port] Solaris 2.9: fix broken IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT macros. 1775. [bug] Only compile getnetent_r.c when threaded. [RT #13205] 1774. [port] Aix: Silence compiler warnings / build failures. [RT #13154] 1773. [bug] Fast retry on host / net unreachable. [RT #13153] 1772. [placeholder] 1771. [placeholder] 1770. [bug] named-checkconf failed to report missing a missing file clause for rbt{64} master/hint zones. [RT#13009] 1769. [port] win32: change compiler flags /MTd ==> /MDd, /MT ==> /MD. 1768. [bug] nsecnoexistnodata() could be called with a non-NSEC rdataset. [RT #12907] 1767. [port] Builds on IPv6 platforms without IPv6 Advanced API support for (struct in6_pktinfo) failed. [RT #13077] 1766. [bug] Update the master file timestamp on successful refresh as well as the journal's timestamp. [RT# 13062] 1765. [bug] configure --with-openssl=auto failed. [RT #12937] 1764. [bug] dns_zone_replacedb failed to emit a error message if there was no SOA record in the replacement db. [RT #13016] 1763. [func] Perform sanity checks on NS records which refer to 'in zone' names. [RT #13002] 1762. [bug] isc_interfaceiter_create() could return ISC_R_SUCCESS even when it failed. [RT #12995] 1761. [bug] 'rndc dumpdb' didn't report unassociated entries. [RT #12971] 1760. [bug] Host / net unreachable was not penalising rtt estimates. [RT #12970] 1759. [bug] Named failed to startup if the OS supported IPv6 but had no IPv6 interfaces configured. [RT #12942] 1758. [func] Don't send notify messages to self. [RT #12933] 1757. [func] host now can turn on memory debugging flags with '-m'. 1756. [func] named-checkconf now checks the logging configuration. [RT #12352] 1755. [func] allow-update is now settable at the options / view level. [RT #6636] 1754. [bug] We weren't always attempting to query the parent server for the DS records at the zone cut. [RT #12774] 1753. [bug] Don't serve a slave zone which has no NS records. [RT #12894] 1752. [port] Move isc_app_start() to after ns_os_daemonise() as some fork() implementations unblock the signals that are blocked by isc_app_start(). [RT #12810] 1751. [bug] --enable-getifaddrs failed under linux. [RT #12867] 1750. [port] lib/bind/make/rules.in:subdirs was not bash friendly. [RT #12864] 1749. [bug] 'check-names response ignore;' failed to ignore. [RT #12866] 1748. [func] dig now returns the byte count for axfr/ixfr. 1747. [bug] BIND 8 compatibility: named/named-checkconf failed to parse "host-statistics-max" in named.conf. 1746. [func] Make public the function to read a key file, dst_key_read_public(). [RT #12450] 1745. [bug] Dig/host/nslookup accept replies from link locals regardless of scope if no scope was specified when query was sent. [RT #12745] 1744. [bug] If tuple2msgname() failed to convert a tuple to a name a REQUIRE could be triggered. [RT #12796] 1743. [bug] If isc_taskmgr_create() was not able to create the requested number of worker threads then destruction of the manager would trigger an INSIST() failure. [RT #12790] 1742. [bug] Deleting all records at a node then adding a previously existing record, in a single UPDATE transaction, failed to leave / regenerate the associated RRSIG records. [RT #12788] 1741. [bug] Deleting all records at a node in a secure zone using a update-policy grant failed. [RT #12787] 1740. [bug] Replace rbt's hash algorithm as it performed badly with certain zones. [RT #12729] NOTE: a hash context now needs to be established via isc_hash_create() if the application was not already doing this. 1739. [bug] dns_rbt_deletetree() could incorrectly return ISC_R_QUOTA. [RT #12695] 1738. [bug] Enable overrun checking by default. [RT #12695] 1737. [bug] named failed if more than 16 masters were specified. [RT #12627] 1736. [bug] dst_key_fromnamedfile() could fail to read a public key. [RT #12687] 1735. [bug] 'dig +sigtrace' could die with a REQUIRE failure. [RE #12688] 1734. [cleanup] 'rndc-confgen -a -t' remove extra '/' in path. [RT #12588] 1733. [bug] Return non-zero exit status on initial load failure. [RT #12658] 1732. [bug] 'rrset-order name "*"' wasn't being applied to ".". [RT #12467] 1731. [port] darwin: relax version test in ifconfig.sh. [RT #12581] 1730. [port] Determine the length type used by the socket API. [RT #12581] 1729. [func] Improve check-names error messages. 1728. [doc] Update check-names documentation. 1727. [bug] named-checkzone: check-names support didn't match documentation. 1726. [port] aix5: add support for aix5. 1725. [port] linux: update error message on interaction of threads, capabilities and setuid support (named -u). [RT #12541] 1724. [bug] Look for DNSKEY records with "dig +sigtrace". [RT #12557] 1723. [cleanup] Silence compiler warnings from t_tasks.c. [RT #12493] 1722. [bug] Don't commit the journal on malformed ixfr streams. [RT #12519] 1721. [bug] Error message from the journal processing were not always identifying the relevant journal. [RT #12519] 1720. [bug] 'dig +chase' did not terminate on a RFC 2308 Type 1 negative response. [RT #12506] 1719. [bug] named was not correctly caching a RFC 2308 Type 1 negative response. [RT #12506] 1718. [bug] nsupdate was not handling RFC 2308 Type 3 negative responses when looking for the zone / master server. [RT #12506] 1717. [port] solaris: ifconfig.sh did not support Solaris 10. "ifconfig.sh down" didn't work for Solaris 9. 1716. [doc] named.conf(5) was being installed in the wrong location. [RT# 12441] 1715. [func] 'dig +trace' now randomly selects the next servers to try. Report if there is a bad delegation. 1714. [bug] dig/host/nslookup were only trying the first address when a nameserver was specified by name. [RT #12286] 1713. [port] linux: extend capset failure message to say: please ensure that the capset kernel module is loaded. see insmod(8) 1712. [bug] Missing FULLCHECK for "trusted-key" in dig. 1711. [func] 'rndc unfreeze' has been deprecated by 'rndc thaw'. 1710. [func] 'rndc notify zone [class [view]]' resend the NOTIFY messages for the specified zone. [RT #9479] 1709. [port] solaris: add SMF support from Sun. 1708. [cleanup] Replaced dns_fullname_hash() with dns_name_fullhash() for conformance to the name space convention. Binary backward compatibility to the old function name is provided. [RT #12376] 1707. [contrib] sdb/ldap updated to version 1.0-beta. 1706. [bug] 'rndc stop' failed to cause zones to be flushed sometimes. [RT #12328] 1705. [func] Allow the journal's name to be changed via named.conf. 1704. [port] lwres needed a snprintf() implementation for platforms without snprintf(). Add missing "#include ". [RT #12321] 1703. [bug] named would loop sending NOTIFY messages when it failed to receive a response. [RT #12322] 1702. [bug] also-notify should not be applied to built in zones. [RT #12323] 1701. [doc] A minimal named.conf man page. 1700. [func] nslookup is no longer to be treated as deprecated. Remove "deprecated" warning message. Add man page. 1699. [bug] dnssec-signzone can generate "not exact" errors when resigning. [RT #12281] 1698. [doc] Use reserved IPv6 documentation prefix. 1697. [bug] xxx-source{,-v6} was not effective when it specified one of listening addresses and a different port than the listening port. [RT #12257] 1696. [bug] dnssec-signzone failed to clean out nodes that consisted of only NSEC and RRSIG records. [RT #12154] 1695. [bug] DS records when forwarding require special handling. [RT #12133] 1694. [bug] Report if the builtin views of "_default" / "_bind" are defined in named.conf. [RT #12023] 1693. [bug] max-journal-size was not effective for master zones with ixfr-from-differences set. [RT# 12024] 1692. [bug] Don't set -I, -L and -R flags when libcrypto is in /usr/lib. [RT #11971] 1691. [bug] sdb's attachversion was not complete. [RT #11990] 1690. [bug] Delay detaching view from the client until UPDATE processing completes when shutting down. [RT #11714] 1689. [bug] DNS_NAME_TOREGION() and DNS_NAME_SPLIT() macros contained gratuitous semicolons. [RT #11707] 1688. [bug] LDFLAGS was not supported. 1687. [bug] Race condition in dispatch. [RT #10272] 1686. [bug] Named sent a extraneous NOTIFY when it received a redundant UPDATE request. [RT #11943] 1685. [bug] Change #1679 loop tests weren't quite right. 1684. [func] ixfr-from-differences now takes master and slave in addition to yes and no at the options and view levels. 1683. [bug] dig +sigchase could leak memory. [RT #11445] 1682. [port] Update configure test for (long long) printf format. [RT #5066] 1681. [bug] Only set SO_REUSEADDR when a port is specified in isc_socket_bind(). [RT #11742] 1680. [func] rndc: the source address can now be specified. 1679. [bug] When there was a single nameserver with multiple addresses for a zone not all addresses were tried. [RT #11706] 1678. [bug] RRSIG should use TYPEXXXXX for unknown types. 1677. [bug] dig: +aaonly didn't work, +aaflag undocumented. 1676. [func] New option "allow-query-cache". This lets allow-query be used to specify the default zone access level rather than having to have every zone override the global value. allow-query-cache can be set at both the options and view levels. If allow-query-cache is not set allow-query applies. 1675. [bug] named would sometimes add extra NSEC records to the authority section. 1674. [port] linux: increase buffer size used to scan /proc/net/if_inet6. 1673. [port] linux: issue a error messages if IPv6 interface scans fails. 1672. [cleanup] Tests which only function in a threaded build now return R:THREADONLY (rather than R:UNTESTED) in a non-threaded build. 1671. [contrib] queryperf: add NAPTR to the list of known types. 1670. [func] Log UPDATE requests to slave zones without an acl as "disabled" at debug level 3. [RT# 11657] 1669. [placeholder] 1668. [bug] DIG_SIGCHASE was making bin/dig/host dump core. 1667. [port] linux: not all versions have IF_NAMESIZE. 1666. [bug] The optional port on hostnames in dual-stack-servers was being ignored. 1665. [func] rndc now allows addresses to be set in the server clauses. 1664. [bug] nsupdate needed KEY for SIG(0), not DNSKEY. 1663. [func] Look for OpenSSL by default. 1662. [bug] Change #1658 failed to change one use of 'type' to 'keytype'. 1661. [bug] Restore dns_name_concatenate() call in adb.c:set_target(). [RT #11582] 1660. [bug] win32: connection_reset_fix() was being called unconditionally. [RT #11595] 1659. [cleanup] Cleanup some messages that were referring to KEY vs DNSKEY, NXT vs NSEC and SIG vs RRSIG. 1658. [func] Update dnssec-keygen to default to KEY for HMAC-MD5 and DH. Tighten which options apply to KEY and DNSKEY records. 1657. [doc] ARM: document query log output. 1656. [doc] Update DNSSEC description in ARM to cover DS, NSEC DNSKEY and RRSIG. [RT #11542] 1655. [bug] Logging multiple versions w/o a size was broken. [RT #11446] 1654. [bug] isc_result_totext() contained array bounds read error. 1653. [func] Add key type checking to dst_key_fromfilename(), DST_TYPE_KEY should be used to read TSIG, TKEY and SIG(0) keys. 1652. [bug] TKEY still uses KEY. 1651. [bug] dig: process multiple dash options. 1650. [bug] dig, nslookup: flush standard out after each command. 1649. [bug] Silence "unexpected non-minimal diff" message. [RT #11206] 1648. [func] Update dnssec-lookaside named.conf syntax to support multiple dnssec-lookaside namespaces (not yet implemented). 1647. [bug] It was possible trigger a INSIST when chasing a DS record that required walking back over a empty node. [RT #11445] 1646. [bug] win32: logging file versions didn't work with non-UNC filenames. [RT#11486] 1645. [bug] named could trigger a REQUIRE failure if multiple masters with keys are specified. 1644. [bug] Update the journal modification time after a successful refresh query. [RT #11436] 1643. [bug] dns_db_closeversion() could leak memory / node references. [RT #11163] 1642. [port] Support OpenSSL implementations which don't have DSA support. [RT #11360] 1641. [bug] Update the check-names description in ARM. [RT #11389] 1640. [bug] win32: isc_socket_cancel(ISC_SOCKCANCEL_ACCEPT) was incorrectly closing the socket. [RT #11291] 1639. [func] Initial dlv system test. 1638. [bug] "ixfr-from-differences" could generate a REQUIRE failure if the journal open failed. [RT #11347] 1637. [bug] Node reference leak on error in addnoqname(). 1636. [bug] The dump done callback could get ISC_R_SUCCESS even if a error had occurred. The database version no longer matched the version of the database that was dumped. 1635. [bug] Memory leak on error in query_addds(). 1634. [bug] named didn't supply a useful error message when it detected duplicate views. [RT #11208] 1633. [bug] named should return NOTIMP to update requests to a slaves without a allow-update-forwarding acl specified. [RT #11331] 1632. [bug] nsupdate failed to send prerequisite only UPDATE messages. [RT #11288] 1631. [bug] dns_journal_compact() could sometimes corrupt the journal. [RT #11124] 1630. [contrib] queryperf: add support for IPv6 transport. 1629. [func] dig now supports IPv6 scoped addresses with the extended format in the local-server part. [RT #8753] 1628. [bug] Typo in Compaq Trucluster support. [RT# 11264] 1627. [bug] win32: sockets were not being closed when the last external reference was removed. [RT# 11179] 1626. [bug] --enable-getifaddrs was broken. [RT#11259] 1625. [bug] named failed to load/transfer RFC2535 signed zones which contained CNAMES. [RT# 11237] 1624. [bug] zonemgr_putio() call should be locked. [RT# 11163] 1623. [bug] A serial number of zero was being displayed in the "sending notifies" log message when also-notify was used. [RT #11177] 1622. [func] probe the system to see if IPV6_(RECV)PKTINFO is available, and suppress wildcard binding if not. 1621. [bug] match-destinations did not work for IPv6 TCP queries. [RT# 11156] 1620. [func] When loading a zone report if it is signed. [RT #11149] 1619. [bug] Missing ISC_LIST_UNLINK in end_reserved_dispatches(). [RT# 11118] 1618. [bug] Fencepost errors in dns_name_ishostname() and dns_name_ismailbox() could trigger a INSIST(). 1617. [port] win32: VC++ 6.0 support. 1616. [compat] Ensure that named's version is visible in the core dump. [RT #11127] 1615. [port] Define ISC_SOCKADDR_LEN_T based on _BSD_SOCKLEN_T_ if it is defined. 1614. [port] win32: silence resource limit messages. [RT# 11101] 1613. [bug] Builds would fail on machines w/o a if_nametoindex(). Missing #ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX/#endif. [RT #11119] 1612. [bug] check-names at the option/view level could trigger an INSIST. [RT# 11116] 1611. [bug] solaris: IPv6 interface scanning failed to cope with no active IPv6 interfaces. 1610. [bug] On dual stack machines "dig -b" failed to set the address type to be looked up with "@server". [RT #11069] 1609. [func] dig now has support to chase DNSSEC signature chains. Requires -DDIG_SIGCHASE=1 to be set in STD_CDEFINES. DNSSEC validation code in dig coded by Olivier Courtay (olivier.courtay@irisa.fr) for the IDsA project (http://idsa.irisa.fr). 1608. [func] dig and host now accept -4/-6 to select IP transport to use when making queries. 1607. [bug] dig, host and nslookup were still using random() to generate query ids. [RT# 11013] 1606. [bug] DLV insecurity proof was failing. 1605. [func] New dns_db_find() option DNS_DBFIND_COVERINGNSEC. 1604. [bug] A xfrout_ctx_create() failure would result in xfrout_ctx_destroy() being called with a partially initialized structure. 1603. [bug] nsupdate: set interactive based on isatty(). [RT# 10929] 1602. [bug] Logging to a file failed unless a size was specified. [RT# 10925] 1601. [bug] Silence spurious warning 'both "recursion no;" and "allow-recursion" active' warning from view "_bind". [RT# 10920] 1600. [bug] Duplicate zone pre-load checks were not case insensitive. 1599. [bug] Fix memory leak on error path when checking named.conf. 1598. [func] Specify that certain parts of the namespace must be secure (dnssec-must-be-secure). 1597. [func] Allow notify-source and query-source to be specified on a per server basis similar to transfer-source. [RT #6496] 1596. [func] Accept 'notify-source' style syntax for query-source. 1595. [func] New notify type 'master-only'. Enable notify for master zones only. 1594. [bug] 'rndc dumpdb' could prevent named from answering queries while the dump was in progress. [RT #10565] 1593. [bug] rndc should return "unknown command" to unknown commands. [RT# 10642] 1592. [bug] configure_view() could leak a dispatch. [RT# 10675] 1591. [bug] libbind: updated to BIND 8.4.5. 1590. [port] netbsd: update thread support. 1589. [func] DNSSEC lookaside validation. 1588. [bug] win32: TCP sockets could become blocked. [RT #10115] 1587. [bug] dns_message_settsigkey() failed to clear existing key. [RT #10590] 1586. [func] "check-names" is now implemented. 1585. [placeholder] 1584. [bug] "make test" failed with a read only source tree. [RT #10461] 1583. [bug] Records add via UPDATE failed to get the correct trust level. [RT #10452] 1582. [bug] rrset-order failed to work on RRsets with more than 32 elements. [RT #10381] 1581. [func] Disable DNSSEC support by default. To enable DNSSEC specify "dnssec-enable yes;" in named.conf. 1580. [bug] Zone destruction on final detach takes a long time. [RT #3746] 1579. [bug] Multiple task managers could not be created. 1578. [bug] Don't use CLASS E IPv4 addresses when resolving. [RT #10346] 1577. [bug] Use isc_uint32_t in ultrasparc optimizer bug workaround code. [RT #10331] 1576. [bug] Race condition in dns_dispatch_addresponse(). [RT# 10272] 1575. [func] Log TSIG name on TSIG verify failure. [RT #4404] 1574. [bug] Don't attempt to open the controls socket(s) when running tests. [RT #9091] 1573. [port] linux: update to libtool 1.5.2 so that "make install DESTDIR=/xx" works with "configure --with-libtool". [RT #9941] 1572. [bug] nsupdate: sign the soa query to find the enclosing zone if the server is specified. [RT #10148] 1571. [bug] rbt:hash_node() could fail leaving the hash table in an inconsistent state. [RT #10208] 1570. [bug] nsupdate failed to handle classes other than IN. New keyword 'class' which sets the default class. [RT #10202] 1569. [func] nsupdate new command 'answer' which displays the complete answer message to the last update. 1568. [bug] nsupdate now reports that the update failed in interactive mode. [RT# 10236] 1567. [maint] B.ROOT-SERVERS.NET is now 192.228.79.201. 1566. [port] Support for the cmsg framework on Solaris and HP/UX. This also solved the problem that match-destinations for IPv6 addresses did not work on these systems. [RT #10221] 1565. [bug] CD flag should be copied to outgoing queries unless the query is under a secure entry point in which case CD should be set. 1564. [func] Attempt to provide a fallback entropy source to be used if named is running chrooted and named is unable to open entropy source within the chroot area. [RT #10133] 1563. [bug] Gracefully fail when unable to obtain neither an IPv4 nor an IPv6 dispatch. [RT #10230] 1562. [bug] isc_socket_create() and isc_socket_accept() could leak memory under error conditions. [RT #10230] 1561. [bug] It was possible to release the same name twice if named ran out of memory. [RT #10197] 1560. [port] FreeBSD: work around FreeBSD 5.2 mapping EAI_NODATA and EAI_NONAME to the same value. 1559. [port] named should ignore SIGFSZ. 1558. [func] New DNSSEC 'disable-algorithms'. Support entry into child zones for which we don't have a supported algorithm. Such child zones are treated as unsigned. 1557. [func] Implement missing DNSSEC tests for * NOQNAME proof with wildcard answers. * NOWILDARD proof with NXDOMAIN. Cache and return NOQNAME with wildcard answers. 1556. [bug] nsupdate now treats all names as fully qualified. [RT #6427] 1555. [func] 'rrset-order cyclic' no longer has a random starting point per query. [RT #7572] 1554. [bug] dig, host, nslookup failed when no nameservers were specified in /etc/resolv.conf. [RT #8232] 1553. [bug] The windows socket code could stop accepting connections. [RT#10115] 1552. [bug] Accept NOTIFY requests from mapped masters if matched-mapped is set. [RT #10049] 1551. [port] Open "/dev/null" before calling chroot(). 1550. [port] Call tzset(), if available, before calling chroot(). 1549. [func] named-checkzone can now write out the zone contents in a easily parsable format (-D and -o). 1548. [bug] When parsing APL records it was possible to silently accept out of range ADDRESSFAMILY values. [RT# 9979] 1547. [bug] Named wasted memory recording duplicate lame zone entries. [RT #9341] 1546. [bug] We were rejecting valid secure CNAME to negative answers. 1545. [bug] It was possible to leak memory if named was unable to bind to the specified transfer source and TSIG was being used. [RT #10120] 1544. [bug] Named would logged a single entry to a file despite it being over the specified size limit. 1543. [bug] Logging using "versions unlimited" did not work. 1542. [placeholder] 1541. [func] NSEC now uses new bitmap format. 1540. [bug] "rndc reload " was silently accepted. [RT #8934] 1539. [bug] Open UDP sockets for notify-source and transfer-source that use reserved ports at startup. [RT #9475] 1538. [placeholder] rt9997 1537. [func] New option "querylog". If set specify whether query logging is to be enabled or disabled at startup. 1536. [bug] Windows socket code failed to log a error description when returning ISC_R_UNEXPECTED. [RT #9998] 1535. [placeholder] 1534. [bug] Race condition when priming cache. [RT# 9940] 1533. [func] Warn if both "recursion no;" and "allow-recursion" are active. [RT# 4389] 1532. [port] netbsd: the configure test for requires . 1531. [port] AIX more libtool fixes. 1530. [bug] It was possible to trigger a INSIST() failure if a slave master file was removed at just the correct moment. [RT #9462] 1529. [bug] "notify explicit;" failed to log that NOTIFY messages were being sent for the zone. [RT# 9442] 1528. [cleanup] Simplify some dns_name_ functions based on the deprecation of bitstring labels. 1527. [cleanup] Reduce the number of gettimeofday() calls without losing necessary timer granularity. 1526. [func] Implemented "additional section caching (or acache)", an internal cache framework for additional section content to improve response performance. Several configuration options were provided to control the behavior. 1525. [bug] dns_cache_create() could trigger a REQUIRE failure in isc_mem_put() during error cleanup. [RT# 9360] 1524. [port] AIX needs to be able to resolve all symbols when creating shared libraries (--with-libtool). 1523. [bug] Fix race condition in rbtdb. [RT# 9189] 1522. [bug] dns_db_findnode() relax the requirements on 'name'. [RT# 9286] 1521. [bug] dns_view_createresolver() failed to check the result from isc_mem_create(). [RT# 9294] 1520. [protocol] Add SSHFP (SSH Finger Print) type. 1519. [bug] dnssec-signzone:nsec_setbit() computed the wrong length of the new bitmap. 1518. [bug] dns_nsec_buildrdata(), and hence dns_nsec_build(), contained a off-by-one error when working out the number of octets in the bitmap. 1517. [port] Support for IPv6 interface scanning on HP/UX and TrueUNIX 5.1. 1516. [func] Roll the DNSSEC types to RRSIG, NSEC and DNSKEY. 1515. [func] Allow transfer source to be set in a server statement. [RT #6496] 1514. [bug] named: isc_hash_destroy() was being called too early. [RT #9160] 1513. [doc] Add "US" to root-delegation-only exclude list. 1512. [bug] Extend the delegation-only logging to return query type, class and responding nameserver. 1511. [bug] delegation-only was generating false positives on negative answers from sub-zones. 1510. [func] New view option "root-delegation-only". Apply delegation-only check to all TLDs and root. Note there are some TLDs that are NOT delegation only (e.g. DE, LV, US and MUSEUM) these can be excluded from the checks by using exclude. root-delegation-only exclude { "DE"; "LV"; "US"; "MUSEUM"; }; 1509. [bug] Hint zones should accept delegation-only. Forward zone should not accept delegation-only. 1508. [bug] Don't apply delegation-only checks to answers from forwarders. 1507. [bug] Handle BIND 8 style returns to NS queries to parents when making delegation-only checks. 1506. [bug] Wrong return type for dns_view_isdelegationonly(). 1505. [bug] Uninitialized rdataset in sdb. [RT #8750] 1504. [func] New zone type "delegation-only". 1503. [port] win32: install libeay32.dll outside of system32. 1502. [bug] nsupdate: adjust timeouts for UPDATE requests over TCP. 1501. [func] Allow TCP queue length to be specified via named.conf, tcp-listen-queue. 1500. [bug] host failed to lookup MX records. Also look up AAAA records. 1499. [bug] isc_random need to be seeded better if arc4random() is not used. 1498. [port] bsdos: 5.x support. 1497. [placeholder] 1496. [port] test for pthread_attr_setstacksize(). 1495. [cleanup] Replace hash functions with universal hash. 1494. [security] Turn on RSA BLINDING as a precaution. 1493. [placeholder] 1492. [cleanup] Preserve rwlock quota context when upgrading / downgrading. [RT #5599] 1491. [bug] dns_master_dump*() would produce extraneous $ORIGIN lines. [RT #6206] 1490. [bug] Accept reading state as well as working state in ns_client_next(). [RT #6813] 1489. [compat] Treat 'allow-update' on slave zones as a warning. [RT #3469] 1488. [bug] Don't override trust levels for glue addresses. [RT #5764] 1487. [bug] A REQUIRE() failure could be triggered if a zone was queued for transfer and the zone was then removed. [RT #6189] 1486. [bug] isc_print_snprintf() '%%' consumed one too many format characters. [RT# 8230] 1485. [bug] gen failed to handle high type values. [RT #6225] 1484. [bug] The number of records reported after a AXFR was wrong. [RT #6229] 1483. [bug] dig axfr failed if the message id in the answer failed to match that in the request. Only the id in the first message is required to match. [RT #8138] 1482. [bug] named could fail to start if the kernel supports IPv6 but no interfaces are configured. Similarly for IPv4. [RT #6229] 1481. [bug] Refresh and stub queries failed to use masters keys if specified. [RT #7391] 1480. [bug] Provide replay protection for rndc commands. Full replay protection requires both rndc and named to be updated. Partial replay protection (limited exposure after restart) is provided if just named is updated. 1479. [bug] cfg_create_tuple() failed to handle out of memory cleanup. parse_list() would leak memory on syntax errors. 1478. [port] ifconfig.sh didn't account for other virtual interfaces. It now takes a optional argument to specify the first interface number. [RT #3907] 1477. [bug] memory leak using stub zones and TSIG. 1476. [placeholder] 1475. [port] Probe for old sprintf(). 1474. [port] Provide strtoul() and memmove() for platforms without them. 1473. [bug] create_map() and create_string() failed to handle out of memory cleanup. [RT #6813] 1472. [contrib] idnkit-1.0 from JPNIC, replaces mdnkit. 1471. [bug] libbind: updated to BIND 8.4.0. 1470. [bug] Incorrect length passed to snprintf. [RT #5966] 1469. [func] Log end of outgoing zone transfer at same level as the start of transfer is logged. [RT #4441] 1468. [func] Internal zones are no longer counted for 'rndc status'. [RT #4706] 1467. [func] $GENERATES now supports optional class and ttl. 1466. [bug] lwresd configuration errors resulted in memory and lock leaks. [RT #5228] 1465. [bug] isc_base64_decodestring() and isc_base64_tobuffer() failed to check that trailing bits were zero allowing some invalid base64 strings to be accepted. [RT #5397] 1464. [bug] Preserve "out of zone" data for outgoing zone transfers. [RT #5192] 1463. [bug] dns_rdata_from{wire,struct}() failed to catch bad NXT bit maps. [RT #5577] 1462. [bug] parse_sizeval() failed to check the token type. [RT #5586] 1461. [bug] Remove deadlock from rbtdb code. [RT #5599] 1460. [bug] inet_pton() failed to reject certain malformed IPv6 literals. 1459. [placeholder] 1458. [cleanup] sprintf() -> snprintf(). 1457. [port] Provide strlcat() and strlcpy() for platforms without them. 1456. [contrib] gen-data-queryperf.py from Stephane Bortzmeyer. 1455. [bug] missing from server grammar in doc/misc/options. [RT #5616] 1454. [port] Use getifaddrs() if available for interface scanning. --disable-getifaddrs to override. Glibc currently has a getifaddrs() that does not support IPv6. Use --enable-getifaddrs=glibc to force the use of this version under linux machines. 1453. [doc] ARM: $GENERATE example wasn't accurate. [RT #5298] 1452. [placeholder] 1451. [bug] rndc-confgen didn't exit with a error code for all failures. [RT #5209] 1450. [bug] Fetching expired glue failed under certain circumstances. [RT #5124] 1449. [bug] query_addbestns() didn't handle running out of memory gracefully. 1448. [bug] Handle empty wildcards labels. 1447. [bug] We were casting (unsigned int) to and from (void *). rdataset->private4 is now rdataset->privateuint4 to reflect a type change. 1446. [func] Implemented undocumented alternate transfer sources from BIND 8. See use-alt-transfer-source, alt-transfer-source and alt-transfer-source-v6. SECURITY: use-alt-transfer-source is ENABLED unless you are using views. This may cause a security risk resulting in accidental disclosure of wrong zone content if the master supplying different source content based on IP address. If you are not certain ISC recommends setting use-alt-transfer-source no; 1445. [bug] DNS_ADBFIND_STARTATROOT broke stub zones. This has been replaced with DNS_ADBFIND_STARTATZONE which causes the search to start using the closest zone. 1444. [func] dns_view_findzonecut2() allows you to specify if the cache should be searched for zone cuts. 1443. [func] Masters lists can now be specified and referenced in zone masters clauses and other masters lists. 1442. [func] New functions for manipulating port lists: dns_portlist_create(), dns_portlist_add(), dns_portlist_remove(), dns_portlist_match(), dns_portlist_attach() and dns_portlist_detach(). 1441. [func] It is now possible to tell dig to bind to a specific source port. 1440. [func] It is now possible to tell named to avoid using certain source ports (avoid-v4-udp-ports, avoid-v6-udp-ports). 1439. [bug] Named could return NOERROR with certain NOTIFY failures. Return NOTAUTH if the NOTIFY zone is not being served. 1438. [func] Log TSIG (if any) when logging NOTIFY requests. 1437. [bug] Leave space for stdio to work in. [RT #5033] 1436. [func] dns_zonemgr_resumexfrs() can be used to restart stalled transfers. 1435. [bug] zmgr_resume_xfrs() was being called read locked rather than write locked. zmgr_resume_xfrs() was not being called if the zone was being shutdown. 1434. [bug] "rndc reconfig" failed to initiate the initial zone transfer of new slave zones. 1433. [bug] named could trigger a REQUIRE failure if it could not get a file descriptor when attempting to write a master file. [RT #4347] 1432. [func] The advertised EDNS UDP buffer size can now be set via named.conf (edns-udp-size). 1431. [bug] isc_print_snprintf() "%s" with precision could walk off end of argument. [RT #5191] 1430. [port] linux: IPv6 interface scanning support. 1429. [bug] Prevent the cache getting locked to old servers. 1428. [placeholder] 1427. [bug] Race condition in adb with threaded build. 1426. [placeholder] 1425. [port] linux/libbind: define __USE_MISC when testing *_r() function prototypes in netdb.h. [RT #4921] 1424. [bug] EDNS version not being correctly printed. 1423. [contrib] queryperf: added A6 and SRV. 1422. [func] Log name/type/class when denying a query. [RT #4663] 1421. [func] Differentiate updates that don't succeed due to prerequisites (unsuccessful) vs other reasons (failed). 1420. [port] solaris: work around gcc optimizer bug. 1419. [port] openbsd: use /dev/arandom. [RT #4950] 1418. [bug] 'rndc reconfig' did not cause new slaves to load. 1417. [func] ID.SERVER/CHAOS is now a built in zone. See "server-id" for how to configure. 1416. [bug] Empty node should return NOERROR NODATA, not NXDOMAIN. [RT #4715] 1415. [func] DS TTL now derived from NS ttl. NXT TTL now derived from SOA MINIMUM. 1414. [func] Support for KSK flag. 1413. [func] Explicitly request the (re-)generation of DS records from keysets (dnssec-signzone -g). 1412. [func] You can now specify servers to be tried if a nameserver has IPv6 address and you only support IPv4 or the reverse. See dual-stack-servers. 1411. [bug] empty nodes should stop wildcard matches. [RT #4802] 1410. [func] Handle records that live in the parent zone, e.g. DS. 1409. [bug] DS should have attribute DNS_RDATATYPEATTR_DNSSEC. 1408. [bug] "make distclean" was not complete. [RT #4700] 1407. [bug] lfsr incorrectly implements the shift register. [RT #4617] 1406. [bug] dispatch initializes one of the LFSR's with a incorrect polynomial. [RT #4617] 1405. [func] Use arc4random() if available. 1404. [bug] libbind: ns_name_ntol() could overwrite a zero length buffer. 1403. [func] dnssec-signzone, dnssec-keygen, dnssec-makekeyset dnssec-signkey now report their version in the usage message. 1402. [cleanup] A6 has been moved to experimental and is no longer fully supported. 1401. [bug] adb wasn't clearing state when the timer expired. 1400. [bug] Block the addition of wildcard NS records by IXFR or UPDATE. [RT #3502] 1399. [bug] Use serial number arithmetic when testing SIG timestamps. [RT #4268] 1398. [doc] ARM: notify-also should have been also-notify. [RT #4345] 1397. [maint] J.ROOT-SERVERS.NET is now 192.58.128.30. 1396. [func] dnssec-signzone: adjust the default signing time by 1 hour to allow for clock skew. 1395. [port] OpenSSL 0.9.7 defines CRYPTO_LOCK_ENGINE but doesn't have a working implementation. [RT #4079] 1394. [func] It is now possible to check if a particular element is in a acl. Remove duplicate entries from the localnets acl. 1393. [port] Bind to individual IPv6 interfaces if IPV6_IPV6ONLY is not available in the kernel to prevent accidently listening on IPv4 interfaces. 1392. [bug] named-checkzone: update usage. 1391. [func] Add support for IPv6 scoped addresses in named. 1390. [func] host now supports ixfr. 1389. [bug] named could fail to rotate long log files. [RT #3666] 1388. [port] irix: check for sys/sysctl.h and NET_RT_IFLIST before defining HAVE_IFLIST_SYSCTL. [RT #3770] 1387. [bug] named could crash due to an access to invalid memory space (which caused an assertion failure) in incremental cleaning. [RT #3588] 1386. [bug] named-checkzone -z stopped on errors in a zone. [RT #3653] 1385. [bug] Setting serial-query-rate to 10 would trigger a REQUIRE failure. 1384. [bug] host was incompatible with BIND 8 in its exit code and in the output with the -l option. [RT #3536] 1383. [func] Track the serial number in a IXFR response and log if a mismatch occurs. This is a more specific error than "not exact". [RT #3445] 1382. [bug] make install failed with --enable-libbind. [RT #3656] 1381. [bug] named failed to correctly process answers that contained DNAME records where the resulting CNAME resulted in a negative answer. 1380. [func] 'rndc recursing' dump recursing queries to 'recursing-file = "named.recursing";'. 1379. [func] 'rndc status' now reports tcp and recursion quota states. 1378. [func] Improved positive feedback for 'rndc {reload|refresh}. 1377. [func] dns_zone_load{new}() now reports if the zone was loaded, queued for loading to up to date. 1376. [func] New function dns_zone_logc() to log to specified category. 1375. [func] 'rndc dumpdb' now dumps the adb cache along with the data cache. 1374. [func] dns_adb_dump() now logs the lame zones associated with each server. 1373. [bug] Recovery from expired glue failed under certain circumstances. 1372. [bug] named crashes with an assertion failure on exit when sharing the same port for listening and querying, and changing listening addresses several times. [RT# 3509] 1371. [bug] notify-source-v6, transfer-source-v6 and query-source-v6 with explicit addresses and using the same ports as named was listening on could interfere with named's ability to answer queries sent to those addresses. 1370. [bug] dig '+[no]recurse' was incorrectly documented. 1369. [bug] Adding an NS record as the lexicographically last record in a secure zone didn't work. 1368. [func] remove support for bitstring labels. 1367. [func] Use response times to select forwarders. 1366. [contrib] queryperf usage was incomplete. Add '-h' for help. 1365. [func] "localhost" and "localnets" acls now include IPv6 addresses / prefixes. 1364. [func] Log file name when unable to open memory statistics and dump database files. [RT# 3437] 1363. [func] Listen-on-v6 now supports specific addresses. 1362. [bug] remove IFF_RUNNING test when scanning interfaces. 1361. [func] log the reason for rejecting a server when resolving queries. 1360. [bug] --enable-libbind would fail when not built in the source tree for certain OS's. 1359. [security] Support patches OpenSSL libraries. http://www.cert.org/advisories/CA-2002-23.html 1358. [bug] It was possible to trigger a INSIST when debugging large dynamic updates. [RT #3390] 1357. [bug] nsupdate was extremely wasteful of memory. 1356. [tuning] Reduce the number of events / quantum for zone tasks. 1355. [bug] Fix DNSSEC wildcard proof for CNAME/DNAME. 1354. [doc] lwres man pages had illegal nroff. 1353. [contrib] sdb/ldap to version 0.9. 1352. [bug] dig, host, nslookup when falling back to TCP use the current search entry (if any). [RT #3374] 1351. [bug] lwres_getipnodebyname() returned the wrong name when given a IPv4 literal, af=AF_INET6 and AI_MAPPED was set. 1350. [bug] dns_name_fromtext() failed to handle too many labels gracefully. 1349. [security] Minimum OpenSSL version now 0.9.6e (was 0.9.5a). http://www.cert.org/advisories/CA-2002-23.html 1348. [port] win32: Rewrote code to use I/O Completion Ports in socket.c and eliminating a host of socket errors. Performance is enhanced. 1347. [placeholder] 1346. [placeholder] 1345. [port] Use a explicit -Wformat with gcc. Not all versions include it in -Wall. 1344. [func] Log if the serial number on the master has gone backwards. If you have multiple machines specified in the masters clause you may want to set 'multi-master yes;' to suppress this warning. 1343. [func] Log successful notifies received (info). Adjust log level for failed notifies to notice. 1342. [func] Log remote address with TCP dispatch failures. 1341. [func] Allow a rate limiter to be stalled. 1340. [bug] Delay and spread out the startup refresh load. 1339. [func] dig, host and nslookup now use IP6.ARPA for nibble lookups. Bit string lookups are no longer attempted. 1338. [placeholder] 1337. [placeholder] 1336. [func] Nibble lookups under IP6.ARPA are now supported by dns_byaddr_create(). dns_byaddr_createptrname() is deprecated, use dns_byaddr_createptrname2() instead. 1335. [bug] When performing a nonexistence proof, the validator should discard parent NXTs from higher in the DNS. 1334. [bug] When signing/verifying rdatasets, duplicate rdatas need to be suppressed. 1333. [contrib] queryperf now reports a summary of returned rcodes (-c), rcodes are printed in mnemonic form (-v). 1332. [func] Report the current serial with periodic commits when rolling forward the journal. 1331. [func] Generate DNSSEC wildcard proofs. 1330. [bug] When processing events (non-threaded) only allow the task one chance to use to use its quantum. 1329. [func] named-checkzone will now check if nameservers that appear to be IP addresses. Available modes "fail", "warn" (default) and "ignore" the results of the check. 1328. [bug] The validator could incorrectly verify an invalid negative proof. 1327. [bug] The validator would incorrectly mark data as insecure when seeing a bogus signature before a correct signature. 1326. [bug] DNAME/CNAME signatures were not being cached when validation was not being performed. [RT #3284] 1325. [bug] If the tcpquota was exhausted it was possible to to trigger a INSIST() failure. 1324. [port] darwin: ifconfig.sh now supports darwin. 1323. [port] linux: Slackware 4.0 needs . [RT #3205] 1322. [bug] dnssec-signzone usage message was misleading. 1321. [bug] If the last RRset in a zone is glue, dnssec-signzone would incorrectly duplicate its output and sign it. 1320. [doc] query-source-v6 was missing from options section. [RT #3218] 1319. [func] libbind: log attempts to exploit #1318. 1318. [bug] libbind: Remote buffer overrun. 1317. [port] libbind: TrueUNIX 5.1 does not like __align as a element name. 1316. [bug] libbind: gethostans() could get out of sync parsing the response if there was a very long CNAME chain. 1315. [bug] Options should apply to the internal _bind view. 1314. [port] Handle ECONNRESET from sendmsg() [unix]. 1313. [func] Query log now says if the query was signed (S) or if EDNS was used (E). 1312. [func] Log TSIG key used w/ outgoing zone transfers. 1311. [bug] lwres_getrrsetbyname leaked memory. [RT #3159] 1310. [bug] 'rndc stop' failed to cause zones to be flushed sometimes. [RT #3157] 1309. [func] Log that a zone transfer was covered by a TSIG. 1308. [func] DS (delegation signer) support. 1307. [bug] nsupdate: allow white space base64 key data. 1306. [bug] Badly encoded LOC record when the size, horizontal precision or vertical precision was 0.1m. 1305. [bug] Document that internal zones are included in the rndc status results. 1304. [func] New function: dns_zone_name(). 1303. [func] Option 'flush-zones-on-shutdown ;'. 1302. [func] Extended rndc dumpdb to support dumping of zones and view selection: 'dumpdb [-all|-zones|-cache] [view]'. 1301. [func] New category 'update-security'. 1300. [port] Compaq Trucluster support. 1299. [bug] Set AI_ADDRCONFIG when looking up addresses via getaddrinfo() (affects dig, host, nslookup, rndc and nsupdate). 1298. [bug] The CINCLUDES macro in lib/dns/sec/dst/Makefile could be left with a trailing "\" after configure has been run. 1297. [port] linux: make handling EINVAL from socket() no longer conditional on #ifdef LINUX. 1296. [bug] isc_log_closefilelogs() needed to lock the log context. 1295. [bug] isc_log_setdebuglevel() needed to lock the log context. 1294. [func] libbind: no longer attempts bit string labels for IPv6 reverse resolution. Try IP6.ARPA then IP6.INT for nibble style resolution. 1293. [func] Entropy can now be retrieved from EGDs. [RT #2438] 1292. [func] Enable IPv6 support when using ioctl style interface scanning and OS supports SIOCGLIFADDR using struct if_laddrreq. 1291. [func] Enable IPv6 support when using sysctl style interface scanning. 1290. [func] "dig axfr" now reports the number of messages as well as the number of records. 1289. [port] See if -ldl is required for OpenSSL? [RT #2672] 1288. [bug] Adjusted REQUIRE's in lib/dns/name.c to better reflect written requirements. 1287. [bug] REQUIRE that DNS_DBADD_MERGE only be set when adding a rdataset to a zone db in the rbtdb implementation of addrdataset. 1286. [bug] dns_name_downcase() enforce requirement that target != NULL or name->buffer != NULL. 1285. [func] lwres: probe the system to see what address families are currently in use. 1284. [bug] The RTT estimate on unused servers was not aged. [RT #2569] 1283. [func] Use "dataready" accept filter if available. 1282. [port] libbind: hpux 11.11 interface scanning. 1281. [func] Log zone when unable to get private keys to update zone. Log zone when NXT records are missing from secure zone. 1280. [bug] libbind: escape '(' and ')' when converting to presentation form. 1279. [port] Darwin uses (unsigned long) for size_t. [RT #2590] 1278. [func] dig: now supports +[no]cl +[no]ttlid. 1277. [func] You can now create your own customized printing styles: dns_master_stylecreate() and dns_master_styledestroy(). 1276. [bug] libbind: const pointer conflicts in res_debug.c. 1275. [port] libbind: hpux: treat all hpux systems as BIG_ENDIAN. 1274. [bug] Memory leak in lwres_gnbarequest_parse(). 1273. [port] libbind: solaris: 64 bit binary compatibility. 1272. [contrib] Berkeley DB 4.0 sdb implementation from Nuno Miguel Rodrigues . 1271. [bug] "recursion available: {denied,approved}" was too confusing. 1270. [bug] Check that system inet_pton() and inet_ntop() support AF_INET6. 1269. [port] Openserver: ifconfig.sh support. 1268. [port] Openserver: the value FD_SETSIZE depends on whether is included or not. Be consistent. 1267. [func] isc_file_openunique() now creates file using mode 0666 rather than 0600. 1266. [bug] ISC_LINK_INIT, ISC_LINK_UNLINK, ISC_LIST_DEQUEUE, __ISC_LINK_UNLINKUNSAFE and __ISC_LIST_DEQUEUEUNSAFE are not C++ compatible, use *_TYPE versions instead. 1265. [bug] libbind: LINK_INIT and UNLINK were not compatible with C++, use LINK_INIT_TYPE and UNLINK_TYPE instead. 1264. [placeholder] 1263. [bug] Reference after free error if dns_dispatchmgr_create() failed. 1262. [bug] ns_server_destroy() failed to set *serverp to NULL. 1261. [func] libbind: ns_sign2() and ns_sign_tcp() now provide support for compressed TSIG owner names. 1260. [func] libbind: res_update can now update IPv6 servers, new function res_findzonecut2(). 1259. [bug] libbind: get_salen() IPv6 support was broken for OSs w/o sa_len. 1258. [bug] libbind: res_nametotype() and res_nametoclass() were broken. 1257. [bug] Failure to write pid-file should not be fatal on reload. [RT #2861] 1256. [contrib] 'queryperf' now has EDNS (-e) + DNSSEC DO (-D) support. 1255. [bug] When verifying that an NXT proves nonexistence, check the rcode of the message and only do the matching NXT check. That is, for NXDOMAIN responses, check that the name is in the range between the NXT owner and next name, and for NOERROR NODATA responses, check that the type is not present in the NXT bitmap. 1254. [func] preferred-glue option from BIND 8.3. 1253. [bug] The dnssec system test failed to remove the correct files. 1252. [bug] Dig, host and nslookup were not checking the address the answer was coming from against the address it was sent to. [RT# 2692] 1251. [port] win32: a make file contained absolute version specific references. 1250. [func] Nsupdate will report the address the update was sent to. 1249. [bug] Missing masters clause was not handled gracefully. [RT #2703] 1248. [bug] DESTDIR was not being propagated between makes. 1247. [bug] Don't reset the interface index for link/site local addresses. [RT #2576] 1246. [func] New functions isc_sockaddr_issitelocal(), isc_sockaddr_islinklocal(), isc_netaddr_issitelocal() and isc_netaddr_islinklocal(). 1245. [bug] Treat ENOBUFS, ENOMEM and ENFILE as soft errors for accept(). 1244. [bug] Receiving a TCP message from a blackhole address would prevent further messages being received over that interface. 1243. [bug] It was possible to trigger a REQUIRE() in dns_message_findtype(). [RT #2659] 1242. [bug] named-checkzone failed if a journal existed. [RT #2657] 1241. [bug] Drop received UDP messages with a zero source port as these are invariably forged. [RT #2621] 1240. [bug] It was possible to leak zone references by specifying an incorrect zone to rndc. 1239. [bug] Under certain circumstances named could continue to use a name after it had been freed triggering INSIST() failures. [RT #2614] 1238. [bug] It is possible to lockup the server when shutting down if notifies were being processed. [RT #2591] 1237. [bug] nslookup: "set q=type" failed. 1236. [bug] dns_rdata{class,type}_fromtext() didn't handle non NULL terminated text regions. [RT #2588] 1235. [func] Report 'out of memory' errors from openssl. 1234. [bug] contrib/sdb: 'zonetodb' failed to call dns_result_register(). DNS_R_SEENINCLUDE should not be fatal. 1233. [bug] The flags field of a KEY record can be expressed in hex as well as decimal. 1232. [bug] unix/errno2result() didn't handle EADDRNOTAVAIL. 1231. [port] HPUX 11.11 recvmsg() can return spurious EADDRNOTAVAIL. 1230. [bug] isccc_cc_isreply() and isccc_cc_isack() were broken. 1229. [bug] named would crash if it received a TSIG signed query as part of an AXFR response. [RT #2570] 1228. [bug] 'make install' did not depend on 'make all'. [RT #2559] 1227. [bug] dns_lex_getmastertoken() now returns ISC_R_BADNUMBER if a number was expected and some other token was found. [RT#2532] 1226. [func] Use EDNS for zone refresh queries. [RT #2551] 1225. [func] dns_message_setopt() no longer requires that dns_message_renderbegin() to have been called. 1224. [bug] 'rrset-order' and 'sortlist' should be additive not exclusive. 1223. [func] 'rrset-order' partially works 'cyclic' and 'random' are supported. 1222. [bug] Specifying 'port *' did not always result in a system selected (non-reserved) port being used. [RT #2537] 1221. [bug] Zone types 'master', 'slave' and 'stub' were not being compared case insensitively. [RT #2542] 1220. [func] Support for APL rdata type. 1219. [func] Named now reports the TSIG extended error code when signature verification fails. [RT #1651] 1218. [bug] Named incorrectly returned SERVFAIL rather than NOTAUTH when there was a TSIG BADTIME error. [RT #2519] 1217. [func] Report locations of previous key definition when a duplicate is detected. 1216. [bug] Multiple server clauses for the same server were not reported. [RT #2514] 1215. [port] solaris: add support to ifconfig.sh for x86 2.5.1 1214. [bug] Win32: isc_file_renameunique() could leave zero length files behind. 1213. [func] Report view associated with client if it is not a standard view (_default or _bind). 1212. [port] libbind: 64k answer buffers were causing stack space to be exceeded for certain OS. Use heap space instead. 1211. [bug] dns_name_fromtext() incorrectly handled certain valid octal bitlabels. [RT #2483] 1210. [bug] libbind: getnameinfo() failed to lookup IPv4 mapped / compatible addresses. [RT #2461] 1209. [bug] Dig, host, nslookup were not checking the message ids on the responses. [RT #2454] 1208. [bug] dns_master_load*() failed to log a error message if an error was detected when parsing the ownername of a record. [RT #2448] 1207. [bug] libbind: getaddrinfo() could call freeaddrinfo() with an invalid pointer. 1206. [bug] SERVFAIL and NOTIMP responses to an EDNS query should trigger a non-EDNS retry. 1205. [bug] OPT, TSIG and TKEY cannot be used to set the "class" of the message. [RT #2449] 1204. [bug] libbind: res_nupdate() failed to update the name server addresses before sending the update. 1203. [func] Report locations of previous acl and zone definitions when a duplicate is detected. 1202. [func] New functions: cfg_obj_line() and cfg_obj_file(). 1201. [bug] Require that if 'callbacks' is passed to dns_rdata_fromtext(), callbacks->error and callbacks->warn are initialized. 1200. [bug] Log 'errno' that we are unable to convert to isc_result_t. [RT #2404] 1199. [doc] ARM reference to RFC 2157 should have been RFC 1918. [RT #2436] 1198. [bug] OPT printing style was not consistent with the way the header fields are printed. The DO bit was not reported if set. Report if any of the MBZ bits are set. 1197. [bug] Attempts to define the same acl multiple times were not detected. 1196. [contrib] update mdnkit to 2.2.3. 1195. [bug] Attempts to redefine builtin acls should be caught. [RT #2403] 1194. [bug] Not all duplicate zone definitions were being detected at the named.conf checking stage. [RT #2431] 1193. [bug] dig +besteffort parsing didn't handle packet truncation. dns_message_parse() has new flag DNS_MESSAGE_IGNORETRUNCATION. 1192. [bug] The seconds fields in LOC records were restricted to three decimal places. More decimal places should be allowed but warned about. 1191. [bug] A dynamic update removing the last non-apex name in a secure zone would fail. [RT #2399] 1190. [func] Add the "rndc freeze" and "rndc unfreeze" commands. [RT #2394] 1189. [bug] On some systems, malloc(0) returns NULL, which could cause the caller to report an out of memory error. [RT #2398] 1188. [bug] Dynamic updates of a signed zone would fail if some of the zone private keys were unavailable. 1187. [bug] named was incorrectly returning DNSSEC records in negative responses when the DO bit was not set. 1186. [bug] isc_hex_tobuffer(,,length = 0) failed to unget the EOL token when reading to end of line. 1185. [bug] libbind: don't assume statp->_u._ext.ext is valid unless RES_INIT is set when calling res_*init(). 1184. [bug] libbind: call res_ndestroy() if RES_INIT is set when res_*init() is called. 1183. [bug] Handle ENOSR error when writing to the internal control pipe. [RT #2395] 1182. [bug] The server could throw an assertion failure when constructing a negative response packet. 1181. [func] Add the "key-directory" configuration statement, which allows the server to look for online signing keys in alternate directories. 1180. [func] dnssec-keygen should always generate keys with protocol 3 (DNSSEC), since it's less confusing that way. 1179. [func] Add SIG(0) support to nsupdate. 1178. [bug] Follow and cache (if appropriate) A6 and other data chains to completion in the additional section. 1177. [func] Report view when loading zones if it is not a standard view (_default or _bind). [RT #2270] 1176. [doc] Document that allow-v6-synthesis is only performed for clients that are supplied recursive service. [RT #2260] 1175. [bug] named-checkzone and named-checkconf failed to call dns_result_register() at startup which could result in runtime exceptions when printing "out of memory" errors. [RT #2335] 1174. [bug] Win32: add WSAECONNRESET to the expected errors from connect(). [RT #2308] 1173. [bug] Potential memory leaks in isc_log_create() and isc_log_settag(). [RT #2336] 1172. [doc] Add CERT, GPOS, KX, NAPTR, NSAP, PX and TXT to table of RR types in ARM. 1171. [func] Added function isc_region_compare(), updated files in lib/dns to use this function instead of local one. 1170. [bug] Don't attempt to print the token when a I/O error occurs when parsing named.conf. [RT #2275] 1169. [func] Identify recursive queries in the query log. 1168. [bug] Empty also-notify clauses were not handled. [RT #2309] 1167. [contrib] nslint-2.1a3 (from author). 1166. [bug] "Not Implemented" should be reported as NOTIMP, not NOTIMPL. [RT #2281] 1165. [bug] We were rejecting notify-source{-v6} in zone clauses. 1164. [bug] Empty masters clauses in slave / stub zones were not handled gracefully. [RT #2262] 1163. [func] isc_time_formattimestamp() now includes the year. 1162. [bug] The allow-notify option was not accepted in slave zone statements. 1161. [bug] named-checkzone looped on unbalanced brackets. [RT #2248] 1160. [bug] Generating Diffie-Hellman keys longer than 1024 bits could fail. [RT #2241] 1159. [bug] MD and MF are not permitted to be loaded by RFC1123. 1158. [func] Report the client's address when logging notify messages. 1157. [func] match-clients and match-destinations now accept keys. [RT #2045] 1156. [port] The configure test for strsep() incorrectly succeeded on certain patched versions of AIX 4.3.3. [RT #2190] 1155. [func] Recover from master files being removed from under us. 1154. [bug] Don't attempt to obtain the netmask of a interface if there is no address configured. [RT #2176] 1153. [func] 'rndc {stop|halt} -p' now reports the process id of the instance of named being shutdown. 1152. [bug] libbind: read buffer overflows. 1151. [bug] nslookup failed to check that the arguments to the port, timeout, and retry options were valid integers and in range. [RT #2099] 1150. [bug] named incorrectly accepted TTL values containing plus or minus signs, such as 1d+1h-1s. 1149. [func] New function isc_parse_uint32(). 1148. [func] 'rndc-confgen -a' now provides positive feedback. 1147. [func] Set IPV6_V6ONLY on IPv6 sockets if supported by the OS. listen-on-v6 { any; }; should no longer result in IPv4 queries be accepted. Similarly control { inet :: ... }; should no longer result in IPv4 connections being accepted. This can be overridden at compile time by defining ISC_ALLOW_MAPPED=1. 1146. [func] Allow IPV6_IPV6ONLY to be set/cleared on a socket if supported by the OS by a new function isc_socket_ipv6only(). 1145. [func] "host" no longer reports a NOERROR/NODATA response by printing nothing. [RT #2065] 1144. [bug] rndc-confgen would crash if both the -a and -t options were specified. [RT #2159] 1143. [bug] When a trusted-keys statement was present and named was built without crypto support, it would leak memory. 1142. [bug] dnssec-signzone would fail to delete temporary files in some failure cases. [RT #2144] 1141. [bug] When named rejected a control message, it would leak a file descriptor and memory. It would also fail to respond, causing rndc to hang. [RT #2139, #2164] 1140. [bug] rndc-confgen did not accept IPv6 addresses as arguments to the -s option. [RT #2138] 1139. [func] It is now possible to flush a given name from the cache(s) via 'rndc flushname name [view]'. [RT #2051] 1138. [func] It is now possible to flush a given name from the cache by calling the new function dns_cache_flushname(). 1137. [func] It is now possible to flush a given name from the ADB by calling the new function dns_adb_flushname(). 1136. [bug] CNAME records synthesized from DNAMEs did not have a TTL of zero as required by RFC2672. [RT #2129] 1135. [func] You can now override the default syslog() facility for named/lwresd at compile time. [RT #1982] 1134. [bug] Multi-threaded servers could deadlock in ferror() when reloading zone files. [RT #1951, #1998] 1133. [bug] IN6_IS_ADDR_LOOPBACK was not portably defined on platforms without IN6_IS_ADDR_LOOPBACK. [RT #2106] 1132. [func] Improve UPDATE prerequisite failure diagnostic messages. 1131. [bug] The match-destinations view option did not work with IPv6 destinations. [RT #2073, #2074] 1130. [bug] Log messages reporting an out-of-range serial number did not include the out-of-range number but the following token. [RT #2076] 1129. [bug] Multi-threaded servers could crash under heavy resolution load due to a race condition. [RT #2018] 1128. [func] sdb drivers can now provide RR data in either text or wire format, the latter using the new functions dns_sdb_putrdata() and dns_sdb_putnamedrdata(). 1127. [func] rndc: If the server to contact has multiple addresses, try all of them. 1126. [bug] The server could access a freed event if shut down while a client start event was pending delivery. [RT #2061] 1125. [bug] rndc: -k option was missing from usage message. [RT #2057] 1124. [doc] dig: +[no]dnssec, +[no]besteffort and +[no]fail are now documented. [RT #2052] 1123. [bug] dig +[no]fail did not match description. [RT #2052] 1122. [tuning] Resolution timeout reduced from 90 to 30 seconds. [RT #2046] 1121. [bug] The server could attempt to access a NULL zone table if shut down while resolving. [RT #1587, #2054] 1120. [bug] Errors in options were not fatal. [RT #2002] 1119. [func] Added support in Win32 for NTFS file/directory ACL's for access control. 1118. [bug] On multi-threaded servers, a race condition could cause an assertion failure in resolver.c during resolver shutdown. [RT #2029] 1117. [port] The configure check for in6addr_loopback incorrectly succeeded on AIX 4.3 when compiling with -O2 because the test code was optimized away. [RT #2016] 1116. [bug] Setting transfers in a server clause, transfers-in, or transfers-per-ns to a value greater than 2147483647 disabled transfers. [RT #2002] 1115. [func] Set maximum values for cleaning-interval, heartbeat-interval, interface-interval, max-transfer-idle-in, max-transfer-idle-out, max-transfer-time-in, max-transfer-time-out, statistics-interval of 28 days and sig-validity-interval of 3660 days. [RT #2002] 1114. [port] Ignore more accept() errors. [RT #2021] 1113. [bug] The allow-update-forwarding option was ignored when specified in a view. [RT #2014] 1112. [placeholder] 1111. [bug] Multi-threaded servers could deadlock processing recursive queries due to a locking hierarchy violation in adb.c. [RT #2017] 1110. [bug] dig should only accept valid abbreviations of +options. [RT #2003] 1109. [bug] nsupdate accepted illegal ttl values. 1108. [bug] On Win32, rndc was hanging when named was not running due to failure to select for exceptional conditions in select(). [RT #1870] 1107. [bug] nsupdate could catch an assertion failure if an invalid domain name was given as the argument to the "zone" command. 1106. [bug] After seeing an out of range TTL, nsupdate would treat all TTLs as out of range. [RT #2001] 1105. [port] OpenUNIX 8 enable threads by default. [RT #1970] 1104. [bug] Invalid arguments to the transfer-format option could cause an assertion failure. [RT #1995] 1103. [port] OpenUNIX 8 support (ifconfig.sh). [RT #1970] 1102. [doc] Note that query logging is enabled by directing the queries category to a channel. 1101. [bug] Array bounds read error in lwres_gai_strerror. 1100. [bug] libbind: DNSSEC key ids were computed incorrectly. 1099. [cleanup] libbind: defining REPORT_ERRORS in lib/bind/dst caused compile time errors. 1098. [bug] libbind: HMAC-MD5 key files are now mode 0600. 1097. [func] libbind: RES_PRF_TRUNC for dig. 1096. [func] libbind: "DNSSEC OK" (DO) support. 1095. [func] libbind: resolver option: no-tld-query. disables trying unqualified as a tld. no_tld_query is also supported for FreeBSD compatibility. 1094. [func] libbind: add support gcc's format string checking. 1093. [doc] libbind: miscellaneous nroff fixes. 1092. [bug] libbind: get*by*() failed to check if res_init() had been called. 1091. [bug] libbind: misplaced va_end(). 1090. [bug] libbind: dns_ho.c:add_hostent() was not returning the amount of memory consumed resulting in garbage address being returned. Alignment calculations were wasting space. We weren't suppressing duplicate addresses. 1089. [func] libbind: inet_{cidr,net}_{pton,ntop}() now have IPv6 support. 1088. [port] libbind: MPE/iX C.70 (incomplete) 1087. [bug] libbind: struct __res_state too large on 64 bit arch. 1086. [port] libbind: sunos: old sprintf. 1085. [port] libbind: solaris: sys_nerr and sys_errlist do not exist when compiling in 64 bit mode. 1084. [cleanup] libbind: gai_strerror() rewritten. 1083. [bug] The default control channel listened on the wildcard address, not the loopback as documented. [RT #1975] 1082. [bug] The -g option to named incorrectly caused logging to be sent to syslog in addition to stderr. [RT #1974] 1081. [bug] Multicast queries were incorrectly identified based on the source address, not the destination address. 1080. [bug] BIND 8 compatibility: accept bare IP prefixes as the second element of a two-element top level sort list statement. [RT #1964] 1079. [bug] BIND 8 compatibility: accept bare elements at top level of sort list treating them as if they were a single element list. [RT #1963] 1078. [bug] We failed to correct bad tv_usec values in one case. [RT #1966] 1077. [func] Do not accept further recursive clients when the total number of recursive lookups being processed exceeds max-recursive-clients, even if some of the lookups are internally generated. [RT #1915, #1938] 1076. [bug] A badly defined global key could trigger an assertion on load/reload if views were used. [RT #1947] 1075. [bug] Out-of-range network prefix lengths were not reported. [RT #1954] 1074. [bug] Running out of memory in dump_rdataset() could cause an assertion failure. [RT #1946] 1073. [bug] The ADB cache cleaning should also be space driven. [RT #1915, #1938] 1072. [bug] The TCP client quota could be exceeded when recursion occurred. [RT #1937] 1071. [bug] Sockets listening for TCP DNS connections specified an excessive listen backlog. [RT #1937] 1070. [bug] Copy DNSSEC OK (DO) to response as specified by draft-ietf-dnsext-dnssec-okbit-03.txt. 1069. [placeholder] 1068. [bug] errno could be overwritten by catgets(). [RT #1921] 1067. [func] Allow quotas to be soft, isc_quota_soft(). 1066. [bug] Provide a thread safe wrapper for strerror(). [RT #1689] 1065. [func] Runtime support to select new / old style interface scanning using ioctls. 1064. [bug] Do not shut down active network interfaces if we are unable to scan the interface list. [RT #1921] 1063. [bug] libbind: "make install" was failing on IRIX. [RT #1919] 1062. [bug] If the control channel listener socket was shut down before server exit, the listener object could be freed twice. [RT #1916] 1061. [bug] If periodic cache cleaning happened to start while cleaning due to reaching the configured maximum cache size was in progress, the server could catch an assertion failure. [RT #1912] 1060. [func] Move refresh, stub and notify UDP retry processing into dns_request. 1059. [func] dns_request now support will now retry UDP queries, dns_request_createvia2() and dns_request_createraw2(). 1058. [func] Limited lifetime ticker timers are now available, isc_timertype_limited. 1057. [bug] Reloading the server after adding a "file" clause to a zone statement could cause the server to crash due to a typo in change 1016. 1056. [bug] Rndc could catch an assertion failure on SIGINT due to an uninitialized variable. [RT #1908] 1055. [func] Version and hostname queries can now be disabled using "version none;" and "hostname none;", respectively. 1054. [bug] On Win32, cfg_categories and cfg_modules need to be exported from the libisccfg DLL. 1053. [bug] Dig did not increase its timeout when receiving AXFRs unless the +time option was used. [RT #1904] 1052. [bug] Journals were not being created in binary mode resulting in "journal format not recognized" error under Win32. [RT #1889] 1051. [bug] Do not ignore a network interface completely just because it has a noncontiguous netmask. Instead, omit it from the localnets ACL and issue a warning. [RT #1891] 1050. [bug] Log messages reporting malformed IP addresses in address lists such as that of the forwarders option failed to include the correct error code, file name, and line number. [RT #1890] 1049. [func] "pid-file none;" will disable writing a pid file. [RT #1848] 1048. [bug] Servers built with -DISC_MEM_USE_INTERNAL_MALLOC=1 didn't work. 1047. [bug] named was incorrectly refusing all requests signed with a TSIG key derived from an unsigned TKEY negotiation with a NOERROR response. [RT #1886] 1046. [bug] The help message for the --with-openssl configure option was inaccurate. [RT #1880] 1045. [bug] It was possible to skip saving glue for a nameserver for a stub zone. 1044. [bug] Specifying allow-transfer, notify-source, or notify-source-v6 in a stub zone was not treated as an error. 1043. [bug] Specifying a transfer-source or transfer-source-v6 option in the zone statement for a master zone was not treated as an error. [RT #1876] 1042. [bug] The "config" logging category did not work properly. [RT #1873] 1041. [bug] Dig/host/nslookup could catch an assertion failure on SIGINT due to an uninitialized variable. [RT #1867] 1040. [bug] Multiple listen-on-v6 options with different ports were not accepted. [RT #1875] 1039. [bug] Negative responses with CNAMEs in the answer section were cached incorrectly. [RT #1862] 1038. [bug] In servers configured with a tkey-domain option, TKEY queries with an owner name other than the root could cause an assertion failure. [RT #1866, #1869] 1037. [bug] Negative responses whose authority section contain SOA or NS records whose owner names are not equal equal to or parents of the query name should be rejected. [RT #1862] 1036. [func] Silently drop requests received via multicast as long as there is no final multicast DNS standard. 1035. [bug] If we respond to multicast queries (which we currently do not), respond from a unicast address as specified in RFC 1123. [RT #137] 1034. [bug] Ignore the RD bit on multicast queries as specified in RFC 1123. [RT #137] 1033. [bug] Always respond to requests with an unsupported opcode with NOTIMP, even if we don't have a matching view or cannot determine the class. 1032. [func] hostname.bind/txt/chaos now returns the name of the machine hosting the nameserver. This is useful in diagnosing problems with anycast servers. 1031. [bug] libbind.a: isc__gettimeofday() infinite recursion. [RT #1858] 1030. [bug] On systems with no resolv.conf file, nsupdate exited with an error rather than defaulting to using the loopback address. [RT #1836] 1029. [bug] Some named.conf errors did not cause the loading of the configuration file to return a failure status even though they were logged. [RT #1847] 1028. [bug] On Win32, dig/host/nslookup looked for resolv.conf in the wrong directory. [RT #1833] 1027. [bug] RRs having the reserved type 0 should be rejected. [RT #1471] 1026. [placeholder] 1025. [bug] Don't use multicast addresses to resolve iterative queries. [RT #101] 1024. [port] Compilation failed on HP-UX 11.11 due to incompatible use of the SIOCGLIFCONF macro name. [RT #1831] 1023. [func] Accept hints without TTLs. 1022. [bug] Don't report empty root hints as "extra data". [RT #1802] 1021. [bug] On Win32, log message timestamps were one month later than they should have been, and the server would exhibit unspecified behavior in December. 1020. [bug] IXFR log messages did not distinguish between true IXFRs, AXFR-style IXFRs, and mere version polls. [RT #1811] 1019. [bug] The value of the lame-ttl option was limited to 18000 seconds, not 1800 seconds as documented. [RT #1803] 1018. [bug] The default log channel was not always initialized correctly. [RT #1813] 1017. [bug] When specifying TSIG keys to dig and nsupdate using the -k option, they must be HMAC-MD5 keys. [RT #1810] 1016. [bug] Slave zones with no backup file were re-transferred on every server reload. 1015. [bug] Log channels that had a "versions" option but no "size" option failed to create numbered log files. [RT #1783] 1014. [bug] Some queries would cause statistics counters to increment more than once or not at all. [RT #1321] 1013. [bug] It was possible to cancel a query twice when marking a server as bogus or by having a blackhole acl. [RT #1776] 1012. [bug] The -p option to named did not behave as documented. 1011. [cleanup] Removed isc_dir_current(). 1010. [bug] The server could attempt to execute a command channel command after initiating server shutdown, causing an assertion failure. [RT #1766] 1009. [port] OpenUNIX 8 support. [RT #1728] 1008. [port] libtool.m4, ltmain.sh from libtool-1.4.2. 1007. [port] config.guess, config.sub from autoconf-2.52. 1006. [bug] If a KEY RR was found missing during DNSSEC validation, an assertion failure could subsequently be triggered in the resolver. [RT #1763] 1005. [bug] Don't copy nonzero RCODEs from request to response. [RT #1765] 1004. [port] Deal with recvfrom() returning EHOSTDOWN. [RT #1770] 1003. [func] Add the +retry option to dig. 1002. [bug] When reporting an unknown class name in named.conf, including the file name and line number. [RT #1759] 1001. [bug] win32 socket code doio_recv was not catching a WSACONNRESET error when a client was timing out the request and closing its socket. [RT #1745] 1000. [bug] BIND 8 compatibility: accept "HESIOD" as an alias for class "HS". [RT #1759] 999. [func] "rndc retransfer zone [class [view]]" added. [RT #1752] 998. [func] named-checkzone now has arguments to specify the chroot directory (-t) and working directory (-w). [RT #1755] 997. [func] Add support for RSA-SHA1 keys (RFC3110). 996. [func] Issue warning if the configuration filename contains the chroot path. 995. [bug] dig, host, nslookup: using a raw IPv6 address as a target address should be fatal on a IPv4 only system. 994. [func] Treat non-authoritative responses to queries for type NS as referrals even if the NS records are in the answer section, because BIND 8 servers incorrectly send them that way. This is necessary for DNSSEC validation of the NS records of a secure zone to succeed when the parent is a BIND 8 server. [RT #1706] 993. [func] dig: -v now reports the version. 992. [doc] dig: ~/.digrc is now documented. 991. [func] Lower UDP refresh timeout messages to level debug 1. 990. [bug] The rndc-confgen man page was not installed. 989. [bug] Report filename if $INCLUDE fails for file related errors. [RT #1736] 988. [bug] 'additional-from-auth no;' did not work reliably in the case of queries answered from the cache. [RT #1436] 987. [bug] "dig -help" didn't show "+[no]stats". 986. [bug] "dig +noall" failed to clear stats and command printing. 985. [func] Consider network interfaces to be up iff they have a nonzero IP address rather than based on the IFF_UP flag. [RT #1160] 984. [bug] Multi-threading should be enabled by default on Solaris 2.7 and newer, but it wasn't. 983. [func] The server now supports generating IXFR difference sequences for non-dynamic zones by comparing zone versions, when enabled using the new config option "ixfr-from-differences". [RT #1727] 982. [func] If "memstatistics-file" is set in options the memory statistics will be written to it. 981. [func] The dnssec tools can now take multiple '-r randomfile' arguments. 980. [bug] Incoming zone transfers restarting after an error could trigger an assertion failure. [RT #1692] 979. [func] Incremental master file dumping. dns_master_dumpinc(), dns_master_dumptostreaminc(), dns_dumpctx_attach(), dns_dumpctx_detach(), dns_dumpctx_cancel(), dns_dumpctx_db() and dns_dumpctx_version(). 978. [bug] dns_db_attachversion() had an invalid REQUIRE() condition. 977. [bug] Improve "not at top of zone" error message. 976. [func] named-checkconf can now test load master zones (named-checkconf -z). [RT #1468] 975. [bug] "max-cache-size default;" as a view option caused an assertion failure. 974. [bug] "max-cache-size unlimited;" as a global option was not accepted. 973. [bug] Failed to log the question name when logging: "bad zone transfer request: non-authoritative zone (NOTAUTH)". 972. [bug] The file modification time code in zone.c was using the wrong epoch. [RT #1667] 971. [placeholder] 970. [func] 'max-journal-size' can now be used to set a target size for a journal. 969. [func] dig now supports the undocumented dig 8 feature of allowing arbitrary labels, not just dotted decimal quads, with the -x option. This can be used to conveniently look up RFC2317 names as in "dig -x 10.0.0.0-127". [RT #827, #1576, #1598] 968. [bug] On win32, the isc_time_now() function was unnecessarily calling strtime(). [RT #1671] 967. [bug] On win32, the link for bindevt was not including the required resource file to enable the event viewer to interpret the error messages in the event log, [RT #1668] 966. [placeholder] 965. [bug] Including data other than root server NS and A records in the root hint file could cause a rbtdb node reference leak. [RT #1581, #1618] 964. [func] Warn if data other than root server NS and A records are found in the root hint file. [RT #1581, #1618] 963. [bug] Bad ISC_LANG_ENDDECLS. [RT #1645] 962. [bug] libbind: bad "#undef", don't attempt to install non-existent nlist.h. [RT #1640] 961. [bug] Tried to use a IPV6 feature when ISC_PLATFORM_HAVEIPV6 was not defined. [RT #1482] 960. [port] liblwres failed to build on systems with support for getrrsetbyname() in the OS. [RT #1592] 959. [port] On FreeBSD, determine the number of CPUs by calling sysctlbyname(). [RT #1584] 958. [port] ssize_t is not available on all platforms. [RT #1607] 957. [bug] sys/select.h inclusion was broken on older platforms. [RT #1607] 956. [bug] ns_g_autorndcfile changed to ns_g_keyfile in named/win32/os.c due to code changes in change #953. win32 .make file for rndc-confgen updated to add include path for os.h header. --- 9.2.0rc1 released --- 955. [bug] When using views, the zone's class was not being inherited from the view's class. [RT #1583] 954. [bug] When requesting AXFRs or IXFRs using dig, host, or nslookup, the RD bit should not be set as zone transfers are inherently non-recursive. [RT #1575] 953. [func] The /var/run/named.key file from change #843 has been replaced by /etc/rndc.key. Both named and rndc will look for this file and use it to configure a default control channel key if not already configured using a different method (rndc.conf / controls). Unlike named.key, rndc.key is not created automatically; it must be created by manually running "rndc-confgen -a". 952. [bug] The server required manual intervention to serve the affected zones if it died between creating a journal and committing the first change to it. 951. [bug] CFLAGS was not passed to the linker when linking some of the test programs under bin/tests. [RT #1555]. 950. [bug] Explicit TTLs did not properly override $TTL due to a bug in change 834. [RT #1558] 949. [bug] host was unable to print records larger than 512 bytes. [RT #1557] --- 9.2.0b2 released --- 948. [port] Integrated support for building on Windows NT / Windows 2000. 947. [bug] dns_rdata_soa_t had a badly named element "mname" which was really the RNAME field from RFC1035. To avoid confusion and silent errors that would occur it the "origin" and "mname" elements were given their correct names "mname" and "rname" respectively, the "mname" element is renamed to "contact". 946. [cleanup] doc/misc/options is now machine-generated from the configuration parser syntax tables, and therefore more likely to be correct. 945. [func] Add the new view-specific options "match-destinations" and "match-recursive-only". 944. [func] Check for expired signatures on load. 943. [bug] The server could crash when receiving a command via rndc if the configuration file listed only nonexistent keys in the controls statement. [RT #1530] 942. [port] libbind: GETNETBYADDR_ADDR_T was not correctly defined on some platforms. 941. [bug] The configuration checker crashed if a slave zone didn't contain a masters statement. [RT #1514] 940. [bug] Double zone locking failure on error path. [RT #1510] --- 9.2.0b1 released --- 939. [port] Add the --disable-linux-caps option to configure for systems that manage capabilities outside of named. [RT #1503] 938. [placeholder] 937. [bug] A race when shutting down a zone could trigger a INSIST() failure. [RT #1034] 936. [func] Warn about IPv4 addresses that are not complete dotted quads. [RT #1084] 935. [bug] inet_pton failed to reject leading zeros. 934. [port] Deal with systems where accept() spuriously returns ECONNRESET. 933. [bug] configure failed doing libbind on platforms not supported by BIND 8. [RT #1496] --- 9.2.0a3 released --- 932. [bug] Use INSTALL_SCRIPT, not INSTALL_PROGRAM, when installing isc-config.sh. [RT #198, #1466] 931. [bug] The controls statement only attempted to verify messages using the first key in the key list. (9.2.0a1/a2 only). 930. [func] Query performance testing tool added as contrib/queryperf. 929. [placeholder] 928. [bug] nsupdate would send empty update packets if the send (or empty line) command was run after another send but before any new updates or prerequisites were specified. It should simply ignore this command. 927. [bug] Don't hold the zone lock for the entire dump to disk. [RT #1423] 926. [bug] The resolver could deadlock with the ADB when shutting down (multi-threaded builds only). [RT #1324] 925. [cleanup] Remove openssl from the distribution; require that --with-openssl be specified if DNSSEC is needed. 924. [port] Extend support for pre-RFC2133 IPv6 implementation. [RT #987] 923. [bug] Multiline TSIG secrets (and other multiline strings) were not accepted in named.conf. [RT #1469] 922. [func] Added two new lwres_getrrsetbyname() result codes, ERR_NONAME and ERR_NODATA. 921. [bug] lwres returned an incorrect error code if it received a truncated message. 920. [func] Increase the lwres receive buffer size to 16K. [RT #1451] 919. [placeholder] 918. [func] In nsupdate, TSIG errors are no longer treated as fatal errors. 917. [func] New nsupdate command 'key', allowing TSIG keys to be specified in the nsupdate command stream rather than the command line. 916. [bug] Specifying type ixfr to dig without specifying a serial number failed in unexpected ways. 915. [func] The named-checkconf and named-checkzone programs now have a '-v' option for printing their version. [RT #1151] 914. [bug] Global 'server' statements were rejected when using views, even though they were accepted in 9.1. [RT #1368] 913. [bug] Cache cleaning was not sufficiently aggressive. [RT #1441, #1444] 912. [bug] Attempts to set the 'additional-from-cache' or 'additional-from-auth' option to 'no' in a server with recursion enabled will now be ignored and cause a warning message. [RT #1145] 911. [placeholder] 910. [port] Some pre-RFC2133 IPv6 implementations do not define IN6ADDR_ANY_INIT. [RT #1416] 909. [placeholder] 908. [func] New program, rndc-confgen, to simplify setting up rndc. 907. [func] The ability to get entropy from either the random device, a user-provided file or from the keyboard was migrated from the DNSSEC tools to libisc as isc_entropy_usebestsource(). 906. [port] Separated the system independent portion of lib/isc/unix/entropy.c into lib/isc/entropy.c and added lib/isc/win32/entropy.c. 905. [bug] Configuring a forward "zone" for the root domain did not work. [RT #1418] 904. [bug] The server would leak memory if attempting to use an expired TSIG key. [RT #1406] 903. [bug] dig should not crash when receiving a TCP packet of length 0. 902. [bug] The -d option was ignored if both -t and -g were also specified. 901. [placeholder] 900. [bug] A config.guess update changed the system identification string of FreeBSD systems; configure and bin/tests/system/ifconfig.sh now recognize the new string. --- 9.2.0a2 released --- 899. [bug] lib/dns/soa.c failed to compile on many platforms due to inappropriate use of a void value. [RT #1372, #1373, #1386, #1387, #1395] 898. [bug] "dig" failed to set a nonzero exit status on UDP query timeout. [RT #1323] 897. [bug] A config.guess update changed the system identification string of UnixWare systems; configure now recognizes the new string. 896. [bug] If a configuration file is set on named's command line and it has a relative pathname, the current directory (after any possible jailing resulting from named -t) will be prepended to it so that reloading works properly even when a directory option is present. 895. [func] New function, isc_dir_current(), akin to POSIX's getcwd(). 894. [bug] When using the DNSSEC tools, a message intended to warn when the keyboard was being used because of the lack of a suitable random device was not being printed. 893. [func] Removed isc_file_test() and added isc_file_exists() for the basic functionality that was being added with isc_file_test(). 892. [placeholder] 891. [bug] Return an error when a SIG(0) signed response to an unsigned query is seen. This should actually do the verification, but it's not currently possible. [RT #1391] 890. [cleanup] The man pages no longer require the mandoc macros and should now format cleanly using most versions of nroff, and HTML versions of the man pages have been added. Both are generated from DocBook source. 889. [port] Eliminated blank lines before .TH in nroff man pages since they cause problems with some versions of nroff. [RT #1390] 888. [bug] Don't die when using TKEY to delete a nonexistent TSIG key. [RT #1392] 887. [port] Detect broken compilers that can't call static functions from inline functions. [RT #1212] 886. [placeholder] 885. [placeholder] 884. [placeholder] 883. [placeholder] 882. [placeholder] 881. [placeholder] 880. [placeholder] 879. [placeholder] 878. [placeholder] 877. [placeholder] 876. [placeholder] 875. [placeholder] 874. [placeholder] 873. [placeholder] 872. [placeholder] 871. [placeholder] 870. [placeholder] 869. [placeholder] 868. [placeholder] 867. [placeholder] 866. [func] Close debug only file channels when debug is set to zero. [RT #1246] 865. [bug] The new configuration parser did not allow the optional debug level in a "severity debug" clause of a logging channel to be omitted. This is now allowed and treated as "severity debug 1;" like it does in BIND 8.2.4, not as "severity debug 0;" like it did in BIND 9.1. [RT #1367] 864. [cleanup] Multi-threading is now enabled by default on OSF1, Solaris 2.7 and newer, AIX, IRIX, and HP-UX. 863. [bug] If an error occurred while an outgoing zone transfer was starting up, the server could access a domain name that had already been freed when logging a message saying that the transfer was starting. [RT #1383] 862. [bug] Use after realloc(), non portable pointer arithmetic in grmerge(). 861. [port] Add support for Mac OS X, by making it equivalent to Darwin. This was derived from the config.guess file shipped with Mac OS X. [RT #1355] 860. [func] Drop cross class glue in zone transfers. 859. [bug] Cache cleaning now won't swamp the CPU if there is a persistent over limit condition. 858. [func] isc_mem_setwater() no longer requires that when the callback function is non-NULL then its hi_water argument must be greater than its lo_water argument (they can now be equal) or that they be non-zero. 857. [cleanup] Use ISC_MAGIC() to define all magic numbers for structs, for our friends in EBCDIC-land. 856. [func] Allow partial rdatasets to be returned in answer and authority sections to help non-TCP capable clients recover from truncation. [RT #1301] 855. [bug] Stop spurious "using RFC 1035 TTL semantics" warnings. 854. [bug] The config parser didn't properly handle config options that were specified in units of time other than seconds. [RT #1372] 853. [bug] configure_view_acl() failed to detach existing acls. [RT #1374] 852. [bug] Handle responses from servers which do not know about IXFR. 851. [cleanup] The obsolete support-ixfr option was not properly ignored. --- 9.2.0a1 released --- 850. [bug] dns_rbt_findnode() would not find nodes that were split on a bitstring label somewhere other than in the last label of the node. [RT #1351] 849. [func] will ensure INADDR_LOOPBACK is defined. 848. [func] A minimum max-cache-size of two megabytes is enforced by the cache cleaner. 847. [func] Added isc_file_test(), which currently only has some very basic functionality to test for the existence of a file, whether a pathname is absolute, or whether a pathname is the fundamental representation of the current directory. It is intended that this function can be expanded to test other things a programmer might want to know about a file. 846. [func] A non-zero 'param' to dst_key_generate() when making an hmac-md5 key means that good entropy is not required. 845. [bug] The access rights on the public file of a symmetric key are now restricted as soon as the file is opened, rather than after it has been written and closed. 844. [func] will ensure INADDR_LOOPBACK is defined, just as does. 843. [func] If no controls statement is present in named.conf, or if any inet phrase of a controls statement is lacking a keys clause, then a key will be automatically generated by named and an rndc.conf-style file named named.key will be written that uses it. rndc will use this file only if its normal configuration file, or one provided on the command line, does not exist. 842. [func] 'rndc flush' now takes an optional view. 841. [bug] When sdb modules were not declared threadsafe, their create and destroy functions were not serialized. 840. [bug] The config file parser could print the wrong file name if an error was detected after an included file was parsed. [RT #1353] 839. [func] Dump packets for which there was no view or that the class could not be determined to category "unmatched". 838. [port] UnixWare 7.x.x is now suported by bin/tests/system/ifconfig.sh. 837. [cleanup] Multi-threading is now enabled by default only on OSF1, Solaris 2.7 and newer, and AIX. 836. [func] Upgraded libtool to 1.4. 835. [bug] The dispatcher could enter a busy loop if it got an I/O error receiving on a UDP socket. [RT #1293] 834. [func] Accept (but warn about) master files beginning with an SOA record without an explicit TTL field and lacking a $TTL directive, by using the SOA MINTTL as a default TTL. This is for backwards compatibility with old versions of BIND 8, which accepted such files without warning although they are illegal according to RFC1035. 833. [cleanup] Moved dns_soa_*() from to , and extended them to support all the integer-valued fields of the SOA RR. 832. [bug] The default location for named.conf in named-checkconf should depend on --sysconfdir like it does in named. [RT #1258] 831. [placeholder] 830. [func] Implement 'rndc status'. 829. [bug] The DNS_R_ZONECUT result code should only be returned when an ANY query is made with DNS_DBFIND_GLUEOK set. In all other ANY query cases, returning the delegation is better. 828. [bug] The errno value from recvfrom() could be overwritten by logging code. [RT #1293] 827. [bug] When an IXFR protocol error occurs, the slave should retry with AXFR. 826. [bug] Some IXFR protocol errors were not detected. 825. [bug] zone.c:ns_query() detached from the wrong zone reference. [RT #1264] 824. [bug] Correct line numbers reported by dns_master_load(). [RT #1263] 823. [func] The output of "dig -h" now goes to stdout so that it can easily be piped through "more". [RT #1254] 822. [bug] Sending nxrrset prerequisites would crash nsupdate. [RT #1248] 821. [bug] The program name used when logging to syslog should be stripped of leading path components. [RT #1178, #1232] 820. [bug] Name server address lookups failed to follow A6 chains into the glue of local authoritative zones. 819. [bug] In certain cases, the resolver's attempts to restart an address lookup at the root could cause the fetch to deadlock (with itself) instead of restarting. [RT #1225] 818. [bug] Certain pathological responses to ANY queries could cause an assertion failure. [RT #1218] 817. [func] Adjust timeouts for dialup zone queries. 816. [bug] Report potential problems with log file accessibility at configuration time, since such problems can't reliably be reported at the time they actually occur. 815. [bug] If a log file was specified with a path separator character (i.e. "/") in its name and the directory did not exist, the log file's name was treated as though it were the directory name. [RT #1189] 814. [bug] Socket objects left over from accept() failures were incorrectly destroyed, causing corruption of socket manager data structures. 813. [bug] File descriptors exceeding FD_SETSIZE were handled badly. [RT #1192] 812. [bug] dig sometimes printed incomplete IXFR responses due to an uninitialized variable. [RT #1188] 811. [bug] Parentheses were not quoted in zone dumps. [RT #1194] 810. [bug] The signer name in SIG records was not properly down-cased when signing/verifying records. [RT #1186] 809. [bug] Configuring a non-local address as a transfer-source could cause an assertion failure during load. 808. [func] Add 'rndc flush' to flush the server's cache. 807. [bug] When setting up TCP connections for incoming zone transfers, the transfer-source port was not ignored like it should be. 806. [bug] DNS_R_SEENINCLUDE was failing to propagate back up the calling stack to the zone maintenance level, causing zones to not reload when an included file was touched but the top-level zone file was not. 805. [bug] When using "forward only", missing root hints should not cause queries to fail. [RT #1143] 804. [bug] Attempting to obtain entropy could fail in some situations. This would be most common on systems with user-space threads. [RT #1131] 803. [bug] Treat all SIG queries as if they have the CD bit set, otherwise no data will be returned [RT #749] 802. [bug] DNSSEC key tags were computed incorrectly in almost all cases. [RT #1146] 801. [bug] nsupdate should treat lines beginning with ';' as comments. [RT #1139] 800. [bug] dnssec-signzone produced incorrect statistics for large zones. [RT #1133] 799. [bug] The ADB didn't find AAAA glue in a zone unless A6 glue was also present. 798. [bug] nsupdate should be able to reject bad input lines and continue. [RT #1130] 797. [func] Issue a warning if the 'directory' option contains a relative path. [RT #269] 796. [func] When a size limit is associated with a log file, only roll it when the size is reached, not every time the log file is opened. [RT #1096] 795. [func] Add the +multiline option to dig. [RT #1095] 794. [func] Implement the "port" and "default-port" statements in rndc.conf. 793. [cleanup] The DNSSEC tools could create filenames that were illegal or contained shell meta-characters. They now use a different text encoding of names that doesn't have these problems. [RT #1101] 792. [cleanup] Replace the OMAPI command channel protocol with a simpler one. 791. [bug] The command channel now works over IPv6. 790. [bug] Wildcards created using dynamic update or IXFR could fail to match. [RT #1111] 789. [bug] The "localhost" and "localnets" ACLs did not match when used as the second element of a two-element sortlist item. 788. [func] Add the "match-mapped-addresses" option, which causes IPv6 v4mapped addresses to be treated as IPv4 addresses for the purpose of acl matching. 787. [bug] The DNSSEC tools failed to downcase domain names when mapping them into file names. 786. [bug] When DNSSEC signing/verifying data, owner names were not properly down-cased. 785. [bug] A race condition in the resolver could cause an assertion failure. [RT #673, #872, #1048] 784. [bug] nsupdate and other programs would not quit properly if some signals were blocked by the caller. [RT #1081] 783. [bug] Following CNAMEs could cause an assertion failure when either using an sdb database or under very rare conditions. 782. [func] Implement the "serial-query-rate" option. 781. [func] Avoid error packet loops by dropping duplicate FORMERR responses. [RT #1006] 780. [bug] Error handling code dealing with out of memory or other rare errors could lead to assertion failures by calling functions on uninitialized names. [RT #1065] 779. [func] Added the "minimal-responses" option. 778. [bug] When starting cache cleaning, cleaning_timer_action() returned without first pausing the iterator, which could cause deadlock. [RT #998] 777. [bug] An empty forwarders list in a zone failed to override global forwarders. [RT #995] 776. [func] Improved error reporting in denied messages. [RT #252] 775. [placeholder] 774. [func] max-cache-size is implemented. 773. [func] Added isc_rwlock_trylock() to attempt to lock without blocking. 772. [bug] Owner names could be incorrectly omitted from cache dumps in the presence of negative caching entries. [RT #991] 771. [cleanup] TSIG errors related to unsynchronized clocks are logged better. [RT #919] 770. [func] Add the "edns yes_or_no" statement to the server clause. [RT #524] 769. [func] Improved error reporting when parsing rdata. [RT #740] 768. [bug] The server did not emit an SOA when a CNAME or DNAME chain ended in NXDOMAIN in an authoritative zone. 767. [placeholder] 766. [bug] A few cases in query_find() could leak fname. This would trigger the mpctx->allocated == 0 assertion when the server exited. [RT #739, #776, #798, #812, #818, #821, #845, #892, #935, #966] 765. [func] ACL names are once again case insensitive, like in BIND 8. [RT #252] 764. [func] Configuration files now allow "include" directives in more places, such as inside the "view" statement. [RT #377, #728, #860] 763. [func] Configuration files no longer have reserved words. [RT #731, #753] 762. [cleanup] The named.conf and rndc.conf file parsers have been completely rewritten. 761. [bug] _REENTRANT was still defined when building with --disable-threads. 760. [contrib] Significant enhancements to the pgsql sdb driver. 759. [bug] The resolver didn't turn off "avoid fetches" mode when restarting, possibly causing resolution to fail when it should not. This bug only affected platforms which support both IPv4 and IPv6. [RT #927] 758. [bug] The "avoid fetches" code did not treat negative cache entries correctly, causing fetches that would be useful to be avoided. This bug only affected platforms which support both IPv4 and IPv6. [RT #927] 757. [func] Log zone transfers. 756. [bug] dns_zone_load() could "return" success when no master file was configured. 755. [bug] Fix incorrectly formatted log messages in zone.c. 754. [bug] Certain failure conditions sending UDP packets could cause the server to retry the transmission indefinitely. [RT #902] 753. [bug] dig, host, and nslookup would fail to contact a remote server if getaddrinfo() returned an IPv6 address on a system that doesn't support IPv6. [RT #917] 752. [func] Correct bad tv_usec elements returned by gettimeofday(). 751. [func] Log successful zone loads / transfers. [RT #898] 750. [bug] A query should not match a DNAME whose trust level is pending. [RT #916] 749. [bug] When a query matched a DNAME in a secure zone, the server did not return the signature of the DNAME. [RT #915] 748. [doc] List supported RFCs in doc/misc/rfc-compliance. [RT #781] 747. [bug] The code to determine whether an IXFR was possible did not properly check for a database that could not have a journal. [RT #865, #908] 746. [bug] The sdb didn't clone rdatasets properly, causing a crash when the server followed delegations. [RT #905] 745. [func] Report the owner name of records that fail semantic checks while loading. 744. [bug] When returning DNS_R_CNAME or DNS_R_DNAME as the result of an ANY or SIG query, the resolver failed to setup the return event's rdatasets, causing an assertion failure in the query code. [RT #881] 743. [bug] Receiving a large number of certain malformed answers could cause named to stop responding. [RT #861] 742. [placeholder] 741. [port] Support openssl-engine. [RT #709] 740. [port] Handle openssl library mismatches slightly better. 739. [port] Look for /dev/random in configure, rather than assuming it will be there for only a predefined set of OSes. 738. [bug] If a non-threadsafe sdb driver supported AXFR and received an AXFR request, it would deadlock or die with an assertion failure. [RT #852] 737. [port] stdtime.c failed to compile on certain platforms. 736. [func] New functions isc_task_{begin,end}exclusive(). 735. [doc] Add BIND 4 migration notes. 734. [bug] An attempt to re-lock the zone lock could occur if the server was shutdown during a zone transfer. [RT #830] 733. [bug] Reference counts of dns_acl_t objects need to be locked but were not. [RT #801, #821] 732. [bug] Glue with 0 TTL could also cause SERVFAIL. [RT #828] 731. [bug] Certain zone errors could cause named-checkzone to fail ungracefully. [RT #819] 730. [bug] lwres_getaddrinfo() returns the correct result when it fails to contact a server. [RT #768] 729. [port] pthread_setconcurrency() needs to be called on Solaris. 728. [bug] Fix comment processing on master file directives. [RT# 757] 727. [port] Work around OS bug where accept() succeeds but fails to fill in the peer address of the accepted connection, by treating it as an error rather than an assertion failure. [RT #809] 726. [func] Implement the "trace" and "notrace" commands in rndc. 725. [bug] Installing man pages could fail. 724. [func] New libisc functions isc_netaddr_any(), isc_netaddr_any6(). 723. [bug] Referrals whose NS RRs had a 0 TTL caused the resolver to return DNS_R_SERVFAIL. [RT #783] 722. [func] Allow incremental loads to be canceled. 721. [cleanup] Load manager and dns_master_loadfilequota() are no more. 720. [bug] Server could enter infinite loop in dispatch.c:do_cancel(). [RT #733] 719. [bug] Rapid reloads could trigger an assertion failure. [RT #743, #763] 718. [cleanup] "internal" is no longer a reserved word in named.conf. [RT #753, #731] 717. [bug] Certain TKEY processing failure modes could reference an uninitialized variable, causing the server to crash. [RT #750] 716. [bug] The first line of a $INCLUDE master file was lost if an origin was specified. [RT #744] 715. [bug] Resolving some A6 chains could cause an assertion failure in adb.c. [RT #738] 714. [bug] Preserve interval timers across reloads unless changed. [RT# 729] 713. [func] named-checkconf takes '-t directory' similar to named. [RT #726] 712. [bug] Sending a large signed update message caused an assertion failure. [RT #718] 711. [bug] The libisc and liblwres implementations of inet_ntop contained an off by one error. 710. [func] The forwarders statement now takes an optional port. [RT #418] 709. [bug] ANY or SIG queries for data with a TTL of 0 would return SERVFAIL. [RT #620] 708. [bug] When building with --with-openssl, the openssl headers included with BIND 9 should not be used. [RT #702] 707. [func] The "filename" argument to named-checkzone is no longer optional, to reduce confusion. [RT #612] 706. [bug] Zones with an explicit "allow-update { none; };" were considered dynamic and therefore not reloaded on SIGHUP or "rndc reload". 705. [port] Work out resource limit type for use where rlim_t is not available. [RT #695] 704. [port] RLIMIT_NOFILE is not available on all platforms. [RT #695] 703. [port] sys/select.h is needed on older platforms. [RT #695] 702. [func] If the address 0.0.0.0 is seen in resolv.conf, use 127.0.0.1 instead. [RT #693] 701. [func] Root hints are now fully optional. Class IN views use compiled-in hints by default, as before. Non-IN views with no root hints now provide authoritative service but not recursion. A warning is logged if a view has neither root hints nor authoritative data for the root. [RT #696] 700. [bug] $GENERATE range check was wrong. [RT #688] 699. [bug] The lexer mishandled empty quoted strings. [RT #694] 698. [bug] Aborting nsupdate with ^C would lead to several race conditions. 697. [bug] nsupdate was not compatible with the undocumented BIND 8 behavior of ignoring TTLs in "update delete" commands. [RT #693] 696. [bug] lwresd would die with an assertion failure when passed a zero-length name. [RT #692] 695. [bug] If the resolver attempted to query a blackholed or bogus server, the resolution would fail immediately. 694. [bug] $GENERATE did not produce the last entry. [RT #682, #683] 693. [bug] An empty lwres statement in named.conf caused the server to crash while loading. 692. [bug] Deal with systems that have getaddrinfo() but not gai_strerror(). [RT #679] 691. [bug] Configuring per-view forwarders caused an assertion failure. [RT #675, #734] 690. [func] $GENERATE now supports DNAME. [RT #654] 689. [doc] man pages are now installed. [RT #210] 688. [func] "make tags" now works on systems with the "Exuberant Ctags" etags. 687. [bug] Only say we have IPv6, with sufficient functionality, if it has actually been tested. [RT #586] 686. [bug] dig and nslookup can now be properly aborted during blocking operations. [RT #568] 685. [bug] nslookup should use the search list/domain options from resolv.conf by default. [RT #405, #630] 684. [bug] Memory leak with view forwarders. [RT #656] 683. [bug] File descriptor leak in isc_lex_openfile(). 682. [bug] nslookup displayed SOA records incorrectly. [RT #665] 681. [bug] $GENERATE specifying output format was broken. [RT #653] 680. [bug] dns_rdata_fromstruct() mishandled options bigger than 255 octets. 679. [bug] $INCLUDE could leak memory and file descriptors on reload. [RT #639] 678. [bug] "transfer-format one-answer;" could trigger an assertion failure. [RT #646] 677. [bug] dnssec-signzone would occasionally use the wrong ttl for database operations and fail. [RT #643] 676. [bug] Log messages about lame servers to category 'lame-servers' rather than 'resolver', so as not to be gratuitously incompatible with BIND 8. 675. [bug] TKEY queries could cause the server to leak memory. 674. [func] Allow messages to be TSIG signed / verified using a offset from the current time. 673. [func] The server can now convert RFC1886-style recursive lookup requests into RFC2874-style lookups, when enabled using the new option "allow-v6-synthesis". 672. [bug] The wrong time was in the "time signed" field when replying with BADTIME error. 671. [bug] The message code was failing to parse a message with no question section and a TSIG record. [RT #628] 670. [bug] The lwres replacements for getaddrinfo and getipnodebyname didn't properly check for the existence of the sockaddr sa_len field. 669. [bug] dnssec-keygen now makes the public key file non-world-readable for symmetric keys. [RT #403] 668. [func] named-checkzone now reports multiple errors in master files. 667. [bug] On Linux, running named with the -u option and a non-world-readable configuration file didn't work. [RT #626] 666. [bug] If a request sent by dig is longer than 512 bytes, use TCP. 665. [bug] Signed responses were not sent when the size of the TSIG + question exceeded the maximum message size. [RT #628] 664. [bug] The t_tasks and t_timers module tests are now skipped when building without threads, since they require threads. 663. [func] Accept a size_spec, not just an integer, in the (unimplemented and ignored) max-ixfr-log-size option for compatibility with recent versions of BIND 8. [RT #613] 662. [bug] dns_rdata_fromtext() failed to log certain errors. 661. [bug] Certain UDP IXFR requests caused an assertion failure (mpctx->allocated == 0). [RT #355, #394, #623] 660. [port] Detect multiple CPUs on HP-UX and IRIX. 659. [performance] Rewrite the name compression code to be much faster. 658. [cleanup] Remove all vestiges of 16 bit global compression. 657. [bug] When a listen-on statement in an lwres block does not specify a port, use 921, not 53. Also update the listen-on documentation. [RT #616] 656. [func] Treat an unescaped newline in a quoted string as an error. This means that TXT records with missing close quotes should have meaningful errors printed. 655. [bug] Improve error reporting on unexpected eof when loading zones. [RT #611] 654. [bug] Origin was being forgotten in TCP retries in dig. [RT #574] 653. [bug] +defname option in dig was reversed in sense. [RT #549] 652. [bug] zone_saveunique() did not report the new name. 651. [func] The AD bit in responses now has the meaning specified in . 650. [bug] SIG(0) records were being generated and verified incorrectly. [RT #606] 649. [bug] It was possible to join to an already running fctx after it had "cloned" its events, but before it sent them. In this case, the event of the newly joined fetch would not contain the answer, and would trigger the INSIST() in fctx_sendevents(). In BIND 9.0, this bug did not trigger an INSIST(), but caused the fetch to fail with a SERVFAIL result. [RT #588, #597, #605, #607] 648. [port] Add support for pre-RFC2133 IPv6 implementations. 647. [bug] Resolver queries sent after following multiple referrals had excessively long retransmission timeouts due to incorrectly counting the referrals as "restarts". 646. [bug] The UnixWare ISC_PLATFORM_FIXIN6INADDR fix in isc/net.h didn't _cleanly_ fix the problem it was trying to fix. 645. [port] BSD/OS 3.0 needs pthread_init(). [RT #603] 644. [bug] #622 needed more work. [RT #562] 643. [bug] xfrin error messages made more verbose, added class of the zone. [RT# 599] 642. [bug] Break the exit_check() race in the zone module. [RT #598] --- 9.1.0b2 released --- 641. [bug] $GENERATE caused a uninitialized link to be used. [RT #595] 640. [bug] Memory leak in error path could cause "mpctx->allocated == 0" failure. [RT #584] 639. [bug] Reading entropy from the keyboard would sometimes fail. [RT #591] 638. [port] lib/isc/random.c needed to explicitly include time.h to get a prototype for time() when pthreads was not being used. [RT #592] 637. [port] Use isc_u?int64_t instead of (unsigned) long long in lib/isc/print.c. Also allow lib/isc/print.c to be compiled even if the platform does not need it. [RT #592] 636. [port] Shut up MSVC++ about a possible loss of precision in the ISC__BUFFER_PUTUINT*() macros. [RT #592] 635. [bug] Reloading a server with a configured blackhole list would cause an assertion. [RT #590] 634. [bug] A log file will completely stop being written when it reaches the maximum size in all cases, not just when versioning is also enabled. [RT #570] 633. [port] Cope with rlim_t missing on BSD/OS systems. [RT #575] 632. [bug] The index array of the journal file was corrupted as it was written to disk. 631. [port] Build without thread support on systems without pthreads. 630. [bug] Locking failure in zone code. [RT #582] 629. [bug] 9.1.0b1 dereferenced a null pointer and crashed when responding to a UDP IXFR request. 628. [bug] If the root hints contained only AAAA addresses, named would be unable to perform resolution. 627. [bug] The EDNS0 blackhole detection code of change 324 waited for three retransmissions to each server, which takes much too long when a domain has many name servers and all of them drop EDNS0 queries. Now we retry without EDNS0 after three consecutive timeouts, even if they are all from different servers. [RT #143] 626. [bug] The lightweight resolver daemon no longer crashes when asked for a SIG rrset. [RT #558] 625. [func] Zones now inherit their class from the enclosing view. 624. [bug] The zone object could get timer events after it had been destroyed, causing a server crash. [RT #571] 623. [func] Added "named-checkconf" and "named-checkzone" program for syntax checking named.conf files and zone files, respectively. 622. [bug] A canceled request could be destroyed before dns_request_destroy() was called. [RT #562] 621. [port] Disable IPv6 at runtime if IPv6 sockets are unusable. This mostly affects Red Hat Linux 7.0, which has conflicts between libc and the kernel. 620. [bug] dns_master_load*inc() now require 'task' and 'load' to be non-null. Also 'done' will not be called if dns_master_load*inc() fails immediately. [RT #565] 619. [placeholder] 618. [bug] Queries to a signed zone could sometimes cause an assertion failure. 617. [bug] When using dynamic update to add a new RR to an existing RRset with a different TTL, the journal entries generated from the update did not include explicit deletions and re-additions of the existing RRs to update their TTL to the new value. 616. [func] dnssec-signzone -t output now includes performance statistics. 615. [bug] dnssec-signzone did not like child keysets signed by multiple keys. 614. [bug] Checks for uninitialized link fields were prone to false positives, causing assertion failures. The checks are now disabled by default and may be re-enabled by defining ISC_LIST_CHECKINIT. 613. [bug] "rndc reload zone" now reloads primary zones. It previously only updated slave and stub zones, if an SOA query indicated an out of date serial. 612. [cleanup] Shutup a ridiculously noisy HP-UX compiler that complains relentlessly about how its treatment of 'const' has changed as well as how casting sometimes tightens alignment constraints. 611. [func] allow-notify can be used to permit processing of notify messages from hosts other than a slave's masters. 610. [func] rndc dumpdb is now supported. 609. [bug] getrrsetbyname() would crash lwresd if the server found more SIGs than answers. [RT #554] 608. [func] dnssec-signzone now adds a comment to the zone with the time the file was signed. 607. [bug] nsupdate would fail if it encountered a CNAME or DNAME in a response to an SOA query. [RT #515] 606. [bug] Compiling with --disable-threads failed due to isc_thread_self() being incorrectly defined as an integer rather than a function. 605. [func] New function isc_lex_getlasttokentext(). 604. [bug] The named.conf parser could print incorrect line numbers when long comments were present. 603. [bug] Make dig handle multiple types or classes on the same query more correctly. 602. [func] Cope automatically with UnixWare's broken IN6_IS_ADDR_* macros. [RT #539] 601. [func] Return a non-zero exit code if an update fails in nsupdate. 600. [bug] Reverse lookups sometimes failed in dig, etc... 599. [func] Added four new functions to the libisc log API to support i18n messages. isc_log_iwrite(), isc_log_ivwrite(), isc_log_iwrite1() and isc_log_ivwrite1() were added. 598. [bug] An update-policy statement would cause the server to assert while loading. [RT #536] 597. [func] dnssec-signzone is now multi-threaded. 596. [bug] DNS_RDATASLAB_FORCE and DNS_RDATASLAB_EXACT are not mutually exclusive. 595. [port] On Linux 2.2, socket() returns EINVAL when it should return EAFNOSUPPORT. Work around this. [RT #531] 594. [func] sdb drivers are now assumed to not be thread-safe unless the DNS_SDBFLAG_THREADSAFE flag is supplied. 593. [bug] If a secure zone was missing all its NXTs and a dynamic update was attempted, the server entered an infinite loop. 592. [bug] The sig-validity-interval option now specifies a number of days, not seconds. This matches the documentation. [RT #529] --- 9.1.0b1 released --- 591. [bug] Work around non-reentrancy in openssl by disabling pre-computation in keys. 590. [doc] There are now man pages for the lwres library in doc/man/lwres. 589. [bug] The server could deadlock if a zone was updated while being transferred out. 588. [bug] ctx->in_use was not being correctly initialized when when pushing a file for $INCLUDE. [RT #523] 587. [func] A warning is now printed if the "allow-update" option allows updates based on the source IP address, to alert users to the fact that this is insecure and becoming increasingly so as servers capable of update forwarding are being deployed. 586. [bug] multiple views with the same name were fatal. [RT #516] 585. [func] dns_db_addrdataset() and and dns_rdataslab_merge() now support 'exact' additions in a similar manner to dns_db_subtractrdataset() and dns_rdataslab_subtract(). 584. [func] You can now say 'notify explicit'; to suppress notification of the servers listed in NS records and notify only those servers listed in the 'also-notify' option. 583. [func] "rndc querylog" will now toggle logging of queries, like "ndc querylog" in BIND 8. 582. [bug] dns_zone_idetach() failed to lock the zone. [RT #199, #463] 581. [bug] log severity was not being correctly processed. [RT #485] 580. [func] Ignore trailing garbage on incoming DNS packets, for interoperability with broken server implementations. [RT #491] 579. [bug] nsupdate did not take a filename to read update from. [RT #492] 578. [func] New config option "notify-source", to specify the source address for notify messages. 577. [func] Log illegal RDATA combinations. e.g. multiple singleton types, cname and other data. 576. [doc] isc_log_create() description did not match reality. 575. [bug] isc_log_create() was not setting internal state correctly to reflect the default channels created. 574. [bug] TSIG signed queries sent by the resolver would fail to have their responses validated and would leak memory. 573. [bug] The journal files of IXFRed slave zones were inadvertently discarded on server reload, causing "journal out of sync with zone" errors on subsequent reloads. [RT #482] 572. [bug] Quoted strings were not accepted as key names in address match lists. 571. [bug] It was possible to create an rdataset of singleton type which had more than one rdata. [RT #154] [RT #279] 570. [bug] rbtdb.c allowed zones containing nodes which had both a CNAME and "other data". [RT #154] 569. [func] The DNSSEC AD bit will not be set on queries which have not requested a DNSSEC response. 568. [func] Add sample simple database drivers in contrib/sdb. 567. [bug] Setting the zone transfer timeout to zero caused an assertion failure. [RT #302] 566. [func] New public function dns_timer_setidle(). 565. [func] Log queries more like BIND 8: query logging is now done to category "queries", level "info". [RT #169] 564. [func] Add sortlist support to lwresd. 563. [func] New public functions dns_rdatatype_format() and dns_rdataclass_format(), for convenient formatting of rdata type/class mnemonics in log messages. 562. [cleanup] Moved lib/dns/*conf.c to bin/named where they belong. 561. [func] The 'datasize', 'stacksize', 'coresize' and 'files' clauses of the options{} statement are now implemented. 560. [bug] dns_name_split did not properly the resulting prefix when a maximal length bitstring label was split which was preceded by another bitstring label. [RT #429] 559. [bug] dns_name_split did not properly create the suffix when splitting within a maximal length bitstring label. 558. [func] New functions, isc_resource_getlimit and isc_resource_setlimit. 557. [func] Symbolic constants for libisc integral types. 556. [func] The DNSSEC OK bit in the EDNS extended flags is now implemented. Responses to queries without this bit set will not contain any DNSSEC records. 555. [bug] A slave server attempting a zone transfer could crash with an assertion failure on certain malformed responses from the master. [RT #457] 554. [bug] In some cases, not all of the dnssec tools were properly installed. 553. [bug] Incoming zone transfers deferred due to quota were not started when quota was increased but only when a transfer in progress finished. [RT #456] 552. [bug] We were not correctly detecting the end of all c-style comments. [RT #455] 551. [func] Implemented the 'sortlist' option. 550. [func] Support unknown rdata types and classes. 549. [bug] "make" did not immediately abort the build when a subdirectory make failed [RT #450]. 548. [func] The lexer now ungets tokens more correctly. 547. [placeholder] 546. [func] Option 'lame-ttl' is now implemented. 545. [func] Name limit and counting options removed from dig; they didn't work properly, and cannot be correctly implemented without significant changes. 544. [func] Add statistics option, enable statistics-file option, add RNDC option "dump-statistics" to write out a query statistics file. 543. [doc] The 'port' option is now documented. 542. [func] Add support for update forwarding as required for full compliance with RFC2136. It is turned off by default and can be enabled using the 'allow-update-forwarding' option. 541. [func] Add bogus server support. 540. [func] Add dialup support. 539. [func] Support the blackhole option. 538. [bug] fix buffer overruns by 1 in lwres_getnameinfo(). 537. [placeholder] 536. [func] Use transfer-source{-v6} when sending refresh queries. Transfer-source{-v6} now take a optional port parameter for setting the UDP source port. The port parameter is ignored for TCP. 535. [func] Use transfer-source{-v6} when forwarding update requests. 534. [func] Ancestors have been removed from RBT chains. Ancestor information can be discerned via node parent pointers. 533. [func] Incorporated name hashing into the RBT database to improve search speed. 532. [func] Implement DNS UPDATE pseudo records using DNS_RDATA_UPDATE flag. 531. [func] Rdata really should be initialized before being assigned to (dns_rdata_fromwire(), dns_rdata_fromtext(), dns_rdata_clone(), dns_rdata_fromregion()), check that it is. 530. [func] New function dns_rdata_invalidate(). 529. [bug] 521 contained a bug which caused zones to always reload. [RT #410] 528. [func] The ISC_LIST_XXXX macros now perform sanity checks on their arguments. ISC_LIST_XXXXUNSAFE can be use to skip the checks however use with caution. 527. [func] New function dns_rdata_clone(). 526. [bug] nsupdate incorrectly refused to add RRs with a TTL of 0. 525. [func] New arguments 'options' for dns_db_subtractrdataset(), and 'flags' for dns_rdataslab_subtract() allowing you to request that the RR's must exist prior to deletion. DNS_R_NOTEXACT is returned if the condition is not met. 524. [func] The 'forward' and 'forwarders' statement in non-forward zones should work now. 523. [doc] The source to the Administrator Reference Manual is now an XML file using the DocBook DTD, and is included in the distribution. The plain text version of the ARM is temporarily unavailable while we figure out how to generate readable plain text from the XML. 522. [func] The lightweight resolver daemon can now use a real configuration file, and its functionality can be provided by a name server. Also, the -p and -P options to lwresd have been reversed. 521. [bug] Detect master files which contain $INCLUDE and always reload. [RT #196] 520. [bug] Upgraded libtool to 1.3.5, which makes shared library builds almost work on AIX (and possibly others). 519. [bug] dns_name_split() would improperly split some bitstring labels, zeroing a few of the least significant bits in the prefix part. When such an improperly created prefix was returned to the RBT database, the bogus label was dutifully stored, corrupting the tree. [RT #369] 518. [bug] The resolver did not realize that a DNAME which was "the answer" to the client's query was "the answer", and such queries would fail. [RT #399] 517. [bug] The resolver's DNAME code would trigger an assertion if there was more than one DNAME in the chain. [RT #399] 516. [bug] Cache lookups which had a NULL node pointer, e.g. those by dns_view_find(), and which would match a DNAME, would trigger an INSIST(!search.need_cleanup) assertion. [RT #399] 515. [bug] The ssu table was not being attached / detached by dns_zone_[sg]etssutable. [RT#397] 514. [func] Retry refresh and notify queries if they timeout. [RT #388] 513. [func] New functionality added to rdnc and server to allow individual zones to be refreshed or reloaded. 512. [bug] The zone transfer code could throw an exception with an invalid IXFR stream. 511. [bug] The message code could throw an assertion on an out of memory failure. [RT #392] 510. [bug] Remove spurious view notify warning. [RT #376] 509. [func] Add support for write of zone files on shutdown. 508. [func] dns_message_parse() can now do a best-effort attempt, which should allow dig to print more invalid messages. 507. [func] New functions dns_zone_flush(), dns_zt_flushanddetach() and dns_view_flushanddetach(). 506. [func] Do not fail to start on errors in zone files. 505. [bug] nsupdate was printing "unknown result code". [RT #373] 504. [bug] The zone was not being marked as dirty when updated via IXFR. 503. [bug] dumptime was not being set along with DNS_ZONEFLG_NEEDDUMP. 502. [func] On a SERVFAIL reply, DiG will now try the next server in the list, unless the +fail option is specified. 501. [bug] Incorrect port numbers were being displayed by nslookup. [RT #352] 500. [func] Nearly useless +details option removed from DiG. 499. [func] In DiG, specifying a class with -c or type with -t changes command-line parsing so that classes and types are only recognized if following -c or -t. This allows hosts with the same name as a class or type to be looked up. 498. [doc] There is now a man page for "dig" in doc/man/bin/dig.1. 497. [bug] The error messages printed when an IP match list contained a network address with a nonzero host part where not sufficiently detailed. [RT #365] 496. [bug] named didn't sanity check numeric parameters. [RT #361] 495. [bug] nsupdate was unable to handle large records. [RT #368] 494. [func] Do not cache NXDOMAIN responses for SOA queries. 493. [func] Return non-cachable (ttl = 0) NXDOMAIN responses for SOA queries. This makes it easier to locate the containing zone without polluting intermediate caches. 492. [bug] attempting to reload a zone caused the server fail to shutdown cleanly. [RT #360] 491. [bug] nsupdate would segfault when sending certain prerequisites with empty RDATA. [RT #356] 490. [func] When a slave/stub zone has not yet successfully obtained an SOA containing the zone's configured retry time, perform the SOA query retries using exponential backoff. [RT #337] 489. [func] The zone manager now has a "i/o" queue. 488. [bug] Locks weren't properly destroyed in some cases. 487. [port] flockfile() is not defined on all systems. 486. [bug] nslookup: "set all" and "server" commands showed the incorrect port number if a port other than 53 was specified. [RT #352] 485. [func] When dig had more than one server to query, it would send all of the messages at the same time. Add rate limiting of the transmitted messages. 484. [bug] When the server was reloaded after removing addresses from the named.conf "listen-on" statement, sockets were still listening on the removed addresses due to reference count loops. [RT #325] 483. [bug] nslookup: "set all" showed a "search" option but it was not settable. 482. [bug] nslookup: a plain "server" or "lserver" should be treated as a lookup. 481. [bug] nslookup:get_next_command() stack size could exceed per thread limit. 480. [bug] strtok() is not thread safe. [RT #349] 479. [func] The test suite can now be run by typing "make check" or "make test" at the top level. 478. [bug] "make install" failed if the directory specified with --prefix did not already exist. 477. [bug] The the isc-config.sh script could be installed before its directory was created. [RT #324] 476. [bug] A zone could expire while a zone transfer was in progress triggering a INSIST failure. [RT #329] 475. [bug] query_getzonedb() sometimes returned a non-null version on failure. This caused assertion failures when generating query responses where names subject to additional section processing pointed to a zone to which access had been denied by means of the allow-query option. [RT #336] 474. [bug] The mnemonic of the CHAOS class is CH according to RFC1035, but it was printed and read only as CHAOS. We now accept both forms as input, and print it as CH. [RT #305] 473. [bug] nsupdate overran the end of the list of name servers when no servers could be reached, typically causing it to print the error message "dns_request_create: not implemented". 472. [bug] Off-by-one error caused isc_time_add() to sometimes produce invalid time values. 471. [bug] nsupdate didn't compile on HP/UX 10.20 470. [func] $GENERATE is now supported. See also doc/misc/migration. 469. [bug] "query-source address * port 53;" now works. 468. [bug] dns_master_load*() failed to report file and line number in certain error conditions. 467. [bug] dns_master_load*() failed to log an error if pushfile() failed. 466. [bug] dns_master_load*() could return success when it failed. 465. [cleanup] Allow 0 to be set as an omapi_value_t value by omapi_value_storeint(). 464. [cleanup] Build with openssl's RSA code instead of dnssafe. 463. [bug] nsupdate sent malformed SOA queries to the second and subsequent name servers in resolv.conf if the query sent to the first one failed. 462. [bug] --disable-ipv6 should work now. 461. [bug] Specifying an unknown key in the "keys" clause of the "controls" statement caused a NULL pointer dereference. [RT #316] 460. [bug] Much of the DNSSEC code only worked with class IN. 459. [bug] Nslookup processed the "set" command incorrectly. 458. [bug] Nslookup didn't properly check class and type values. [RT #305] 457. [bug] Dig/host/hslookup didn't properly handle connect timeouts in certain situations, causing an unnecessary warning message to be printed. 456. [bug] Stub zones were not resetting the refresh and expire counters, loadtime or clearing the DNS_ZONE_REFRESH (refresh in progress) flag upon successful update. This disabled further refreshing of the stub zone, causing it to eventually expire. [RT #300] 455. [doc] Document IPv4 prefix notation does not require a dotted decimal quad but may be just dotted decimal. 454. [bug] Enforce dotted decimal and dotted decimal quad where documented as such in named.conf. [RT #304, RT #311] 453. [bug] Warn if the obsolete option "maintain-ixfr-base" is specified in named.conf. [RT #306] 452. [bug] Warn if the unimplemented option "statistics-file" is specified in named.conf. [RT #301] 451. [func] Update forwarding implemented. 450. [func] New function ns_client_sendraw(). 449. [bug] isc_bitstring_copy() only works correctly if the two bitstrings have the same lsb0 value, but this requirement was not documented, nor was there a REQUIRE for it. 448. [bug] Host output formatting change, to match v8. [RT #255] 447. [bug] Dig didn't properly retry in TCP mode after a truncated reply. [RT #277] 446. [bug] Confusing notify log message. [RT #298] 445. [bug] Doing a 0 bit isc_bitstring_copy() of an lsb0 bitstring triggered a REQUIRE statement. The REQUIRE statement was incorrect. [RT #297] 444. [func] "recursion denied" messages are always logged at debug level 1, now, rather than sometimes at ERROR. This silences these warnings in the usual case, where some clients set the RD bit in all queries. 443. [bug] When loading a master file failed because of an unrecognized RR type name, the error message did not include the file name and line number. [RT #285] 442. [bug] TSIG signed messages that did not match any view crashed the server. [RT #290] 441. [bug] Nodes obscured by a DNAME were inaccessible even when DNS_DBFIND_GLUEOK was set. 440. [func] New function dns_zone_forwardupdate(). 439. [func] New function dns_request_createraw(). 438. [func] New function dns_message_getrawmessage(). 437. [func] Log NOTIFY activity to the notify channel. 436. [bug] If recvmsg() returned EHOSTUNREACH or ENETUNREACH, which sometimes happens on Linux, named would enter a busy loop. Also, unexpected socket errors were not logged at a high enough logging level to be useful in diagnosing this situation. [RT #275] 435. [bug] dns_zone_dump() overwrote existing zone files rather than writing to a temporary file and renaming. This could lead to empty or partial zone files being left around in certain error conditions involving the initial transfer of a slave zone, interfering with subsequent server startup. [RT #282] 434. [func] New function isc_file_isabsolute(). 433. [func] isc_base64_decodestring() now accepts newlines within the base64 data. This makes it possible to break up the key data in a "trusted-keys" statement into multiple lines. [RT #284] 432. [func] Added refresh/retry jitter. The actual refresh/ retry time is now a random value between 75% and 100% of the configured value. 431. [func] Log at ISC_LOG_INFO when a zone is successfully loaded. 430. [bug] Rewrote the lightweight resolver client management code to handle shutdown correctly and general cleanup. 429. [bug] The space reserved for a TSIG record in a response was 2 bytes too short, leading to message generation failures. 428. [bug] rbtdb.c:find_closest_nxt() erroneously returned DNS_R_BADDB for nodes which had neither NXT nor SIG NXT (e.g. glue). This could cause SERVFAILs when generating negative responses in a secure zone. 427. [bug] Avoid going into an infinite loop when the validator gets a negative response to a key query where the records are signed by the missing key. 426. [bug] Attempting to generate an oversized RSA key could cause dnssec-keygen to dump core. 425. [bug] Warn about the auth-nxdomain default value change if there is no auth-nxdomain statement in the config file. [RT #287] 424. [bug] notify_createmessage() could trigger an assertion failure when creating the notify message failed, e.g. due to corrupt zones with multiple SOA records. [RT #279] 423. [bug] When responding to a recursive query, errors that occur after following a CNAME should cause the query to fail. [RT #274] 422. [func] get rid of isc_random_t, and make isc_random_get() and isc_random_jitter() use rand() internally instead of local state. Note that isc_random_*() functions are only for weak, non-critical "randomness" such as timing jitter and such. 421. [bug] nslookup would exit when given a blank line as input. 420. [bug] nslookup failed to implement the "exit" command. 419. [bug] The certificate type PKIX was misspelled as SKIX. 418. [bug] At debug levels >= 10, getting an unexpected socket receive error would crash the server while trying to log the error message. 417. [func] Add isc_app_block() and isc_app_unblock(), which allow an application to handle signals while blocking. 416. [bug] Slave zones with no master file tried to use a NULL pointer for a journal file name when they received an IXFR. [RT #273] 415. [bug] The logging code leaked file descriptors. 414. [bug] Server did not shut down until all incoming zone transfers were finished. 413. [bug] Notify could attempt to use the zone database after it had been unloaded. [RT#267] 412. [bug] named -v didn't print the version. 411. [bug] A typo in the HS A code caused an assertion failure. 410. [bug] lwres_gethostbyname() and company set lwres_h_errno to a random value on success. 409. [bug] If named was shut down early in the startup process, ns_omapi_shutdown() would attempt to lock an uninitialized mutex. [RT #262] 408. [bug] stub zones could leak memory and reference counts if all the masters were unreachable. 407. [bug] isc_rwlock_lock() would needlessly block readers when it reached the read quota even if no writers were waiting. 406. [bug] Log messages were occasionally lost or corrupted due to a race condition in isc_log_doit(). 405. [func] Add support for selective forwarding (forward zones) 404. [bug] The request library didn't completely work with IPv6. 403. [bug] "host" did not use the search list. 402. [bug] Treat undefined acls as errors, rather than warning and then later throwing an assertion. [RT #252] 401. [func] Added simple database API. 400. [bug] SIG(0) signing and verifying was done incorrectly. [RT #249] 399. [bug] When reloading the server with a config file containing a syntax error, it could catch an assertion failure trying to perform zone maintenance on, or sending notifies from, tentatively created zones whose views were never fully configured and lacked an address database and request manager. 398. [bug] "dig" sometimes caught an assertion failure when using TSIG, depending on the key length. 397. [func] Added utility functions dns_view_gettsig() and dns_view_getpeertsig(). 396. [doc] There is now a man page for "nsupdate" in doc/man/bin/nsupdate.8. 395. [bug] nslookup printed incorrect RR type mnemonics for RRs of type >= 21 [RT #237]. 394. [bug] Current name was not propagated via $INCLUDE. 393. [func] Initial answer while loading (awl) support. Entry points: dns_master_loadfileinc(), dns_master_loadstreaminc(), dns_master_loadbufferinc(). Note: calls to dns_master_load*inc() should be rate be rate limited so as to not use up all file descriptors. 392. [func] Add ISC_R_FAMILYNOSUPPORT. Returned when OS does not support the given address family requested. 391. [clarity] ISC_R_FAMILY -> ISC_R_FAMILYMISMATCH. 390. [func] The function dns_zone_setdbtype() now takes an argc/argv style vector of words and sets both the zone database type and its arguments, making the functions dns_zone_adddbarg() and dns_zone_cleardbargs() unnecessary. 389. [bug] Attempting to send a request over IPv6 using dns_request_create() on a system without IPv6 support caused an assertion failure [RT #235]. 388. [func] dig and host can now do reverse ipv6 lookups. 387. [func] Add dns_byaddr_createptrname(), which converts an address into the name used by a PTR query. 386. [bug] Missing strdup() of ACL name caused random ACL matching failures [RT #228]. 385. [cleanup] Removed functions dns_zone_equal(), dns_zone_print(), and dns_zt_print(). 384. [bug] nsupdate was incorrectly limiting TTLs to 65535 instead of 2147483647. 383. [func] When writing a master file, print the SOA and NS records (and their SIGs) before other records. 382. [bug] named -u failed on many Linux systems where the libc provided kernel headers do not match the current kernel. 381. [bug] Check for IPV6_RECVPKTINFO and use it instead of IPV6_PKTINFO if found. [RT #229] 380. [bug] nsupdate didn't work with IPv6. 379. [func] New library function isc_sockaddr_anyofpf(). 378. [func] named and lwresd will log the command line arguments they were started with in the "starting ..." message. 377. [bug] When additional data lookups were refused due to "allow-query", the databases were still being attached causing reference leaks. 376. [bug] The server should always use good entropy when performing cryptographic functions needing entropy. 375. [bug] Per-zone "allow-query" did not properly override the view/global one for CNAME targets and additional data [RT #220]. 374. [bug] SOA in authoritative negative responses had wrong TTL. 373. [func] nslookup is now installed by "make install". 372. [bug] Deal with Microsoft DNS servers appending two bytes of garbage to zone transfer requests. 371. [bug] At high debug levels, doing an outgoing zone transfer of a very large RRset could cause an assertion failure during logging. 370. [bug] The error messages for roll-forward failures were overly terse. 369. [func] Support new named.conf options, view and zone statements: max-retry-time, min-retry-time, max-refresh-time, min-refresh-time. 368. [func] Restructure the internal ".bind" view so that more zones can be added to it. 367. [bug] Allow proper selection of server on nslookup command line. 366. [func] Allow use of '-' batch file in dig for stdin. 365. [bug] nsupdate -k leaked memory. 364. [func] Added additional-from-{cache,auth} 363. [placeholder] 362. [bug] rndc no longer aborts if the configuration file is missing an options statement. [RT #209] 361. [func] When the RBT find or chain functions set the name and origin for a node that stores the root label the name is now set to an empty name, instead of ".", to simplify later use of the name and origin by dns_name_concatenate(), dns_name_totext() or dns_name_format(). 360. [func] dns_name_totext() and dns_name_format() now allow an empty name to be passed, which is formatted as "@". 359. [bug] dnssec-signzone occasionally signed glue records. 358. [cleanup] Rename the intermediate files used by the dnssec programs. 357. [bug] The zone file parser crashed if the argument to $INCLUDE was a quoted string. 356. [cleanup] isc_task_send no longer requires event->sender to be non-null. 355. [func] Added isc_dir_createunique(), similar to mkdtemp(). 354. [doc] Man pages for the dnssec tools are now included in the distribution, in doc/man/dnssec. 353. [bug] double increment in lwres/gethost.c:copytobuf(). [RT# 187] 352. [bug] Race condition in dns_client_t startup could cause an assertion failure. 351. [bug] Constructing a response with rcode SERVFAIL to a TSIG signed query could crash the server. 350. [bug] Also-notify lists specified in the global options block were not correctly reference counted, causing a memory leak. 349. [bug] Processing a query with the CD bit set now works as expected. 348. [func] New boolean named.conf options 'additional-from-auth' and 'additional-from-cache' now supported in view and global options statement. 347. [bug] Don't crash if an argument is left off options in dig. 346. [placeholder] 345. [bug] Large-scale changes/cleanups to dig: * Significantly improve structure handling * Don't pre-load entire batch files * Add name/rr counting/limiting * Fix SIGINT handling * Shorten timeouts to match v8's behavior 344. [bug] When shutting down, lwresd sometimes tried to shut down its client tasks twice, triggering an assertion. 343. [bug] Although zone maintenance SOA queries and notify requests were signed with TSIG keys when configured for the server in case, the TSIG was not verified on the response. 342. [bug] The wrong name was being passed to dns_name_dup() when generating a TSIG key using TKEY. 341. [func] Support 'key' clause in named.conf zone masters statement to allow authentication via TSIG keys: masters { 10.0.0.1 port 5353 key "foo"; 10.0.0.2 ; }; 340. [bug] The top-level COPYRIGHT file was missing from the distribution. 339. [bug] DNSSEC validation of the response to an ANY query at a name with a CNAME RR in a secure zone triggered an assertion failure. 338. [bug] lwresd logged to syslog as named, not lwresd. 337. [bug] "dig" did not recognize "nsap-ptr" as an RR type on the command line. 336. [bug] "dig -f" used 64 k of memory for each line in the file. It now uses much less, though still proportionally to the file size. 335. [bug] named would occasionally attempt recursion when it was disallowed or undesired. 334. [func] Added hmac-md5 to libisc. 333. [bug] The resolver incorrectly accepted referrals to domains that were not parents of the query name, causing assertion failures. 332. [func] New function dns_name_reset(). 331. [bug] Only log "recursion denied" if RD is set. [RT #178] 330. [bug] Many debugging messages were partially formatted even when debugging was turned off, causing a significant decrease in query performance. 329. [func] omapi_auth_register() now takes a size_t argument for the length of a key's secret data. Previously OMAPI only stored secrets up to the first NUL byte. 328. [func] Added isc_base64_decodestring(). 327. [bug] rndc.conf parser wasn't correctly recognizing an IP address where a host specification was required. 326. [func] 'keys' in an 'inet' control statement is now required and must have at least one item in it. A "not supported" warning is now issued if a 'unix' control channel is defined. 325. [bug] isc_lex_gettoken was processing octal strings when ISC_LEXOPT_CNUMBER was not set. 324. [func] In the resolver, turn EDNS0 off if there is no response after a number of retransmissions. This is to allow queries some chance of succeeding even if all the authoritative servers of a zone silently discard EDNS0 requests instead of sending an error response like they ought to. 323. [bug] dns_rbt_findname() did not ignore empty rbt nodes. Because of this, servers authoritative for a parent and grandchild zone but not authoritative for the intervening child zone did not correctly issue referrals to the servers of the child zone. 322. [bug] Queries for KEY RRs are now sent to the parent server before the authoritative one, making DNSSEC insecurity proofs work in many cases where they previously didn't. 321. [bug] When synthesizing a CNAME RR for a DNAME response, query_addcname() failed to initialize the type and class of the CNAME dns_rdata_t, causing random failures. 320. [func] Multiple rndc changes: parses an rndc.conf file, uses authentication to talk to named, command line syntax changed. This will all be described in the ARM. 319. [func] The named.conf "controls" statement is now used to configure the OMAPI command channel. 318. [func] dns_c_ndcctx_destroy() could never return anything except ISC_R_SUCCESS; made it have void return instead. 317. [func] Use callbacks from libomapi to determine if a new connection is valid, and if a key requested to be used with that connection is valid. 316. [bug] Generate a warning if we detect an unexpected but treat as . 315. [bug] Handle non-empty blanks lines. [RT #163] 314. [func] The named.conf controls statement can now have more than one key specified for the inet clause. 313. [bug] When parsing resolv.conf, don't terminate on an error. Instead, parse as much as possible, but still return an error if one was found. 312. [bug] Increase the number of allowed elements in the resolv.conf search path from 6 to 8. If there are more than this, ignore the remainder rather than returning a failure in lwres_conf_parse. 311. [bug] lwres_conf_parse failed when the first line of resolv.conf was empty or a comment. 310. [func] Changes to named.conf "controls" statement (inet subtype only) - support "keys" clause controls { inet * port 1024 allow { any; } keys { "foo"; } } - allow "port xxx" to be left out of statement, in which case it defaults to omapi's default port of 953. 309. [bug] When sending a referral, the server did not look for name server addresses as glue in the zone holding the NS RRset in the case where this zone was not the same as the one where it looked for name server addresses as authoritative data. 308. [bug] Treat a SOA record not at top of zone as an error when loading a zone. [RT #154] 307. [bug] When canceling a query, the resolver didn't check for isc_socket_sendto() calls that did not yet have their completion events posted, so it could (rarely) end up destroying the query context and then want to use it again when the send event posted, triggering an assertion as it tried to cancel an already-canceled query. [RT #77] 306. [bug] Reading HMAC-MD5 private key files didn't work. 305. [bug] When reloading the server with a config file containing a syntax error, it could catch an assertion failure trying to perform zone maintenance on tentatively created zones whose views were never fully configured and lacked an address database. 304. [bug] If more than LWRES_CONFMAXNAMESERVERS servers are listed in resolv.conf, silently ignore them instead of returning failure. 303. [bug] Add additional sanity checks to differentiate a AXFR response vs a IXFR response. [RT #157] 302. [bug] In dig, host, and nslookup, MXNAME should be large enough to hold any legal domain name in presentation format + terminating NULL. 301. [bug] Uninitialized pointer in host:printmessage(). [RT #159] 300. [bug] Using both and didn't work on platforms lacking IPv6 because each included their own ipv6 header file for the missing definitions. Now each library's ipv6.h defines the wrapper symbol of the other (ISC_IPV6_H and LWRES_IPV6_H). 299. [cleanup] Get the user and group information before changing the root directory, so the administrator does not need to keep a copy of the user and group databases in the chroot'ed environment. Suggested by Hakan Olsson. 298. [bug] A mutex deadlock occurred during shutdown of the interface manager under certain conditions. Digital Unix systems were the most affected. 297. [bug] Specifying a key name that wasn't fully qualified in certain parts of the config file could cause an assertion failure. 296. [bug] "make install" from a separate build directory failed unless configure had been run in the source directory, too. 295. [bug] When invoked with type==CNAME and a message not constructed by dns_message_parse(), dns_message_findname() failed to find anything due to checking for attribute bits that are set only in dns_message_parse(). This caused an infinite loop when constructing the response to an ANY query at a CNAME in a secure zone. 294. [bug] If we run out of space in while processing glue when reading a master file and commit "current name" reverts to "name_current" instead of staying as "name_glue". 293. [port] Add support for FreeBSD 4.0 system tests. 292. [bug] Due to problems with the way some operating systems handle simultaneous listening on IPv4 and IPv6 addresses, the server no longer listens on IPv6 addresses by default. To revert to the previous behavior, specify "listen-on-v6 { any; };" in the config file. 291. [func] Caching servers no longer send outgoing queries over TCP just because the incoming recursive query was a TCP one. 290. [cleanup] +twiddle option to dig (for testing only) removed. 289. [cleanup] dig is now installed in $bindir instead of $sbindir. host is now installed in $bindir. (Be sure to remove any $sbindir/dig from a previous release.) 288. [func] rndc is now installed by "make install" into $sbindir. 287. [bug] rndc now works again as "rndc 127.1 reload" (for only that task). Parsing its configuration file and using digital signatures for authentication has been disabled until named supports the "controls" statement, post-9.0.0. 286. [bug] On Solaris 2, when named inherited a signal state where SIGHUP had the SIG_IGN action, SIGHUP would be ignored rather than causing the server to reload its configuration. 285. [bug] A change made to the dst API for beta4 inadvertently broke OMAPI's creation of a dst key from an incoming message, causing an assertion to be triggered. Fixed. 284. [func] The DNSSEC key generation and signing tools now generate randomness from keyboard input on systems that lack /dev/random. 283. [cleanup] The 'lwresd' program is now a link to 'named'. 282. [bug] The lexer now returns ISC_R_RANGE if parsed integer is too big for an unsigned long. 281. [bug] Fixed list of recognized config file category names. 280. [func] Add isc-config.sh, which can be used to more easily build applications that link with our libraries. 279. [bug] Private omapi function symbols shared between two or more files in libomapi.a were not namespace protected using the ISC convention of starting with the library name and two underscores ("omapi__"...) 278. [bug] bin/named/logconf.c:category_fromconf() didn't take note of when isc_log_categorybyname() wasn't able to find the category name and would then apply the channel list of the unknown category to all categories. 277. [bug] isc_log_categorybyname() and isc_log_modulebyname() would fail to find the first member of any category or module array apart from the internal defaults. Thus, for example, the "notify" category was improperly configured by named. 276. [bug] dig now supports maximum sized TCP messages. 275. [bug] The definition of lwres_gai_strerror() was missing the lwres_ prefix. 274. [bug] TSIG AXFR verify failed when talking to a BIND 8 server. 273. [func] The default for the 'transfer-format' option is now 'many-answers'. This will break zone transfers to BIND 4.9.5 and older unless there is an explicit 'one-answer' configuration. 272. [bug] The sending of large TCP responses was canceled in mid-transmission due to a race condition caused by the failure to set the client object's "newstate" variable correctly when transitioning to the "working" state. 271. [func] Attempt to probe the number of cpus in named if unspecified rather than defaulting to 1. 270. [func] Allow maximum sized TCP answers. 269. [bug] Failed DNSSEC validations could cause an assertion failure by causing clone_results() to be called with with hevent->node == NULL. 268. [doc] A plain text version of the Administrator Reference Manual is now included in the distribution, as doc/arm/Bv9ARM.txt. 267. [func] Nsupdate is now provided in the distribution. 266. [bug] zone.c:save_nsrrset() node was not initialized. 265. [bug] dns_request_create() now works for TCP. 264. [func] Dispatch can not take TCP sockets in connecting state. Set DNS_DISPATCHATTR_CONNECTED when calling dns_dispatch_createtcp() for connected TCP sockets or call dns_dispatch_starttcp() when the socket is connected. 263. [func] New logging channel type 'stderr' channel some-name { stderr; severity error; } 262. [bug] 'master' was not initialized in zone.c:stub_callback(). 261. [func] Add dns_zone_markdirty(). 260. [bug] Running named as a non-root user failed on Linux kernels new enough to support retaining capabilities after setuid(). 259. [func] New random-device and random-seed-file statements for global options block of named.conf. Both accept a single string argument. 258. [bug] Fixed printing of lwres_addr_t.address field. 257. [bug] The server detached the last zone manager reference too early, while it could still be in use by queries. This manifested itself as assertion failures during the shutdown process for busy name servers. [RT #133] 256. [func] isc_ratelimiter_t now has attach/detach semantics, and isc_ratelimiter_shutdown guarantees that the rate limiter is detached from its task. 255. [func] New function dns_zonemgr_attach(). 254. [bug] Suppress "query denied" messages on additional data lookups. --- 9.0.0b4 released --- 253. [func] resolv.conf parser now recognizes ';' and '#' as comments (anywhere in line, not just as the beginning). 252. [bug] resolv.conf parser mishandled masks on sortlists. It also aborted when an unrecognized keyword was seen, now it silently ignores the entire line. 251. [bug] lwresd caught an assertion failure on startup. 250. [bug] fixed handling of size+unit when value would be too large for internal representation. 249. [cleanup] max-cache-size config option now takes a size-spec like 'datasize', except 'default' is not allowed. 248. [bug] global lame-ttl option was not being printed when config structures were written out. 247. [cleanup] Rename cache-size config option to max-cache-size. 246. [func] Rename global option cachesize to cache-size and add corresponding option to view statement. 245. [bug] If an uncompressed name will take more than 255 bytes and the buffer is sufficiently long, dns_name_fromwire should return DNS_R_FORMERR, not ISC_R_NOSPACE. This bug caused cause the server to catch an assertion failure when it received a query for a name longer than 255 bytes. 244. [bug] empty named.conf file and empty options statement are now parsed properly. 243. [func] new cachesize option for named.conf 242. [cleanup] fixed incorrect warning about auth-nxdomain usage. 241. [cleanup] nscount and soacount have been removed from the dns_master_*() argument lists. 240. [func] databases now come in three flavours: zone, cache and stub. 239. [func] If ISC_MEM_DEBUG is enabled, the variable isc_mem_debugging controls whether messages are printed or not. 238. [cleanup] A few more compilation warnings have been quieted: + missing sigwait prototype on BSD/OS 4.0/4.0.1. + PTHREAD_ONCE_INIT unbraced initializer warnings on Solaris 2.8. + IN6ADDR_ANY_INIT unbraced initializer warnings on BSD/OS 4.*, Linux and Solaris 2.8. 237. [bug] If connect() returned ENOBUFS when the resolver was initiating a TCP query, the socket didn't get destroyed, and the server did not shut down cleanly. 236. [func] Added new listen-on-v6 config file statement. 235. [func] Consider it a config file error if a listen-on statement has an IPv6 address in it, or a listen-on-v6 statement has an IPv4 address in it. 234. [bug] Allow a trusted-key's first field (domain-name) be either a quoted or an unquoted string, instead of requiring a quoted string. 233. [cleanup] Convert all config structure integer values to unsigned integer (isc_uint32_t) to match grammar. 232. [bug] Allow slave zones to not have a file. 231. [func] Support new 'port' clause in config file options section. Causes 'listen-on', 'masters' and 'also-notify' statements to use its value instead of default (53). 230. [func] Replace the dst sign/verify API with a cleaner one. 229. [func] Support config file sig-validity-interval statement in options, views and zone statements (master zones only). 228. [cleanup] Logging messages in config module stripped of trailing period. 227. [cleanup] The enumerated identifiers dns_rdataclass_*, dns_rcode_*, dns_opcode_*, and dns_trust_* are also now cast to their appropriate types, as with dns_rdatatype_* in item number 225 below. 226. [func] dns_name_totext() now always prints the root name as '.', even when omit_final_dot is true. 225. [cleanup] The enumerated dns_rdatatype_* identifiers are now cast to dns_rdatatype_t via macros of their same name so that they are of the proper integral type wherever a dns_rdatatype_t is needed. 224. [cleanup] The entire project builds cleanly with gcc's -Wcast-qual and -Wwrite-strings warnings enabled, which is now the default when using gcc. (Warnings from confparser.c, because of yacc's code, are unfortunately to be expected.) 223. [func] Several functions were re-prototyped to qualify one or more of their arguments with "const". Similarly, several functions that return pointers now have those pointers qualified with const. 222. [bug] The global 'also-notify' option was ignored. 221. [bug] An uninitialized variable was sometimes passed to dns_rdata_freestruct() when loading a zone, causing an assertion failure. 220. [cleanup] Set the default outgoing port in the view, and set it in sockaddrs returned from the ADB. [31-May-2000 explorer] 219. [bug] Signed truncated messages more correctly follow the respective specs. 218. [func] When an rdataset is signed, its ttl is normalized based on the signature validity period. 217. [func] Also-notify and trusted-keys can now be used in the 'view' statement. 216. [func] The 'max-cache-ttl' and 'max-ncache-ttl' options now work. 215. [bug] Failures at certain points in request processing could cause the assertion INSIST(client->lockview == NULL) to be triggered. 214. [func] New public function isc_netaddr_format(), for formatting network addresses in log messages. 213. [bug] Don't leak memory when reloading the zone if an update-policy clause was present in the old zone. 212. [func] Added dns_message_get/settsigkey, to make TSIG key management reasonable. 211. [func] The 'key' and 'server' statements can now occur inside 'view' statements. 210. [bug] The 'allow-transfer' option was ignored for slave zones, and the 'transfers-per-ns' option was was ignored for all zones. 209. [cleanup] Upgraded openssl files to new version 0.9.5a 208. [func] Added ISC_OFFSET_MAXIMUM for the maximum value of an isc_offset_t. 207. [func] The dnssec tools properly use the logging subsystem. 206. [cleanup] dst now stores the key name as a dns_name_t, not a char *. 205. [cleanup] On IRIX, turn off the mostly harmless warnings 1692 ("prototyped function redeclared without prototype") and 1552 ("variable ... set but not used") when compiling in the lib/dns/sec/{dnssafe,openssl} directories, which contain code imported from outside sources. 204. [cleanup] On HP/UX, pass +vnocompatwarnings to the linker to quiet the warnings that "The linked output may not run on a PA 1.x system." 203. [func] notify and zone soa queries are now tsig signed when appropriate. 202. [func] isc_lex_getsourceline() changed from returning int to returning unsigned long, the type of its underlying counter. 201. [cleanup] Removed the test/sdig program, it has been replaced by bin/dig/dig. --- 9.0.0b3 released --- 200. [bug] Failures in sending query responses to clients (e.g., running out of network buffers) were not logged. 199. [bug] isc_heap_delete() sometimes violated the heap invariant, causing timer events not to be posted when due. 198. [func] Dispatch managers hold memory pools which any managed dispatcher may use. This allows us to avoid dipping into the memory context for most allocations. [19-May-2000 explorer] 197. [bug] When an incoming AXFR or IXFR completes, the zone's internal state is refreshed from the SOA data. [19-May-2000 explorer] 196. [func] Dispatchers can be shared easily between views and/or interfaces. [19-May-2000 explorer] 195. [bug] Including the NXT record of the root domain in a negative response caused an assertion failure. 194. [doc] The PDF version of the Administrator's Reference Manual is no longer included in the ISC BIND9 distribution. 193. [func] changed dst_key_free() prototype. 192. [bug] Zone configuration validation is now done at end of config file parsing, and before loading callbacks. 191. [func] Patched to compile on UnixWare 7.x. This platform is not directly supported by the ISC. 190. [cleanup] The DNSSEC tools have been moved to a separate directory dnssec/ and given the following new, more descriptive names: dnssec-keygen dnssec-signzone dnssec-signkey dnssec-makekeyset Their command line arguments have also been changed to be more consistent. dnssec-keygen now prints the name of the generated key files (sans extension) on standard output to simplify its use in automated scripts. 189. [func] isc_time_secondsastimet(), a new function, will ensure that the number of seconds in an isc_time_t does not exceed the range of a time_t, or return ISC_R_RANGE. Similarly, isc_time_now(), isc_time_nowplusinterval(), isc_time_add() and isc_time_subtract() now check the range for overflow/underflow. In the case of isc_time_subtract, this changed a calling requirement (ie, something that could generate an assertion) into merely a condition that returns an error result. isc_time_add() and isc_time_subtract() were void- valued before but now return isc_result_t. 188. [func] Log a warning message when an incoming zone transfer contains out-of-zone data. 187. [func] isc_ratelimiter_enqueue() has an additional argument 'task'. 186. [func] dns_request_getresponse() has an additional argument 'preserve_order'. 185. [bug] Fixed up handling of ISC_MEMCLUSTER_LEGACY. Several public functions did not have an isc__ prefix, and referred to functions that had previously been renamed. 184. [cleanup] Variables/functions which began with two leading underscores were made to conform to the ANSI/ISO standard, which says that such names are reserved. 183. [func] ISC_LOG_PRINTTAG option for log channels. Useful for logging the program name or other identifier. 182. [cleanup] New command-line parameters for dnssec tools 181. [func] Added dst_key_buildfilename and dst_key_parsefilename 180. [func] New isc_result_t ISC_R_RANGE. Supersedes DNS_R_RANGE. 179. [func] options named.conf statement *must* now come before any zone or view statements. 178. [func] Post-load of named.conf check verifies a slave zone has non-empty list of masters defined. 177. [func] New per-zone boolean: enable-zone yes | no ; intended to let a zone be disabled without having to comment out the entire zone statement. 176. [func] New global and per-view option: max-cache-ttl number 175. [func] New global and per-view option: additional-data internal | minimal | maximal; 174. [func] New public function isc_sockaddr_format(), for formatting socket addresses in log messages. 173. [func] Keep a queue of zones waiting for zone transfer quota so that a new transfer can be dispatched immediately whenever quota becomes available. 172. [bug] $TTL directive was sometimes missing from dumped master files because totext_ctx_init() failed to initialize ctx->current_ttl_valid. 171. [cleanup] On NetBSD systems, the mit-pthreads or unproven-pthreads library is now always used unless --with-ptl2 is explicitly specified on the configure command line. The --with-mit-pthreads option is no longer needed and has been removed. 170. [cleanup] Remove inter server consistency checks from zone, these should return as a separate module in 9.1. dns_zone_checkservers(), dns_zone_checkparents(), dns_zone_checkchildren(), dns_zone_checkglue(). Remove dns_zone_setadb(), dns_zone_setresolver(), dns_zone_setrequestmgr() these should now be found via the view. 169. [func] ratelimiter can now process N events per interval. 168. [bug] include statements in named.conf caused syntax errors due to not consuming the semicolon ending the include statement before switching input streams. 167. [bug] Make lack of masters for a slave zone a soft error. 166. [bug] Keygen was overwriting existing keys if key_id conflicted, now it will retry, and non-null keys with key_id == 0 are not generated anymore. Key was not able to generate NOAUTHCONF DSA key, increased RSA key size to 2048 bits. 165. [cleanup] Silence "end-of-loop condition not reached" warnings from Solaris compiler. 164. [func] Added functions isc_stdio_open(), isc_stdio_close(), isc_stdio_seek(), isc_stdio_read(), isc_stdio_write(), isc_stdio_flush(), isc_stdio_sync(), isc_file_remove() to encapsulate nonportable usage of errno and sync. 163. [func] Added result codes ISC_R_FILENOTFOUND and ISC_R_FILEEXISTS. 162. [bug] Ensure proper range for arguments to ctype.h functions. 161. [cleanup] error in yyparse prototype that only HPUX caught. 160. [cleanup] getnet*() are not going to be implemented at this stage. 159. [func] Redefinition of config file elements is now an error (instead of a warning). 158. [bug] Log channel and category list copy routines weren't assigning properly to output parameter. 157. [port] Fix missing prototype for getopt(). 156. [func] Support new 'database' statement in zone. database "quoted-string"; 155. [bug] ns_notify_start() was not detaching the found zone. 154. [func] The signer now logs libdns warnings to stderr even when not verbose, and in a nicer format. 153. [func] dns_rdata_tostruct() 'mctx' is now optional. If 'mctx' is NULL then you need to preserve the 'rdata' until you have finished using the structure as there may be references to the associated memory. If 'mctx' is non-NULL it is guaranteed that there are no references to memory associated with 'rdata'. dns_rdata_freestruct() must be called if 'mctx' was non-NULL and may safely be called if 'mctx' was NULL. 152. [bug] keygen dumped core if domain name argument was omitted from command line. 151. [func] Support 'disabled' statement in zone config (causes zone to be parsed and then ignored). Currently must come after the 'type' clause. 150. [func] Support optional ports in masters and also-notify statements: masters [ port xxx ] { y.y.y.y [ port zzz ] ; } 149. [cleanup] Removed unused argument 'olist' from dns_c_view_unsetordering(). 148. [cleanup] Stop issuing some warnings about some configuration file statements that were not implemented, but now are. 147. [bug] Changed yacc union size to be smaller for yaccs that put yacc-stack on the real stack. 146. [cleanup] More general redundant header file cleanup. Rather than continuing to itemize every header which changed, this changelog entry just notes that if a header file did not need another header file that it was including in order to provide its advertised functionality, the inclusion of the other header file was removed. See util/check-includes for how this was tested. 145. [cleanup] Added and ISC_LANG_BEGINDECLS/ ISC_LANG_ENDDECLS to header files that had function prototypes, and removed it from those that did not. 144. [cleanup] libdns header files too numerous to name were made to conform to the same style for multiple inclusion protection. 143. [func] Added function dns_rdatatype_isknown(). 142. [cleanup] does not need or . 141. [bug] Corrupt requests with multiple questions could cause an assertion failure. 140. [cleanup] does not need or . 139. [cleanup] now includes instead of and . 138. [cleanup] isc_strtouq moved from str.[ch] to string.[ch] and renamed isc_string_touint64. isc_strsep moved from strsep.c to string.c and renamed isc_string_separate. 137. [cleanup] , , , and made to conform to the same style for multiple inclusion protection. 136. [cleanup] , , and Win32's needed ISC_LANG_BEGINDECLS/ISC_LANG_ENDDECLS. 135. [cleanup] Win32's did not need or , now uses in place of , and needed ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS. 134. [cleanup] does not need . 133. [cleanup] needs . 132. [cleanup] does not need , but does need . 131. [cleanup] and need for ISC_R_* codes used in macros. 130. [cleanup] does not need or , and now includes instead of . 129. [bug] The 'default_debug' log channel was not set up when 'category default' was present in the config file 128. [cleanup] had ISC_LANG_BEGINDECLS instead of ISC_LANG_ENDDECLS at end of header. 127. [cleanup] The contracts for the comparison routines dns_name_fullcompare(), dns_name_compare(), dns_name_rdatacompare(), and dns_rdata_compare() now specify that the order value returned is < 0, 0, or > 0 instead of -1, 0, or 1. 126. [cleanup] and need . 125. [cleanup] , , , , , , and do not need . 124. [func] signer now imports parent's zone key signature and creates null keys/sets zone status bit for children when necessary 123. [cleanup] does not need . 122. [cleanup] does not need or . 121. [cleanup] does not need or . Multiple inclusion protection symbol fixed from ISC_SYMBOL_H to ISC_SYMTAB_H. isc_symtab_t moved to . 120. [cleanup] does not need , , , or . 119. [cleanup] structure definitions for generic rdata structures do not have _generic_ in their names. 118. [cleanup] libdns.a is now namespace-clean, on NetBSD, excepting YACC crust (yyparse, etc) [2000-apr-27 explorer] 117. [cleanup] libdns.a changes: dns_zone_clearnotify() and dns_zone_addnotify() are replaced by dns_zone_setnotifyalso(). dns_zone_clearmasters() and dns_zone_addmaster() are replaced by dns_zone_setmasters(). 116. [func] Added for isc_offset_t (aka off_t on Unix systems). 115. [port] Shut up the -Wmissing-declarations warning about 's __sputaux on BSD/OS pre-4.1. 114. [cleanup] does not need or . 113. [func] Utility programs dig and host added. 112. [cleanup] does not need . 111. [cleanup] does not need or . 110. [cleanup] does not need or . 109. [bug] "make depend" did nothing for bin/tests/{db,mem,sockaddr,tasks,timers}/. 108. [cleanup] DNS_SETBIT/DNS_GETBIT/DNS_CLEARBIT moved from to and renamed to DNS_BIT_SET/DNS_BIT_GET/DNS_BIT_CLEAR. 107. [func] Add keysigner and keysettool. 106. [func] Allow dnssec verifications to ignore the validity period. Used by several of the dnssec tools. 105. [doc] doc/dev/coding.html expanded with other implicit conventions the developers have used. 104. [bug] Made compress_add and compress_find static to lib/dns/compress.c. 103. [func] libisc buffer API changes for : Added: isc_buffer_base(b) (pointer) isc_buffer_current(b) (pointer) isc_buffer_active(b) (pointer) isc_buffer_used(b) (pointer) isc_buffer_length(b) (int) isc_buffer_usedlength(b) (int) isc_buffer_consumedlength(b) (int) isc_buffer_remaininglength(b) (int) isc_buffer_activelength(b) (int) isc_buffer_availablelength(b) (int) Removed: ISC_BUFFER_USEDCOUNT(b) ISC_BUFFER_AVAILABLECOUNT(b) isc_buffer_type(b) Changed names: isc_buffer_used(b, r) -> isc_buffer_usedregion(b, r) isc_buffer_available(b, r) -> isc_buffer_available_region(b, r) isc_buffer_consumed(b, r) -> isc_buffer_consumedregion(b, r) isc_buffer_active(b, r) -> isc_buffer_activeregion(b, r) isc_buffer_remaining(b, r) -> isc_buffer_remainingregion(b, r) Buffer types were removed, so the ISC_BUFFERTYPE_* macros are no more, and the type argument to isc_buffer_init and isc_buffer_allocate were removed. isc_buffer_putstr is now void (instead of isc_result_t) and requires that the caller ensure that there is enough available buffer space for the string. 102. [port] Correctly detect inet_aton, inet_pton and inet_ptop on BSD/OS 4.1. 101. [cleanup] Quieted EGCS warnings from lib/isc/print.c. 100. [cleanup] does not need or . isc_random_t moved to . 99. [cleanup] Rate limiter now has separate shutdown() and destroy() functions, and it guarantees that all queued events are delivered even in the shutdown case. 98. [cleanup] does not need or unless ISC_PLATFORM_NEEDVSNPRINTF is defined. 97. [cleanup] does not need or . 96. [cleanup] does not need . 95. [cleanup] does not need . 94. [cleanup] Some installed header files did not compile as C++. 93. [cleanup] does not need . 92. [cleanup] does not need , , or . 91. [cleanup] does not need or . 90. [cleanup] Removed unneeded ISC_LANG_BEGINDECLS/ISC_LANG_ENDDECLS from . 89. [cleanup] does not need . 88. [cleanup] does not need or . isc_interface_t and isc_interfaceiter_t moved to . 87. [cleanup] does not need , or . 86. [cleanup] isc_bufferlist_t moved from to . 85. [cleanup] does not need , , , or . 84. [func] allow-query ACL checks now apply to all data added to a response. 83. [func] If the server is authoritative for both a delegating zone and its (nonsecure) delegatee, and a query is made for a KEY RR at the top of the delegatee, then the server will look for a KEY in the delegator if it is not found in the delegatee. 82. [cleanup] does not need . 81. [cleanup] and do not need . 80. [cleanup] does not need or . 79. [cleanup] does not need . 78. [cleanup] lwres_conftest renamed to lwresconf_test for consistency with other *_test programs. 77. [cleanup] typedef of isc_time_t and isc_interval_t moved from to . 76. [cleanup] Rewrote keygen. 75. [func] Don't load a zone if its database file is older than the last time the zone was loaded. 74. [cleanup] Removed mktemplate.o and ufile.o from libisc.a, subsumed by file.o. 73. [func] New "file" API in libisc, including new function isc_file_getmodtime, isc_mktemplate renamed to isc_file_mktemplate and isc_ufile renamed to isc_file_openunique. By no means an exhaustive API, it is just what's needed for now. 72. [func] DNS_RBTFIND_NOPREDECESSOR and DNS_RBTFIND_NOOPTIONS added for dns_rbt_findnode, the former to disable the setting of the chain to the predecessor, and the latter to make clear when no options are set. 71. [cleanup] Made explicit the implicit REQUIREs of isc_time_seconds, isc_time_nanoseconds, and isc_time_subtract. 70. [func] isc_time_set() added. 69. [bug] The zone object's master and also-notify lists grew longer with each server reload. 68. [func] Partial support for SIG(0) on incoming messages. 67. [performance] Allow use of alternate (compile-time supplied) OpenSSL libraries/headers. 66. [func] Data in authoritative zones should have a trust level beyond secure. 65. [cleanup] Removed obsolete typedef of dns_zone_callbackarg_t from . 64. [func] The RBT, DB, and zone table APIs now allow the caller find the most-enclosing superdomain of a name. 63. [func] Generate NOTIFY messages. 62. [func] Add UDP refresh support. 61. [cleanup] Use single quotes consistently in log messages. 60. [func] Catch and disallow singleton types on message parse. 59. [bug] Cause net/host unreachable to be a hard error when sending and receiving. 58. [bug] bin/named/query.c could sometimes trigger the (client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) == 0 assertion in query_newname(). 57. [func] Added dns_nxt_typepresent() 56. [bug] SIG records were not properly returned in cached negative answers. 55. [bug] Responses containing multiple names in the authority section were not negatively cached. 54. [bug] If a fetch with sigrdataset==NULL joined one with sigrdataset!=NULL or vice versa, the resolver could catch an assertion or lose signature data, respectively. 53. [port] freebsd 4.0: lib/isc/unix/socket.c requires . 52. [bug] rndc: taskmgr and socketmgr were not initialized to NULL. 51. [cleanup] dns/compress.h and dns/zt.h did not need to include dns/rbt.h; it was needed only by compress.c and zt.c. 50. [func] RBT deletion no longer requires a valid chain to work, and dns_rbt_deletenode was added. 49. [func] Each cache now has its own mctx. 48. [func] isc_task_create() no longer takes an mctx. isc_task_mem() has been eliminated. 47. [func] A number of modules now use memory context reference counting. 46. [func] Memory contexts are now reference counted. Added isc_mem_inuse() and isc_mem_preallocate(). Renamed isc_mem_destroy_check() to isc_mem_setdestroycheck(). 45. [bug] The trusted-key statement incorrectly loaded keys. 44. [bug] Don't include authority data if it would force us to unset the AD bit in the message. 43. [bug] DNSSEC verification of cached rdatasets was failing. 42. [cleanup] Simplified logging of messages with embedded domain names by introducing a new convenience function dns_name_format(). 41. [func] Use PR_SET_KEEPCAPS on Linux 2.3.99-pre3 and later to allow 'named' to run as a non-root user while retaining the ability to bind() to privileged ports. 40. [func] Introduced new logging category "dnssec" and logging module "dns/validator". 39. [cleanup] Moved the typedefs for isc_region_t, isc_textregion_t, and isc_lex_t to . 38. [bug] TSIG signed incoming zone transfers work now. 37. [bug] If the first RR in an incoming zone transfer was not an SOA, the server died with an assertion failure instead of just reporting an error. 36. [cleanup] Change DNS_R_SUCCESS (and others) to ISC_R_SUCCESS 35. [performance] Log messages which are of a level too high to be logged by any channel in the logging configuration will not cause the log mutex to be locked. 34. [bug] Recursion was allowed even with 'recursion no'. 33. [func] The RBT now maintains a parent pointer at each node. 32. [cleanup] bin/lwresd/client.c needs for memset() prototype. 31. [bug] Use ${LIBTOOL} to compile bin/named/main.@O@. 30. [func] config file grammar change to support optional class type for a view. 29. [func] support new config file view options: auth-nxdomain recursion query-source query-source-v6 transfer-source transfer-source-v6 max-transfer-time-out max-transfer-idle-out transfer-format request-ixfr provide-ixfr cleaning-interval fetch-glue notify rfc2308-type1 lame-ttl max-ncache-ttl min-roots 28. [func] support lame-ttl, min-roots and serial-queries config global options. 27. [bug] Only include on BSD/OS 4.[01]*. Including it on other platforms (eg, NetBSD) can cause a forced #error from the C preprocessor. 26. [func] new match-clients statement in config file view. 25. [bug] make install failed to install and . 24. [cleanup] Eliminate some unnecessary #includes of header files from header files. 23. [cleanup] Provide more context in log messages about client requests, using a new function ns_client_log(). 22. [bug] SIGs weren't returned in the answer section when the query resulted in a fetch. 21. [port] Look at STD_CINCLUDES after CINCLUDES during compilation, so additional system include directories can be searched but header files in the bind9 source tree with conflicting names take precedence. This avoids issues with installed versions of dnssafe and openssl. 20. [func] Configuration file post-load validation of zones failed if there were no zones. 19. [bug] dns_zone_notifyreceive() failed to unlock the zone lock in certain error cases. 18. [bug] Use AC_TRY_LINK rather than AC_TRY_COMPILE in configure.in to check for presence of in6addr_any. 17. [func] Do configuration file post-load validation of zones. 16. [bug] put quotes around key names on config file output to avoid possible keyword clashes. 15. [func] Add dns_name_dupwithoffsets(). This function is improves comparison performance for duped names. 14. [bug] free_rbtdb() could have 'put' unallocated memory in an unlikely error path. 13. [bug] lib/dns/master.c and lib/dns/xfrin.c didn't ignore out-of-zone data. 12. [bug] Fixed possible uninitialized variable error. 11. [bug] axfr_rrstream_first() didn't check the result code of db_rr_iterator_first(), possibly causing an assertion to be triggered later. 10. [bug] A bug in the code which makes EDNS0 OPT records in bin/named/client.c and lib/dns/resolver.c could trigger an assertion. 9. [cleanup] replaced bit-setting code in confctx.c and replaced repeated code with macro calls. 8. [bug] Shutdown of incoming zone transfer accessed freed memory. 7. [cleanup] removed 'listen-on' from view statement. 6. [bug] quote RR names when generating config file to prevent possible clash with config file keywords (such as 'key'). 5. [func] syntax change to named.conf file: new ssu grant/deny statements must now be enclosed by an 'update-policy' block. 4. [port] bin/named/unix/os.c didn't compile on systems with linux 2.3 kernel includes due to conflicts between C library includes and the kernel includes. We now get only what we need from , and avoid pulling in other linux kernel .h files. 3. [bug] TKEYs go in the answer section of responses, not the additional section. 2. [bug] Generating cryptographic randomness failed on systems without /dev/random. 1. [bug] The installdirs rule in lib/isc/unix/include/isc/Makefile.in had a typo which prevented the isc directory from being created if it didn't exist. --- 9.0.0b2 released --- # This tells Emacs to use hard tabs in this file. # Local Variables: # indent-tabs-mode: t # End: Index: stable/9/contrib/bind9/bin/named/config.c =================================================================== --- stable/9/contrib/bind9/bin/named/config.c (revision 275668) +++ stable/9/contrib/bind9/bin/named/config.c (revision 275669) @@ -1,872 +1,871 @@ /* * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2001-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: config.c,v 1.123 2012/01/06 23:46:41 tbox Exp $ */ - /*! \file */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bind.keys.h" /*% default configuration */ static char defaultconf[] = "\ options {\n\ # blackhole {none;};\n" #ifndef WIN32 " coresize default;\n\ datasize default;\n\ files unlimited;\n\ stacksize default;\n" #endif "# session-keyfile \"" NS_LOCALSTATEDIR "/run/named/session.key\";\n\ session-keyname local-ddns;\n\ session-keyalg hmac-sha256;\n\ deallocate-on-exit true;\n\ # directory \n\ dump-file \"named_dump.db\";\n\ fake-iquery no;\n\ has-old-clients false;\n\ heartbeat-interval 60;\n\ host-statistics no;\n\ interface-interval 60;\n\ listen-on {any;};\n\ listen-on-v6 {none;};\n\ match-mapped-addresses no;\n\ max-rsa-exponent-size 0; /* no limit */\n\ memstatistics-file \"named.memstats\";\n\ multiple-cnames no;\n\ # named-xfer ;\n\ # pid-file \"" NS_LOCALSTATEDIR "/run/named/named.pid\"; /* or /lwresd.pid */\n\ bindkeys-file \"" NS_SYSCONFDIR "/bind.keys\";\n\ port 53;\n\ recursing-file \"named.recursing\";\n\ secroots-file \"named.secroots\";\n\ " #ifdef PATH_RANDOMDEV "\ random-device \"" PATH_RANDOMDEV "\";\n\ " #endif "\ recursive-clients 1000;\n\ resolver-query-timeout 10;\n\ rrset-order { order random; };\n\ serial-queries 20;\n\ serial-query-rate 20;\n\ server-id none;\n\ statistics-file \"named.stats\";\n\ statistics-interval 60;\n\ tcp-clients 100;\n\ tcp-listen-queue 10;\n\ # tkey-dhkey \n\ # tkey-gssapi-credential \n\ # tkey-domain \n\ transfers-per-ns 2;\n\ transfers-in 10;\n\ transfers-out 10;\n\ treat-cr-as-space true;\n\ use-id-pool true;\n\ use-ixfr true;\n\ edns-udp-size 4096;\n\ max-udp-size 4096;\n\ request-nsid false;\n\ reserved-sockets 512;\n\ \n\ /* DLV */\n\ dnssec-lookaside . trust-anchor dlv.isc.org;\n\ \n\ /* view */\n\ allow-notify {none;};\n\ allow-update-forwarding {none;};\n\ allow-query-cache { localnets; localhost; };\n\ allow-query-cache-on { any; };\n\ allow-recursion { localnets; localhost; };\n\ allow-recursion-on { any; };\n\ # allow-v6-synthesis ;\n\ # sortlist \n\ # topology \n\ auth-nxdomain false;\n\ minimal-responses false;\n\ recursion true;\n\ provide-ixfr true;\n\ request-ixfr true;\n\ fetch-glue no;\n\ rfc2308-type1 no;\n\ additional-from-auth true;\n\ additional-from-cache true;\n\ query-source address *;\n\ query-source-v6 address *;\n\ notify-source *;\n\ notify-source-v6 *;\n\ cleaning-interval 0; /* now meaningless */\n\ min-roots 2;\n\ lame-ttl 600;\n\ max-ncache-ttl 10800; /* 3 hours */\n\ max-cache-ttl 604800; /* 1 week */\n\ transfer-format many-answers;\n\ max-cache-size 0;\n\ check-names master fail;\n\ check-names slave warn;\n\ check-names response ignore;\n\ check-dup-records warn;\n\ check-mx warn;\n\ check-spf warn;\n\ acache-enable no;\n\ acache-cleaning-interval 60;\n\ max-acache-size 16M;\n\ dnssec-enable yes;\n\ dnssec-validation yes; \n\ dnssec-accept-expired no;\n\ clients-per-query 10;\n\ max-clients-per-query 100;\n\ + max-recursion-depth 7;\n\ zero-no-soa-ttl-cache no;\n\ nsec3-test-zone no;\n\ allow-new-zones no;\n\ " #ifdef ALLOW_FILTER_AAAA_ON_V4 " filter-aaaa-on-v4 no;\n\ filter-aaaa { any; };\n\ " #endif " /* zone */\n\ allow-query {any;};\n\ allow-query-on {any;};\n\ allow-transfer {any;};\n\ notify yes;\n\ # also-notify \n\ notify-delay 5;\n\ notify-to-soa no;\n\ dialup no;\n\ # forward \n\ # forwarders \n\ maintain-ixfr-base no;\n\ # max-ixfr-log-size \n\ transfer-source *;\n\ transfer-source-v6 *;\n\ alt-transfer-source *;\n\ alt-transfer-source-v6 *;\n\ max-transfer-time-in 120;\n\ max-transfer-time-out 120;\n\ max-transfer-idle-in 60;\n\ max-transfer-idle-out 60;\n\ max-retry-time 1209600; /* 2 weeks */\n\ min-retry-time 500;\n\ max-refresh-time 2419200; /* 4 weeks */\n\ min-refresh-time 300;\n\ multi-master no;\n\ dnssec-secure-to-insecure no;\n\ sig-validity-interval 30; /* days */\n\ sig-signing-nodes 100;\n\ sig-signing-signatures 10;\n\ sig-signing-type 65534;\n\ inline-signing no;\n\ zone-statistics terse;\n\ max-journal-size unlimited;\n\ ixfr-from-differences false;\n\ check-wildcard yes;\n\ check-sibling yes;\n\ check-integrity yes;\n\ check-mx-cname warn;\n\ check-srv-cname warn;\n\ zero-no-soa-ttl yes;\n\ update-check-ksk yes;\n\ serial-update-method increment;\n\ dnssec-update-mode maintain;\n\ dnssec-dnskey-kskonly no;\n\ dnssec-loadkeys-interval 60;\n\ try-tcp-refresh yes; /* BIND 8 compat */\n\ };\n\ " "#\n\ # Zones in the \"_bind\" view are NOT counted in the count of zones.\n\ #\n\ view \"_bind\" chaos {\n\ recursion no;\n\ notify no;\n\ allow-new-zones no;\n\ " #ifdef USE_RRL " # Prevent use of this zone in DNS amplified reflection DoS attacks\n\ rate-limit {\n\ responses-per-second 3;\n\ slip 0;\n\ min-table-size 10;\n\ };\n\ " #endif /* USE_RRL */ " zone \"version.bind\" chaos {\n\ type master;\n\ database \"_builtin version\";\n\ };\n\ \n\ zone \"hostname.bind\" chaos {\n\ type master;\n\ database \"_builtin hostname\";\n\ };\n\ \n\ zone \"authors.bind\" chaos {\n\ type master;\n\ database \"_builtin authors\";\n\ };\n\ \n\ zone \"id.server\" chaos {\n\ type master;\n\ database \"_builtin id\";\n\ };\n\ };\n\ " "#\n\ # Default trusted key(s) for builtin DLV support\n\ # (used if \"dnssec-lookaside auto;\" is set and\n\ # sysconfdir/bind.keys doesn't exist).\n\ #\n\ # BEGIN MANAGED KEYS\n" /* Imported from bind.keys.h: */ MANAGED_KEYS "# END MANAGED KEYS\n\ "; isc_result_t ns_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf) { isc_buffer_t b; isc_buffer_init(&b, defaultconf, sizeof(defaultconf) - 1); isc_buffer_add(&b, sizeof(defaultconf) - 1); return (cfg_parse_buffer(parser, &b, &cfg_type_namedconf, conf)); } isc_result_t ns_config_get(const cfg_obj_t **maps, const char *name, const cfg_obj_t **obj) { int i; for (i = 0;; i++) { if (maps[i] == NULL) return (ISC_R_NOTFOUND); if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS) return (ISC_R_SUCCESS); } } isc_result_t ns_checknames_get(const cfg_obj_t **maps, const char *which, const cfg_obj_t **obj) { const cfg_listelt_t *element; const cfg_obj_t *checknames; const cfg_obj_t *type; const cfg_obj_t *value; int i; for (i = 0;; i++) { if (maps[i] == NULL) return (ISC_R_NOTFOUND); checknames = NULL; if (cfg_map_get(maps[i], "check-names", &checknames) == ISC_R_SUCCESS) { /* * Zone map entry is not a list. */ if (checknames != NULL && !cfg_obj_islist(checknames)) { *obj = checknames; return (ISC_R_SUCCESS); } for (element = cfg_list_first(checknames); element != NULL; element = cfg_list_next(element)) { value = cfg_listelt_value(element); type = cfg_tuple_get(value, "type"); if (strcasecmp(cfg_obj_asstring(type), which) == 0) { *obj = cfg_tuple_get(value, "mode"); return (ISC_R_SUCCESS); } } } } } int ns_config_listcount(const cfg_obj_t *list) { const cfg_listelt_t *e; int i = 0; for (e = cfg_list_first(list); e != NULL; e = cfg_list_next(e)) i++; return (i); } isc_result_t ns_config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass, dns_rdataclass_t *classp) { isc_textregion_t r; isc_result_t result; if (!cfg_obj_isstring(classobj)) { *classp = defclass; return (ISC_R_SUCCESS); } DE_CONST(cfg_obj_asstring(classobj), r.base); r.length = strlen(r.base); result = dns_rdataclass_fromtext(classp, &r); if (result != ISC_R_SUCCESS) cfg_obj_log(classobj, ns_g_lctx, ISC_LOG_ERROR, "unknown class '%s'", r.base); return (result); } isc_result_t ns_config_gettype(const cfg_obj_t *typeobj, dns_rdatatype_t deftype, dns_rdatatype_t *typep) { isc_textregion_t r; isc_result_t result; if (!cfg_obj_isstring(typeobj)) { *typep = deftype; return (ISC_R_SUCCESS); } DE_CONST(cfg_obj_asstring(typeobj), r.base); r.length = strlen(r.base); result = dns_rdatatype_fromtext(typep, &r); if (result != ISC_R_SUCCESS) cfg_obj_log(typeobj, ns_g_lctx, ISC_LOG_ERROR, "unknown type '%s'", r.base); return (result); } dns_zonetype_t ns_config_getzonetype(const cfg_obj_t *zonetypeobj) { dns_zonetype_t ztype = dns_zone_none; const char *str; str = cfg_obj_asstring(zonetypeobj); if (strcasecmp(str, "master") == 0) ztype = dns_zone_master; else if (strcasecmp(str, "slave") == 0) ztype = dns_zone_slave; else if (strcasecmp(str, "stub") == 0) ztype = dns_zone_stub; else if (strcasecmp(str, "static-stub") == 0) ztype = dns_zone_staticstub; else if (strcasecmp(str, "redirect") == 0) ztype = dns_zone_redirect; else INSIST(0); return (ztype); } isc_result_t ns_config_getiplist(const cfg_obj_t *config, const cfg_obj_t *list, in_port_t defport, isc_mem_t *mctx, isc_sockaddr_t **addrsp, isc_uint32_t *countp) { int count, i = 0; const cfg_obj_t *addrlist; const cfg_obj_t *portobj; const cfg_listelt_t *element; isc_sockaddr_t *addrs; in_port_t port; isc_result_t result; INSIST(addrsp != NULL && *addrsp == NULL); INSIST(countp != NULL); addrlist = cfg_tuple_get(list, "addresses"); count = ns_config_listcount(addrlist); portobj = cfg_tuple_get(list, "port"); if (cfg_obj_isuint32(portobj)) { isc_uint32_t val = cfg_obj_asuint32(portobj); if (val > ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port '%u' out of range", val); return (ISC_R_RANGE); } port = (in_port_t) val; } else if (defport != 0) port = defport; else { result = ns_config_getport(config, &port); if (result != ISC_R_SUCCESS) return (result); } addrs = isc_mem_get(mctx, count * sizeof(isc_sockaddr_t)); if (addrs == NULL) return (ISC_R_NOMEMORY); for (element = cfg_list_first(addrlist); element != NULL; element = cfg_list_next(element), i++) { INSIST(i < count); addrs[i] = *cfg_obj_assockaddr(cfg_listelt_value(element)); if (isc_sockaddr_getport(&addrs[i]) == 0) isc_sockaddr_setport(&addrs[i], port); } INSIST(i == count); *addrsp = addrs; *countp = count; return (ISC_R_SUCCESS); } void ns_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp, isc_uint32_t count) { INSIST(addrsp != NULL && *addrsp != NULL); isc_mem_put(mctx, *addrsp, count * sizeof(isc_sockaddr_t)); *addrsp = NULL; } static isc_result_t get_masters_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) { isc_result_t result; const cfg_obj_t *masters = NULL; const cfg_listelt_t *elt; result = cfg_map_get(cctx, "masters", &masters); if (result != ISC_R_SUCCESS) return (result); for (elt = cfg_list_first(masters); elt != NULL; elt = cfg_list_next(elt)) { const cfg_obj_t *list; const char *listname; list = cfg_listelt_value(elt); listname = cfg_obj_asstring(cfg_tuple_get(list, "name")); if (strcasecmp(listname, name) == 0) { *ret = list; return (ISC_R_SUCCESS); } } return (ISC_R_NOTFOUND); } isc_result_t ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list, isc_mem_t *mctx, isc_sockaddr_t **addrsp, dns_name_t ***keysp, isc_uint32_t *countp) { isc_uint32_t addrcount = 0, keycount = 0, i = 0; isc_uint32_t listcount = 0, l = 0, j; isc_uint32_t stackcount = 0, pushed = 0; isc_result_t result; const cfg_listelt_t *element; const cfg_obj_t *addrlist; const cfg_obj_t *portobj; in_port_t port; dns_fixedname_t fname; isc_sockaddr_t *addrs = NULL; dns_name_t **keys = NULL; struct { const char *name; } *lists = NULL; struct { const cfg_listelt_t *element; in_port_t port; } *stack = NULL; REQUIRE(addrsp != NULL && *addrsp == NULL); REQUIRE(keysp != NULL && *keysp == NULL); REQUIRE(countp != NULL); newlist: addrlist = cfg_tuple_get(list, "addresses"); portobj = cfg_tuple_get(list, "port"); if (cfg_obj_isuint32(portobj)) { isc_uint32_t val = cfg_obj_asuint32(portobj); if (val > ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port '%u' out of range", val); result = ISC_R_RANGE; goto cleanup; } port = (in_port_t) val; } else { result = ns_config_getport(config, &port); if (result != ISC_R_SUCCESS) goto cleanup; } result = ISC_R_NOMEMORY; element = cfg_list_first(addrlist); resume: for ( ; element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *addr; const cfg_obj_t *key; const char *keystr; isc_buffer_t b; addr = cfg_tuple_get(cfg_listelt_value(element), "masterselement"); key = cfg_tuple_get(cfg_listelt_value(element), "key"); if (!cfg_obj_issockaddr(addr)) { const char *listname = cfg_obj_asstring(addr); isc_result_t tresult; /* Grow lists? */ if (listcount == l) { void * new; isc_uint32_t newlen = listcount + 16; size_t newsize, oldsize; newsize = newlen * sizeof(*lists); oldsize = listcount * sizeof(*lists); new = isc_mem_get(mctx, newsize); if (new == NULL) goto cleanup; if (listcount != 0) { memmove(new, lists, oldsize); isc_mem_put(mctx, lists, oldsize); } lists = new; listcount = newlen; } /* Seen? */ for (j = 0; j < l; j++) if (strcasecmp(lists[j].name, listname) == 0) break; if (j < l) continue; tresult = get_masters_def(config, listname, &list); if (tresult == ISC_R_NOTFOUND) { cfg_obj_log(addr, ns_g_lctx, ISC_LOG_ERROR, "masters \"%s\" not found", listname); result = tresult; goto cleanup; } if (tresult != ISC_R_SUCCESS) goto cleanup; lists[l++].name = listname; /* Grow stack? */ if (stackcount == pushed) { void * new; isc_uint32_t newlen = stackcount + 16; size_t newsize, oldsize; newsize = newlen * sizeof(*stack); oldsize = stackcount * sizeof(*stack); new = isc_mem_get(mctx, newsize); if (new == NULL) goto cleanup; if (stackcount != 0) { memmove(new, stack, oldsize); isc_mem_put(mctx, stack, oldsize); } stack = new; stackcount = newlen; } /* * We want to resume processing this list on the * next element. */ stack[pushed].element = cfg_list_next(element); stack[pushed].port = port; pushed++; goto newlist; } if (i == addrcount) { void * new; isc_uint32_t newlen = addrcount + 16; size_t newsize, oldsize; newsize = newlen * sizeof(isc_sockaddr_t); oldsize = addrcount * sizeof(isc_sockaddr_t); new = isc_mem_get(mctx, newsize); if (new == NULL) goto cleanup; if (addrcount != 0) { memmove(new, addrs, oldsize); isc_mem_put(mctx, addrs, oldsize); } addrs = new; addrcount = newlen; newsize = newlen * sizeof(dns_name_t *); oldsize = keycount * sizeof(dns_name_t *); new = isc_mem_get(mctx, newsize); if (new == NULL) goto cleanup; if (keycount != 0) { memmove(new, keys, oldsize); isc_mem_put(mctx, keys, oldsize); } keys = new; keycount = newlen; } addrs[i] = *cfg_obj_assockaddr(addr); if (isc_sockaddr_getport(&addrs[i]) == 0) isc_sockaddr_setport(&addrs[i], port); keys[i] = NULL; i++; /* Increment here so that cleanup on error works. */ if (!cfg_obj_isstring(key)) continue; keys[i - 1] = isc_mem_get(mctx, sizeof(dns_name_t)); if (keys[i - 1] == NULL) goto cleanup; dns_name_init(keys[i - 1], NULL); keystr = cfg_obj_asstring(key); isc_buffer_constinit(&b, keystr, strlen(keystr)); isc_buffer_add(&b, strlen(keystr)); dns_fixedname_init(&fname); result = dns_name_fromtext(dns_fixedname_name(&fname), &b, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_name_dup(dns_fixedname_name(&fname), mctx, keys[i - 1]); if (result != ISC_R_SUCCESS) goto cleanup; } if (pushed != 0) { pushed--; element = stack[pushed].element; port = stack[pushed].port; goto resume; } if (i < addrcount) { void * new; size_t newsize, oldsize; newsize = i * sizeof(isc_sockaddr_t); oldsize = addrcount * sizeof(isc_sockaddr_t); if (i != 0) { new = isc_mem_get(mctx, newsize); if (new == NULL) goto cleanup; memmove(new, addrs, newsize); } else new = NULL; isc_mem_put(mctx, addrs, oldsize); addrs = new; addrcount = i; newsize = i * sizeof(dns_name_t *); oldsize = keycount * sizeof(dns_name_t *); if (i != 0) { new = isc_mem_get(mctx, newsize); if (new == NULL) goto cleanup; memmove(new, keys, newsize); } else new = NULL; isc_mem_put(mctx, keys, oldsize); keys = new; keycount = i; } if (lists != NULL) isc_mem_put(mctx, lists, listcount * sizeof(*lists)); if (stack != NULL) isc_mem_put(mctx, stack, stackcount * sizeof(*stack)); INSIST(keycount == addrcount); *addrsp = addrs; *keysp = keys; *countp = addrcount; return (ISC_R_SUCCESS); cleanup: if (addrs != NULL) isc_mem_put(mctx, addrs, addrcount * sizeof(isc_sockaddr_t)); if (keys != NULL) { for (j = 0; j < i; j++) { if (keys[j] == NULL) continue; if (dns_name_dynamic(keys[j])) dns_name_free(keys[j], mctx); isc_mem_put(mctx, keys[j], sizeof(dns_name_t)); } isc_mem_put(mctx, keys, keycount * sizeof(dns_name_t *)); } if (lists != NULL) isc_mem_put(mctx, lists, listcount * sizeof(*lists)); if (stack != NULL) isc_mem_put(mctx, stack, stackcount * sizeof(*stack)); return (result); } void ns_config_putipandkeylist(isc_mem_t *mctx, isc_sockaddr_t **addrsp, dns_name_t ***keysp, isc_uint32_t count) { unsigned int i; dns_name_t **keys = *keysp; INSIST(addrsp != NULL && *addrsp != NULL); isc_mem_put(mctx, *addrsp, count * sizeof(isc_sockaddr_t)); for (i = 0; i < count; i++) { if (keys[i] == NULL) continue; if (dns_name_dynamic(keys[i])) dns_name_free(keys[i], mctx); isc_mem_put(mctx, keys[i], sizeof(dns_name_t)); } isc_mem_put(mctx, *keysp, count * sizeof(dns_name_t *)); *addrsp = NULL; *keysp = NULL; } isc_result_t ns_config_getport(const cfg_obj_t *config, in_port_t *portp) { const cfg_obj_t *maps[3]; const cfg_obj_t *options = NULL; const cfg_obj_t *portobj = NULL; isc_result_t result; int i; (void)cfg_map_get(config, "options", &options); i = 0; if (options != NULL) maps[i++] = options; maps[i++] = ns_g_defaults; maps[i] = NULL; result = ns_config_get(maps, "port", &portobj); INSIST(result == ISC_R_SUCCESS); if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port '%u' out of range", cfg_obj_asuint32(portobj)); return (ISC_R_RANGE); } *portp = (in_port_t)cfg_obj_asuint32(portobj); return (ISC_R_SUCCESS); } struct keyalgorithms { const char *str; enum { hmacnone, hmacmd5, hmacsha1, hmacsha224, hmacsha256, hmacsha384, hmacsha512 } hmac; unsigned int type; isc_uint16_t size; } algorithms[] = { { "hmac-md5", hmacmd5, DST_ALG_HMACMD5, 128 }, { "hmac-md5.sig-alg.reg.int", hmacmd5, DST_ALG_HMACMD5, 0 }, { "hmac-md5.sig-alg.reg.int.", hmacmd5, DST_ALG_HMACMD5, 0 }, { "hmac-sha1", hmacsha1, DST_ALG_HMACSHA1, 160 }, { "hmac-sha224", hmacsha224, DST_ALG_HMACSHA224, 224 }, { "hmac-sha256", hmacsha256, DST_ALG_HMACSHA256, 256 }, { "hmac-sha384", hmacsha384, DST_ALG_HMACSHA384, 384 }, { "hmac-sha512", hmacsha512, DST_ALG_HMACSHA512, 512 }, { NULL, hmacnone, DST_ALG_UNKNOWN, 0 } }; isc_result_t ns_config_getkeyalgorithm(const char *str, dns_name_t **name, isc_uint16_t *digestbits) { return (ns_config_getkeyalgorithm2(str, name, NULL, digestbits)); } isc_result_t ns_config_getkeyalgorithm2(const char *str, dns_name_t **name, unsigned int *typep, isc_uint16_t *digestbits) { int i; size_t len = 0; isc_uint16_t bits; isc_result_t result; for (i = 0; algorithms[i].str != NULL; i++) { len = strlen(algorithms[i].str); if (strncasecmp(algorithms[i].str, str, len) == 0 && (str[len] == '\0' || (algorithms[i].size != 0 && str[len] == '-'))) break; } if (algorithms[i].str == NULL) return (ISC_R_NOTFOUND); if (str[len] == '-') { result = isc_parse_uint16(&bits, str + len + 1, 10); if (result != ISC_R_SUCCESS) return (result); if (bits > algorithms[i].size) return (ISC_R_RANGE); } else if (algorithms[i].size == 0) bits = 128; else bits = algorithms[i].size; if (name != NULL) { switch (algorithms[i].hmac) { case hmacmd5: *name = dns_tsig_hmacmd5_name; break; case hmacsha1: *name = dns_tsig_hmacsha1_name; break; case hmacsha224: *name = dns_tsig_hmacsha224_name; break; case hmacsha256: *name = dns_tsig_hmacsha256_name; break; case hmacsha384: *name = dns_tsig_hmacsha384_name; break; case hmacsha512: *name = dns_tsig_hmacsha512_name; break; default: INSIST(0); } } if (typep != NULL) *typep = algorithms[i].type; if (digestbits != NULL) *digestbits = bits; return (ISC_R_SUCCESS); } Index: stable/9/contrib/bind9/bin/named/query.c =================================================================== --- stable/9/contrib/bind9/bin/named/query.c (revision 275668) +++ stable/9/contrib/bind9/bin/named/query.c (revision 275669) @@ -1,7801 +1,7800 @@ /* * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id$ */ /*! \file */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 0 /* * It has been recommended that DNS64 be changed to return excluded * AAAA addresses if DNS64 synthesis does not occur. This minimises * the impact on the lookup results. While most DNS AAAA lookups are * done to send IP packets to a host, not all of them are and filtering * excluded addresses has a negative impact on those uses. */ #define dns64_bis_return_excluded_addresses 1 #endif /*% Partial answer? */ #define PARTIALANSWER(c) (((c)->query.attributes & \ NS_QUERYATTR_PARTIALANSWER) != 0) /*% Use Cache? */ #define USECACHE(c) (((c)->query.attributes & \ NS_QUERYATTR_CACHEOK) != 0) /*% Recursion OK? */ #define RECURSIONOK(c) (((c)->query.attributes & \ NS_QUERYATTR_RECURSIONOK) != 0) /*% Recursing? */ #define RECURSING(c) (((c)->query.attributes & \ NS_QUERYATTR_RECURSING) != 0) /*% Cache glue ok? */ #define CACHEGLUEOK(c) (((c)->query.attributes & \ NS_QUERYATTR_CACHEGLUEOK) != 0) /*% Want Recursion? */ #define WANTRECURSION(c) (((c)->query.attributes & \ NS_QUERYATTR_WANTRECURSION) != 0) /*% Want DNSSEC? */ #define WANTDNSSEC(c) (((c)->attributes & \ NS_CLIENTATTR_WANTDNSSEC) != 0) /*% Want WANTAD? */ #define WANTAD(c) (((c)->attributes & \ NS_CLIENTATTR_WANTAD) != 0) /*% No authority? */ #define NOAUTHORITY(c) (((c)->query.attributes & \ NS_QUERYATTR_NOAUTHORITY) != 0) /*% No additional? */ #define NOADDITIONAL(c) (((c)->query.attributes & \ NS_QUERYATTR_NOADDITIONAL) != 0) /*% Secure? */ #define SECURE(c) (((c)->query.attributes & \ NS_QUERYATTR_SECURE) != 0) /*% DNS64 A lookup? */ #define DNS64(c) (((c)->query.attributes & \ NS_QUERYATTR_DNS64) != 0) #define DNS64EXCLUDE(c) (((c)->query.attributes & \ NS_QUERYATTR_DNS64EXCLUDE) != 0) /*% No QNAME Proof? */ #define NOQNAME(r) (((r)->attributes & \ DNS_RDATASETATTR_NOQNAME) != 0) #if 0 #define CTRACE(m) isc_log_write(ns_g_lctx, \ NS_LOGCATEGORY_CLIENT, \ NS_LOGMODULE_QUERY, \ ISC_LOG_DEBUG(3), \ "client %p: %s", client, (m)) #define QTRACE(m) isc_log_write(ns_g_lctx, \ NS_LOGCATEGORY_GENERAL, \ NS_LOGMODULE_QUERY, \ ISC_LOG_DEBUG(3), \ "query %p: %s", query, (m)) #else #define CTRACE(m) ((void)m) #define QTRACE(m) ((void)m) #endif #define DNS_GETDB_NOEXACT 0x01U #define DNS_GETDB_NOLOG 0x02U #define DNS_GETDB_PARTIAL 0x04U #define DNS_GETDB_IGNOREACL 0x08U #define PENDINGOK(x) (((x) & DNS_DBFIND_PENDINGOK) != 0) typedef struct client_additionalctx { ns_client_t *client; dns_rdataset_t *rdataset; } client_additionalctx_t; static isc_result_t query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype); static isc_boolean_t validate(ns_client_t *client, dns_db_t *db, dns_name_t *name, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset); static void query_findclosestnsec3(dns_name_t *qname, dns_db_t *db, dns_dbversion_t *version, ns_client_t *client, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, dns_name_t *fname, isc_boolean_t exact, dns_name_t *found); static inline void log_queryerror(ns_client_t *client, isc_result_t result, int line, int level); static void rpz_st_clear(ns_client_t *client); /*% * Increment query statistics counters. */ static inline void inc_stats(ns_client_t *client, isc_statscounter_t counter) { dns_zone_t *zone = client->query.authzone; isc_stats_t *zonestats; #ifdef NEWSTATS dns_rdatatype_t qtype; dns_rdataset_t *rdataset; dns_stats_t *querystats = NULL; #endif isc_stats_increment(ns_g_server->nsstats, counter); if (zone == NULL) return; /* Do regular response type stats */ zonestats = dns_zone_getrequeststats(zone); if (zonestats != NULL) isc_stats_increment(zonestats, counter); #ifdef NEWSTATS /* Do query type statistics * * We only increment per-type if we're using the authoritative * answer counter, preventing double-counting. */ if (counter == dns_nsstatscounter_authans) { querystats = dns_zone_getrcvquerystats(zone); if (querystats != NULL) { rdataset = ISC_LIST_HEAD(client->query.qname->list); if (rdataset != NULL) { qtype = rdataset->type; dns_rdatatypestats_increment(querystats, qtype); } } } #endif } static void query_send(ns_client_t *client) { isc_statscounter_t counter; if ((client->message->flags & DNS_MESSAGEFLAG_AA) == 0) inc_stats(client, dns_nsstatscounter_nonauthans); else inc_stats(client, dns_nsstatscounter_authans); if (client->message->rcode == dns_rcode_noerror) { dns_section_t answer = DNS_SECTION_ANSWER; if (ISC_LIST_EMPTY(client->message->sections[answer])) { if (client->query.isreferral) counter = dns_nsstatscounter_referral; else counter = dns_nsstatscounter_nxrrset; } else counter = dns_nsstatscounter_success; } else if (client->message->rcode == dns_rcode_nxdomain) counter = dns_nsstatscounter_nxdomain; else /* We end up here in case of YXDOMAIN, and maybe others */ counter = dns_nsstatscounter_failure; inc_stats(client, counter); ns_client_send(client); } static void query_error(ns_client_t *client, isc_result_t result, int line) { int loglevel = ISC_LOG_DEBUG(3); switch (result) { case DNS_R_SERVFAIL: loglevel = ISC_LOG_DEBUG(1); inc_stats(client, dns_nsstatscounter_servfail); break; case DNS_R_FORMERR: inc_stats(client, dns_nsstatscounter_formerr); break; default: inc_stats(client, dns_nsstatscounter_failure); break; } log_queryerror(client, result, line, loglevel); ns_client_error(client, result); } static void query_next(ns_client_t *client, isc_result_t result) { if (result == DNS_R_DUPLICATE) inc_stats(client, dns_nsstatscounter_duplicate); else if (result == DNS_R_DROP) inc_stats(client, dns_nsstatscounter_dropped); else inc_stats(client, dns_nsstatscounter_failure); ns_client_next(client, result); } static inline void query_freefreeversions(ns_client_t *client, isc_boolean_t everything) { ns_dbversion_t *dbversion, *dbversion_next; unsigned int i; for (dbversion = ISC_LIST_HEAD(client->query.freeversions), i = 0; dbversion != NULL; dbversion = dbversion_next, i++) { dbversion_next = ISC_LIST_NEXT(dbversion, link); /* * If we're not freeing everything, we keep the first three * dbversions structures around. */ if (i > 3 || everything) { ISC_LIST_UNLINK(client->query.freeversions, dbversion, link); isc_mem_put(client->mctx, dbversion, sizeof(*dbversion)); } } } void ns_query_cancel(ns_client_t *client) { LOCK(&client->query.fetchlock); if (client->query.fetch != NULL) { dns_resolver_cancelfetch(client->query.fetch); client->query.fetch = NULL; } UNLOCK(&client->query.fetchlock); } static inline void query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) { dns_rdataset_t *rdataset = *rdatasetp; CTRACE("query_putrdataset"); if (rdataset != NULL) { if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); dns_message_puttemprdataset(client->message, rdatasetp); } CTRACE("query_putrdataset: done"); } static inline void query_reset(ns_client_t *client, isc_boolean_t everything) { isc_buffer_t *dbuf, *dbuf_next; ns_dbversion_t *dbversion, *dbversion_next; /*% * Reset the query state of a client to its default state. */ /* * Cancel the fetch if it's running. */ ns_query_cancel(client); /* * Cleanup any active versions. */ for (dbversion = ISC_LIST_HEAD(client->query.activeversions); dbversion != NULL; dbversion = dbversion_next) { dbversion_next = ISC_LIST_NEXT(dbversion, link); dns_db_closeversion(dbversion->db, &dbversion->version, ISC_FALSE); dns_db_detach(&dbversion->db); ISC_LIST_INITANDAPPEND(client->query.freeversions, dbversion, link); } ISC_LIST_INIT(client->query.activeversions); if (client->query.authdb != NULL) dns_db_detach(&client->query.authdb); if (client->query.authzone != NULL) dns_zone_detach(&client->query.authzone); if (client->query.dns64_aaaa != NULL) query_putrdataset(client, &client->query.dns64_aaaa); if (client->query.dns64_sigaaaa != NULL) query_putrdataset(client, &client->query.dns64_sigaaaa); if (client->query.dns64_aaaaok != NULL) { isc_mem_put(client->mctx, client->query.dns64_aaaaok, client->query.dns64_aaaaoklen * sizeof(isc_boolean_t)); client->query.dns64_aaaaok = NULL; client->query.dns64_aaaaoklen = 0; } query_freefreeversions(client, everything); for (dbuf = ISC_LIST_HEAD(client->query.namebufs); dbuf != NULL; dbuf = dbuf_next) { dbuf_next = ISC_LIST_NEXT(dbuf, link); if (dbuf_next != NULL || everything) { ISC_LIST_UNLINK(client->query.namebufs, dbuf, link); isc_buffer_free(&dbuf); } } if (client->query.restarts > 0) { /* * client->query.qname was dynamically allocated. */ dns_message_puttempname(client->message, &client->query.qname); } client->query.qname = NULL; client->query.attributes = (NS_QUERYATTR_RECURSIONOK | NS_QUERYATTR_CACHEOK | NS_QUERYATTR_SECURE); client->query.restarts = 0; client->query.timerset = ISC_FALSE; if (client->query.rpz_st != NULL) { rpz_st_clear(client); if (everything) { isc_mem_put(client->mctx, client->query.rpz_st, sizeof(*client->query.rpz_st)); client->query.rpz_st = NULL; } } client->query.origqname = NULL; client->query.dboptions = 0; client->query.fetchoptions = 0; client->query.gluedb = NULL; client->query.authdbset = ISC_FALSE; client->query.isreferral = ISC_FALSE; client->query.dns64_options = 0; client->query.dns64_ttl = ISC_UINT32_MAX; } static void query_next_callback(ns_client_t *client) { query_reset(client, ISC_FALSE); } void ns_query_free(ns_client_t *client) { query_reset(client, ISC_TRUE); } static inline isc_result_t query_newnamebuf(ns_client_t *client) { isc_buffer_t *dbuf; isc_result_t result; CTRACE("query_newnamebuf"); /*% * Allocate a name buffer. */ dbuf = NULL; result = isc_buffer_allocate(client->mctx, &dbuf, 1024); if (result != ISC_R_SUCCESS) { CTRACE("query_newnamebuf: isc_buffer_allocate failed: done"); return (result); } ISC_LIST_APPEND(client->query.namebufs, dbuf, link); CTRACE("query_newnamebuf: done"); return (ISC_R_SUCCESS); } static inline isc_buffer_t * query_getnamebuf(ns_client_t *client) { isc_buffer_t *dbuf; isc_result_t result; isc_region_t r; CTRACE("query_getnamebuf"); /*% * Return a name buffer with space for a maximal name, allocating * a new one if necessary. */ if (ISC_LIST_EMPTY(client->query.namebufs)) { result = query_newnamebuf(client); if (result != ISC_R_SUCCESS) { CTRACE("query_getnamebuf: query_newnamebuf failed: done"); return (NULL); } } dbuf = ISC_LIST_TAIL(client->query.namebufs); INSIST(dbuf != NULL); isc_buffer_availableregion(dbuf, &r); if (r.length < 255) { result = query_newnamebuf(client); if (result != ISC_R_SUCCESS) { CTRACE("query_getnamebuf: query_newnamebuf failed: done"); return (NULL); } dbuf = ISC_LIST_TAIL(client->query.namebufs); isc_buffer_availableregion(dbuf, &r); INSIST(r.length >= 255); } CTRACE("query_getnamebuf: done"); return (dbuf); } static inline void query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) { isc_region_t r; CTRACE("query_keepname"); /*% * 'name' is using space in 'dbuf', but 'dbuf' has not yet been * adjusted to take account of that. We do the adjustment. */ REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) != 0); dns_name_toregion(name, &r); isc_buffer_add(dbuf, r.length); dns_name_setbuffer(name, NULL); client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED; } static inline void query_releasename(ns_client_t *client, dns_name_t **namep) { dns_name_t *name = *namep; /*% * 'name' is no longer needed. Return it to our pool of temporary * names. If it is using a name buffer, relinquish its exclusive * rights on the buffer. */ CTRACE("query_releasename"); if (dns_name_hasbuffer(name)) { INSIST((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) != 0); client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED; } dns_message_puttempname(client->message, namep); CTRACE("query_releasename: done"); } static inline dns_name_t * query_newname(ns_client_t *client, isc_buffer_t *dbuf, isc_buffer_t *nbuf) { dns_name_t *name; isc_region_t r; isc_result_t result; REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) == 0); CTRACE("query_newname"); name = NULL; result = dns_message_gettempname(client->message, &name); if (result != ISC_R_SUCCESS) { CTRACE("query_newname: dns_message_gettempname failed: done"); return (NULL); } isc_buffer_availableregion(dbuf, &r); isc_buffer_init(nbuf, r.base, r.length); dns_name_init(name, NULL); dns_name_setbuffer(name, nbuf); client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED; CTRACE("query_newname: done"); return (name); } static inline dns_rdataset_t * query_newrdataset(ns_client_t *client) { dns_rdataset_t *rdataset; isc_result_t result; CTRACE("query_newrdataset"); rdataset = NULL; result = dns_message_gettemprdataset(client->message, &rdataset); if (result != ISC_R_SUCCESS) { CTRACE("query_newrdataset: " "dns_message_gettemprdataset failed: done"); return (NULL); } dns_rdataset_init(rdataset); CTRACE("query_newrdataset: done"); return (rdataset); } static inline isc_result_t query_newdbversion(ns_client_t *client, unsigned int n) { unsigned int i; ns_dbversion_t *dbversion; for (i = 0; i < n; i++) { dbversion = isc_mem_get(client->mctx, sizeof(*dbversion)); if (dbversion != NULL) { dbversion->db = NULL; dbversion->version = NULL; ISC_LIST_INITANDAPPEND(client->query.freeversions, dbversion, link); } else { /* * We only return ISC_R_NOMEMORY if we couldn't * allocate anything. */ if (i == 0) return (ISC_R_NOMEMORY); else return (ISC_R_SUCCESS); } } return (ISC_R_SUCCESS); } static inline ns_dbversion_t * query_getdbversion(ns_client_t *client) { isc_result_t result; ns_dbversion_t *dbversion; if (ISC_LIST_EMPTY(client->query.freeversions)) { result = query_newdbversion(client, 1); if (result != ISC_R_SUCCESS) return (NULL); } dbversion = ISC_LIST_HEAD(client->query.freeversions); INSIST(dbversion != NULL); ISC_LIST_UNLINK(client->query.freeversions, dbversion, link); return (dbversion); } isc_result_t ns_query_init(ns_client_t *client) { isc_result_t result; ISC_LIST_INIT(client->query.namebufs); ISC_LIST_INIT(client->query.activeversions); ISC_LIST_INIT(client->query.freeversions); client->query.restarts = 0; client->query.timerset = ISC_FALSE; client->query.rpz_st = NULL; client->query.qname = NULL; result = isc_mutex_init(&client->query.fetchlock); if (result != ISC_R_SUCCESS) return (result); client->query.fetch = NULL; client->query.authdb = NULL; client->query.authzone = NULL; client->query.authdbset = ISC_FALSE; client->query.isreferral = ISC_FALSE; client->query.dns64_aaaa = NULL; client->query.dns64_sigaaaa = NULL; client->query.dns64_aaaaok = NULL; client->query.dns64_aaaaoklen = 0; query_reset(client, ISC_FALSE); result = query_newdbversion(client, 3); if (result != ISC_R_SUCCESS) { DESTROYLOCK(&client->query.fetchlock); return (result); } result = query_newnamebuf(client); if (result != ISC_R_SUCCESS) query_freefreeversions(client, ISC_TRUE); return (result); } static inline ns_dbversion_t * query_findversion(ns_client_t *client, dns_db_t *db) { ns_dbversion_t *dbversion; /*% * We may already have done a query related to this * database. If so, we must be sure to make subsequent * queries from the same version. */ for (dbversion = ISC_LIST_HEAD(client->query.activeversions); dbversion != NULL; dbversion = ISC_LIST_NEXT(dbversion, link)) { if (dbversion->db == db) break; } if (dbversion == NULL) { /* * This is a new zone for this query. Add it to * the active list. */ dbversion = query_getdbversion(client); if (dbversion == NULL) return (NULL); dns_db_attach(db, &dbversion->db); dns_db_currentversion(db, &dbversion->version); dbversion->acl_checked = ISC_FALSE; dbversion->queryok = ISC_FALSE; ISC_LIST_APPEND(client->query.activeversions, dbversion, link); } return (dbversion); } static inline isc_result_t query_validatezonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, unsigned int options, dns_zone_t *zone, dns_db_t *db, dns_dbversion_t **versionp) { isc_result_t result; dns_acl_t *queryacl, *queryonacl; ns_dbversion_t *dbversion; REQUIRE(zone != NULL); REQUIRE(db != NULL); /* * This limits our searching to the zone where the first name * (the query target) was looked for. This prevents following * CNAMES or DNAMES into other zones and prevents returning * additional data from other zones. */ if (!client->view->additionalfromauth && client->query.authdbset && db != client->query.authdb) return (DNS_R_REFUSED); /* * Non recursive query to a static-stub zone is prohibited; its * zone content is not public data, but a part of local configuration * and should not be disclosed. */ if (dns_zone_gettype(zone) == dns_zone_staticstub && !RECURSIONOK(client)) { return (DNS_R_REFUSED); } /* * If the zone has an ACL, we'll check it, otherwise * we use the view's "allow-query" ACL. Each ACL is only checked * once per query. * * Also, get the database version to use. */ /* * Get the current version of this database. */ dbversion = query_findversion(client, db); if (dbversion == NULL) return (DNS_R_SERVFAIL); if ((options & DNS_GETDB_IGNOREACL) != 0) goto approved; if (dbversion->acl_checked) { if (!dbversion->queryok) return (DNS_R_REFUSED); goto approved; } queryacl = dns_zone_getqueryacl(zone); if (queryacl == NULL) { queryacl = client->view->queryacl; if ((client->query.attributes & NS_QUERYATTR_QUERYOKVALID) != 0) { /* * We've evaluated the view's queryacl already. If * NS_QUERYATTR_QUERYOK is set, then the client is * allowed to make queries, otherwise the query should * be refused. */ dbversion->acl_checked = ISC_TRUE; if ((client->query.attributes & NS_QUERYATTR_QUERYOK) == 0) { dbversion->queryok = ISC_FALSE; return (DNS_R_REFUSED); } dbversion->queryok = ISC_TRUE; goto approved; } } result = ns_client_checkaclsilent(client, NULL, queryacl, ISC_TRUE); if ((options & DNS_GETDB_NOLOG) == 0) { char msg[NS_CLIENT_ACLMSGSIZE("query")]; if (result == ISC_R_SUCCESS) { if (isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(3))) { ns_client_aclmsg("query", name, qtype, client->view->rdclass, msg, sizeof(msg)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_DEBUG(3), "%s approved", msg); } } else { ns_client_aclmsg("query", name, qtype, client->view->rdclass, msg, sizeof(msg)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_INFO, "%s denied", msg); } } if (queryacl == client->view->queryacl) { if (result == ISC_R_SUCCESS) { /* * We were allowed by the default * "allow-query" ACL. Remember this so we * don't have to check again. */ client->query.attributes |= NS_QUERYATTR_QUERYOK; } /* * We've now evaluated the view's query ACL, and * the NS_QUERYATTR_QUERYOK attribute is now valid. */ client->query.attributes |= NS_QUERYATTR_QUERYOKVALID; } /* If and only if we've gotten this far, check allow-query-on too */ if (result == ISC_R_SUCCESS) { queryonacl = dns_zone_getqueryonacl(zone); if (queryonacl == NULL) queryonacl = client->view->queryonacl; result = ns_client_checkaclsilent(client, &client->destaddr, queryonacl, ISC_TRUE); if ((options & DNS_GETDB_NOLOG) == 0 && result != ISC_R_SUCCESS) ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_INFO, "query-on denied"); } dbversion->acl_checked = ISC_TRUE; if (result != ISC_R_SUCCESS) { dbversion->queryok = ISC_FALSE; return (DNS_R_REFUSED); } dbversion->queryok = ISC_TRUE; approved: /* Transfer ownership, if necessary. */ if (versionp != NULL) *versionp = dbversion->version; return (ISC_R_SUCCESS); } static inline isc_result_t query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, unsigned int options, dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp) { isc_result_t result; unsigned int ztoptions; dns_zone_t *zone = NULL; dns_db_t *db = NULL; isc_boolean_t partial = ISC_FALSE; REQUIRE(zonep != NULL && *zonep == NULL); REQUIRE(dbp != NULL && *dbp == NULL); /*% * Find a zone database to answer the query. */ ztoptions = ((options & DNS_GETDB_NOEXACT) != 0) ? DNS_ZTFIND_NOEXACT : 0; result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL, &zone); if (result == DNS_R_PARTIALMATCH) partial = ISC_TRUE; if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) result = dns_zone_getdb(zone, &db); if (result != ISC_R_SUCCESS) goto fail; result = query_validatezonedb(client, name, qtype, options, zone, db, versionp); if (result != ISC_R_SUCCESS) goto fail; /* Transfer ownership. */ *zonep = zone; *dbp = db; if (partial && (options & DNS_GETDB_PARTIAL) != 0) return (DNS_R_PARTIALMATCH); return (ISC_R_SUCCESS); fail: if (zone != NULL) dns_zone_detach(&zone); if (db != NULL) dns_db_detach(&db); return (result); } static void rpz_log_rewrite(ns_client_t *client, isc_boolean_t disabled, dns_rpz_policy_t policy, dns_rpz_type_t type, dns_zone_t *zone, dns_name_t *rpz_qname) { isc_stats_t *zonestats; char qname_buf[DNS_NAME_FORMATSIZE]; char rpz_qname_buf[DNS_NAME_FORMATSIZE]; /* * Count enabled rewrites in the global counter. * Count both enabled and disabled rewrites for each zone. */ if (!disabled && policy != DNS_RPZ_POLICY_PASSTHRU) { isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_rpz_rewrites); } if (zone != NULL) { zonestats = dns_zone_getrequeststats(zone); if (zonestats != NULL) isc_stats_increment(zonestats, dns_nsstatscounter_rpz_rewrites); } if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL)) return; dns_name_format(client->query.qname, qname_buf, sizeof(qname_buf)); dns_name_format(rpz_qname, rpz_qname_buf, sizeof(rpz_qname_buf)); ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY, DNS_RPZ_INFO_LEVEL, "%srpz %s %s rewrite %s via %s", disabled ? "disabled " : "", dns_rpz_type2str(type), dns_rpz_policy2str(policy), qname_buf, rpz_qname_buf); } static void rpz_log_fail(ns_client_t *client, int level, dns_rpz_type_t rpz_type, dns_name_t *name, const char *str, isc_result_t result) { char namebuf1[DNS_NAME_FORMATSIZE]; char namebuf2[DNS_NAME_FORMATSIZE]; if (!isc_log_wouldlog(ns_g_lctx, level)) return; /* * bin/tests/system/rpz/tests.sh looks for "rpz.*failed". */ dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1)); dns_name_format(name, namebuf2, sizeof(namebuf2)); ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS, NS_LOGMODULE_QUERY, level, "rpz %s rewrite %s via %s %sfailed: %s", dns_rpz_type2str(rpz_type), namebuf1, namebuf2, str, isc_result_totext(result)); } /* * Get a policy rewrite zone database. */ static isc_result_t rpz_getdb(ns_client_t *client, dns_rpz_type_t rpz_type, dns_name_t *rpz_qname, dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp) { char namebuf1[DNS_NAME_FORMATSIZE]; char namebuf2[DNS_NAME_FORMATSIZE]; dns_dbversion_t *rpz_version = NULL; isc_result_t result; result = query_getzonedb(client, rpz_qname, dns_rdatatype_any, DNS_GETDB_IGNOREACL, zonep, dbp, &rpz_version); if (result == ISC_R_SUCCESS) { if (isc_log_wouldlog(ns_g_lctx, DNS_RPZ_DEBUG_LEVEL2)) { dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1)); dns_name_format(rpz_qname, namebuf2, sizeof(namebuf2)); ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY, DNS_RPZ_DEBUG_LEVEL2, "try rpz %s rewrite %s via %s", dns_rpz_type2str(rpz_type), namebuf1, namebuf2); } *versionp = rpz_version; return (ISC_R_SUCCESS); } rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, rpz_qname, "query_getzonedb() ", result); return (result); } static inline isc_result_t query_getcachedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, dns_db_t **dbp, unsigned int options) { isc_result_t result; isc_boolean_t check_acl; dns_db_t *db = NULL; REQUIRE(dbp != NULL && *dbp == NULL); /*% * Find a cache database to answer the query. * This may fail with DNS_R_REFUSED if the client * is not allowed to use the cache. */ if (!USECACHE(client)) return (DNS_R_REFUSED); dns_db_attach(client->view->cachedb, &db); if ((client->query.attributes & NS_QUERYATTR_CACHEACLOKVALID) != 0) { /* * We've evaluated the view's cacheacl already. If * NS_QUERYATTR_CACHEACLOK is set, then the client is * allowed to make queries, otherwise the query should * be refused. */ check_acl = ISC_FALSE; if ((client->query.attributes & NS_QUERYATTR_CACHEACLOK) == 0) goto refuse; } else { /* * We haven't evaluated the view's queryacl yet. */ check_acl = ISC_TRUE; } if (check_acl) { isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0); char msg[NS_CLIENT_ACLMSGSIZE("query (cache)")]; result = ns_client_checkaclsilent(client, NULL, client->view->cacheacl, ISC_TRUE); if (result == ISC_R_SUCCESS) { /* * We were allowed by the "allow-query-cache" ACL. * Remember this so we don't have to check again. */ client->query.attributes |= NS_QUERYATTR_CACHEACLOK; if (log && isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(3))) { ns_client_aclmsg("query (cache)", name, qtype, client->view->rdclass, msg, sizeof(msg)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_DEBUG(3), "%s approved", msg); } } else if (log) { ns_client_aclmsg("query (cache)", name, qtype, client->view->rdclass, msg, sizeof(msg)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_INFO, "%s denied", msg); } /* * We've now evaluated the view's query ACL, and * the NS_QUERYATTR_CACHEACLOKVALID attribute is now valid. */ client->query.attributes |= NS_QUERYATTR_CACHEACLOKVALID; if (result != ISC_R_SUCCESS) goto refuse; } /* Approved. */ /* Transfer ownership. */ *dbp = db; return (ISC_R_SUCCESS); refuse: result = DNS_R_REFUSED; if (db != NULL) dns_db_detach(&db); return (result); } static inline isc_result_t query_getdb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, unsigned int options, dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp, isc_boolean_t *is_zonep) { isc_result_t result; isc_result_t tresult; unsigned int namelabels; unsigned int zonelabels; dns_zone_t *zone = NULL; dns_db_t *tdbp; REQUIRE(zonep != NULL && *zonep == NULL); tdbp = NULL; /* Calculate how many labels are in name. */ namelabels = dns_name_countlabels(name); zonelabels = 0; /* Try to find name in bind's standard database. */ result = query_getzonedb(client, name, qtype, options, &zone, dbp, versionp); /* See how many labels are in the zone's name. */ if (result == ISC_R_SUCCESS && zone != NULL) zonelabels = dns_name_countlabels(dns_zone_getorigin(zone)); /* * If # zone labels < # name labels, try to find an even better match * Only try if a DLZ driver is loaded for this view */ if (zonelabels < namelabels && client->view->dlzdatabase != NULL) { tresult = dns_dlzfindzone(client->view, name, zonelabels, &tdbp); /* If we successful, we found a better match. */ if (tresult == ISC_R_SUCCESS) { /* * If the previous search returned a zone, detach it. */ if (zone != NULL) dns_zone_detach(&zone); /* * If the previous search returned a database, * detach it. */ if (*dbp != NULL) dns_db_detach(dbp); /* * If the previous search returned a version, clear it. */ *versionp = NULL; /* * Get our database version. */ dns_db_currentversion(tdbp, versionp); /* * Be sure to return our database. */ *dbp = tdbp; /* * We return a null zone, No stats for DLZ zones. */ zone = NULL; result = tresult; } } /* If successful, Transfer ownership of zone. */ if (result == ISC_R_SUCCESS) { *zonep = zone; /* * If neither attempt above succeeded, return the cache instead */ *is_zonep = ISC_TRUE; } else if (result == ISC_R_NOTFOUND) { result = query_getcachedb(client, name, qtype, dbp, options); *is_zonep = ISC_FALSE; } return (result); } static inline isc_boolean_t query_isduplicate(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type, dns_name_t **mnamep) { dns_section_t section; dns_name_t *mname = NULL; isc_result_t result; CTRACE("query_isduplicate"); for (section = DNS_SECTION_ANSWER; section <= DNS_SECTION_ADDITIONAL; section++) { result = dns_message_findname(client->message, section, name, type, 0, &mname, NULL); if (result == ISC_R_SUCCESS) { /* * We've already got this RRset in the response. */ CTRACE("query_isduplicate: true: done"); return (ISC_TRUE); } else if (result == DNS_R_NXRRSET) { /* * The name exists, but the rdataset does not. */ if (section == DNS_SECTION_ADDITIONAL) break; } else RUNTIME_CHECK(result == DNS_R_NXDOMAIN); mname = NULL; } if (mnamep != NULL) *mnamep = mname; CTRACE("query_isduplicate: false: done"); return (ISC_FALSE); } static isc_result_t query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) { ns_client_t *client = arg; isc_result_t result, eresult; dns_dbnode_t *node; dns_db_t *db; dns_name_t *fname, *mname; dns_rdataset_t *rdataset, *sigrdataset, *trdataset; isc_buffer_t *dbuf; isc_buffer_t b; dns_dbversion_t *version; isc_boolean_t added_something, need_addname; dns_zone_t *zone; dns_rdatatype_t type; dns_clientinfomethods_t cm; dns_clientinfo_t ci; REQUIRE(NS_CLIENT_VALID(client)); REQUIRE(qtype != dns_rdatatype_any); if (!WANTDNSSEC(client) && dns_rdatatype_isdnssec(qtype)) return (ISC_R_SUCCESS); CTRACE("query_addadditional"); /* * Initialization. */ eresult = ISC_R_SUCCESS; fname = NULL; rdataset = NULL; sigrdataset = NULL; trdataset = NULL; db = NULL; version = NULL; node = NULL; added_something = ISC_FALSE; need_addname = ISC_FALSE; zone = NULL; dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); /* * We treat type A additional section processing as if it * were "any address type" additional section processing. * To avoid multiple lookups, we do an 'any' database * lookup and iterate over the node. */ if (qtype == dns_rdatatype_a) type = dns_rdatatype_any; else type = qtype; /* * Get some resources. */ dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); rdataset = query_newrdataset(client); if (fname == NULL || rdataset == NULL) goto cleanup; if (WANTDNSSEC(client)) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) goto cleanup; } /* * Look for a zone database that might contain authoritative * additional data. */ result = query_getzonedb(client, name, qtype, DNS_GETDB_NOLOG, &zone, &db, &version); if (result != ISC_R_SUCCESS) goto try_cache; CTRACE("query_addadditional: db_find"); /* * Since we are looking for authoritative data, we do not set * the GLUEOK flag. Glue will be looked for later, but not * necessarily in the same database. */ node = NULL; result = dns_db_findext(db, name, version, type, client->query.dboptions, client->now, &node, fname, &cm, &ci, rdataset, sigrdataset); if (result == ISC_R_SUCCESS) { if (sigrdataset != NULL && !dns_db_issecure(db) && dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); goto found; } 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); version = NULL; dns_db_detach(&db); /* * No authoritative data was found. The cache is our next best bet. */ try_cache: result = query_getcachedb(client, name, qtype, &db, DNS_GETDB_NOLOG); if (result != ISC_R_SUCCESS) /* * Most likely the client isn't allowed to query the cache. */ goto try_glue; /* * Attempt to validate glue. */ if (sigrdataset == NULL) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) goto cleanup; } result = dns_db_findext(db, name, version, type, client->query.dboptions | DNS_DBFIND_GLUEOK | DNS_DBFIND_ADDITIONALOK, client->now, &node, fname, &cm, &ci, rdataset, sigrdataset); if (result == DNS_R_GLUE && validate(client, db, fname, rdataset, sigrdataset)) result = ISC_R_SUCCESS; if (!WANTDNSSEC(client)) query_putrdataset(client, &sigrdataset); if (result == ISC_R_SUCCESS) goto found; 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); dns_db_detach(&db); try_glue: /* * No cached data was found. Glue is our last chance. * RFC1035 sayeth: * * 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. * * This is the "special search". Note that we must search * the zone where the NS record resides, not the zone it * points to, and that we only do the search in the delegation * case (identified by client->query.gluedb being set). */ if (client->query.gluedb == NULL) goto cleanup; /* * Don't poison caches using the bailiwick protection model. */ if (!dns_name_issubdomain(name, dns_db_origin(client->query.gluedb))) goto cleanup; dns_db_attach(client->query.gluedb, &db); result = dns_db_findext(db, name, version, type, client->query.dboptions | DNS_DBFIND_GLUEOK, client->now, &node, fname, &cm, &ci, rdataset, sigrdataset); if (!(result == ISC_R_SUCCESS || result == DNS_R_ZONECUT || result == DNS_R_GLUE)) goto cleanup; found: /* * We have found a potential additional data rdataset, or * at least a node to iterate over. */ query_keepname(client, fname, dbuf); /* * If we have an rdataset, add it to the additional data * section. */ mname = NULL; if (dns_rdataset_isassociated(rdataset) && !query_isduplicate(client, fname, type, &mname)) { if (mname != NULL) { INSIST(mname != fname); query_releasename(client, &fname); fname = mname; } else need_addname = ISC_TRUE; ISC_LIST_APPEND(fname->list, rdataset, link); trdataset = rdataset; rdataset = NULL; added_something = ISC_TRUE; /* * Note: we only add SIGs if we've added the type they cover, * so we do not need to check if the SIG rdataset is already * in the response. */ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { ISC_LIST_APPEND(fname->list, sigrdataset, link); sigrdataset = NULL; } } if (qtype == dns_rdatatype_a) { #ifdef ALLOW_FILTER_AAAA_ON_V4 isc_boolean_t have_a = ISC_FALSE; #endif /* * We now go looking for A and AAAA records, along with * their signatures. * * XXXRTH This code could be more efficient. */ if (rdataset != NULL) { if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); } else { rdataset = query_newrdataset(client); if (rdataset == NULL) goto addname; } if (sigrdataset != NULL) { if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); } else if (WANTDNSSEC(client)) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) goto addname; } if (query_isduplicate(client, fname, dns_rdatatype_a, NULL)) goto aaaa_lookup; result = dns_db_findrdataset(db, node, version, dns_rdatatype_a, 0, client->now, rdataset, sigrdataset); if (result == DNS_R_NCACHENXDOMAIN) goto addname; if (result == DNS_R_NCACHENXRRSET) { dns_rdataset_disassociate(rdataset); if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); } if (result == ISC_R_SUCCESS) { mname = NULL; #ifdef ALLOW_FILTER_AAAA_ON_V4 have_a = ISC_TRUE; #endif if (!query_isduplicate(client, fname, dns_rdatatype_a, &mname)) { if (mname != fname) { if (mname != NULL) { query_releasename(client, &fname); fname = mname; } else need_addname = ISC_TRUE; } ISC_LIST_APPEND(fname->list, rdataset, link); added_something = ISC_TRUE; if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { ISC_LIST_APPEND(fname->list, sigrdataset, link); sigrdataset = query_newrdataset(client); } rdataset = query_newrdataset(client); if (rdataset == NULL) goto addname; if (WANTDNSSEC(client) && sigrdataset == NULL) goto addname; } else { dns_rdataset_disassociate(rdataset); if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); } } aaaa_lookup: if (query_isduplicate(client, fname, dns_rdatatype_aaaa, NULL)) goto addname; result = dns_db_findrdataset(db, node, version, dns_rdatatype_aaaa, 0, client->now, rdataset, sigrdataset); if (result == DNS_R_NCACHENXDOMAIN) goto addname; if (result == DNS_R_NCACHENXRRSET) { dns_rdataset_disassociate(rdataset); if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); } if (result == ISC_R_SUCCESS) { mname = NULL; /* * There's an A; check whether we're filtering AAAA */ #ifdef ALLOW_FILTER_AAAA_ON_V4 if (have_a && (client->filter_aaaa == dns_v4_aaaa_break_dnssec || (client->filter_aaaa == dns_v4_aaaa_filter && (!WANTDNSSEC(client) || sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset))))) goto addname; #endif if (!query_isduplicate(client, fname, dns_rdatatype_aaaa, &mname)) { if (mname != fname) { if (mname != NULL) { query_releasename(client, &fname); fname = mname; } else need_addname = ISC_TRUE; } ISC_LIST_APPEND(fname->list, rdataset, link); added_something = ISC_TRUE; if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { ISC_LIST_APPEND(fname->list, sigrdataset, link); sigrdataset = NULL; } rdataset = NULL; } } } addname: CTRACE("query_addadditional: addname"); /* * If we haven't added anything, then we're done. */ if (!added_something) goto cleanup; /* * We may have added our rdatasets to an existing name, if so, then * need_addname will be ISC_FALSE. Whether we used an existing name * or a new one, we must set fname to NULL to prevent cleanup. */ if (need_addname) dns_message_addname(client->message, fname, DNS_SECTION_ADDITIONAL); fname = NULL; /* * In a few cases, we want to add additional data for additional * data. It's simpler to just deal with special cases here than * to try to create a general purpose mechanism and allow the * rdata implementations to do it themselves. * * This involves recursion, but the depth is limited. The * most complex case is adding a SRV rdataset, which involves * recursing to add address records, which in turn can cause * recursion to add KEYs. */ if (type == dns_rdatatype_srv && trdataset != NULL) { /* * If we're adding SRV records to the additional data * section, it's helpful if we add the SRV additional data * as well. */ eresult = dns_rdataset_additionaldata(trdataset, query_addadditional, client); } cleanup: CTRACE("query_addadditional: cleanup"); query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); if (node != NULL) dns_db_detachnode(db, &node); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); CTRACE("query_addadditional: done"); return (eresult); } static inline void query_discardcache(ns_client_t *client, dns_rdataset_t *rdataset_base, dns_rdatasetadditional_t additionaltype, dns_rdatatype_t type, dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp, dns_dbnode_t **nodep, dns_name_t *fname) { dns_rdataset_t *rdataset; while ((rdataset = ISC_LIST_HEAD(fname->list)) != NULL) { ISC_LIST_UNLINK(fname->list, rdataset, link); query_putrdataset(client, &rdataset); } if (*versionp != NULL) dns_db_closeversion(*dbp, versionp, ISC_FALSE); if (*nodep != NULL) dns_db_detachnode(*dbp, nodep); if (*dbp != NULL) dns_db_detach(dbp); if (*zonep != NULL) dns_zone_detach(zonep); (void)dns_rdataset_putadditional(client->view->acache, rdataset_base, additionaltype, type); } static inline isc_result_t query_iscachevalid(dns_zone_t *zone, dns_db_t *db, dns_db_t *db0, dns_dbversion_t *version) { isc_result_t result = ISC_R_SUCCESS; dns_dbversion_t *version_current = NULL; dns_db_t *db_current = db0; if (db_current == NULL) { result = dns_zone_getdb(zone, &db_current); if (result != ISC_R_SUCCESS) return (result); } dns_db_currentversion(db_current, &version_current); if (db_current != db || version_current != version) { result = ISC_R_FAILURE; goto cleanup; } cleanup: dns_db_closeversion(db_current, &version_current, ISC_FALSE); if (db0 == NULL && db_current != NULL) dns_db_detach(&db_current); return (result); } static isc_result_t query_addadditional2(void *arg, dns_name_t *name, dns_rdatatype_t qtype) { client_additionalctx_t *additionalctx = arg; dns_rdataset_t *rdataset_base; ns_client_t *client; isc_result_t result, eresult; dns_dbnode_t *node, *cnode; dns_db_t *db, *cdb; dns_name_t *fname, *mname0, cfname; dns_rdataset_t *rdataset, *sigrdataset; dns_rdataset_t *crdataset, *crdataset_next; isc_buffer_t *dbuf; isc_buffer_t b; dns_dbversion_t *version, *cversion; isc_boolean_t added_something, need_addname, needadditionalcache; isc_boolean_t need_sigrrset; dns_zone_t *zone; dns_rdatatype_t type; dns_rdatasetadditional_t additionaltype; dns_clientinfomethods_t cm; dns_clientinfo_t ci; /* * If we don't have an additional cache call query_addadditional. */ client = additionalctx->client; REQUIRE(NS_CLIENT_VALID(client)); if (qtype != dns_rdatatype_a || client->view->acache == NULL) { /* * This function is optimized for "address" types. For other * types, use a generic routine. * XXX: ideally, this function should be generic enough. */ return (query_addadditional(additionalctx->client, name, qtype)); } /* * Initialization. */ rdataset_base = additionalctx->rdataset; eresult = ISC_R_SUCCESS; fname = NULL; rdataset = NULL; sigrdataset = NULL; db = NULL; cdb = NULL; version = NULL; cversion = NULL; node = NULL; cnode = NULL; added_something = ISC_FALSE; need_addname = ISC_FALSE; zone = NULL; needadditionalcache = ISC_FALSE; POST(needadditionalcache); additionaltype = dns_rdatasetadditional_fromauth; dns_name_init(&cfname, NULL); dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); CTRACE("query_addadditional2"); /* * We treat type A additional section processing as if it * were "any address type" additional section processing. * To avoid multiple lookups, we do an 'any' database * lookup and iterate over the node. * XXXJT: this approach can cause a suboptimal result when the cache * DB only has partial address types and the glue DB has remaining * ones. */ type = dns_rdatatype_any; /* * Get some resources. */ dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); if (fname == NULL) goto cleanup; dns_name_setbuffer(&cfname, &b); /* share the buffer */ /* Check additional cache */ result = dns_rdataset_getadditional(rdataset_base, additionaltype, type, client->view->acache, &zone, &cdb, &cversion, &cnode, &cfname, client->message, client->now); if (result != ISC_R_SUCCESS) goto findauthdb; if (zone == NULL) { CTRACE("query_addadditional2: auth zone not found"); goto try_cache; } /* Is the cached DB up-to-date? */ result = query_iscachevalid(zone, cdb, NULL, cversion); if (result != ISC_R_SUCCESS) { CTRACE("query_addadditional2: old auth additional cache"); query_discardcache(client, rdataset_base, additionaltype, type, &zone, &cdb, &cversion, &cnode, &cfname); goto findauthdb; } if (cnode == NULL) { /* * We have a negative cache. We don't have to check the zone * ACL, since the result (not using this zone) would be same * regardless of the result. */ CTRACE("query_addadditional2: negative auth additional cache"); dns_db_closeversion(cdb, &cversion, ISC_FALSE); dns_db_detach(&cdb); dns_zone_detach(&zone); goto try_cache; } result = query_validatezonedb(client, name, qtype, DNS_GETDB_NOLOG, zone, cdb, NULL); if (result != ISC_R_SUCCESS) { query_discardcache(client, rdataset_base, additionaltype, type, &zone, &cdb, &cversion, &cnode, &cfname); goto try_cache; } /* We've got an active cache. */ CTRACE("query_addadditional2: auth additional cache"); dns_db_closeversion(cdb, &cversion, ISC_FALSE); db = cdb; node = cnode; dns_name_clone(&cfname, fname); query_keepname(client, fname, dbuf); goto foundcache; /* * Look for a zone database that might contain authoritative * additional data. */ findauthdb: result = query_getzonedb(client, name, qtype, DNS_GETDB_NOLOG, &zone, &db, &version); if (result != ISC_R_SUCCESS) { /* Cache the negative result */ (void)dns_rdataset_setadditional(rdataset_base, additionaltype, type, client->view->acache, NULL, NULL, NULL, NULL, NULL); goto try_cache; } CTRACE("query_addadditional2: db_find"); /* * Since we are looking for authoritative data, we do not set * the GLUEOK flag. Glue will be looked for later, but not * necessarily in the same database. */ node = NULL; result = dns_db_findext(db, name, version, type, client->query.dboptions, client->now, &node, fname, &cm, &ci, NULL, NULL); if (result == ISC_R_SUCCESS) goto found; /* Cache the negative result */ (void)dns_rdataset_setadditional(rdataset_base, additionaltype, type, client->view->acache, zone, db, version, NULL, fname); if (node != NULL) dns_db_detachnode(db, &node); version = NULL; dns_db_detach(&db); /* * No authoritative data was found. The cache is our next best bet. */ try_cache: additionaltype = dns_rdatasetadditional_fromcache; result = query_getcachedb(client, name, qtype, &db, DNS_GETDB_NOLOG); if (result != ISC_R_SUCCESS) /* * Most likely the client isn't allowed to query the cache. */ goto try_glue; result = dns_db_findext(db, name, version, type, client->query.dboptions | DNS_DBFIND_GLUEOK | DNS_DBFIND_ADDITIONALOK, client->now, &node, fname, &cm, &ci, NULL, NULL); if (result == ISC_R_SUCCESS) goto found; if (node != NULL) dns_db_detachnode(db, &node); dns_db_detach(&db); try_glue: /* * No cached data was found. Glue is our last chance. * RFC1035 sayeth: * * 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. * * This is the "special search". Note that we must search * the zone where the NS record resides, not the zone it * points to, and that we only do the search in the delegation * case (identified by client->query.gluedb being set). */ if (client->query.gluedb == NULL) goto cleanup; /* * Don't poison caches using the bailiwick protection model. */ if (!dns_name_issubdomain(name, dns_db_origin(client->query.gluedb))) goto cleanup; /* Check additional cache */ additionaltype = dns_rdatasetadditional_fromglue; result = dns_rdataset_getadditional(rdataset_base, additionaltype, type, client->view->acache, NULL, &cdb, &cversion, &cnode, &cfname, client->message, client->now); if (result != ISC_R_SUCCESS) goto findglue; result = query_iscachevalid(zone, cdb, client->query.gluedb, cversion); if (result != ISC_R_SUCCESS) { CTRACE("query_addadditional2: old glue additional cache"); query_discardcache(client, rdataset_base, additionaltype, type, &zone, &cdb, &cversion, &cnode, &cfname); goto findglue; } if (cnode == NULL) { /* We have a negative cache. */ CTRACE("query_addadditional2: negative glue additional cache"); dns_db_closeversion(cdb, &cversion, ISC_FALSE); dns_db_detach(&cdb); goto cleanup; } /* Cache hit. */ CTRACE("query_addadditional2: glue additional cache"); dns_db_closeversion(cdb, &cversion, ISC_FALSE); db = cdb; node = cnode; dns_name_clone(&cfname, fname); query_keepname(client, fname, dbuf); goto foundcache; findglue: dns_db_attach(client->query.gluedb, &db); result = dns_db_findext(db, name, version, type, client->query.dboptions | DNS_DBFIND_GLUEOK, client->now, &node, fname, &cm, &ci, NULL, NULL); if (!(result == ISC_R_SUCCESS || result == DNS_R_ZONECUT || result == DNS_R_GLUE)) { /* cache the negative result */ (void)dns_rdataset_setadditional(rdataset_base, additionaltype, type, client->view->acache, NULL, db, version, NULL, fname); goto cleanup; } found: /* * We have found a DB node to iterate over from a DB. * We are going to look for address RRsets (i.e., A and AAAA) in the DB * node we've just found. We'll then store the complete information * in the additional data cache. */ dns_name_clone(fname, &cfname); query_keepname(client, fname, dbuf); needadditionalcache = ISC_TRUE; rdataset = query_newrdataset(client); if (rdataset == NULL) goto cleanup; sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) goto cleanup; if (additionaltype == dns_rdatasetadditional_fromcache && query_isduplicate(client, fname, dns_rdatatype_a, NULL)) goto aaaa_lookup; /* * Find A RRset with sig RRset. Even if we don't find a sig RRset * for a client using DNSSEC, we'll continue the process to make a * complete list to be cached. However, we need to cancel the * caching when something unexpected happens, in order to avoid * caching incomplete information. */ result = dns_db_findrdataset(db, node, version, dns_rdatatype_a, 0, client->now, rdataset, sigrdataset); /* * If we can't promote glue/pending from the cache to secure * then drop it. */ if (result == ISC_R_SUCCESS && additionaltype == dns_rdatasetadditional_fromcache && (DNS_TRUST_PENDING(rdataset->trust) || DNS_TRUST_GLUE(rdataset->trust)) && !validate(client, db, fname, rdataset, sigrdataset)) { dns_rdataset_disassociate(rdataset); if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); result = ISC_R_NOTFOUND; } if (result == DNS_R_NCACHENXDOMAIN) goto setcache; if (result == DNS_R_NCACHENXRRSET) { dns_rdataset_disassociate(rdataset); if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); } if (result == ISC_R_SUCCESS) { /* Remember the result as a cache */ ISC_LIST_APPEND(cfname.list, rdataset, link); if (dns_rdataset_isassociated(sigrdataset)) { ISC_LIST_APPEND(cfname.list, sigrdataset, link); sigrdataset = query_newrdataset(client); } rdataset = query_newrdataset(client); if (sigrdataset == NULL || rdataset == NULL) { /* do not cache incomplete information */ goto foundcache; } } aaaa_lookup: if (additionaltype == dns_rdatasetadditional_fromcache && query_isduplicate(client, fname, dns_rdatatype_aaaa, NULL)) goto foundcache; /* Find AAAA RRset with sig RRset */ result = dns_db_findrdataset(db, node, version, dns_rdatatype_aaaa, 0, client->now, rdataset, sigrdataset); /* * If we can't promote glue/pending from the cache to secure * then drop it. */ if (result == ISC_R_SUCCESS && additionaltype == dns_rdatasetadditional_fromcache && (DNS_TRUST_PENDING(rdataset->trust) || DNS_TRUST_GLUE(rdataset->trust)) && !validate(client, db, fname, rdataset, sigrdataset)) { dns_rdataset_disassociate(rdataset); if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); result = ISC_R_NOTFOUND; } if (result == ISC_R_SUCCESS) { ISC_LIST_APPEND(cfname.list, rdataset, link); rdataset = NULL; if (dns_rdataset_isassociated(sigrdataset)) { ISC_LIST_APPEND(cfname.list, sigrdataset, link); sigrdataset = NULL; } } setcache: /* * Set the new result in the cache if required. We do not support * caching additional data from a cache DB. */ if (needadditionalcache == ISC_TRUE && (additionaltype == dns_rdatasetadditional_fromauth || additionaltype == dns_rdatasetadditional_fromglue)) { (void)dns_rdataset_setadditional(rdataset_base, additionaltype, type, client->view->acache, zone, db, version, node, &cfname); } foundcache: need_sigrrset = ISC_FALSE; mname0 = NULL; for (crdataset = ISC_LIST_HEAD(cfname.list); crdataset != NULL; crdataset = crdataset_next) { dns_name_t *mname; crdataset_next = ISC_LIST_NEXT(crdataset, link); mname = NULL; if (crdataset->type == dns_rdatatype_a || crdataset->type == dns_rdatatype_aaaa) { if (!query_isduplicate(client, fname, crdataset->type, &mname)) { if (mname != fname) { if (mname != NULL) { /* * A different type of this name is * already stored in the additional * section. We'll reuse the name. * Note that this should happen at most * once. Otherwise, fname->link could * leak below. */ INSIST(mname0 == NULL); query_releasename(client, &fname); fname = mname; mname0 = mname; } else need_addname = ISC_TRUE; } ISC_LIST_UNLINK(cfname.list, crdataset, link); ISC_LIST_APPEND(fname->list, crdataset, link); added_something = ISC_TRUE; need_sigrrset = ISC_TRUE; } else need_sigrrset = ISC_FALSE; } else if (crdataset->type == dns_rdatatype_rrsig && need_sigrrset && WANTDNSSEC(client)) { ISC_LIST_UNLINK(cfname.list, crdataset, link); ISC_LIST_APPEND(fname->list, crdataset, link); added_something = ISC_TRUE; /* just in case */ need_sigrrset = ISC_FALSE; } } CTRACE("query_addadditional2: addname"); /* * If we haven't added anything, then we're done. */ if (!added_something) goto cleanup; /* * We may have added our rdatasets to an existing name, if so, then * need_addname will be ISC_FALSE. Whether we used an existing name * or a new one, we must set fname to NULL to prevent cleanup. */ if (need_addname) dns_message_addname(client->message, fname, DNS_SECTION_ADDITIONAL); fname = NULL; cleanup: CTRACE("query_addadditional2: cleanup"); if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); while ((crdataset = ISC_LIST_HEAD(cfname.list)) != NULL) { ISC_LIST_UNLINK(cfname.list, crdataset, link); query_putrdataset(client, &crdataset); } if (fname != NULL) query_releasename(client, &fname); if (node != NULL) dns_db_detachnode(db, &node); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); CTRACE("query_addadditional2: done"); return (eresult); } static inline void query_addrdataset(ns_client_t *client, dns_name_t *fname, dns_rdataset_t *rdataset) { client_additionalctx_t additionalctx; /* * Add 'rdataset' and any pertinent additional data to * 'fname', a name in the response message for 'client'. */ CTRACE("query_addrdataset"); ISC_LIST_APPEND(fname->list, rdataset, link); if (client->view->order != NULL) rdataset->attributes |= dns_order_find(client->view->order, fname, rdataset->type, rdataset->rdclass); rdataset->attributes |= DNS_RDATASETATTR_LOADORDER; if (NOADDITIONAL(client)) return; /* * Add additional data. * * We don't care if dns_rdataset_additionaldata() fails. */ additionalctx.client = client; additionalctx.rdataset = rdataset; (void)dns_rdataset_additionaldata(rdataset, query_addadditional2, &additionalctx); CTRACE("query_addrdataset: done"); } static isc_result_t query_dns64(ns_client_t *client, dns_name_t **namep, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, isc_buffer_t *dbuf, dns_section_t section) { dns_name_t *name, *mname; dns_rdata_t *dns64_rdata; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdatalist_t *dns64_rdatalist; dns_rdataset_t *dns64_rdataset; dns_rdataset_t *mrdataset; isc_buffer_t *buffer; isc_region_t r; isc_result_t result; dns_view_t *view = client->view; isc_netaddr_t netaddr; dns_dns64_t *dns64; unsigned int flags = 0; /*% * To the current response for 'client', add the answer RRset * '*rdatasetp' and an optional signature set '*sigrdatasetp', with * owner name '*namep', to section 'section', unless they are * already there. Also add any pertinent additional data. * * If 'dbuf' is not NULL, then '*namep' is the name whose data is * stored in 'dbuf'. In this case, query_addrrset() guarantees that * when it returns the name will either have been kept or released. */ CTRACE("query_dns64"); name = *namep; mname = NULL; mrdataset = NULL; buffer = NULL; dns64_rdata = NULL; dns64_rdataset = NULL; dns64_rdatalist = NULL; result = dns_message_findname(client->message, section, name, dns_rdatatype_aaaa, rdataset->covers, &mname, &mrdataset); if (result == ISC_R_SUCCESS) { /* * We've already got an RRset of the given name and type. * There's nothing else to do; */ CTRACE("query_dns64: dns_message_findname succeeded: done"); if (dbuf != NULL) query_releasename(client, namep); return (ISC_R_SUCCESS); } else if (result == DNS_R_NXDOMAIN) { /* * The name doesn't exist. */ if (dbuf != NULL) query_keepname(client, name, dbuf); dns_message_addname(client->message, name, section); *namep = NULL; mname = name; } else { RUNTIME_CHECK(result == DNS_R_NXRRSET); if (dbuf != NULL) query_releasename(client, namep); } if (rdataset->trust != dns_trust_secure && (section == DNS_SECTION_ANSWER || section == DNS_SECTION_AUTHORITY)) client->query.attributes &= ~NS_QUERYATTR_SECURE; isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr); result = isc_buffer_allocate(client->mctx, &buffer, view->dns64cnt * 16 * dns_rdataset_count(rdataset)); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_message_gettemprdataset(client->message, &dns64_rdataset); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_message_gettemprdatalist(client->message, &dns64_rdatalist); if (result != ISC_R_SUCCESS) goto cleanup; dns_rdataset_init(dns64_rdataset); dns_rdatalist_init(dns64_rdatalist); dns64_rdatalist->rdclass = dns_rdataclass_in; dns64_rdatalist->type = dns_rdatatype_aaaa; if (client->query.dns64_ttl != ISC_UINT32_MAX) dns64_rdatalist->ttl = ISC_MIN(rdataset->ttl, client->query.dns64_ttl); else dns64_rdatalist->ttl = ISC_MIN(rdataset->ttl, 600); if (RECURSIONOK(client)) flags |= DNS_DNS64_RECURSIVE; /* * We use the signatures from the A lookup to set DNS_DNS64_DNSSEC * as this provides a easy way to see if the answer was signed. */ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) flags |= DNS_DNS64_DNSSEC; for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { for (dns64 = ISC_LIST_HEAD(client->view->dns64); dns64 != NULL; dns64 = dns_dns64_next(dns64)) { dns_rdataset_current(rdataset, &rdata); isc__buffer_availableregion(buffer, &r); INSIST(r.length >= 16); result = dns_dns64_aaaafroma(dns64, &netaddr, client->signer, &ns_g_server->aclenv, flags, rdata.data, r.base); if (result != ISC_R_SUCCESS) { dns_rdata_reset(&rdata); continue; } isc_buffer_add(buffer, 16); isc_buffer_remainingregion(buffer, &r); isc_buffer_forward(buffer, 16); result = dns_message_gettemprdata(client->message, &dns64_rdata); if (result != ISC_R_SUCCESS) goto cleanup; dns_rdata_init(dns64_rdata); dns_rdata_fromregion(dns64_rdata, dns_rdataclass_in, dns_rdatatype_aaaa, &r); ISC_LIST_APPEND(dns64_rdatalist->rdata, dns64_rdata, link); dns64_rdata = NULL; dns_rdata_reset(&rdata); } } if (result != ISC_R_NOMORE) goto cleanup; if (ISC_LIST_EMPTY(dns64_rdatalist->rdata)) goto cleanup; result = dns_rdatalist_tordataset(dns64_rdatalist, dns64_rdataset); if (result != ISC_R_SUCCESS) goto cleanup; client->query.attributes |= NS_QUERYATTR_NOADDITIONAL; dns64_rdataset->trust = rdataset->trust; query_addrdataset(client, mname, dns64_rdataset); dns64_rdataset = NULL; dns64_rdatalist = NULL; dns_message_takebuffer(client->message, &buffer); result = ISC_R_SUCCESS; cleanup: if (buffer != NULL) isc_buffer_free(&buffer); if (dns64_rdata != NULL) dns_message_puttemprdata(client->message, &dns64_rdata); if (dns64_rdataset != NULL) dns_message_puttemprdataset(client->message, &dns64_rdataset); if (dns64_rdatalist != NULL) { for (dns64_rdata = ISC_LIST_HEAD(dns64_rdatalist->rdata); dns64_rdata != NULL; dns64_rdata = ISC_LIST_HEAD(dns64_rdatalist->rdata)) { ISC_LIST_UNLINK(dns64_rdatalist->rdata, dns64_rdata, link); dns_message_puttemprdata(client->message, &dns64_rdata); } dns_message_puttemprdatalist(client->message, &dns64_rdatalist); } CTRACE("query_dns64: done"); return (result); } static void query_filter64(ns_client_t *client, dns_name_t **namep, dns_rdataset_t *rdataset, isc_buffer_t *dbuf, dns_section_t section) { dns_name_t *name, *mname; dns_rdata_t *myrdata; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdatalist_t *myrdatalist; dns_rdataset_t *myrdataset; isc_buffer_t *buffer; isc_region_t r; isc_result_t result; unsigned int i; CTRACE("query_filter64"); INSIST(client->query.dns64_aaaaok != NULL); INSIST(client->query.dns64_aaaaoklen == dns_rdataset_count(rdataset)); name = *namep; mname = NULL; buffer = NULL; myrdata = NULL; myrdataset = NULL; myrdatalist = NULL; result = dns_message_findname(client->message, section, name, dns_rdatatype_aaaa, rdataset->covers, &mname, &myrdataset); if (result == ISC_R_SUCCESS) { /* * We've already got an RRset of the given name and type. * There's nothing else to do; */ CTRACE("query_filter64: dns_message_findname succeeded: done"); if (dbuf != NULL) query_releasename(client, namep); return; } else if (result == DNS_R_NXDOMAIN) { mname = name; *namep = NULL; } else { RUNTIME_CHECK(result == DNS_R_NXRRSET); if (dbuf != NULL) query_releasename(client, namep); dbuf = NULL; } if (rdataset->trust != dns_trust_secure && (section == DNS_SECTION_ANSWER || section == DNS_SECTION_AUTHORITY)) client->query.attributes &= ~NS_QUERYATTR_SECURE; result = isc_buffer_allocate(client->mctx, &buffer, 16 * dns_rdataset_count(rdataset)); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_message_gettemprdataset(client->message, &myrdataset); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_message_gettemprdatalist(client->message, &myrdatalist); if (result != ISC_R_SUCCESS) goto cleanup; dns_rdataset_init(myrdataset); dns_rdatalist_init(myrdatalist); myrdatalist->rdclass = dns_rdataclass_in; myrdatalist->type = dns_rdatatype_aaaa; myrdatalist->ttl = rdataset->ttl; i = 0; for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { if (!client->query.dns64_aaaaok[i++]) continue; dns_rdataset_current(rdataset, &rdata); INSIST(rdata.length == 16); isc_buffer_putmem(buffer, rdata.data, rdata.length); isc_buffer_remainingregion(buffer, &r); isc_buffer_forward(buffer, rdata.length); result = dns_message_gettemprdata(client->message, &myrdata); if (result != ISC_R_SUCCESS) goto cleanup; dns_rdata_init(myrdata); dns_rdata_fromregion(myrdata, dns_rdataclass_in, dns_rdatatype_aaaa, &r); ISC_LIST_APPEND(myrdatalist->rdata, myrdata, link); myrdata = NULL; dns_rdata_reset(&rdata); } if (result != ISC_R_NOMORE) goto cleanup; result = dns_rdatalist_tordataset(myrdatalist, myrdataset); if (result != ISC_R_SUCCESS) goto cleanup; client->query.attributes |= NS_QUERYATTR_NOADDITIONAL; if (mname == name) { if (dbuf != NULL) query_keepname(client, name, dbuf); dns_message_addname(client->message, name, section); dbuf = NULL; } myrdataset->trust = rdataset->trust; query_addrdataset(client, mname, myrdataset); myrdataset = NULL; myrdatalist = NULL; dns_message_takebuffer(client->message, &buffer); cleanup: if (buffer != NULL) isc_buffer_free(&buffer); if (myrdata != NULL) dns_message_puttemprdata(client->message, &myrdata); if (myrdataset != NULL) dns_message_puttemprdataset(client->message, &myrdataset); if (myrdatalist != NULL) { for (myrdata = ISC_LIST_HEAD(myrdatalist->rdata); myrdata != NULL; myrdata = ISC_LIST_HEAD(myrdatalist->rdata)) { ISC_LIST_UNLINK(myrdatalist->rdata, myrdata, link); dns_message_puttemprdata(client->message, &myrdata); } dns_message_puttemprdatalist(client->message, &myrdatalist); } if (dbuf != NULL) query_releasename(client, &name); CTRACE("query_filter64: done"); } static void query_addrrset(ns_client_t *client, dns_name_t **namep, dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp, isc_buffer_t *dbuf, dns_section_t section) { dns_name_t *name, *mname; dns_rdataset_t *rdataset, *mrdataset, *sigrdataset; isc_result_t result; /*% * To the current response for 'client', add the answer RRset * '*rdatasetp' and an optional signature set '*sigrdatasetp', with * owner name '*namep', to section 'section', unless they are * already there. Also add any pertinent additional data. * * If 'dbuf' is not NULL, then '*namep' is the name whose data is * stored in 'dbuf'. In this case, query_addrrset() guarantees that * when it returns the name will either have been kept or released. */ CTRACE("query_addrrset"); name = *namep; rdataset = *rdatasetp; if (sigrdatasetp != NULL) sigrdataset = *sigrdatasetp; else sigrdataset = NULL; mname = NULL; mrdataset = NULL; result = dns_message_findname(client->message, section, name, rdataset->type, rdataset->covers, &mname, &mrdataset); if (result == ISC_R_SUCCESS) { /* * We've already got an RRset of the given name and type. * There's nothing else to do; */ CTRACE("query_addrrset: dns_message_findname succeeded: done"); if (dbuf != NULL) query_releasename(client, namep); return; } else if (result == DNS_R_NXDOMAIN) { /* * The name doesn't exist. */ if (dbuf != NULL) query_keepname(client, name, dbuf); dns_message_addname(client->message, name, section); *namep = NULL; mname = name; } else { RUNTIME_CHECK(result == DNS_R_NXRRSET); if (dbuf != NULL) query_releasename(client, namep); } if (rdataset->trust != dns_trust_secure && (section == DNS_SECTION_ANSWER || section == DNS_SECTION_AUTHORITY)) client->query.attributes &= ~NS_QUERYATTR_SECURE; /* * Note: we only add SIGs if we've added the type they cover, so * we do not need to check if the SIG rdataset is already in the * response. */ query_addrdataset(client, mname, rdataset); *rdatasetp = NULL; if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { /* * We have a signature. Add it to the response. */ ISC_LIST_APPEND(mname->list, sigrdataset, link); *sigrdatasetp = NULL; } CTRACE("query_addrrset: done"); } static inline isc_result_t query_addsoa(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version, unsigned int override_ttl, isc_boolean_t isassociated) { dns_name_t *name; dns_dbnode_t *node; isc_result_t result, eresult; dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; dns_rdataset_t **sigrdatasetp = NULL; dns_clientinfomethods_t cm; dns_clientinfo_t ci; CTRACE("query_addsoa"); /* * Initialization. */ eresult = ISC_R_SUCCESS; name = NULL; rdataset = NULL; node = NULL; dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); /* * Don't add the SOA record for test which set "-T nosoa". */ if (ns_g_nosoa && (!WANTDNSSEC(client) || !isassociated)) return (ISC_R_SUCCESS); /* * Get resources and make 'name' be the database origin. */ result = dns_message_gettempname(client->message, &name); if (result != ISC_R_SUCCESS) return (result); dns_name_init(name, NULL); dns_name_clone(dns_db_origin(db), name); rdataset = query_newrdataset(client); if (rdataset == NULL) { eresult = DNS_R_SERVFAIL; goto cleanup; } if (WANTDNSSEC(client) && dns_db_issecure(db)) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) { eresult = DNS_R_SERVFAIL; goto cleanup; } } /* * Find the SOA. */ result = dns_db_getoriginnode(db, &node); if (result == ISC_R_SUCCESS) { result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa, 0, client->now, rdataset, sigrdataset); } else { dns_fixedname_t foundname; dns_name_t *fname; dns_fixedname_init(&foundname); fname = dns_fixedname_name(&foundname); result = dns_db_findext(db, name, version, dns_rdatatype_soa, client->query.dboptions, 0, &node, fname, &cm, &ci, rdataset, sigrdataset); } if (result != ISC_R_SUCCESS) { /* * This is bad. We tried to get the SOA RR at the zone top * and it didn't work! */ eresult = DNS_R_SERVFAIL; } else { /* * Extract the SOA MINIMUM. */ dns_rdata_soa_t soa; dns_rdata_t rdata = DNS_RDATA_INIT; result = dns_rdataset_first(rdataset); RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &soa, NULL); if (result != ISC_R_SUCCESS) goto cleanup; if (override_ttl != ISC_UINT32_MAX && override_ttl < rdataset->ttl) { rdataset->ttl = override_ttl; if (sigrdataset != NULL) sigrdataset->ttl = override_ttl; } /* * Add the SOA and its SIG to the response, with the * TTLs adjusted per RFC2308 section 3. */ if (rdataset->ttl > soa.minimum) rdataset->ttl = soa.minimum; if (sigrdataset != NULL && sigrdataset->ttl > soa.minimum) sigrdataset->ttl = soa.minimum; if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; query_addrrset(client, &name, &rdataset, sigrdatasetp, NULL, DNS_SECTION_AUTHORITY); } cleanup: query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (name != NULL) query_releasename(client, &name); if (node != NULL) dns_db_detachnode(db, &node); return (eresult); } static inline isc_result_t query_addns(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version) { dns_name_t *name, *fname; dns_dbnode_t *node; isc_result_t result, eresult; dns_fixedname_t foundname; dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; dns_rdataset_t **sigrdatasetp = NULL; dns_clientinfomethods_t cm; dns_clientinfo_t ci; CTRACE("query_addns"); /* * Initialization. */ eresult = ISC_R_SUCCESS; name = NULL; rdataset = NULL; node = NULL; dns_fixedname_init(&foundname); fname = dns_fixedname_name(&foundname); dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); /* * Get resources and make 'name' be the database origin. */ result = dns_message_gettempname(client->message, &name); if (result != ISC_R_SUCCESS) { CTRACE("query_addns: dns_message_gettempname failed: done"); return (result); } dns_name_init(name, NULL); dns_name_clone(dns_db_origin(db), name); rdataset = query_newrdataset(client); if (rdataset == NULL) { CTRACE("query_addns: query_newrdataset failed"); eresult = DNS_R_SERVFAIL; goto cleanup; } if (WANTDNSSEC(client) && dns_db_issecure(db)) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) { CTRACE("query_addns: query_newrdataset failed"); eresult = DNS_R_SERVFAIL; goto cleanup; } } /* * Find the NS rdataset. */ result = dns_db_getoriginnode(db, &node); if (result == ISC_R_SUCCESS) { result = dns_db_findrdataset(db, node, version, dns_rdatatype_ns, 0, client->now, rdataset, sigrdataset); } else { CTRACE("query_addns: calling dns_db_find"); result = dns_db_findext(db, name, NULL, dns_rdatatype_ns, client->query.dboptions, 0, &node, fname, &cm, &ci, rdataset, sigrdataset); CTRACE("query_addns: dns_db_find complete"); } if (result != ISC_R_SUCCESS) { CTRACE("query_addns: " "dns_db_findrdataset or dns_db_find failed"); /* * This is bad. We tried to get the NS rdataset at the zone * top and it didn't work! */ eresult = DNS_R_SERVFAIL; } else { if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; query_addrrset(client, &name, &rdataset, sigrdatasetp, NULL, DNS_SECTION_AUTHORITY); } cleanup: CTRACE("query_addns: cleanup"); query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (name != NULL) query_releasename(client, &name); if (node != NULL) dns_db_detachnode(db, &node); CTRACE("query_addns: done"); return (eresult); } static isc_result_t query_add_cname(ns_client_t *client, dns_name_t *qname, dns_name_t *tname, dns_trust_t trust, dns_ttl_t ttl) { dns_rdataset_t *rdataset; dns_rdatalist_t *rdatalist; dns_rdata_t *rdata; isc_region_t r; dns_name_t *aname; isc_result_t result; /* * We assume the name data referred to by tname won't go away. */ aname = NULL; result = dns_message_gettempname(client->message, &aname); if (result != ISC_R_SUCCESS) return (result); result = dns_name_dup(qname, client->mctx, aname); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &aname); return (result); } rdatalist = NULL; result = dns_message_gettemprdatalist(client->message, &rdatalist); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &aname); return (result); } rdata = NULL; result = dns_message_gettemprdata(client->message, &rdata); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &aname); dns_message_puttemprdatalist(client->message, &rdatalist); return (result); } rdataset = NULL; result = dns_message_gettemprdataset(client->message, &rdataset); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &aname); dns_message_puttemprdatalist(client->message, &rdatalist); dns_message_puttemprdata(client->message, &rdata); return (result); } dns_rdataset_init(rdataset); rdatalist->type = dns_rdatatype_cname; rdatalist->covers = 0; rdatalist->rdclass = client->message->rdclass; rdatalist->ttl = ttl; dns_name_toregion(tname, &r); rdata->data = r.base; rdata->length = r.length; rdata->rdclass = client->message->rdclass; rdata->type = dns_rdatatype_cname; ISC_LIST_INIT(rdatalist->rdata); ISC_LIST_APPEND(rdatalist->rdata, rdata, link); RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == ISC_R_SUCCESS); rdataset->trust = trust; query_addrrset(client, &aname, &rdataset, NULL, NULL, DNS_SECTION_ANSWER); if (rdataset != NULL) { if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); dns_message_puttemprdataset(client->message, &rdataset); } if (aname != NULL) dns_message_puttempname(client->message, &aname); return (ISC_R_SUCCESS); } /* * Mark the RRsets as secure. Update the cache (db) to reflect the * change in trust level. */ static void mark_secure(ns_client_t *client, dns_db_t *db, dns_name_t *name, dns_rdata_rrsig_t *rrsig, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { isc_result_t result; dns_dbnode_t *node = NULL; dns_clientinfomethods_t cm; dns_clientinfo_t ci; isc_stdtime_t now; rdataset->trust = dns_trust_secure; sigrdataset->trust = dns_trust_secure; dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); /* * Save the updated secure state. Ignore failures. */ result = dns_db_findnodeext(db, name, ISC_TRUE, &cm, &ci, &node); if (result != ISC_R_SUCCESS) return; isc_stdtime_get(&now); dns_rdataset_trimttl(rdataset, sigrdataset, rrsig, now, client->view->acceptexpired); (void)dns_db_addrdataset(db, node, NULL, client->now, rdataset, 0, NULL); (void)dns_db_addrdataset(db, node, NULL, client->now, sigrdataset, 0, NULL); dns_db_detachnode(db, &node); } /* * Find the secure key that corresponds to rrsig. * Note: 'keyrdataset' maintains state between successive calls, * there may be multiple keys with the same keyid. * Return ISC_FALSE if we have exhausted all the possible keys. */ static isc_boolean_t get_key(ns_client_t *client, dns_db_t *db, dns_rdata_rrsig_t *rrsig, dns_rdataset_t *keyrdataset, dst_key_t **keyp) { isc_result_t result; dns_dbnode_t *node = NULL; isc_boolean_t secure = ISC_FALSE; dns_clientinfomethods_t cm; dns_clientinfo_t ci; dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); if (!dns_rdataset_isassociated(keyrdataset)) { result = dns_db_findnodeext(db, &rrsig->signer, ISC_FALSE, &cm, &ci, &node); if (result != ISC_R_SUCCESS) return (ISC_FALSE); result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey, 0, client->now, keyrdataset, NULL); dns_db_detachnode(db, &node); if (result != ISC_R_SUCCESS) return (ISC_FALSE); if (keyrdataset->trust != dns_trust_secure) return (ISC_FALSE); result = dns_rdataset_first(keyrdataset); } else result = dns_rdataset_next(keyrdataset); for ( ; result == ISC_R_SUCCESS; result = dns_rdataset_next(keyrdataset)) { dns_rdata_t rdata = DNS_RDATA_INIT; isc_buffer_t b; dns_rdataset_current(keyrdataset, &rdata); isc_buffer_init(&b, rdata.data, rdata.length); isc_buffer_add(&b, rdata.length); result = dst_key_fromdns(&rrsig->signer, rdata.rdclass, &b, client->mctx, keyp); if (result != ISC_R_SUCCESS) continue; if (rrsig->algorithm == (dns_secalg_t)dst_key_alg(*keyp) && rrsig->keyid == (dns_keytag_t)dst_key_id(*keyp) && dst_key_iszonekey(*keyp)) { secure = ISC_TRUE; break; } dst_key_free(keyp); } return (secure); } static isc_boolean_t verify(dst_key_t *key, dns_name_t *name, dns_rdataset_t *rdataset, dns_rdata_t *rdata, ns_client_t *client) { isc_result_t result; dns_fixedname_t fixed; isc_boolean_t ignore = ISC_FALSE; dns_fixedname_init(&fixed); again: result = dns_dnssec_verify3(name, rdataset, key, ignore, client->view->maxbits, client->mctx, rdata, NULL); if (result == DNS_R_SIGEXPIRED && client->view->acceptexpired) { ignore = ISC_TRUE; goto again; } if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) return (ISC_TRUE); return (ISC_FALSE); } /* * Validate the rdataset if possible with available records. */ static isc_boolean_t validate(ns_client_t *client, dns_db_t *db, dns_name_t *name, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_rrsig_t rrsig; dst_key_t *key = NULL; dns_rdataset_t keyrdataset; if (sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset)) return (ISC_FALSE); for (result = dns_rdataset_first(sigrdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(sigrdataset)) { dns_rdata_reset(&rdata); dns_rdataset_current(sigrdataset, &rdata); result = dns_rdata_tostruct(&rdata, &rrsig, NULL); if (result != ISC_R_SUCCESS) return (ISC_FALSE); if (!dns_resolver_algorithm_supported(client->view->resolver, name, rrsig.algorithm)) continue; if (!dns_name_issubdomain(name, &rrsig.signer)) continue; dns_rdataset_init(&keyrdataset); do { if (!get_key(client, db, &rrsig, &keyrdataset, &key)) break; if (verify(key, name, rdataset, &rdata, client)) { dst_key_free(&key); dns_rdataset_disassociate(&keyrdataset); mark_secure(client, db, name, &rrsig, rdataset, sigrdataset); return (ISC_TRUE); } dst_key_free(&key); } while (1); if (dns_rdataset_isassociated(&keyrdataset)) dns_rdataset_disassociate(&keyrdataset); } return (ISC_FALSE); } static void query_addbestns(ns_client_t *client) { dns_db_t *db, *zdb; dns_dbnode_t *node; dns_name_t *fname, *zfname; dns_rdataset_t *rdataset, *sigrdataset, *zrdataset, *zsigrdataset; isc_boolean_t is_zone, use_zone; isc_buffer_t *dbuf; isc_result_t result; dns_dbversion_t *version; dns_zone_t *zone; isc_buffer_t b; dns_clientinfomethods_t cm; dns_clientinfo_t ci; CTRACE("query_addbestns"); fname = NULL; zfname = NULL; rdataset = NULL; zrdataset = NULL; sigrdataset = NULL; zsigrdataset = NULL; node = NULL; db = NULL; zdb = NULL; version = NULL; zone = NULL; is_zone = ISC_FALSE; use_zone = ISC_FALSE; dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); /* * Find the right database. */ result = query_getdb(client, client->query.qname, dns_rdatatype_ns, 0, &zone, &db, &version, &is_zone); if (result != ISC_R_SUCCESS) goto cleanup; db_find: /* * We'll need some resources... */ dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); rdataset = query_newrdataset(client); if (fname == NULL || rdataset == NULL) goto cleanup; /* * Get the RRSIGs if the client requested them or if we may * need to validate answers from the cache. */ if (WANTDNSSEC(client) || !is_zone) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) goto cleanup; } /* * Now look for the zonecut. */ if (is_zone) { result = dns_db_findext(db, client->query.qname, version, dns_rdatatype_ns, client->query.dboptions, client->now, &node, fname, &cm, &ci, rdataset, sigrdataset); if (result != DNS_R_DELEGATION) goto cleanup; if (USECACHE(client)) { query_keepname(client, fname, dbuf); zdb = db; zfname = fname; fname = NULL; zrdataset = rdataset; rdataset = NULL; zsigrdataset = sigrdataset; sigrdataset = NULL; dns_db_detachnode(db, &node); version = NULL; db = NULL; dns_db_attach(client->view->cachedb, &db); is_zone = ISC_FALSE; goto db_find; } } else { result = dns_db_findzonecut(db, client->query.qname, client->query.dboptions, client->now, &node, fname, rdataset, sigrdataset); if (result == ISC_R_SUCCESS) { if (zfname != NULL && !dns_name_issubdomain(fname, zfname)) { /* * We found a zonecut in the cache, but our * zone delegation is better. */ use_zone = ISC_TRUE; } } else if (result == ISC_R_NOTFOUND && zfname != NULL) { /* * We didn't find anything in the cache, but we * have a zone delegation, so use it. */ use_zone = ISC_TRUE; } else goto cleanup; } if (use_zone) { query_releasename(client, &fname); fname = zfname; zfname = NULL; /* * We've already done query_keepname() on * zfname, so we must set dbuf to NULL to * prevent query_addrrset() from trying to * call query_keepname() again. */ dbuf = NULL; query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); rdataset = zrdataset; zrdataset = NULL; sigrdataset = zsigrdataset; zsigrdataset = NULL; } /* * Attempt to validate RRsets that are pending or that are glue. */ if ((DNS_TRUST_PENDING(rdataset->trust) || (sigrdataset != NULL && DNS_TRUST_PENDING(sigrdataset->trust))) && !validate(client, db, fname, rdataset, sigrdataset) && !PENDINGOK(client->query.dboptions)) goto cleanup; if ((DNS_TRUST_GLUE(rdataset->trust) || (sigrdataset != NULL && DNS_TRUST_GLUE(sigrdataset->trust))) && !validate(client, db, fname, rdataset, sigrdataset) && SECURE(client) && WANTDNSSEC(client)) goto cleanup; /* * If the answer is secure only add NS records if they are secure * when the client may be looking for AD in the response. */ if (SECURE(client) && (WANTDNSSEC(client) || WANTAD(client)) && ((rdataset->trust != dns_trust_secure) || (sigrdataset != NULL && sigrdataset->trust != dns_trust_secure))) goto cleanup; /* * If the client doesn't want DNSSEC we can discard the sigrdataset * now. */ if (!WANTDNSSEC(client)) query_putrdataset(client, &sigrdataset); query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); cleanup: if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); if (node != NULL) dns_db_detachnode(db, &node); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); if (zdb != NULL) { query_putrdataset(client, &zrdataset); if (zsigrdataset != NULL) query_putrdataset(client, &zsigrdataset); if (zfname != NULL) query_releasename(client, &zfname); dns_db_detach(&zdb); } } static void fixrdataset(ns_client_t *client, dns_rdataset_t **rdataset) { if (*rdataset == NULL) *rdataset = query_newrdataset(client); else if (dns_rdataset_isassociated(*rdataset)) dns_rdataset_disassociate(*rdataset); } static void fixfname(ns_client_t *client, dns_name_t **fname, isc_buffer_t **dbuf, isc_buffer_t *nbuf) { if (*fname == NULL) { *dbuf = query_getnamebuf(client); if (*dbuf == NULL) return; *fname = query_newname(client, *dbuf, nbuf); } } static void query_addds(ns_client_t *client, dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, dns_name_t *name) { dns_fixedname_t fixed; dns_name_t *fname = NULL; dns_name_t *rname; dns_rdataset_t *rdataset, *sigrdataset; isc_buffer_t *dbuf, b; isc_result_t result; unsigned int count; CTRACE("query_addds"); rname = NULL; rdataset = NULL; sigrdataset = NULL; /* * We'll need some resources... */ rdataset = query_newrdataset(client); sigrdataset = query_newrdataset(client); if (rdataset == NULL || sigrdataset == NULL) goto cleanup; /* * Look for the DS record, which may or may not be present. */ result = dns_db_findrdataset(db, node, version, dns_rdatatype_ds, 0, client->now, rdataset, sigrdataset); /* * If we didn't find it, look for an NSEC. */ if (result == ISC_R_NOTFOUND) result = dns_db_findrdataset(db, node, version, dns_rdatatype_nsec, 0, client->now, rdataset, sigrdataset); if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) goto addnsec3; if (!dns_rdataset_isassociated(rdataset) || !dns_rdataset_isassociated(sigrdataset)) goto addnsec3; /* * We've already added the NS record, so if the name's not there, * we have other problems. Use this name rather than calling * query_addrrset(). */ result = dns_message_firstname(client->message, DNS_SECTION_AUTHORITY); if (result != ISC_R_SUCCESS) goto cleanup; rname = NULL; dns_message_currentname(client->message, DNS_SECTION_AUTHORITY, &rname); result = dns_message_findtype(rname, dns_rdatatype_ns, 0, NULL); if (result != ISC_R_SUCCESS) goto cleanup; ISC_LIST_APPEND(rname->list, rdataset, link); ISC_LIST_APPEND(rname->list, sigrdataset, link); rdataset = NULL; sigrdataset = NULL; return; addnsec3: if (!dns_db_iszone(db)) goto cleanup; /* * Add the NSEC3 which proves the DS does not exist. */ dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); dns_fixedname_init(&fixed); if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); query_findclosestnsec3(name, db, version, client, rdataset, sigrdataset, fname, ISC_TRUE, dns_fixedname_name(&fixed)); if (!dns_rdataset_isassociated(rdataset)) goto cleanup; query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); /* * Did we find the closest provable encloser instead? * If so add the nearest to the closest provable encloser. */ if (!dns_name_equal(name, dns_fixedname_name(&fixed))) { count = dns_name_countlabels(dns_fixedname_name(&fixed)) + 1; dns_name_getlabelsequence(name, dns_name_countlabels(name) - count, count, dns_fixedname_name(&fixed)); fixfname(client, &fname, &dbuf, &b); fixrdataset(client, &rdataset); fixrdataset(client, &sigrdataset); if (fname == NULL || rdataset == NULL || sigrdataset == NULL) goto cleanup; query_findclosestnsec3(dns_fixedname_name(&fixed), db, version, client, rdataset, sigrdataset, fname, ISC_FALSE, NULL); if (!dns_rdataset_isassociated(rdataset)) goto cleanup; query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); } cleanup: if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); } static void query_addwildcardproof(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, isc_boolean_t ispositive, isc_boolean_t nodata) { isc_buffer_t *dbuf, b; dns_name_t *fname; dns_rdataset_t *rdataset, *sigrdataset; dns_fixedname_t wfixed; dns_name_t *wname; dns_dbnode_t *node; unsigned int options; unsigned int olabels, nlabels, labels; isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_nsec_t nsec; isc_boolean_t have_wname; int order; dns_fixedname_t cfixed; dns_name_t *cname; dns_clientinfomethods_t cm; dns_clientinfo_t ci; CTRACE("query_addwildcardproof"); fname = NULL; rdataset = NULL; sigrdataset = NULL; node = NULL; dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); /* * Get the NOQNAME proof then if !ispositive * get the NOWILDCARD proof. * * DNS_DBFIND_NOWILD finds the NSEC records that covers the * name ignoring any wildcard. From the owner and next names * of this record you can compute which wildcard (if it exists) * will match by finding the longest common suffix of the * owner name and next names with the qname and prefixing that * with the wildcard label. * * e.g. * Given: * example SOA * example NSEC b.example * b.example A * b.example NSEC a.d.example * a.d.example A * a.d.example NSEC g.f.example * g.f.example A * g.f.example NSEC z.i.example * z.i.example A * z.i.example NSEC example * * QNAME: * a.example -> example NSEC b.example * owner common example * next common example * wild *.example * d.b.example -> b.example NSEC a.d.example * owner common b.example * next common example * wild *.b.example * a.f.example -> a.d.example NSEC g.f.example * owner common example * next common f.example * wild *.f.example * j.example -> z.i.example NSEC example * owner common example * next common example * wild *.example */ options = client->query.dboptions | DNS_DBFIND_NOWILD; dns_fixedname_init(&wfixed); wname = dns_fixedname_name(&wfixed); again: have_wname = ISC_FALSE; /* * We'll need some resources... */ dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); rdataset = query_newrdataset(client); sigrdataset = query_newrdataset(client); if (fname == NULL || rdataset == NULL || sigrdataset == NULL) goto cleanup; result = dns_db_findext(db, name, version, dns_rdatatype_nsec, options, 0, &node, fname, &cm, &ci, rdataset, sigrdataset); if (node != NULL) dns_db_detachnode(db, &node); if (!dns_rdataset_isassociated(rdataset)) { /* * No NSEC proof available, return NSEC3 proofs instead. */ dns_fixedname_init(&cfixed); cname = dns_fixedname_name(&cfixed); /* * Find the closest encloser. */ dns_name_copy(name, cname, NULL); while (result == DNS_R_NXDOMAIN) { labels = dns_name_countlabels(cname) - 1; /* * Sanity check. */ if (labels == 0U) goto cleanup; dns_name_split(cname, labels, NULL, cname); result = dns_db_findext(db, cname, version, dns_rdatatype_nsec, options, 0, NULL, fname, &cm, &ci, NULL, NULL); } /* * Add closest (provable) encloser NSEC3. */ query_findclosestnsec3(cname, db, NULL, client, rdataset, sigrdataset, fname, ISC_TRUE, cname); if (!dns_rdataset_isassociated(rdataset)) goto cleanup; if (!ispositive) query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); /* * Replace resources which were consumed by query_addrrset. */ if (fname == NULL) { dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); } if (rdataset == NULL) rdataset = query_newrdataset(client); else if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); if (sigrdataset == NULL) sigrdataset = query_newrdataset(client); else if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); if (fname == NULL || rdataset == NULL || sigrdataset == NULL) goto cleanup; /* * Add no qname proof. */ labels = dns_name_countlabels(cname) + 1; if (dns_name_countlabels(name) == labels) dns_name_copy(name, wname, NULL); else dns_name_split(name, labels, NULL, wname); query_findclosestnsec3(wname, db, NULL, client, rdataset, sigrdataset, fname, ISC_FALSE, NULL); if (!dns_rdataset_isassociated(rdataset)) goto cleanup; query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); if (ispositive) goto cleanup; /* * Replace resources which were consumed by query_addrrset. */ if (fname == NULL) { dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); } if (rdataset == NULL) rdataset = query_newrdataset(client); else if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); if (sigrdataset == NULL) sigrdataset = query_newrdataset(client); else if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); if (fname == NULL || rdataset == NULL || sigrdataset == NULL) goto cleanup; /* * Add the no wildcard proof. */ result = dns_name_concatenate(dns_wildcardname, cname, wname, NULL); if (result != ISC_R_SUCCESS) goto cleanup; query_findclosestnsec3(wname, db, NULL, client, rdataset, sigrdataset, fname, nodata, NULL); if (!dns_rdataset_isassociated(rdataset)) goto cleanup; query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); goto cleanup; } else if (result == DNS_R_NXDOMAIN) { if (!ispositive) result = dns_rdataset_first(rdataset); if (result == ISC_R_SUCCESS) { dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &nsec, NULL); } if (result == ISC_R_SUCCESS) { (void)dns_name_fullcompare(name, fname, &order, &olabels); (void)dns_name_fullcompare(name, &nsec.next, &order, &nlabels); /* * Check for a pathological condition created when * serving some malformed signed zones and bail out. */ if (dns_name_countlabels(name) == nlabels) goto cleanup; if (olabels > nlabels) dns_name_split(name, olabels, NULL, wname); else dns_name_split(name, nlabels, NULL, wname); result = dns_name_concatenate(dns_wildcardname, wname, wname, NULL); if (result == ISC_R_SUCCESS) have_wname = ISC_TRUE; dns_rdata_freestruct(&nsec); } query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); } if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); if (have_wname) { ispositive = ISC_TRUE; /* prevent loop */ if (!dns_name_equal(name, wname)) { name = wname; goto again; } } cleanup: if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); } static void query_addnxrrsetnsec(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version, dns_name_t **namep, dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp) { dns_name_t *name; dns_rdataset_t *sigrdataset; dns_rdata_t sigrdata; dns_rdata_rrsig_t sig; unsigned int labels; isc_buffer_t *dbuf, b; dns_name_t *fname; isc_result_t result; name = *namep; if ((name->attributes & DNS_NAMEATTR_WILDCARD) == 0) { query_addrrset(client, namep, rdatasetp, sigrdatasetp, NULL, DNS_SECTION_AUTHORITY); return; } if (sigrdatasetp == NULL) return; sigrdataset = *sigrdatasetp; if (sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset)) return; result = dns_rdataset_first(sigrdataset); if (result != ISC_R_SUCCESS) return; dns_rdata_init(&sigrdata); dns_rdataset_current(sigrdataset, &sigrdata); result = dns_rdata_tostruct(&sigrdata, &sig, NULL); if (result != ISC_R_SUCCESS) return; labels = dns_name_countlabels(name); if ((unsigned int)sig.labels + 1 >= labels) return; /* XXX */ query_addwildcardproof(client, db, version, client->query.qname, ISC_TRUE, ISC_FALSE); /* * We'll need some resources... */ dbuf = query_getnamebuf(client); if (dbuf == NULL) return; fname = query_newname(client, dbuf, &b); if (fname == NULL) return; dns_name_split(name, sig.labels + 1, NULL, fname); /* This will succeed, since we've stripped labels. */ RUNTIME_CHECK(dns_name_concatenate(dns_wildcardname, fname, fname, NULL) == ISC_R_SUCCESS); query_addrrset(client, &fname, rdatasetp, sigrdatasetp, dbuf, DNS_SECTION_AUTHORITY); } static void query_resume(isc_task_t *task, isc_event_t *event) { dns_fetchevent_t *devent = (dns_fetchevent_t *)event; dns_fetch_t *fetch; ns_client_t *client; isc_boolean_t fetch_canceled, client_shuttingdown; isc_result_t result; isc_logcategory_t *logcategory = NS_LOGCATEGORY_QUERY_EERRORS; int errorloglevel; /* * Resume a query after recursion. */ UNUSED(task); REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); client = devent->ev_arg; REQUIRE(NS_CLIENT_VALID(client)); REQUIRE(task == client->task); REQUIRE(RECURSING(client)); LOCK(&client->query.fetchlock); if (client->query.fetch != NULL) { /* * This is the fetch we've been waiting for. */ INSIST(devent->fetch == client->query.fetch); client->query.fetch = NULL; fetch_canceled = ISC_FALSE; /* * Update client->now. */ isc_stdtime_get(&client->now); } else { /* * This is a fetch completion event for a canceled fetch. * Clean up and don't resume the find. */ fetch_canceled = ISC_TRUE; } UNLOCK(&client->query.fetchlock); INSIST(client->query.fetch == NULL); client->query.attributes &= ~NS_QUERYATTR_RECURSING; fetch = devent->fetch; devent->fetch = NULL; /* * If this client is shutting down, or this transaction * has timed out, do not resume the find. */ client_shuttingdown = ns_client_shuttingdown(client); if (fetch_canceled || client_shuttingdown) { if (devent->node != NULL) dns_db_detachnode(devent->db, &devent->node); if (devent->db != NULL) dns_db_detach(&devent->db); query_putrdataset(client, &devent->rdataset); if (devent->sigrdataset != NULL) query_putrdataset(client, &devent->sigrdataset); isc_event_free(&event); if (fetch_canceled) query_error(client, DNS_R_SERVFAIL, __LINE__); else query_next(client, ISC_R_CANCELED); /* * This may destroy the client. */ ns_client_detach(&client); } else { result = query_find(client, devent, 0); if (result != ISC_R_SUCCESS) { if (result == DNS_R_SERVFAIL) errorloglevel = ISC_LOG_DEBUG(2); else errorloglevel = ISC_LOG_DEBUG(4); if (isc_log_wouldlog(ns_g_lctx, errorloglevel)) { dns_resolver_logfetch(fetch, ns_g_lctx, logcategory, NS_LOGMODULE_QUERY, errorloglevel, ISC_FALSE); } } } dns_resolver_destroyfetch(&fetch); } static isc_result_t query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, dns_name_t *qdomain, dns_rdataset_t *nameservers, isc_boolean_t resuming) { isc_result_t result; dns_rdataset_t *rdataset, *sigrdataset; isc_sockaddr_t *peeraddr; if (!resuming) inc_stats(client, dns_nsstatscounter_recursion); /* * We are about to recurse, which means that this client will * be unavailable for serving new requests for an indeterminate * amount of time. If this client is currently responsible * for handling incoming queries, set up a new client * object to handle them while we are waiting for a * response. There is no need to replace TCP clients * because those have already been replaced when the * connection was accepted (if allowed by the TCP quota). */ if (client->recursionquota == NULL) { result = isc_quota_attach(&ns_g_server->recursionquota, &client->recursionquota); if (result == ISC_R_SOFTQUOTA) { static isc_stdtime_t last = 0; isc_stdtime_t now; isc_stdtime_get(&now); if (now != last) { last = now; ns_client_log(client, NS_LOGCATEGORY_CLIENT, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "recursive-clients soft limit " "exceeded (%d/%d/%d), " "aborting oldest query", client->recursionquota->used, client->recursionquota->soft, client->recursionquota->max); } ns_client_killoldestquery(client); result = ISC_R_SUCCESS; } else if (result == ISC_R_QUOTA) { static isc_stdtime_t last = 0; isc_stdtime_t now; isc_stdtime_get(&now); if (now != last) { last = now; ns_client_log(client, NS_LOGCATEGORY_CLIENT, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "no more recursive clients " "(%d/%d/%d): %s", ns_g_server->recursionquota.used, ns_g_server->recursionquota.soft, ns_g_server->recursionquota.max, isc_result_totext(result)); } ns_client_killoldestquery(client); } if (result == ISC_R_SUCCESS && !client->mortal && (client->attributes & NS_CLIENTATTR_TCP) == 0) { result = ns_client_replace(client); if (result != ISC_R_SUCCESS) { ns_client_log(client, NS_LOGCATEGORY_CLIENT, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "ns_client_replace() failed: %s", isc_result_totext(result)); isc_quota_detach(&client->recursionquota); } } if (result != ISC_R_SUCCESS) return (result); ns_client_recursing(client); } /* * Invoke the resolver. */ REQUIRE(nameservers == NULL || nameservers->type == dns_rdatatype_ns); REQUIRE(client->query.fetch == NULL); rdataset = query_newrdataset(client); if (rdataset == NULL) return (ISC_R_NOMEMORY); if (WANTDNSSEC(client)) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) { query_putrdataset(client, &rdataset); return (ISC_R_NOMEMORY); } } else sigrdataset = NULL; if (client->query.timerset == ISC_FALSE) ns_client_settimeout(client, 60); if ((client->attributes & NS_CLIENTATTR_TCP) == 0) peeraddr = &client->peeraddr; else peeraddr = NULL; - result = dns_resolver_createfetch2(client->view->resolver, + result = dns_resolver_createfetch3(client->view->resolver, qname, qtype, qdomain, nameservers, NULL, peeraddr, client->message->id, - client->query.fetchoptions, - client->task, - query_resume, client, + client->query.fetchoptions, 0, + client->task, query_resume, client, rdataset, sigrdataset, &client->query.fetch); if (result == ISC_R_SUCCESS) { /* * Record that we're waiting for an event. A client which * is shutting down will not be destroyed until all the * events have been received. */ } else { query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); } return (result); } static inline void rpz_clean(dns_zone_t **zonep, dns_db_t **dbp, dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp) { if (nodep != NULL && *nodep != NULL) { REQUIRE(dbp != NULL && *dbp != NULL); dns_db_detachnode(*dbp, nodep); } if (dbp != NULL && *dbp != NULL) dns_db_detach(dbp); if (zonep != NULL && *zonep != NULL) dns_zone_detach(zonep); if (rdatasetp != NULL && *rdatasetp != NULL && dns_rdataset_isassociated(*rdatasetp)) dns_rdataset_disassociate(*rdatasetp); } static void rpz_match_clear(dns_rpz_st_t *st) { rpz_clean(&st->m.zone, &st->m.db, &st->m.node, &st->m.rdataset); st->m.version = NULL; } static inline isc_result_t rpz_ready(ns_client_t *client, dns_zone_t **zonep, dns_db_t **dbp, dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp) { REQUIRE(rdatasetp != NULL); rpz_clean(zonep, dbp, nodep, rdatasetp); if (*rdatasetp == NULL) { *rdatasetp = query_newrdataset(client); if (*rdatasetp == NULL) return (DNS_R_SERVFAIL); } return (ISC_R_SUCCESS); } static void rpz_st_clear(ns_client_t *client) { dns_rpz_st_t *st = client->query.rpz_st; if (st->m.rdataset != NULL) query_putrdataset(client, &st->m.rdataset); rpz_match_clear(st); rpz_clean(NULL, &st->r.db, NULL, NULL); if (st->r.ns_rdataset != NULL) query_putrdataset(client, &st->r.ns_rdataset); if (st->r.r_rdataset != NULL) query_putrdataset(client, &st->r.r_rdataset); rpz_clean(&st->q.zone, &st->q.db, &st->q.node, NULL); if (st->q.rdataset != NULL) query_putrdataset(client, &st->q.rdataset); if (st->q.sigrdataset != NULL) query_putrdataset(client, &st->q.sigrdataset); st->state = 0; st->m.type = DNS_RPZ_TYPE_BAD; st->m.policy = DNS_RPZ_POLICY_MISS; } /* * Get NS, A, or AAAA rrset for response policy zone checks. */ static isc_result_t rpz_rrset_find(ns_client_t *client, dns_rpz_type_t rpz_type, dns_name_t *name, dns_rdatatype_t type, dns_db_t **dbp, dns_dbversion_t *version, dns_rdataset_t **rdatasetp, isc_boolean_t resuming) { dns_rpz_st_t *st; isc_boolean_t is_zone; dns_dbnode_t *node; dns_fixedname_t fixed; dns_name_t *found; isc_result_t result; dns_clientinfomethods_t cm; dns_clientinfo_t ci; dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); st = client->query.rpz_st; if ((st->state & DNS_RPZ_RECURSING) != 0) { INSIST(st->r.r_type == type); INSIST(dns_name_equal(name, st->r_name)); INSIST(*rdatasetp == NULL || !dns_rdataset_isassociated(*rdatasetp)); st->state &= ~DNS_RPZ_RECURSING; *dbp = st->r.db; st->r.db = NULL; if (*rdatasetp != NULL) query_putrdataset(client, rdatasetp); *rdatasetp = st->r.r_rdataset; st->r.r_rdataset = NULL; result = st->r.r_result; if (result == DNS_R_DELEGATION) { rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, name, "rpz_rrset_find(1) ", result); st->m.policy = DNS_RPZ_POLICY_ERROR; result = DNS_R_SERVFAIL; } return (result); } result = rpz_ready(client, NULL, NULL, NULL, rdatasetp); if (result != ISC_R_SUCCESS) { st->m.policy = DNS_RPZ_POLICY_ERROR; return (result); } if (*dbp != NULL) { is_zone = ISC_FALSE; } else { dns_zone_t *zone; version = NULL; zone = NULL; result = query_getdb(client, name, type, 0, &zone, dbp, &version, &is_zone); if (result != ISC_R_SUCCESS) { rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, name, "rpz_rrset_find(2) ", result); st->m.policy = DNS_RPZ_POLICY_ERROR; if (zone != NULL) dns_zone_detach(&zone); return (result); } if (zone != NULL) dns_zone_detach(&zone); } node = NULL; dns_fixedname_init(&fixed); found = dns_fixedname_name(&fixed); result = dns_db_findext(*dbp, name, version, type, DNS_DBFIND_GLUEOK, client->now, &node, found, &cm, &ci, *rdatasetp, NULL); if (result == DNS_R_DELEGATION && is_zone && USECACHE(client)) { /* * Try the cache if we're authoritative for an * ancestor but not the domain itself. */ rpz_clean(NULL, dbp, &node, rdatasetp); version = NULL; dns_db_attach(client->view->cachedb, dbp); result = dns_db_findext(*dbp, name, version, dns_rdatatype_ns, 0, client->now, &node, found, &cm, &ci, *rdatasetp, NULL); } rpz_clean(NULL, dbp, &node, NULL); if (result == DNS_R_DELEGATION) { rpz_clean(NULL, NULL, NULL, rdatasetp); /* * Recurse for NS rrset or A or AAAA rrset for an NS. * Do not recurse for addresses for the query name. */ if (rpz_type == DNS_RPZ_TYPE_IP) { result = DNS_R_NXRRSET; } else { dns_name_copy(name, st->r_name, NULL); result = query_recurse(client, type, st->r_name, NULL, NULL, resuming); if (result == ISC_R_SUCCESS) { st->state |= DNS_RPZ_RECURSING; result = DNS_R_DELEGATION; } } } return (result); } /* * Check the IP address in an A or AAAA rdataset against * the IP or NSIP response policy rules of a view. */ static isc_result_t rpz_rewrite_ip(ns_client_t *client, dns_rdataset_t *rdataset, dns_rpz_type_t rpz_type) { dns_rpz_st_t *st; dns_dbversion_t *version; dns_zone_t *zone; dns_db_t *db; dns_rpz_zone_t *rpz; isc_result_t result; st = client->query.rpz_st; if (st->m.rdataset == NULL) { st->m.rdataset = query_newrdataset(client); if (st->m.rdataset == NULL) return (DNS_R_SERVFAIL); } zone = NULL; db = NULL; for (rpz = ISC_LIST_HEAD(client->view->rpz_zones); rpz != NULL; rpz = ISC_LIST_NEXT(rpz, link)) { if (!RECURSIONOK(client) && rpz->recursive_only) continue; /* * Do not check policy zones that cannot replace a policy * already known to match. */ if (st->m.policy != DNS_RPZ_POLICY_MISS) { if (st->m.rpz->num < rpz->num) break; if (st->m.rpz->num == rpz->num && st->m.type < rpz_type) continue; } /* * Find the database for this policy zone to get its radix tree. */ version = NULL; result = rpz_getdb(client, rpz_type, &rpz->origin, &zone, &db, &version); if (result != ISC_R_SUCCESS) { rpz_clean(&zone, &db, NULL, NULL); continue; } /* * Look for a better (e.g. longer prefix) hit for an IP address * in this rdataset in this radix tree than than the previous * hit, if any. Note the domain name and quality of the * best hit. */ dns_db_rpz_findips(rpz, rpz_type, zone, db, version, rdataset, st, client->query.rpz_st->qname); rpz_clean(&zone, &db, NULL, NULL); } return (ISC_R_SUCCESS); } /* * Look for an A or AAAA rdataset * and check for IP or NSIP rewrite policy rules. */ static isc_result_t rpz_rewrite_rrset(ns_client_t *client, dns_rpz_type_t rpz_type, dns_rdatatype_t type, dns_name_t *name, dns_db_t **dbp, dns_dbversion_t *version, dns_rdataset_t **rdatasetp, isc_boolean_t resuming) { isc_result_t result; result = rpz_rrset_find(client, rpz_type, name, type, dbp, version, rdatasetp, resuming); switch (result) { case ISC_R_SUCCESS: case DNS_R_GLUE: case DNS_R_ZONECUT: result = rpz_rewrite_ip(client, *rdatasetp, rpz_type); break; case DNS_R_EMPTYNAME: case DNS_R_EMPTYWILD: case DNS_R_NXDOMAIN: case DNS_R_NCACHENXDOMAIN: case DNS_R_NXRRSET: case DNS_R_NCACHENXRRSET: case ISC_R_NOTFOUND: result = ISC_R_SUCCESS; break; case DNS_R_DELEGATION: case DNS_R_DUPLICATE: case DNS_R_DROP: break; case DNS_R_CNAME: case DNS_R_DNAME: rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, rpz_type, name, "NS address rewrite rrset ", result); result = ISC_R_SUCCESS; break; default: if (client->query.rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) { client->query.rpz_st->m.policy = DNS_RPZ_POLICY_ERROR; rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, name, "NS address rewrite rrset ", result); } break; } return (result); } /* * Look for both A and AAAA rdatasets * and check for IP or NSIP rewrite policy rules. * Look only for addresses that will be in the ANSWER section * when checking for IP rules. */ static isc_result_t rpz_rewrite_rrsets(ns_client_t *client, dns_rpz_type_t rpz_type, dns_name_t *name, dns_rdatatype_t type, dns_rdataset_t **rdatasetp, isc_boolean_t resuming) { dns_rpz_st_t *st; dns_dbversion_t *version; dns_db_t *ipdb; isc_result_t result; st = client->query.rpz_st; version = NULL; ipdb = NULL; if ((st->state & DNS_RPZ_DONE_IPv4) == 0 && ((rpz_type == DNS_RPZ_TYPE_NSIP) ? (st->state & DNS_RPZ_HAVE_NSIPv4) : (st->state & DNS_RPZ_HAVE_IP)) != 0 && (type == dns_rdatatype_any || type == dns_rdatatype_a)) { result = rpz_rewrite_rrset(client, rpz_type, dns_rdatatype_a, name, &ipdb, version, rdatasetp, resuming); if (result == ISC_R_SUCCESS) st->state |= DNS_RPZ_DONE_IPv4; } else { result = ISC_R_SUCCESS; } if (result == ISC_R_SUCCESS && ((rpz_type == DNS_RPZ_TYPE_NSIP) ? (st->state & DNS_RPZ_HAVE_NSIPv6) : (st->state & DNS_RPZ_HAVE_IP)) != 0 && (type == dns_rdatatype_any || type == dns_rdatatype_aaaa)) { result = rpz_rewrite_rrset(client, rpz_type, dns_rdatatype_aaaa, name, &ipdb, version, rdatasetp, resuming); } if (ipdb != NULL) dns_db_detach(&ipdb); return (result); } /* * Get the rrset from a response policy zone. */ static isc_result_t rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef, dns_name_t *sname, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp, dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp, dns_rpz_policy_t *policyp) { dns_rpz_policy_t policy; dns_fixedname_t fixed; dns_name_t *found; isc_result_t result; dns_clientinfomethods_t cm; dns_clientinfo_t ci; REQUIRE(nodep != NULL); dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); result = rpz_ready(client, zonep, dbp, nodep, rdatasetp); if (result != ISC_R_SUCCESS) { *policyp = DNS_RPZ_POLICY_ERROR; return (result); } /* * Try to get either a CNAME or the type of record demanded by the * request from the policy zone. */ *versionp = NULL; result = rpz_getdb(client, rpz_type, qnamef, zonep, dbp, versionp); if (result != ISC_R_SUCCESS) { *policyp = DNS_RPZ_POLICY_MISS; return (DNS_R_NXDOMAIN); } dns_fixedname_init(&fixed); found = dns_fixedname_name(&fixed); result = dns_db_findext(*dbp, qnamef, *versionp, dns_rdatatype_any, 0, client->now, nodep, found, &cm, &ci, *rdatasetp, NULL); if (result == ISC_R_SUCCESS) { dns_rdatasetiter_t *rdsiter; rdsiter = NULL; result = dns_db_allrdatasets(*dbp, *nodep, *versionp, 0, &rdsiter); if (result != ISC_R_SUCCESS) { dns_db_detachnode(*dbp, nodep); rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef, "allrdatasets() ", result); *policyp = DNS_RPZ_POLICY_ERROR; return (DNS_R_SERVFAIL); } for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; result = dns_rdatasetiter_next(rdsiter)) { dns_rdatasetiter_current(rdsiter, *rdatasetp); if ((*rdatasetp)->type == dns_rdatatype_cname || (*rdatasetp)->type == qtype) break; dns_rdataset_disassociate(*rdatasetp); } dns_rdatasetiter_destroy(&rdsiter); if (result != ISC_R_SUCCESS) { if (result != ISC_R_NOMORE) { rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef, "rdatasetiter ", result); *policyp = DNS_RPZ_POLICY_ERROR; return (DNS_R_SERVFAIL); } /* * Ask again to get the right DNS_R_DNAME/NXRRSET/... * result if there is neither a CNAME nor target type. */ if (dns_rdataset_isassociated(*rdatasetp)) dns_rdataset_disassociate(*rdatasetp); dns_db_detachnode(*dbp, nodep); if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig) result = DNS_R_NXRRSET; else result = dns_db_findext(*dbp, qnamef, *versionp, qtype, 0, client->now, nodep, found, &cm, &ci, *rdatasetp, NULL); } } switch (result) { case ISC_R_SUCCESS: if ((*rdatasetp)->type != dns_rdatatype_cname) { policy = DNS_RPZ_POLICY_RECORD; } else { policy = dns_rpz_decode_cname(rpz, *rdatasetp, sname); if ((policy == DNS_RPZ_POLICY_RECORD || policy == DNS_RPZ_POLICY_WILDCNAME) && qtype != dns_rdatatype_cname && qtype != dns_rdatatype_any) result = DNS_R_CNAME; } break; case DNS_R_NXRRSET: policy = DNS_RPZ_POLICY_NODATA; break; case DNS_R_DNAME: /* * DNAME policy RRs have very few if any uses that are not * better served with simple wildcards. Making the work would * require complications to get the number of labels matched * in the name or the found name to the main DNS_R_DNAME case * in query_find(). */ dns_rdataset_disassociate(*rdatasetp); dns_db_detachnode(*dbp, nodep); /* * Fall through to treat it as a miss. */ case DNS_R_NXDOMAIN: case DNS_R_EMPTYNAME: /* * If we don't get a qname hit, * see if it is worth looking for other types. */ (void)dns_db_rpz_enabled(*dbp, client->query.rpz_st); dns_db_detach(dbp); dns_zone_detach(zonep); result = DNS_R_NXDOMAIN; policy = DNS_RPZ_POLICY_MISS; break; default: dns_db_detach(dbp); dns_zone_detach(zonep); rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef, "", result); return (DNS_R_SERVFAIL); } *policyp = policy; return (result); } /* * Build and look for a QNAME or NSDNAME owner name in a response policy zone. */ static isc_result_t rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, dns_rpz_type_t rpz_type, dns_rdataset_t **rdatasetp) { dns_rpz_st_t *st; dns_rpz_zone_t *rpz; dns_fixedname_t prefixf, rpz_qnamef; dns_name_t *prefix, *suffix, *rpz_qname; dns_zone_t *zone; dns_db_t *db; dns_dbversion_t *version; dns_dbnode_t *node; dns_rpz_policy_t policy; unsigned int labels; isc_result_t result; st = client->query.rpz_st; zone = NULL; db = NULL; node = NULL; for (rpz = ISC_LIST_HEAD(client->view->rpz_zones); rpz != NULL; rpz = ISC_LIST_NEXT(rpz, link)) { if (!RECURSIONOK(client) && rpz->recursive_only) continue; /* * Do not check policy zones that cannot replace a policy * already known to match. */ if (st->m.policy != DNS_RPZ_POLICY_MISS) { if (st->m.rpz->num < rpz->num) break; if (st->m.rpz->num == rpz->num && st->m.type < rpz_type) continue; } /* * Construct the policy's owner name. */ dns_fixedname_init(&prefixf); prefix = dns_fixedname_name(&prefixf); dns_name_split(qname, 1, prefix, NULL); if (rpz_type == DNS_RPZ_TYPE_NSDNAME) suffix = &rpz->nsdname; else suffix = &rpz->origin; dns_fixedname_init(&rpz_qnamef); rpz_qname = dns_fixedname_name(&rpz_qnamef); for (;;) { result = dns_name_concatenate(prefix, suffix, rpz_qname, NULL); if (result == ISC_R_SUCCESS) break; INSIST(result == DNS_R_NAMETOOLONG); /* * Trim the name until it is not too long. */ labels = dns_name_countlabels(prefix); if (labels < 2) { rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, suffix, "concatentate() ", result); return (ISC_R_SUCCESS); } if (labels+1 == dns_name_countlabels(qname)) { rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, rpz_type, suffix, "concatentate() ", result); } dns_name_split(prefix, labels - 1, NULL, prefix); } /* * See if the policy record exists and get its policy. */ result = rpz_find(client, qtype, rpz_qname, qname, rpz, rpz_type, &zone, &db, &version, &node, rdatasetp, &policy); switch (result) { case DNS_R_NXDOMAIN: break; case DNS_R_SERVFAIL: rpz_clean(&zone, &db, &node, rdatasetp); st->m.policy = DNS_RPZ_POLICY_ERROR; return (DNS_R_SERVFAIL); default: /* * We are dealing with names here. * With more than one applicable policy, prefer * the earliest configured policy, * QNAME over IP over NSDNAME over NSIP, * and the smallest name. * Because of the testing above, * we known st->m.rpz->num >= rpz->num and either * st->m.rpz->num > rpz->num or st->m.type >= rpz_type */ if (st->m.policy != DNS_RPZ_POLICY_MISS && rpz->num == st->m.rpz->num && (st->m.type < rpz_type || (st->m.type == rpz_type && 0 >= dns_name_compare(rpz_qname, st->qname)))) continue; #if 0 /* * This code would block a customer reported information * leak of rpz rules by rewriting requests in the * rpz-ip, rpz-nsip, rpz-nsdname,and rpz-passthru TLDs. * Without this code, a bad guy could request * 24.0.3.2.10.rpz-ip. to find the policy rule for * 10.2.3.0/14. It is an insignificant leak and this * code is not worth its cost, because the bad guy * could publish "evil.com A 10.2.3.4" and request * evil.com to get the same information. * Keep code with "#if 0" in case customer demand * is irresistible. * * We have the less frequent case of a triggered * policy. Check that we have not trigger on one * of the pretend RPZ TLDs. * This test would make it impossible to rewrite * names in TLDs that start with "rpz-" should * ICANN ever allow such TLDs. */ labels = dns_name_countlabels(qname); if (labels >= 2) { dns_label_t label; dns_name_getlabel(qname, labels-2, &label); if (label.length >= sizeof(DNS_RPZ_PREFIX)-1 && strncasecmp((const char *)label.base+1, DNS_RPZ_PREFIX, sizeof(DNS_RPZ_PREFIX)-1) == 0) continue; } #endif /* * Merely log DNS_RPZ_POLICY_DISABLED hits. */ if (rpz->policy == DNS_RPZ_POLICY_DISABLED) { rpz_log_rewrite(client, ISC_TRUE, policy, rpz_type, zone, rpz_qname); continue; } rpz_match_clear(st); st->m.rpz = rpz; st->m.type = rpz_type; st->m.prefix = 0; st->m.policy = policy; st->m.result = result; dns_name_copy(rpz_qname, st->qname, NULL); if (*rdatasetp != NULL && dns_rdataset_isassociated(*rdatasetp)) { dns_rdataset_t *trdataset; trdataset = st->m.rdataset; st->m.rdataset = *rdatasetp; *rdatasetp = trdataset; st->m.ttl = ISC_MIN(st->m.rdataset->ttl, rpz->max_policy_ttl); } else { st->m.ttl = ISC_MIN(DNS_RPZ_TTL_DEFAULT, rpz->max_policy_ttl); } st->m.node = node; node = NULL; st->m.db = db; db = NULL; st->m.version = version; st->m.zone = zone; zone = NULL; } } rpz_clean(&zone, &db, &node, rdatasetp); return (ISC_R_SUCCESS); } static void rpz_rewrite_ns_skip(ns_client_t *client, dns_name_t *nsname, isc_result_t result, int level, const char *str) { dns_rpz_st_t *st; st = client->query.rpz_st; if (str != NULL) rpz_log_fail(client, level, DNS_RPZ_TYPE_NSIP, nsname, str, result); if (st->r.ns_rdataset != NULL && dns_rdataset_isassociated(st->r.ns_rdataset)) dns_rdataset_disassociate(st->r.ns_rdataset); st->r.label--; } /* * Look for response policy zone QNAME, NSIP, and NSDNAME rewriting. */ static isc_result_t rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, isc_boolean_t resuming) { dns_rpz_st_t *st; dns_rdataset_t *rdataset; dns_fixedname_t nsnamef; dns_name_t *nsname; isc_boolean_t ck_ip; isc_result_t result; st = client->query.rpz_st; if (st == NULL) { st = isc_mem_get(client->mctx, sizeof(*st)); if (st == NULL) return (ISC_R_NOMEMORY); st->state = 0; memset(&st->m, 0, sizeof(st->m)); st->m.type = DNS_RPZ_TYPE_BAD; st->m.policy = DNS_RPZ_POLICY_MISS; st->m.ttl = ~0; memset(&st->r, 0, sizeof(st->r)); memset(&st->q, 0, sizeof(st->q)); dns_fixedname_init(&st->_qnamef); dns_fixedname_init(&st->_r_namef); dns_fixedname_init(&st->_fnamef); st->qname = dns_fixedname_name(&st->_qnamef); st->r_name = dns_fixedname_name(&st->_r_namef); st->fname = dns_fixedname_name(&st->_fnamef); client->query.rpz_st = st; } /* * There is nothing to rewrite if the main query failed. */ switch (qresult) { case ISC_R_SUCCESS: case DNS_R_GLUE: case DNS_R_ZONECUT: ck_ip = ISC_TRUE; break; case DNS_R_EMPTYNAME: case DNS_R_NXRRSET: case DNS_R_NXDOMAIN: case DNS_R_EMPTYWILD: case DNS_R_NCACHENXDOMAIN: case DNS_R_NCACHENXRRSET: case DNS_R_CNAME: case DNS_R_DNAME: ck_ip = ISC_FALSE; break; case DNS_R_DELEGATION: case ISC_R_NOTFOUND: return (ISC_R_SUCCESS); case ISC_R_FAILURE: case ISC_R_TIMEDOUT: case DNS_R_BROKENCHAIN: rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, DNS_RPZ_TYPE_QNAME, client->query.qname, "stop on qresult in rpz_rewrite() ", qresult); return (ISC_R_SUCCESS); default: rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, DNS_RPZ_TYPE_QNAME, client->query.qname, "stop on unrecognized qresult in rpz_rewrite() ", qresult); return (ISC_R_SUCCESS); } rdataset = NULL; if ((st->state & DNS_RPZ_DONE_QNAME) == 0) { /* * Check rules for the query name if this is the first time * for the current qname, i.e. we've not been recursing. * There is a first time for each name in a CNAME chain. */ result = rpz_rewrite_name(client, qtype, client->query.qname, DNS_RPZ_TYPE_QNAME, &rdataset); if (result != ISC_R_SUCCESS) goto cleanup; st->r.label = dns_name_countlabels(client->query.qname); st->state &= ~(DNS_RPZ_DONE_QNAME_IP | DNS_RPZ_DONE_IPv4); st->state |= DNS_RPZ_DONE_QNAME; } /* * Check known IP addresses for the query name. * Any recursion required for the query has already happened. * Do not check addresses that will not be in the ANSWER section. */ if ((st->state & DNS_RPZ_DONE_QNAME_IP) == 0 && (st->state & DNS_RPZ_HAVE_IP) != 0 && ck_ip) { result = rpz_rewrite_rrsets(client, DNS_RPZ_TYPE_IP, client->query.qname, qtype, &rdataset, resuming); if (result != ISC_R_SUCCESS) goto cleanup; st->state &= ~DNS_RPZ_DONE_IPv4; st->state |= DNS_RPZ_DONE_QNAME_IP; } /* * Stop looking for rules if there are none of the other kinds. */ if ((st->state & (DNS_RPZ_HAVE_NSIPv4 | DNS_RPZ_HAVE_NSIPv6 | DNS_RPZ_HAVE_NSDNAME)) == 0) { result = ISC_R_SUCCESS; goto cleanup; } dns_fixedname_init(&nsnamef); dns_name_clone(client->query.qname, dns_fixedname_name(&nsnamef)); while (st->r.label > client->view->rpz_min_ns_labels) { /* * Get NS rrset for each domain in the current qname. */ if (st->r.label == dns_name_countlabels(client->query.qname)) { nsname = client->query.qname; } else { nsname = dns_fixedname_name(&nsnamef); dns_name_split(client->query.qname, st->r.label, NULL, nsname); } if (st->r.ns_rdataset == NULL || !dns_rdataset_isassociated(st->r.ns_rdataset)) { dns_db_t *db = NULL; result = rpz_rrset_find(client, DNS_RPZ_TYPE_NSDNAME, nsname, dns_rdatatype_ns, &db, NULL, &st->r.ns_rdataset, resuming); if (db != NULL) dns_db_detach(&db); if (st->m.policy == DNS_RPZ_POLICY_ERROR) goto cleanup; switch (result) { case ISC_R_SUCCESS: result = dns_rdataset_first(st->r.ns_rdataset); if (result != ISC_R_SUCCESS) goto cleanup; st->state &= ~(DNS_RPZ_DONE_NSDNAME | DNS_RPZ_DONE_IPv4); break; case DNS_R_DELEGATION: goto cleanup; case DNS_R_EMPTYNAME: case DNS_R_NXRRSET: case DNS_R_EMPTYWILD: case DNS_R_NXDOMAIN: case DNS_R_NCACHENXDOMAIN: case DNS_R_NCACHENXRRSET: case ISC_R_NOTFOUND: case DNS_R_CNAME: case DNS_R_DNAME: rpz_rewrite_ns_skip(client, nsname, result, 0, NULL); continue; case ISC_R_TIMEDOUT: case DNS_R_BROKENCHAIN: case ISC_R_FAILURE: rpz_rewrite_ns_skip(client, nsname, result, DNS_RPZ_DEBUG_LEVEL3, "NS db_find() "); continue; default: rpz_rewrite_ns_skip(client, nsname, result, DNS_RPZ_INFO_LEVEL, "unrecognized NS db_find() "); continue; } } /* * Check all NS names. */ do { dns_rdata_ns_t ns; dns_rdata_t nsrdata = DNS_RDATA_INIT; dns_rdataset_current(st->r.ns_rdataset, &nsrdata); result = dns_rdata_tostruct(&nsrdata, &ns, NULL); dns_rdata_reset(&nsrdata); if (result != ISC_R_SUCCESS) { rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, DNS_RPZ_TYPE_NSIP, nsname, "rdata_tostruct() ", result); st->m.policy = DNS_RPZ_POLICY_ERROR; goto cleanup; } /* * Do nothing about "NS ." */ if (dns_name_equal(&ns.name, dns_rootname)) { dns_rdata_freestruct(&ns); result = dns_rdataset_next(st->r.ns_rdataset); continue; } /* * Check this NS name if we did not handle it * during a previous recursion. */ if ((st->state & DNS_RPZ_DONE_NSDNAME) == 0 && (st->state & DNS_RPZ_HAVE_NSDNAME) != 0) { result = rpz_rewrite_name(client, qtype, &ns.name, DNS_RPZ_TYPE_NSDNAME, &rdataset); if (result != ISC_R_SUCCESS) { dns_rdata_freestruct(&ns); goto cleanup; } st->state |= DNS_RPZ_DONE_NSDNAME; } /* * Check all IP addresses for this NS name. */ result = rpz_rewrite_rrsets(client, DNS_RPZ_TYPE_NSIP, &ns.name, dns_rdatatype_any, &rdataset, resuming); dns_rdata_freestruct(&ns); if (result != ISC_R_SUCCESS) goto cleanup; st->state &= ~(DNS_RPZ_DONE_NSDNAME | DNS_RPZ_DONE_IPv4); result = dns_rdataset_next(st->r.ns_rdataset); } while (result == ISC_R_SUCCESS); dns_rdataset_disassociate(st->r.ns_rdataset); st->r.label--; } /* * Use the best, if any, hit. */ result = ISC_R_SUCCESS; cleanup: if (st->m.policy != DNS_RPZ_POLICY_MISS && st->m.policy != DNS_RPZ_POLICY_ERROR && st->m.rpz->policy != DNS_RPZ_POLICY_GIVEN) st->m.policy = st->m.rpz->policy; if (st->m.policy == DNS_RPZ_POLICY_MISS || st->m.policy == DNS_RPZ_POLICY_PASSTHRU || st->m.policy == DNS_RPZ_POLICY_ERROR) { if (st->m.policy == DNS_RPZ_POLICY_PASSTHRU && result != DNS_R_DELEGATION) rpz_log_rewrite(client, ISC_FALSE, st->m.policy, st->m.type, st->m.zone, st->qname); rpz_match_clear(st); } if (st->m.policy == DNS_RPZ_POLICY_ERROR) { st->m.type = DNS_RPZ_TYPE_BAD; result = DNS_R_SERVFAIL; } query_putrdataset(client, &rdataset); if ((st->state & DNS_RPZ_RECURSING) == 0) rpz_clean(NULL, &st->r.db, NULL, &st->r.ns_rdataset); return (result); } /* * See if response policy zone rewriting is allowed by a lack of interest * by the client in DNSSEC or a lack of signatures. */ static isc_boolean_t rpz_ck_dnssec(ns_client_t *client, isc_result_t result, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { dns_fixedname_t fixed; dns_name_t *found; dns_rdataset_t trdataset; dns_rdatatype_t type; if (client->view->rpz_break_dnssec) return (ISC_TRUE); /* * sigrdataset == NULL if and only !WANTDNSSEC(client) */ if (sigrdataset == NULL) return (ISC_TRUE); if (dns_rdataset_isassociated(sigrdataset)) return (ISC_FALSE); /* * We are happy to rewrite nothing. */ if (rdataset == NULL || !dns_rdataset_isassociated(rdataset)) return (ISC_TRUE); /* * Do not rewrite if there is any sign of signatures. */ if (rdataset->type == dns_rdatatype_nsec || rdataset->type == dns_rdatatype_nsec3 || rdataset->type == dns_rdatatype_rrsig) return (ISC_FALSE); /* * Look for a signature in a negative cache rdataset. */ if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) == 0) return (ISC_TRUE); dns_fixedname_init(&fixed); found = dns_fixedname_name(&fixed); dns_rdataset_init(&trdataset); for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { dns_ncache_current(rdataset, found, &trdataset); type = trdataset.type; dns_rdataset_disassociate(&trdataset); if (type == dns_rdatatype_nsec || type == dns_rdatatype_nsec3 || type == dns_rdatatype_rrsig) return (ISC_FALSE); } return (ISC_TRUE); } /* * Add a CNAME to the query response, including translating foo.evil.com and * *.evil.com CNAME *.example.com * to * foo.evil.com CNAME foo.evil.com.example.com */ static isc_result_t rpz_add_cname(ns_client_t *client, dns_rpz_st_t *st, dns_name_t *cname, dns_name_t *fname, isc_buffer_t *dbuf) { dns_fixedname_t prefix, suffix; unsigned int labels; isc_result_t result; labels = dns_name_countlabels(cname); if (labels > 2 && dns_name_iswildcard(cname)) { dns_fixedname_init(&prefix); dns_name_split(client->query.qname, 1, dns_fixedname_name(&prefix), NULL); dns_fixedname_init(&suffix); dns_name_split(cname, labels-1, NULL, dns_fixedname_name(&suffix)); result = dns_name_concatenate(dns_fixedname_name(&prefix), dns_fixedname_name(&suffix), fname, NULL); if (result == DNS_R_NAMETOOLONG) client->message->rcode = dns_rcode_yxdomain; } else { result = dns_name_copy(cname, fname, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); } if (result != ISC_R_SUCCESS) return (result); query_keepname(client, fname, dbuf); result = query_add_cname(client, client->query.qname, fname, dns_trust_authanswer, st->m.ttl); if (result != ISC_R_SUCCESS) return (result); rpz_log_rewrite(client, ISC_FALSE, st->m.policy, st->m.type, st->m.zone, st->qname); ns_client_qnamereplace(client, fname); /* * Turn off DNSSEC because the results of a * response policy zone cannot verify. */ client->attributes &= ~(NS_CLIENTATTR_WANTDNSSEC | DNS_MESSAGEFLAG_AD); return (ISC_R_SUCCESS); } #define MAX_RESTARTS 16 #define QUERY_ERROR(r) \ do { \ eresult = r; \ want_restart = ISC_FALSE; \ line = __LINE__; \ } while (0) #define RECURSE_ERROR(r) \ do { \ if ((r) == DNS_R_DUPLICATE || (r) == DNS_R_DROP) \ QUERY_ERROR(r); \ else \ QUERY_ERROR(DNS_R_SERVFAIL); \ } while (0) /* * Extract a network address from the RDATA of an A or AAAA * record. * * Returns: * ISC_R_SUCCESS * ISC_R_NOTIMPLEMENTED The rdata is not a known address type. */ static isc_result_t rdata_tonetaddr(const dns_rdata_t *rdata, isc_netaddr_t *netaddr) { struct in_addr ina; struct in6_addr in6a; switch (rdata->type) { case dns_rdatatype_a: INSIST(rdata->length == 4); memmove(&ina.s_addr, rdata->data, 4); isc_netaddr_fromin(netaddr, &ina); return (ISC_R_SUCCESS); case dns_rdatatype_aaaa: INSIST(rdata->length == 16); memmove(in6a.s6_addr, rdata->data, 16); isc_netaddr_fromin6(netaddr, &in6a); return (ISC_R_SUCCESS); default: return (ISC_R_NOTIMPLEMENTED); } } /* * Find the sort order of 'rdata' in the topology-like * ACL forming the second element in a 2-element top-level * sortlist statement. */ static int query_sortlist_order_2element(const dns_rdata_t *rdata, const void *arg) { isc_netaddr_t netaddr; if (rdata_tonetaddr(rdata, &netaddr) != ISC_R_SUCCESS) return (INT_MAX); return (ns_sortlist_addrorder2(&netaddr, arg)); } /* * Find the sort order of 'rdata' in the matching element * of a 1-element top-level sortlist statement. */ static int query_sortlist_order_1element(const dns_rdata_t *rdata, const void *arg) { isc_netaddr_t netaddr; if (rdata_tonetaddr(rdata, &netaddr) != ISC_R_SUCCESS) return (INT_MAX); return (ns_sortlist_addrorder1(&netaddr, arg)); } /* * Find the sortlist statement that applies to 'client' and set up * the sortlist info in in client->message appropriately. */ static void setup_query_sortlist(ns_client_t *client) { isc_netaddr_t netaddr; dns_rdatasetorderfunc_t order = NULL; const void *order_arg = NULL; isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr); switch (ns_sortlist_setup(client->view->sortlist, &netaddr, &order_arg)) { case NS_SORTLISTTYPE_1ELEMENT: order = query_sortlist_order_1element; break; case NS_SORTLISTTYPE_2ELEMENT: order = query_sortlist_order_2element; break; case NS_SORTLISTTYPE_NONE: order = NULL; break; default: INSIST(0); break; } dns_message_setsortorder(client->message, order, order_arg); } static void query_addnoqnameproof(ns_client_t *client, dns_rdataset_t *rdataset) { isc_buffer_t *dbuf, b; dns_name_t *fname; dns_rdataset_t *neg, *negsig; isc_result_t result = ISC_R_NOMEMORY; CTRACE("query_addnoqnameproof"); fname = NULL; neg = NULL; negsig = NULL; dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); neg = query_newrdataset(client); negsig = query_newrdataset(client); if (fname == NULL || neg == NULL || negsig == NULL) goto cleanup; result = dns_rdataset_getnoqname(rdataset, fname, neg, negsig); RUNTIME_CHECK(result == ISC_R_SUCCESS); query_addrrset(client, &fname, &neg, &negsig, dbuf, DNS_SECTION_AUTHORITY); if ((rdataset->attributes & DNS_RDATASETATTR_CLOSEST) == 0) goto cleanup; if (fname == NULL) { dbuf = query_getnamebuf(client); if (dbuf == NULL) goto cleanup; fname = query_newname(client, dbuf, &b); } if (neg == NULL) neg = query_newrdataset(client); else if (dns_rdataset_isassociated(neg)) dns_rdataset_disassociate(neg); if (negsig == NULL) negsig = query_newrdataset(client); else if (dns_rdataset_isassociated(negsig)) dns_rdataset_disassociate(negsig); if (fname == NULL || neg == NULL || negsig == NULL) goto cleanup; result = dns_rdataset_getclosest(rdataset, fname, neg, negsig); RUNTIME_CHECK(result == ISC_R_SUCCESS); query_addrrset(client, &fname, &neg, &negsig, dbuf, DNS_SECTION_AUTHORITY); cleanup: if (neg != NULL) query_putrdataset(client, &neg); if (negsig != NULL) query_putrdataset(client, &negsig); if (fname != NULL) query_releasename(client, &fname); } static inline void answer_in_glue(ns_client_t *client, dns_rdatatype_t qtype) { dns_name_t *name; dns_message_t *msg; dns_section_t section = DNS_SECTION_ADDITIONAL; dns_rdataset_t *rdataset = NULL; msg = client->message; for (name = ISC_LIST_HEAD(msg->sections[section]); name != NULL; name = ISC_LIST_NEXT(name, link)) if (dns_name_equal(name, client->query.qname)) { for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) if (rdataset->type == qtype) break; break; } if (rdataset != NULL) { ISC_LIST_UNLINK(msg->sections[section], name, link); ISC_LIST_PREPEND(msg->sections[section], name, link); ISC_LIST_UNLINK(name->list, rdataset, link); ISC_LIST_PREPEND(name->list, rdataset, link); rdataset->attributes |= DNS_RDATASETATTR_REQUIREDGLUE; } } #define NS_NAME_INIT(A,B) \ { \ DNS_NAME_MAGIC, \ A, sizeof(A), sizeof(B), \ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, \ B, NULL, { (void *)-1, (void *)-1}, \ {NULL, NULL} \ } 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 rfc1918names[] = { NS_NAME_INIT(inaddr10, inaddr10_offsets), NS_NAME_INIT(inaddr16172, inaddr172_offsets), NS_NAME_INIT(inaddr17172, inaddr172_offsets), NS_NAME_INIT(inaddr18172, inaddr172_offsets), NS_NAME_INIT(inaddr19172, inaddr172_offsets), NS_NAME_INIT(inaddr20172, inaddr172_offsets), NS_NAME_INIT(inaddr21172, inaddr172_offsets), NS_NAME_INIT(inaddr22172, inaddr172_offsets), NS_NAME_INIT(inaddr23172, inaddr172_offsets), NS_NAME_INIT(inaddr24172, inaddr172_offsets), NS_NAME_INIT(inaddr25172, inaddr172_offsets), NS_NAME_INIT(inaddr26172, inaddr172_offsets), NS_NAME_INIT(inaddr27172, inaddr172_offsets), NS_NAME_INIT(inaddr28172, inaddr172_offsets), NS_NAME_INIT(inaddr29172, inaddr172_offsets), NS_NAME_INIT(inaddr30172, inaddr172_offsets), NS_NAME_INIT(inaddr31172, inaddr172_offsets), NS_NAME_INIT(inaddr168192, inaddr192_offsets) }; static unsigned char prisoner_data[] = "\010prisoner\004iana\003org"; static unsigned char hostmaster_data[] = "\012hostmaster\014root-servers\003org"; static unsigned char prisoner_offsets[] = { 0, 9, 14, 18 }; static unsigned char hostmaster_offsets[] = { 0, 11, 24, 28 }; static dns_name_t prisoner = NS_NAME_INIT(prisoner_data, prisoner_offsets); static dns_name_t hostmaster = NS_NAME_INIT(hostmaster_data, hostmaster_offsets); static void warn_rfc1918(ns_client_t *client, dns_name_t *fname, dns_rdataset_t *rdataset) { unsigned int i; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_soa_t soa; dns_rdataset_t found; isc_result_t result; for (i = 0; i < (sizeof(rfc1918names)/sizeof(*rfc1918names)); i++) { if (dns_name_issubdomain(fname, &rfc1918names[i])) { dns_rdataset_init(&found); result = dns_ncache_getrdataset(rdataset, &rfc1918names[i], dns_rdatatype_soa, &found); if (result != ISC_R_SUCCESS) return; result = dns_rdataset_first(&found); RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_rdataset_current(&found, &rdata); result = dns_rdata_tostruct(&rdata, &soa, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); if (dns_name_equal(&soa.origin, &prisoner) && dns_name_equal(&soa.contact, &hostmaster)) { char buf[DNS_NAME_FORMATSIZE]; dns_name_format(fname, buf, sizeof(buf)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "RFC 1918 response from " "Internet for %s", buf); } dns_rdataset_disassociate(&found); return; } } } static void query_findclosestnsec3(dns_name_t *qname, dns_db_t *db, dns_dbversion_t *version, ns_client_t *client, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, dns_name_t *fname, isc_boolean_t exact, dns_name_t *found) { unsigned char salt[256]; size_t salt_length; isc_uint16_t iterations; isc_result_t result; unsigned int dboptions; dns_fixedname_t fixed; dns_hash_t hash; dns_name_t name; unsigned int skip = 0, labels; dns_rdata_nsec3_t nsec3; dns_rdata_t rdata = DNS_RDATA_INIT; isc_boolean_t optout; dns_clientinfomethods_t cm; dns_clientinfo_t ci; salt_length = sizeof(salt); result = dns_db_getnsec3parameters(db, version, &hash, NULL, &iterations, salt, &salt_length); if (result != ISC_R_SUCCESS) return; dns_name_init(&name, NULL); dns_name_clone(qname, &name); labels = dns_name_countlabels(&name); dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); /* * Map unknown algorithm to known value. */ if (hash == DNS_NSEC3_UNKNOWNALG) hash = 1; again: dns_fixedname_init(&fixed); result = dns_nsec3_hashname(&fixed, NULL, NULL, &name, dns_db_origin(db), hash, iterations, salt, salt_length); if (result != ISC_R_SUCCESS) return; dboptions = client->query.dboptions | DNS_DBFIND_FORCENSEC3; result = dns_db_findext(db, dns_fixedname_name(&fixed), version, dns_rdatatype_nsec3, dboptions, client->now, NULL, fname, &cm, &ci, rdataset, sigrdataset); if (result == DNS_R_NXDOMAIN) { if (!dns_rdataset_isassociated(rdataset)) { return; } result = dns_rdataset_first(rdataset); INSIST(result == ISC_R_SUCCESS); dns_rdataset_current(rdataset, &rdata); dns_rdata_tostruct(&rdata, &nsec3, NULL); dns_rdata_reset(&rdata); optout = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0); if (found != NULL && optout && dns_name_issubdomain(&name, dns_db_origin(db))) { dns_rdataset_disassociate(rdataset); if (dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); skip++; dns_name_getlabelsequence(qname, skip, labels - skip, &name); ns_client_log(client, DNS_LOGCATEGORY_DNSSEC, NS_LOGMODULE_QUERY, ISC_LOG_DEBUG(3), "looking for closest provable encloser"); goto again; } if (exact) ns_client_log(client, DNS_LOGCATEGORY_DNSSEC, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "expected a exact match NSEC3, got " "a covering record"); } else if (result != ISC_R_SUCCESS) { return; } else if (!exact) ns_client_log(client, DNS_LOGCATEGORY_DNSSEC, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "expected covering NSEC3, got an exact match"); if (found == qname) { if (skip != 0U) dns_name_getlabelsequence(qname, skip, labels - skip, found); } else if (found != NULL) dns_name_copy(&name, found, NULL); return; } #ifdef ALLOW_FILTER_AAAA_ON_V4 static isc_boolean_t is_v4_client(ns_client_t *client) { if (isc_sockaddr_pf(&client->peeraddr) == AF_INET) return (ISC_TRUE); if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr)) return (ISC_TRUE); return (ISC_FALSE); } #endif static isc_uint32_t dns64_ttl(dns_db_t *db, dns_dbversion_t *version) { dns_dbnode_t *node = NULL; dns_rdata_soa_t soa; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_t rdataset; isc_result_t result; isc_uint32_t ttl = ISC_UINT32_MAX; dns_rdataset_init(&rdataset); result = dns_db_getoriginnode(db, &node); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa, 0, 0, &rdataset, NULL); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_rdataset_first(&rdataset); if (result != ISC_R_SUCCESS) goto cleanup; dns_rdataset_current(&rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &soa, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); ttl = ISC_MIN(rdataset.ttl, soa.minimum); cleanup: if (dns_rdataset_isassociated(&rdataset)) dns_rdataset_disassociate(&rdataset); if (node != NULL) dns_db_detachnode(db, &node); return (ttl); } static isc_boolean_t dns64_aaaaok(ns_client_t *client, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { isc_netaddr_t netaddr; dns_dns64_t *dns64 = ISC_LIST_HEAD(client->view->dns64); unsigned int flags = 0; unsigned int i, count; isc_boolean_t *aaaaok; INSIST(client->query.dns64_aaaaok == NULL); INSIST(client->query.dns64_aaaaoklen == 0); INSIST(client->query.dns64_aaaa == NULL); INSIST(client->query.dns64_sigaaaa == NULL); if (dns64 == NULL) return (ISC_TRUE); if (RECURSIONOK(client)) flags |= DNS_DNS64_RECURSIVE; if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) flags |= DNS_DNS64_DNSSEC; count = dns_rdataset_count(rdataset); aaaaok = isc_mem_get(client->mctx, sizeof(isc_boolean_t) * count); isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr); if (dns_dns64_aaaaok(dns64, &netaddr, client->signer, &ns_g_server->aclenv, flags, rdataset, aaaaok, count)) { for (i = 0; i < count; i++) { if (aaaaok != NULL && !aaaaok[i]) { client->query.dns64_aaaaok = aaaaok; client->query.dns64_aaaaoklen = count; break; } } if (i == count && aaaaok != NULL) isc_mem_put(client->mctx, aaaaok, sizeof(isc_boolean_t) * count); return (ISC_TRUE); } if (aaaaok != NULL) isc_mem_put(client->mctx, aaaaok, sizeof(isc_boolean_t) * count); return (ISC_FALSE); } /* * Look for the name and type in the redirection zone. If found update * the arguments as appropriate. Return ISC_TRUE if a update was * performed. * * Only perform the update if the client is in the allow query acl and * returning the update would not cause a DNSSEC validation failure. */ static isc_boolean_t redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset, dns_dbnode_t **nodep, dns_db_t **dbp, dns_dbversion_t **versionp, dns_rdatatype_t qtype) { dns_db_t *db = NULL; dns_dbnode_t *node = NULL; dns_fixedname_t fixed; dns_name_t *found; dns_rdataset_t trdataset; isc_result_t result; dns_rdatatype_t type; dns_clientinfomethods_t cm; dns_clientinfo_t ci; ns_dbversion_t *dbversion; CTRACE("redirect"); if (client->view->redirect == NULL) return (ISC_FALSE); dns_fixedname_init(&fixed); found = dns_fixedname_name(&fixed); dns_rdataset_init(&trdataset); dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); if (WANTDNSSEC(client) && dns_db_iszone(*dbp) && dns_db_issecure(*dbp)) return (ISC_FALSE); if (WANTDNSSEC(client) && dns_rdataset_isassociated(rdataset)) { if (rdataset->trust == dns_trust_secure) return (ISC_FALSE); if (rdataset->trust == dns_trust_ultimate && (rdataset->type == dns_rdatatype_nsec || rdataset->type == dns_rdatatype_nsec3)) return (ISC_FALSE); if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) { for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { dns_ncache_current(rdataset, found, &trdataset); type = trdataset.type; dns_rdataset_disassociate(&trdataset); if (type == dns_rdatatype_nsec || type == dns_rdatatype_nsec3 || type == dns_rdatatype_rrsig) return (ISC_FALSE); } } } result = ns_client_checkaclsilent(client, NULL, dns_zone_getqueryacl(client->view->redirect), ISC_TRUE); if (result != ISC_R_SUCCESS) return (ISC_FALSE); result = dns_zone_getdb(client->view->redirect, &db); if (result != ISC_R_SUCCESS) return (ISC_FALSE); dbversion = query_findversion(client, db); if (dbversion == NULL) { dns_db_detach(&db); return (ISC_FALSE); } /* * Lookup the requested data in the redirect zone. */ result = dns_db_findext(db, client->query.qname, dbversion->version, qtype, 0, client->now, &node, found, &cm, &ci, &trdataset, NULL); if (result != ISC_R_SUCCESS) { if (dns_rdataset_isassociated(&trdataset)) dns_rdataset_disassociate(&trdataset); if (node != NULL) dns_db_detachnode(db, &node); dns_db_detach(&db); return (ISC_FALSE); } CTRACE("redirect: found data: done"); dns_name_copy(found, name, NULL); if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); if (dns_rdataset_isassociated(&trdataset)) { dns_rdataset_clone(&trdataset, rdataset); dns_rdataset_disassociate(&trdataset); } if (*nodep != NULL) dns_db_detachnode(*dbp, nodep); dns_db_detach(dbp); dns_db_attachnode(db, node, nodep); dns_db_attach(db, dbp); dns_db_detachnode(db, &node); dns_db_detach(&db); *versionp = dbversion->version; client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY | NS_QUERYATTR_NOADDITIONAL); return (ISC_TRUE); } /* * Do the bulk of query processing for the current query of 'client'. * If 'event' is non-NULL, we are returning from recursion and 'qtype' * is ignored. Otherwise, 'qtype' is the query type. */ static isc_result_t query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) { dns_db_t *db, *zdb; dns_dbnode_t *node; dns_rdatatype_t type; dns_name_t *fname, *zfname, *tname, *prefix; dns_rdataset_t *rdataset, *trdataset; dns_rdataset_t *sigrdataset, *zrdataset, *zsigrdataset; dns_rdataset_t **sigrdatasetp; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdatasetiter_t *rdsiter; isc_boolean_t want_restart, authoritative, is_zone, need_wildcardproof; isc_boolean_t is_staticstub_zone; unsigned int n, nlabels; dns_namereln_t namereln; int order; isc_buffer_t *dbuf; isc_buffer_t b; isc_result_t result, eresult; dns_fixedname_t fixed; dns_fixedname_t wildcardname; dns_dbversion_t *version, *zversion; dns_zone_t *zone; dns_rdata_cname_t cname; dns_rdata_dname_t dname; unsigned int options; isc_boolean_t empty_wild; dns_rdataset_t *noqname; dns_rpz_st_t *rpz_st; isc_boolean_t resuming; int line = -1; isc_boolean_t dns64_exclude, dns64; dns_clientinfomethods_t cm; dns_clientinfo_t ci; CTRACE("query_find"); /* * One-time initialization. * * It's especially important to initialize anything that the cleanup * code might cleanup. */ eresult = ISC_R_SUCCESS; fname = NULL; zfname = NULL; rdataset = NULL; zrdataset = NULL; sigrdataset = NULL; zsigrdataset = NULL; zversion = NULL; node = NULL; db = NULL; zdb = NULL; version = NULL; zone = NULL; need_wildcardproof = ISC_FALSE; empty_wild = ISC_FALSE; dns64_exclude = dns64 = ISC_FALSE; options = 0; resuming = ISC_FALSE; is_zone = ISC_FALSE; is_staticstub_zone = ISC_FALSE; dns_clientinfomethods_init(&cm, ns_client_sourceip); dns_clientinfo_init(&ci, client); if (event != NULL) { /* * We're returning from recursion. Restore the query context * and resume. */ want_restart = ISC_FALSE; rpz_st = client->query.rpz_st; if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) != 0) { is_zone = rpz_st->q.is_zone; authoritative = rpz_st->q.authoritative; zone = rpz_st->q.zone; rpz_st->q.zone = NULL; node = rpz_st->q.node; rpz_st->q.node = NULL; db = rpz_st->q.db; rpz_st->q.db = NULL; rdataset = rpz_st->q.rdataset; rpz_st->q.rdataset = NULL; sigrdataset = rpz_st->q.sigrdataset; rpz_st->q.sigrdataset = NULL; qtype = rpz_st->q.qtype; rpz_st->r.db = event->db; if (event->node != NULL) dns_db_detachnode(event->db, &event->node); rpz_st->r.r_type = event->qtype; rpz_st->r.r_rdataset = event->rdataset; query_putrdataset(client, &event->sigrdataset); } else { authoritative = ISC_FALSE; qtype = event->qtype; db = event->db; node = event->node; rdataset = event->rdataset; sigrdataset = event->sigrdataset; } if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig) type = dns_rdatatype_any; else type = qtype; if (DNS64(client)) { client->query.attributes &= ~NS_QUERYATTR_DNS64; dns64 = ISC_TRUE; } if (DNS64EXCLUDE(client)) { client->query.attributes &= ~NS_QUERYATTR_DNS64EXCLUDE; dns64_exclude = ISC_TRUE; } /* * We'll need some resources... */ dbuf = query_getnamebuf(client); if (dbuf == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } fname = query_newname(client, dbuf, &b); if (fname == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) != 0) { tname = rpz_st->fname; } else { tname = dns_fixedname_name(&event->foundname); } result = dns_name_copy(tname, fname, NULL); if (result != ISC_R_SUCCESS) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) != 0) { rpz_st->r.r_result = event->result; result = rpz_st->q.result; isc_event_free(ISC_EVENT_PTR(&event)); } else { result = event->result; } resuming = ISC_TRUE; goto resume; } /* * Not returning from recursion. */ /* * If it's a SIG query, we'll iterate the node. */ if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig) type = dns_rdatatype_any; else type = qtype; restart: CTRACE("query_find: restart"); want_restart = ISC_FALSE; authoritative = ISC_FALSE; version = NULL; need_wildcardproof = ISC_FALSE; if (client->view->checknames && !dns_rdata_checkowner(client->query.qname, client->message->rdclass, qtype, ISC_FALSE)) { char namebuf[DNS_NAME_FORMATSIZE]; char typename[DNS_RDATATYPE_FORMATSIZE]; char classname[DNS_RDATACLASS_FORMATSIZE]; dns_name_format(client->query.qname, namebuf, sizeof(namebuf)); dns_rdatatype_format(qtype, typename, sizeof(typename)); dns_rdataclass_format(client->message->rdclass, classname, sizeof(classname)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_QUERY, ISC_LOG_ERROR, "check-names failure %s/%s/%s", namebuf, typename, classname); QUERY_ERROR(DNS_R_REFUSED); goto cleanup; } /* * First we must find the right database. */ options &= DNS_GETDB_NOLOG; /* Preserve DNS_GETDB_NOLOG. */ if (dns_rdatatype_atparent(qtype) && !dns_name_equal(client->query.qname, dns_rootname)) options |= DNS_GETDB_NOEXACT; result = query_getdb(client, client->query.qname, qtype, options, &zone, &db, &version, &is_zone); if ((result != ISC_R_SUCCESS || !is_zone) && !RECURSIONOK(client) && (options & DNS_GETDB_NOEXACT) != 0 && qtype == dns_rdatatype_ds) { /* * Look to see if we are authoritative for the * child zone if the query type is DS. */ dns_db_t *tdb = NULL; dns_zone_t *tzone = NULL; dns_dbversion_t *tversion = NULL; isc_result_t tresult; tresult = query_getzonedb(client, client->query.qname, qtype, DNS_GETDB_PARTIAL, &tzone, &tdb, &tversion); if (tresult == ISC_R_SUCCESS) { options &= ~DNS_GETDB_NOEXACT; query_putrdataset(client, &rdataset); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); version = tversion; db = tdb; zone = tzone; is_zone = ISC_TRUE; result = ISC_R_SUCCESS; } else { if (tdb != NULL) dns_db_detach(&tdb); if (tzone != NULL) dns_zone_detach(&tzone); } } if (result != ISC_R_SUCCESS) { if (result == DNS_R_REFUSED) { if (WANTRECURSION(client)) { inc_stats(client, dns_nsstatscounter_recurserej); } else inc_stats(client, dns_nsstatscounter_authrej); if (!PARTIALANSWER(client)) QUERY_ERROR(DNS_R_REFUSED); } else QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } is_staticstub_zone = ISC_FALSE; if (is_zone) { authoritative = ISC_TRUE; if (zone != NULL && dns_zone_gettype(zone) == dns_zone_staticstub) is_staticstub_zone = ISC_TRUE; } if (event == NULL && client->query.restarts == 0) { if (is_zone) { if (zone != NULL) { /* * if is_zone = true, zone = NULL then this is * a DLZ zone. Don't attempt to attach zone. */ dns_zone_attach(zone, &client->query.authzone); } dns_db_attach(db, &client->query.authdb); } client->query.authdbset = ISC_TRUE; } db_find: CTRACE("query_find: db_find"); /* * We'll need some resources... */ dbuf = query_getnamebuf(client); if (dbuf == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } fname = query_newname(client, dbuf, &b); rdataset = query_newrdataset(client); if (fname == NULL || rdataset == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } if (WANTDNSSEC(client) && (!is_zone || dns_db_issecure(db))) { sigrdataset = query_newrdataset(client); if (sigrdataset == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } } /* * Now look for an answer in the database. */ result = dns_db_findext(db, client->query.qname, version, type, client->query.dboptions, client->now, &node, fname, &cm, &ci, rdataset, sigrdataset); resume: CTRACE("query_find: resume"); #ifdef USE_RRL /* * Rate limit these responses to this client. * Do not delay counting and handling obvious referrals, * since those won't come here again. * Delay handling delegations for which we are certain to recurse and * return here (DNS_R_DELEGATION, not a child of one of our * own zones, and recursion enabled) * Don't mess with responses rewritten by RPZ * Count each response at most once. */ if (client->view->rrl != NULL && ((fname != NULL && dns_name_isabsolute(fname)) || (result == ISC_R_NOTFOUND && !RECURSIONOK(client))) && !(result == DNS_R_DELEGATION && !is_zone && RECURSIONOK(client)) && (client->query.rpz_st == NULL || (client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0)&& (client->query.attributes & NS_QUERYATTR_RRL_CHECKED) == 0) { dns_rdataset_t nc_rdataset; isc_boolean_t wouldlog; char log_buf[DNS_RRL_LOG_BUF_LEN]; isc_result_t nc_result, resp_result; dns_rrl_result_t rrl_result; client->query.attributes |= NS_QUERYATTR_RRL_CHECKED; wouldlog = isc_log_wouldlog(ns_g_lctx, DNS_RRL_LOG_DROP); tname = fname; if (result == DNS_R_NXDOMAIN) { /* * Use the database origin name to rate limit NXDOMAIN */ if (db != NULL) tname = dns_db_origin(db); resp_result = result; } else if (result == DNS_R_NCACHENXDOMAIN && rdataset != NULL && dns_rdataset_isassociated(rdataset) && (rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) { /* * Try to use owner name in the negative cache SOA. */ dns_fixedname_init(&fixed); dns_rdataset_init(&nc_rdataset); for (nc_result = dns_rdataset_first(rdataset); nc_result == ISC_R_SUCCESS; nc_result = dns_rdataset_next(rdataset)) { dns_ncache_current(rdataset, dns_fixedname_name(&fixed), &nc_rdataset); if (nc_rdataset.type == dns_rdatatype_soa) { dns_rdataset_disassociate(&nc_rdataset); tname = dns_fixedname_name(&fixed); break; } dns_rdataset_disassociate(&nc_rdataset); } resp_result = DNS_R_NXDOMAIN; } else if (result == DNS_R_NXRRSET || result == DNS_R_EMPTYNAME) { resp_result = DNS_R_NXRRSET; } else if (result == DNS_R_DELEGATION) { resp_result = result; } else if (result == ISC_R_NOTFOUND) { /* * Handle referral to ".", including when recursion * is off or not requested and the hints have not * been loaded or we have "additional-from-cache no". */ tname = dns_rootname; resp_result = DNS_R_DELEGATION; } else { resp_result = ISC_R_SUCCESS; } rrl_result = dns_rrl(client->view, &client->peeraddr, ISC_TF((client->attributes & NS_CLIENTATTR_TCP) != 0), client->message->rdclass, qtype, tname, resp_result, client->now, wouldlog, log_buf, sizeof(log_buf)); if (rrl_result != DNS_RRL_RESULT_OK) { /* * Log dropped or slipped responses in the query * category so that requests are not silently lost. * Starts of rate-limited bursts are logged in * DNS_LOGCATEGORY_RRL. * * Dropped responses are counted with dropped queries * in QryDropped while slipped responses are counted * with other truncated responses in RespTruncated. */ if (wouldlog) { ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS, NS_LOGMODULE_QUERY, DNS_RRL_LOG_DROP, "%s", log_buf); } if (!client->view->rrl->log_only) { if (rrl_result == DNS_RRL_RESULT_DROP) { /* * These will also be counted in * dns_nsstatscounter_dropped */ inc_stats(client, dns_nsstatscounter_ratedropped); QUERY_ERROR(DNS_R_DROP); } else { /* * These will also be counted in * dns_nsstatscounter_truncatedresp */ inc_stats(client, dns_nsstatscounter_rateslipped); client->message->flags |= DNS_MESSAGEFLAG_TC; if (resp_result == DNS_R_NXDOMAIN) client->message->rcode = dns_rcode_nxdomain; } goto cleanup; } } } #endif /* USE_RRL */ if (!ISC_LIST_EMPTY(client->view->rpz_zones) && (RECURSIONOK(client) || !client->view->rpz_recursive_only) && rpz_ck_dnssec(client, result, rdataset, sigrdataset) && !RECURSING(client) && (client->query.rpz_st == NULL || (client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0) && !dns_name_equal(client->query.qname, dns_rootname)) { isc_result_t rresult; rresult = rpz_rewrite(client, qtype, result, resuming); rpz_st = client->query.rpz_st; switch (rresult) { case ISC_R_SUCCESS: break; case DNS_R_DELEGATION: /* * recursing for NS names or addresses, * so save the main query state */ rpz_st->q.qtype = qtype; rpz_st->q.is_zone = is_zone; rpz_st->q.authoritative = authoritative; rpz_st->q.zone = zone; zone = NULL; rpz_st->q.db = db; db = NULL; rpz_st->q.node = node; node = NULL; rpz_st->q.rdataset = rdataset; rdataset = NULL; rpz_st->q.sigrdataset = sigrdataset; sigrdataset = NULL; dns_name_copy(fname, rpz_st->fname, NULL); rpz_st->q.result = result; client->query.attributes |= NS_QUERYATTR_RECURSING; goto cleanup; default: RECURSE_ERROR(rresult); goto cleanup; } if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS) rpz_st->state |= DNS_RPZ_REWRITTEN; if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS && rpz_st->m.policy != DNS_RPZ_POLICY_PASSTHRU && rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) { if (rpz_st->m.type == DNS_RPZ_TYPE_QNAME) { result = dns_name_copy(client->query.qname, fname, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); } rpz_clean(&zone, &db, &node, NULL); if (rpz_st->m.rdataset != NULL) { query_putrdataset(client, &rdataset); rdataset = rpz_st->m.rdataset; rpz_st->m.rdataset = NULL; } else if (rdataset != NULL && dns_rdataset_isassociated(rdataset)) { dns_rdataset_disassociate(rdataset); } node = rpz_st->m.node; rpz_st->m.node = NULL; db = rpz_st->m.db; rpz_st->m.db = NULL; version = rpz_st->m.version; rpz_st->m.version = NULL; zone = rpz_st->m.zone; rpz_st->m.zone = NULL; switch (rpz_st->m.policy) { case DNS_RPZ_POLICY_NXDOMAIN: result = DNS_R_NXDOMAIN; break; case DNS_RPZ_POLICY_NODATA: result = DNS_R_NXRRSET; break; case DNS_RPZ_POLICY_RECORD: result = rpz_st->m.result; if (qtype == dns_rdatatype_any && result != DNS_R_CNAME) { /* * We will add all of the rdatasets of * the node by iterating, setting the * TTL then. */ if (dns_rdataset_isassociated(rdataset)) dns_rdataset_disassociate(rdataset); } else { /* * We will add this rdataset. */ rdataset->ttl = ISC_MIN(rdataset->ttl, rpz_st->m.ttl); } break; case DNS_RPZ_POLICY_WILDCNAME: result = dns_rdataset_first(rdataset); RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &cname, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_rdata_reset(&rdata); result = rpz_add_cname(client, rpz_st, &cname.cname, fname, dbuf); if (result != ISC_R_SUCCESS) goto cleanup; fname = NULL; want_restart = ISC_TRUE; goto cleanup; case DNS_RPZ_POLICY_CNAME: /* * Add overridding CNAME from a named.conf * response-policy statement */ result = rpz_add_cname(client, rpz_st, &rpz_st->m.rpz->cname, fname, dbuf); if (result != ISC_R_SUCCESS) goto cleanup; fname = NULL; want_restart = ISC_TRUE; goto cleanup; default: INSIST(0); } /* * Turn off DNSSEC because the results of a * response policy zone cannot verify. */ client->attributes &= ~(NS_CLIENTATTR_WANTDNSSEC | DNS_MESSAGEFLAG_AD); query_putrdataset(client, &sigrdataset); rpz_st->q.is_zone = is_zone; is_zone = ISC_TRUE; rpz_log_rewrite(client, ISC_FALSE, rpz_st->m.policy, rpz_st->m.type, zone, rpz_st->qname); } } switch (result) { case ISC_R_SUCCESS: /* * This case is handled in the main line below. */ break; case DNS_R_GLUE: case DNS_R_ZONECUT: /* * These cases are handled in the main line below. */ INSIST(is_zone); authoritative = ISC_FALSE; break; case ISC_R_NOTFOUND: /* * The cache doesn't even have the root NS. Get them from * the hints DB. */ INSIST(!is_zone); if (db != NULL) dns_db_detach(&db); if (client->view->hints == NULL) { /* We have no hints. */ result = ISC_R_FAILURE; } else { dns_db_attach(client->view->hints, &db); result = dns_db_findext(db, dns_rootname, NULL, dns_rdatatype_ns, 0, client->now, &node, fname, &cm, &ci, rdataset, sigrdataset); } if (result != ISC_R_SUCCESS) { /* * Nonsensical root hints may require cleanup. */ 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); /* * We don't have any root server hints, but * we may have working forwarders, so try to * recurse anyway. */ if (RECURSIONOK(client)) { result = query_recurse(client, qtype, client->query.qname, NULL, NULL, resuming); if (result == ISC_R_SUCCESS) { client->query.attributes |= NS_QUERYATTR_RECURSING; if (dns64) client->query.attributes |= NS_QUERYATTR_DNS64; if (dns64_exclude) client->query.attributes |= NS_QUERYATTR_DNS64EXCLUDE; } else RECURSE_ERROR(result); goto cleanup; } else { /* Unable to give root server referral. */ QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } } /* * XXXRTH We should trigger root server priming here. */ /* FALLTHROUGH */ case DNS_R_DELEGATION: authoritative = ISC_FALSE; if (is_zone) { /* * Look to see if we are authoritative for the * child zone if the query type is DS. */ if (!RECURSIONOK(client) && (options & DNS_GETDB_NOEXACT) != 0 && qtype == dns_rdatatype_ds) { dns_db_t *tdb = NULL; dns_zone_t *tzone = NULL; dns_dbversion_t *tversion = NULL; result = query_getzonedb(client, client->query.qname, qtype, DNS_GETDB_PARTIAL, &tzone, &tdb, &tversion); if (result == ISC_R_SUCCESS) { options &= ~DNS_GETDB_NOEXACT; query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); if (node != NULL) dns_db_detachnode(db, &node); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); version = tversion; db = tdb; zone = tzone; authoritative = ISC_TRUE; goto db_find; } if (tdb != NULL) dns_db_detach(&tdb); if (tzone != NULL) dns_zone_detach(&tzone); } /* * We're authoritative for an ancestor of QNAME. */ if (!USECACHE(client) || !RECURSIONOK(client)) { dns_fixedname_t fixed; dns_fixedname_init(&fixed); dns_name_copy(fname, dns_fixedname_name(&fixed), NULL); /* * If we don't have a cache, this is the best * answer. * * If the client is making a nonrecursive * query we always give out the authoritative * delegation. This way even if we get * junk in our cache, we won't fail in our * role as the delegating authority if another * nameserver asks us about a delegated * subzone. * * We enable the retrieval of glue for this * database by setting client->query.gluedb. */ client->query.gluedb = db; client->query.isreferral = ISC_TRUE; /* * We must ensure NOADDITIONAL is off, * because the generation of * additional data is required in * delegations. */ client->query.attributes &= ~NS_QUERYATTR_NOADDITIONAL; if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, DNS_SECTION_AUTHORITY); client->query.gluedb = NULL; if (WANTDNSSEC(client)) query_addds(client, db, node, version, dns_fixedname_name(&fixed)); } else { /* * We might have a better answer or delegation * in the cache. We'll remember the current * values of fname, rdataset, and sigrdataset. * We'll then go looking for QNAME in the * cache. If we find something better, we'll * use it instead. */ query_keepname(client, fname, dbuf); zdb = db; zfname = fname; fname = NULL; zrdataset = rdataset; rdataset = NULL; zsigrdataset = sigrdataset; sigrdataset = NULL; dns_db_detachnode(db, &node); zversion = version; version = NULL; db = NULL; dns_db_attach(client->view->cachedb, &db); is_zone = ISC_FALSE; goto db_find; } } else { if (zfname != NULL && (!dns_name_issubdomain(fname, zfname) || (is_staticstub_zone && dns_name_equal(fname, zfname)))) { /* * In the following cases use "authoritative" * data instead of the cache delegation: * 1. We've already got a delegation from * authoritative data, and it is better * than what we found in the cache. * 2. The query name matches the origin name * of a static-stub zone. This needs to be * considered for the case where the NS of * the static-stub zone and the cached NS * are different. We still need to contact * the nameservers configured in the * static-stub zone. */ query_releasename(client, &fname); fname = zfname; zfname = NULL; /* * We've already done query_keepname() on * zfname, so we must set dbuf to NULL to * prevent query_addrrset() from trying to * call query_keepname() again. */ dbuf = NULL; query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); rdataset = zrdataset; zrdataset = NULL; sigrdataset = zsigrdataset; zsigrdataset = NULL; version = zversion; zversion = NULL; /* * We don't clean up zdb here because we * may still need it. It will get cleaned * up by the main cleanup code. */ } if (RECURSIONOK(client)) { /* * Recurse! */ if (dns_rdatatype_atparent(type)) result = query_recurse(client, qtype, client->query.qname, NULL, NULL, resuming); else if (dns64) result = query_recurse(client, dns_rdatatype_a, client->query.qname, NULL, NULL, resuming); else result = query_recurse(client, qtype, client->query.qname, fname, rdataset, resuming); if (result == ISC_R_SUCCESS) { client->query.attributes |= NS_QUERYATTR_RECURSING; if (dns64) client->query.attributes |= NS_QUERYATTR_DNS64; if (dns64_exclude) client->query.attributes |= NS_QUERYATTR_DNS64EXCLUDE; } else if (result == DNS_R_DUPLICATE || result == DNS_R_DROP) QUERY_ERROR(result); else RECURSE_ERROR(result); } else { dns_fixedname_t fixed; dns_fixedname_init(&fixed); dns_name_copy(fname, dns_fixedname_name(&fixed), NULL); /* * This is the best answer. */ client->query.attributes |= NS_QUERYATTR_CACHEGLUEOK; client->query.gluedb = zdb; client->query.isreferral = ISC_TRUE; /* * We must ensure NOADDITIONAL is off, * because the generation of * additional data is required in * delegations. */ client->query.attributes &= ~NS_QUERYATTR_NOADDITIONAL; if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, DNS_SECTION_AUTHORITY); client->query.gluedb = NULL; client->query.attributes &= ~NS_QUERYATTR_CACHEGLUEOK; if (WANTDNSSEC(client)) query_addds(client, db, node, version, dns_fixedname_name(&fixed)); } } goto cleanup; case DNS_R_EMPTYNAME: case DNS_R_NXRRSET: iszone_nxrrset: INSIST(is_zone); #ifdef dns64_bis_return_excluded_addresses if (dns64) #else if (dns64 && !dns64_exclude) #endif { /* * Restore the answers from the previous AAAA lookup. */ if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); rdataset = client->query.dns64_aaaa; sigrdataset = client->query.dns64_sigaaaa; client->query.dns64_aaaa = NULL; client->query.dns64_sigaaaa = NULL; if (fname == NULL) { dbuf = query_getnamebuf(client); if (dbuf == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } fname = query_newname(client, dbuf, &b); if (fname == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } } dns_name_copy(client->query.qname, fname, NULL); dns64 = ISC_FALSE; #ifdef dns64_bis_return_excluded_addresses /* * Resume the diverted processing of the AAAA response? */ if (dns64_excluded) break; #endif } else if (result == DNS_R_NXRRSET && !ISC_LIST_EMPTY(client->view->dns64) && client->message->rdclass == dns_rdataclass_in && qtype == dns_rdatatype_aaaa) { /* * Look to see if there are A records for this * name. */ INSIST(client->query.dns64_aaaa == NULL); INSIST(client->query.dns64_sigaaaa == NULL); client->query.dns64_aaaa = rdataset; client->query.dns64_sigaaaa = sigrdataset; client->query.dns64_ttl = dns64_ttl(db, version); query_releasename(client, &fname); dns_db_detachnode(db, &node); rdataset = NULL; sigrdataset = NULL; type = qtype = dns_rdatatype_a; rpz_st = client->query.rpz_st; if (rpz_st != NULL) { /* * Arrange for RPZ rewriting of any A records. */ if ((rpz_st->state & DNS_RPZ_REWRITTEN) != 0) is_zone = rpz_st->q.is_zone; rpz_st_clear(client); } dns64 = ISC_TRUE; goto db_find; } /* * Look for a NSEC3 record if we don't have a NSEC record. */ nxrrset_rrsig: if (!dns_rdataset_isassociated(rdataset) && WANTDNSSEC(client)) { if ((fname->attributes & DNS_NAMEATTR_WILDCARD) == 0) { dns_name_t *found; dns_name_t *qname; dns_fixedname_init(&fixed); found = dns_fixedname_name(&fixed); qname = client->query.qname; query_findclosestnsec3(qname, db, version, client, rdataset, sigrdataset, fname, ISC_TRUE, found); /* * Did we find the closest provable encloser * instead? If so add the nearest to the * closest provable encloser. */ if (dns_rdataset_isassociated(rdataset) && !dns_name_equal(qname, found) && !(ns_g_nonearest && qtype != dns_rdatatype_ds)) { unsigned int count; unsigned int skip; /* * Add the closest provable encloser. */ query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf, DNS_SECTION_AUTHORITY); count = dns_name_countlabels(found) + 1; skip = dns_name_countlabels(qname) - count; dns_name_getlabelsequence(qname, skip, count, found); fixfname(client, &fname, &dbuf, &b); fixrdataset(client, &rdataset); fixrdataset(client, &sigrdataset); if (fname == NULL || rdataset == NULL || sigrdataset == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } /* * 'nearest' doesn't exist so * 'exist' is set to ISC_FALSE. */ query_findclosestnsec3(found, db, version, client, rdataset, sigrdataset, fname, ISC_FALSE, NULL); } } else { query_releasename(client, &fname); query_addwildcardproof(client, db, version, client->query.qname, ISC_FALSE, ISC_TRUE); } } if (dns_rdataset_isassociated(rdataset)) { /* * If we've got a NSEC record, we need to save the * name now because we're going call query_addsoa() * below, and it needs to use the name buffer. */ query_keepname(client, fname, dbuf); } else if (fname != NULL) { /* * We're not going to use fname, and need to release * our hold on the name buffer so query_addsoa() * may use it. */ query_releasename(client, &fname); } /* * Add SOA. */ result = query_addsoa(client, db, version, ISC_UINT32_MAX, dns_rdataset_isassociated(rdataset)); if (result != ISC_R_SUCCESS) { QUERY_ERROR(result); goto cleanup; } /* * Add NSEC record if we found one. */ if (WANTDNSSEC(client)) { if (dns_rdataset_isassociated(rdataset)) query_addnxrrsetnsec(client, db, version, &fname, &rdataset, &sigrdataset); } goto cleanup; case DNS_R_EMPTYWILD: empty_wild = ISC_TRUE; /* FALLTHROUGH */ case DNS_R_NXDOMAIN: INSIST(is_zone); if (!empty_wild && redirect(client, fname, rdataset, &node, &db, &version, type)) break; if (dns_rdataset_isassociated(rdataset)) { /* * If we've got a NSEC record, we need to save the * name now because we're going call query_addsoa() * below, and it needs to use the name buffer. */ query_keepname(client, fname, dbuf); } else if (fname != NULL) { /* * We're not going to use fname, and need to release * our hold on the name buffer so query_addsoa() * may use it. */ query_releasename(client, &fname); } /* * Add SOA. If the query was for a SOA record force the * ttl to zero so that it is possible for clients to find * the containing zone of an arbitrary name with a stub * resolver and not have it cached. */ if (qtype == dns_rdatatype_soa && zone != NULL && dns_zone_getzeronosoattl(zone)) result = query_addsoa(client, db, version, 0, dns_rdataset_isassociated(rdataset)); else result = query_addsoa(client, db, version, ISC_UINT32_MAX, dns_rdataset_isassociated(rdataset)); if (result != ISC_R_SUCCESS) { QUERY_ERROR(result); goto cleanup; } if (WANTDNSSEC(client)) { /* * Add NSEC record if we found one. */ if (dns_rdataset_isassociated(rdataset)) query_addrrset(client, &fname, &rdataset, &sigrdataset, NULL, DNS_SECTION_AUTHORITY); query_addwildcardproof(client, db, version, client->query.qname, ISC_FALSE, ISC_FALSE); } /* * Set message rcode. */ if (empty_wild) client->message->rcode = dns_rcode_noerror; else client->message->rcode = dns_rcode_nxdomain; goto cleanup; case DNS_R_NCACHENXDOMAIN: if (redirect(client, fname, rdataset, &node, &db, &version, type)) break; case DNS_R_NCACHENXRRSET: ncache_nxrrset: INSIST(!is_zone); authoritative = ISC_FALSE; /* * Set message rcode, if required. */ if (result == DNS_R_NCACHENXDOMAIN) client->message->rcode = dns_rcode_nxdomain; /* * Look for RFC 1918 leakage from Internet. */ if (result == DNS_R_NCACHENXDOMAIN && qtype == dns_rdatatype_ptr && client->message->rdclass == dns_rdataclass_in && dns_name_countlabels(fname) == 7) warn_rfc1918(client, fname, rdataset); #ifdef dns64_bis_return_excluded_addresses if (dns64) #else if (dns64 && !dns64_exclude) #endif { /* * Restore the answers from the previous AAAA lookup. */ if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); rdataset = client->query.dns64_aaaa; sigrdataset = client->query.dns64_sigaaaa; client->query.dns64_aaaa = NULL; client->query.dns64_sigaaaa = NULL; if (fname == NULL) { dbuf = query_getnamebuf(client); if (dbuf == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } fname = query_newname(client, dbuf, &b); if (fname == NULL) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } } dns_name_copy(client->query.qname, fname, NULL); dns64 = ISC_FALSE; #ifdef dns64_bis_return_excluded_addresses if (dns64_excluded) break; #endif } else if (result == DNS_R_NCACHENXRRSET && !ISC_LIST_EMPTY(client->view->dns64) && client->message->rdclass == dns_rdataclass_in && qtype == dns_rdatatype_aaaa) { /* * Look to see if there are A records for this * name. */ INSIST(client->query.dns64_aaaa == NULL); INSIST(client->query.dns64_sigaaaa == NULL); client->query.dns64_aaaa = rdataset; client->query.dns64_sigaaaa = sigrdataset; /* * If the ttl is zero we need to workout if we have just * decremented to zero or if there was no negative cache * ttl in the answer. */ if (rdataset->ttl != 0) client->query.dns64_ttl = rdataset->ttl; else if (dns_rdataset_first(rdataset) == ISC_R_SUCCESS) client->query.dns64_ttl = 0; query_releasename(client, &fname); dns_db_detachnode(db, &node); rdataset = NULL; sigrdataset = NULL; fname = NULL; type = qtype = dns_rdatatype_a; rpz_st = client->query.rpz_st; if (rpz_st != NULL) { /* * Arrange for RPZ rewriting of any A records. */ if ((rpz_st->state & DNS_RPZ_REWRITTEN) != 0) is_zone = rpz_st->q.is_zone; rpz_st_clear(client); } dns64 = ISC_TRUE; goto db_find; } /* * We don't call query_addrrset() because we don't need any * of its extra features (and things would probably break!). */ query_keepname(client, fname, dbuf); dns_message_addname(client->message, fname, DNS_SECTION_AUTHORITY); ISC_LIST_APPEND(fname->list, rdataset, link); fname = NULL; rdataset = NULL; goto cleanup; case DNS_R_CNAME: /* * Keep a copy of the rdataset. We have to do this because * query_addrrset may clear 'rdataset' (to prevent the * cleanup code from cleaning it up). */ trdataset = rdataset; /* * Add the CNAME to the answer section. */ if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; if (WANTDNSSEC(client) && (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0) { dns_fixedname_init(&wildcardname); dns_name_copy(fname, dns_fixedname_name(&wildcardname), NULL); need_wildcardproof = ISC_TRUE; } if (NOQNAME(rdataset) && WANTDNSSEC(client)) noqname = rdataset; else noqname = NULL; query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, DNS_SECTION_ANSWER); if (noqname != NULL) query_addnoqnameproof(client, noqname); /* * We set the PARTIALANSWER attribute so that if anything goes * wrong later on, we'll return what we've got so far. */ client->query.attributes |= NS_QUERYATTR_PARTIALANSWER; /* * Reset qname to be the target name of the CNAME and restart * the query. */ tname = NULL; result = dns_message_gettempname(client->message, &tname); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_rdataset_first(trdataset); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &tname); goto cleanup; } dns_rdataset_current(trdataset, &rdata); result = dns_rdata_tostruct(&rdata, &cname, NULL); dns_rdata_reset(&rdata); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &tname); goto cleanup; } dns_name_init(tname, NULL); result = dns_name_dup(&cname.cname, client->mctx, tname); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &tname); dns_rdata_freestruct(&cname); goto cleanup; } dns_rdata_freestruct(&cname); ns_client_qnamereplace(client, tname); want_restart = ISC_TRUE; if (!WANTRECURSION(client)) options |= DNS_GETDB_NOLOG; goto addauth; case DNS_R_DNAME: /* * Compare the current qname to the found name. We need * to know how many labels and bits are in common because * we're going to have to split qname later on. */ namereln = dns_name_fullcompare(client->query.qname, fname, &order, &nlabels); INSIST(namereln == dns_namereln_subdomain); /* * Keep a copy of the rdataset. We have to do this because * query_addrrset may clear 'rdataset' (to prevent the * cleanup code from cleaning it up). */ trdataset = rdataset; /* * Add the DNAME to the answer section. */ if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; if (WANTDNSSEC(client) && (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0) { dns_fixedname_init(&wildcardname); dns_name_copy(fname, dns_fixedname_name(&wildcardname), NULL); need_wildcardproof = ISC_TRUE; } query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, DNS_SECTION_ANSWER); /* * We set the PARTIALANSWER attribute so that if anything goes * wrong later on, we'll return what we've got so far. */ client->query.attributes |= NS_QUERYATTR_PARTIALANSWER; /* * Get the target name of the DNAME. */ tname = NULL; result = dns_message_gettempname(client->message, &tname); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_rdataset_first(trdataset); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &tname); goto cleanup; } dns_rdataset_current(trdataset, &rdata); result = dns_rdata_tostruct(&rdata, &dname, NULL); dns_rdata_reset(&rdata); if (result != ISC_R_SUCCESS) { dns_message_puttempname(client->message, &tname); goto cleanup; } dns_name_clone(&dname.dname, tname); dns_rdata_freestruct(&dname); /* * Construct the new qname consisting of * . */ dns_fixedname_init(&fixed); prefix = dns_fixedname_name(&fixed); dns_name_split(client->query.qname, nlabels, prefix, NULL); INSIST(fname == NULL); dbuf = query_getnamebuf(client); if (dbuf == NULL) { dns_message_puttempname(client->message, &tname); goto cleanup; } fname = query_newname(client, dbuf, &b); if (fname == NULL) { dns_message_puttempname(client->message, &tname); goto cleanup; } result = dns_name_concatenate(prefix, tname, fname, NULL); dns_message_puttempname(client->message, &tname); /* * RFC2672, section 4.1, subsection 3c says * we should return YXDOMAIN if the constructed * name would be too long. */ if (result == DNS_R_NAMETOOLONG) client->message->rcode = dns_rcode_yxdomain; if (result != ISC_R_SUCCESS) goto cleanup; query_keepname(client, fname, dbuf); /* * Synthesize a CNAME consisting of * CNAME * with * * Synthesize a CNAME so old old clients that don't understand * DNAME can chain. * * We do not try to synthesize a signature because we hope * that security aware servers will understand DNAME. Also, * even if we had an online key, making a signature * on-the-fly is costly, and not really legitimate anyway * since the synthesized CNAME is NOT in the zone. */ result = query_add_cname(client, client->query.qname, fname, trdataset->trust, trdataset->ttl); if (result != ISC_R_SUCCESS) goto cleanup; /* * Switch to the new qname and restart. */ ns_client_qnamereplace(client, fname); fname = NULL; want_restart = ISC_TRUE; if (!WANTRECURSION(client)) options |= DNS_GETDB_NOLOG; goto addauth; default: /* * Something has gone wrong. */ QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } if (WANTDNSSEC(client) && (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0) { dns_fixedname_init(&wildcardname); dns_name_copy(fname, dns_fixedname_name(&wildcardname), NULL); need_wildcardproof = ISC_TRUE; } #ifdef ALLOW_FILTER_AAAA_ON_V4 if (client->view->v4_aaaa != dns_v4_aaaa_ok && is_v4_client(client) && ns_client_checkaclsilent(client, NULL, client->view->v4_aaaa_acl, ISC_TRUE) == ISC_R_SUCCESS) client->filter_aaaa = client->view->v4_aaaa; else client->filter_aaaa = dns_v4_aaaa_ok; #endif if (type == dns_rdatatype_any) { #ifdef ALLOW_FILTER_AAAA_ON_V4 isc_boolean_t have_aaaa, have_a, have_sig; /* * The filter-aaaa-on-v4 option should * suppress AAAAs for IPv4 clients if there is an A. * If we are not authoritative, assume there is a A * even in if it is not in our cache. This assumption could * be wrong but it is a good bet. */ have_aaaa = ISC_FALSE; have_a = !authoritative; have_sig = ISC_FALSE; #endif /* * XXXRTH Need to handle zonecuts with special case * code. */ n = 0; rdsiter = NULL; result = dns_db_allrdatasets(db, node, version, 0, &rdsiter); if (result != ISC_R_SUCCESS) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } /* * Calling query_addrrset() with a non-NULL dbuf is going * to either keep or release the name. We don't want it to * release fname, since we may have to call query_addrrset() * more than once. That means we have to call query_keepname() * now, and pass a NULL dbuf to query_addrrset(). * * If we do a query_addrrset() below, we must set fname to * NULL before leaving this block, otherwise we might try to * cleanup fname even though we're using it! */ query_keepname(client, fname, dbuf); tname = fname; result = dns_rdatasetiter_first(rdsiter); while (result == ISC_R_SUCCESS) { dns_rdatasetiter_current(rdsiter, rdataset); #ifdef ALLOW_FILTER_AAAA_ON_V4 /* * Notice the presence of A and AAAAs so * that AAAAs can be hidden from IPv4 clients. */ if (client->filter_aaaa != dns_v4_aaaa_ok) { if (rdataset->type == dns_rdatatype_aaaa) have_aaaa = ISC_TRUE; else if (rdataset->type == dns_rdatatype_a) have_a = ISC_TRUE; } #endif if (is_zone && qtype == dns_rdatatype_any && !dns_db_issecure(db) && dns_rdatatype_isdnssec(rdataset->type)) { /* * The zone is transitioning from insecure * to secure. Hide the dnssec records from * ANY queries. */ dns_rdataset_disassociate(rdataset); } else if ((qtype == dns_rdatatype_any || rdataset->type == qtype) && rdataset->type != 0) { #ifdef ALLOW_FILTER_AAAA_ON_V4 if (dns_rdatatype_isdnssec(rdataset->type)) have_sig = ISC_TRUE; #endif if (NOQNAME(rdataset) && WANTDNSSEC(client)) noqname = rdataset; else noqname = NULL; rpz_st = client->query.rpz_st; if (rpz_st != NULL) rdataset->ttl = ISC_MIN(rdataset->ttl, rpz_st->m.ttl); query_addrrset(client, fname != NULL ? &fname : &tname, &rdataset, NULL, NULL, DNS_SECTION_ANSWER); if (noqname != NULL) query_addnoqnameproof(client, noqname); n++; INSIST(tname != NULL); /* * rdataset is non-NULL only in certain * pathological cases involving DNAMEs. */ if (rdataset != NULL) query_putrdataset(client, &rdataset); rdataset = query_newrdataset(client); if (rdataset == NULL) break; } else { /* * We're not interested in this rdataset. */ dns_rdataset_disassociate(rdataset); } result = dns_rdatasetiter_next(rdsiter); } #ifdef ALLOW_FILTER_AAAA_ON_V4 /* * Filter AAAAs if there is an A and there is no signature * or we are supposed to break DNSSEC. */ if (client->filter_aaaa == dns_v4_aaaa_break_dnssec) client->attributes |= NS_CLIENTATTR_FILTER_AAAA; else if (client->filter_aaaa != dns_v4_aaaa_ok && have_aaaa && have_a && (!have_sig || !WANTDNSSEC(client))) client->attributes |= NS_CLIENTATTR_FILTER_AAAA; #endif if (fname != NULL) dns_message_puttempname(client->message, &fname); if (n == 0) { /* * No matching rdatasets found in cache. If we were * searching for RRSIG/SIG, that's probably okay; * otherwise this is an error condition. */ if ((qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig) && result == ISC_R_NOMORE) { if (!is_zone) { authoritative = ISC_FALSE; dns_rdatasetiter_destroy(&rdsiter); client->attributes &= ~NS_CLIENTATTR_RA; goto addauth; } if (qtype == dns_rdatatype_rrsig && dns_db_issecure(db)) { char namebuf[DNS_NAME_FORMATSIZE]; dns_name_format(client->query.qname, namebuf, sizeof(namebuf)); ns_client_log(client, DNS_LOGCATEGORY_DNSSEC, NS_LOGMODULE_QUERY, ISC_LOG_WARNING, "missing signature " "for %s", namebuf); } dns_rdatasetiter_destroy(&rdsiter); fname = query_newname(client, dbuf, &b); goto nxrrset_rrsig; } else result = DNS_R_SERVFAIL; } dns_rdatasetiter_destroy(&rdsiter); if (result != ISC_R_NOMORE) { QUERY_ERROR(DNS_R_SERVFAIL); goto cleanup; } } else { /* * This is the "normal" case -- an ordinary question to which * we know the answer. */ #ifdef ALLOW_FILTER_AAAA_ON_V4 /* * Optionally hide AAAAs from IPv4 clients if there is an A. * We add the AAAAs now, but might refuse to render them later * after DNSSEC is figured out. * This could be more efficient, but the whole idea is * so fundamentally wrong, unavoidably inaccurate, and * unneeded that it is best to keep it as short as possible. */ if (client->filter_aaaa == dns_v4_aaaa_break_dnssec || (client->filter_aaaa == dns_v4_aaaa_filter && (!WANTDNSSEC(client) || sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset)))) { if (qtype == dns_rdatatype_aaaa) { trdataset = query_newrdataset(client); result = dns_db_findrdataset(db, node, version, dns_rdatatype_a, 0, client->now, trdataset, NULL); if (dns_rdataset_isassociated(trdataset)) dns_rdataset_disassociate(trdataset); query_putrdataset(client, &trdataset); /* * We have an AAAA but the A is not in our cache. * Assume any result other than DNS_R_DELEGATION * or ISC_R_NOTFOUND means there is no A and * so AAAAs are ok. * Assume there is no A if we can't recurse * for this client, although that could be * the wrong answer. What else can we do? * Besides, that we have the AAAA and are using * this mechanism suggests that we care more * about As than AAAAs and would have cached * the A if it existed. */ if (result == ISC_R_SUCCESS) { client->attributes |= NS_CLIENTATTR_FILTER_AAAA; } else if (authoritative || !RECURSIONOK(client) || (result != DNS_R_DELEGATION && result != ISC_R_NOTFOUND)) { client->attributes &= ~NS_CLIENTATTR_FILTER_AAAA; } else { /* * This is an ugly kludge to recurse * for the A and discard the result. * * Continue to add the AAAA now. * We'll make a note to not render it * if the recursion for the A succeeds. */ result = query_recurse(client, dns_rdatatype_a, client->query.qname, NULL, NULL, resuming); if (result == ISC_R_SUCCESS) { client->attributes |= NS_CLIENTATTR_FILTER_AAAA_RC; client->query.attributes |= NS_QUERYATTR_RECURSING; } } } else if (qtype == dns_rdatatype_a && (client->attributes & NS_CLIENTATTR_FILTER_AAAA_RC) != 0) { client->attributes &= ~NS_CLIENTATTR_FILTER_AAAA_RC; client->attributes |= NS_CLIENTATTR_FILTER_AAAA; dns_rdataset_disassociate(rdataset); if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) dns_rdataset_disassociate(sigrdataset); goto cleanup; } } #endif /* * Check to see if the AAAA RRset has non-excluded addresses * in it. If not look for a A RRset. */ INSIST(client->query.dns64_aaaaok == NULL); if (qtype == dns_rdatatype_aaaa && !dns64_exclude && !ISC_LIST_EMPTY(client->view->dns64) && client->message->rdclass == dns_rdataclass_in && !dns64_aaaaok(client, rdataset, sigrdataset)) { /* * Look to see if there are A records for this * name. */ client->query.dns64_aaaa = rdataset; client->query.dns64_sigaaaa = sigrdataset; client->query.dns64_ttl = rdataset->ttl; query_releasename(client, &fname); dns_db_detachnode(db, &node); rdataset = NULL; sigrdataset = NULL; type = qtype = dns_rdatatype_a; rpz_st = client->query.rpz_st; if (rpz_st != NULL) { /* * Arrange for RPZ rewriting of any A records. */ if ((rpz_st->state & DNS_RPZ_REWRITTEN) != 0) is_zone = rpz_st->q.is_zone; rpz_st_clear(client); } dns64_exclude = dns64 = ISC_TRUE; goto db_find; } if (sigrdataset != NULL) sigrdatasetp = &sigrdataset; else sigrdatasetp = NULL; if (NOQNAME(rdataset) && WANTDNSSEC(client)) noqname = rdataset; else noqname = NULL; /* * BIND 8 priming queries need the additional section. */ if (is_zone && qtype == dns_rdatatype_ns && dns_name_equal(client->query.qname, dns_rootname)) client->query.attributes &= ~NS_QUERYATTR_NOADDITIONAL; if (dns64) { qtype = type = dns_rdatatype_aaaa; result = query_dns64(client, &fname, rdataset, sigrdataset, dbuf, DNS_SECTION_ANSWER); dns_rdataset_disassociate(rdataset); dns_message_puttemprdataset(client->message, &rdataset); if (result == ISC_R_NOMORE) { #ifndef dns64_bis_return_excluded_addresses if (dns64_exclude) { if (!is_zone) goto cleanup; /* * Add a fake SOA record. */ (void)query_addsoa(client, db, version, 600, ISC_FALSE); goto cleanup; } #endif if (is_zone) goto iszone_nxrrset; else goto ncache_nxrrset; } else if (result != ISC_R_SUCCESS) { eresult = result; goto cleanup; } } else if (client->query.dns64_aaaaok != NULL) { query_filter64(client, &fname, rdataset, dbuf, DNS_SECTION_ANSWER); query_putrdataset(client, &rdataset); } else query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf, DNS_SECTION_ANSWER); if (noqname != NULL) query_addnoqnameproof(client, noqname); /* * We shouldn't ever fail to add 'rdataset' * because it's already in the answer. */ INSIST(rdataset == NULL); } addauth: CTRACE("query_find: addauth"); /* * Add NS records to the authority section (if we haven't already * added them to the answer section). */ if (!want_restart && !NOAUTHORITY(client)) { if (is_zone) { if (!((qtype == dns_rdatatype_ns || qtype == dns_rdatatype_any) && dns_name_equal(client->query.qname, dns_db_origin(db)))) (void)query_addns(client, db, version); } else if (qtype != dns_rdatatype_ns) { if (fname != NULL) query_releasename(client, &fname); query_addbestns(client); } } /* * Add NSEC records to the authority section if they're needed for * DNSSEC wildcard proofs. */ if (need_wildcardproof && dns_db_issecure(db)) query_addwildcardproof(client, db, version, dns_fixedname_name(&wildcardname), ISC_TRUE, ISC_FALSE); cleanup: CTRACE("query_find: cleanup"); /* * General cleanup. */ rpz_st = client->query.rpz_st; if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) == 0) { rpz_match_clear(rpz_st); rpz_st->state &= ~DNS_RPZ_DONE_QNAME; } if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) query_putrdataset(client, &sigrdataset); if (fname != NULL) query_releasename(client, &fname); if (node != NULL) dns_db_detachnode(db, &node); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); if (zdb != NULL) { query_putrdataset(client, &zrdataset); if (zsigrdataset != NULL) query_putrdataset(client, &zsigrdataset); if (zfname != NULL) query_releasename(client, &zfname); dns_db_detach(&zdb); } if (event != NULL) isc_event_free(ISC_EVENT_PTR(&event)); /* * AA bit. */ if (client->query.restarts == 0 && !authoritative) { /* * We're not authoritative, so we must ensure the AA bit * isn't set. */ client->message->flags &= ~DNS_MESSAGEFLAG_AA; } /* * Restart the query? */ if (want_restart && client->query.restarts < MAX_RESTARTS) { client->query.restarts++; goto restart; } #ifdef USE_RRL if (eresult != ISC_R_SUCCESS && (!PARTIALANSWER(client) || WANTRECURSION(client) || eresult == DNS_R_DROP)) #else /* USE_RRL */ if (eresult != ISC_R_SUCCESS && (!PARTIALANSWER(client) || WANTRECURSION(client))) #endif /* USE_RRL */ { if (eresult == DNS_R_DUPLICATE || eresult == DNS_R_DROP) { /* * This was a duplicate query that we are * recursing on or the result of rate limiting. * Don't send a response now for a duplicate query, * because the original will still cause a response. */ query_next(client, eresult); } else { /* * If we don't have any answer to give the client, * or if the client requested recursion and thus wanted * the complete answer, send an error response. */ INSIST(line >= 0); query_error(client, eresult, line); } ns_client_detach(&client); } else if (!RECURSING(client)) { /* * We are done. Set up sortlist data for the message * rendering code, make a final tweak to the AA bit if the * auth-nxdomain config option says so, then render and * send the response. */ setup_query_sortlist(client); /* * If this is a referral and the answer to the question * is in the glue sort it to the start of the additional * section. */ if (ISC_LIST_EMPTY(client->message->sections[DNS_SECTION_ANSWER]) && client->message->rcode == dns_rcode_noerror && (qtype == dns_rdatatype_a || qtype == dns_rdatatype_aaaa)) answer_in_glue(client, qtype); if (client->message->rcode == dns_rcode_nxdomain && client->view->auth_nxdomain == ISC_TRUE) client->message->flags |= DNS_MESSAGEFLAG_AA; /* * If the response is somehow unexpected for the client and this * is a result of recursion, return an error to the caller * to indicate it may need to be logged. */ if (resuming && (ISC_LIST_EMPTY(client->message->sections[DNS_SECTION_ANSWER]) || client->message->rcode != dns_rcode_noerror)) eresult = ISC_R_FAILURE; query_send(client); ns_client_detach(&client); } CTRACE("query_find: done"); return (eresult); } static inline void log_query(ns_client_t *client, unsigned int flags, unsigned int extflags) { char namebuf[DNS_NAME_FORMATSIZE]; char typename[DNS_RDATATYPE_FORMATSIZE]; char classname[DNS_RDATACLASS_FORMATSIZE]; char onbuf[ISC_NETADDR_FORMATSIZE]; dns_rdataset_t *rdataset; int level = ISC_LOG_INFO; if (! isc_log_wouldlog(ns_g_lctx, level)) return; rdataset = ISC_LIST_HEAD(client->query.qname->list); INSIST(rdataset != NULL); dns_name_format(client->query.qname, namebuf, sizeof(namebuf)); dns_rdataclass_format(rdataset->rdclass, classname, sizeof(classname)); dns_rdatatype_format(rdataset->type, typename, sizeof(typename)); isc_netaddr_format(&client->destaddr, onbuf, sizeof(onbuf)); ns_client_log(client, NS_LOGCATEGORY_QUERIES, NS_LOGMODULE_QUERY, level, "query: %s %s %s %s%s%s%s%s%s (%s)", namebuf, classname, typename, WANTRECURSION(client) ? "+" : "-", (client->signer != NULL) ? "S": "", (client->opt != NULL) ? "E" : "", ((client->attributes & NS_CLIENTATTR_TCP) != 0) ? "T" : "", ((extflags & DNS_MESSAGEEXTFLAG_DO) != 0) ? "D" : "", ((flags & DNS_MESSAGEFLAG_CD) != 0) ? "C" : "", onbuf); } static inline void log_queryerror(ns_client_t *client, isc_result_t result, int line, int level) { char namebuf[DNS_NAME_FORMATSIZE]; char typename[DNS_RDATATYPE_FORMATSIZE]; char classname[DNS_RDATACLASS_FORMATSIZE]; const char *namep, *typep, *classp, *sep1, *sep2; dns_rdataset_t *rdataset; if (!isc_log_wouldlog(ns_g_lctx, level)) return; namep = typep = classp = sep1 = sep2 = ""; /* * Query errors can happen for various reasons. In some cases we cannot * even assume the query contains a valid question section, so we should * expect exceptional cases. */ if (client->query.origqname != NULL) { dns_name_format(client->query.origqname, namebuf, sizeof(namebuf)); namep = namebuf; sep1 = " for "; rdataset = ISC_LIST_HEAD(client->query.origqname->list); if (rdataset != NULL) { dns_rdataclass_format(rdataset->rdclass, classname, sizeof(classname)); classp = classname; dns_rdatatype_format(rdataset->type, typename, sizeof(typename)); typep = typename; sep2 = "/"; } } ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS, NS_LOGMODULE_QUERY, level, "query failed (%s)%s%s%s%s%s%s at %s:%d", isc_result_totext(result), sep1, namep, sep2, classp, sep2, typep, __FILE__, line); } void ns_query_start(ns_client_t *client) { isc_result_t result; dns_message_t *message = client->message; dns_rdataset_t *rdataset; ns_client_t *qclient; dns_rdatatype_t qtype; unsigned int saved_extflags = client->extflags; unsigned int saved_flags = client->message->flags; CTRACE("ns_query_start"); /* * Test only. */ if (ns_g_clienttest && (client->attributes & NS_CLIENTATTR_TCP) == 0) RUNTIME_CHECK(ns_client_replace(client) == ISC_R_SUCCESS); /* * Ensure that appropriate cleanups occur. */ client->next = query_next_callback; /* * Behave as if we don't support DNSSEC if not enabled. */ if (!client->view->enablednssec) { message->flags &= ~DNS_MESSAGEFLAG_CD; client->extflags &= ~DNS_MESSAGEEXTFLAG_DO; if (client->opt != NULL) client->opt->ttl &= ~DNS_MESSAGEEXTFLAG_DO; } if ((message->flags & DNS_MESSAGEFLAG_RD) != 0) client->query.attributes |= NS_QUERYATTR_WANTRECURSION; if ((client->extflags & DNS_MESSAGEEXTFLAG_DO) != 0) client->attributes |= NS_CLIENTATTR_WANTDNSSEC; if (client->view->minimalresponses) client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY | NS_QUERYATTR_NOADDITIONAL); if ((client->view->cachedb == NULL) || (!client->view->additionalfromcache)) { /* * We don't have a cache. Turn off cache support and * recursion. */ client->query.attributes &= ~(NS_QUERYATTR_RECURSIONOK|NS_QUERYATTR_CACHEOK); } else if ((client->attributes & NS_CLIENTATTR_RA) == 0 || (message->flags & DNS_MESSAGEFLAG_RD) == 0) { /* * If the client isn't allowed to recurse (due to * "recursion no", the allow-recursion ACL, or the * lack of a resolver in this view), or if it * doesn't want recursion, turn recursion off. */ client->query.attributes &= ~NS_QUERYATTR_RECURSIONOK; } /* * Get the question name. */ result = dns_message_firstname(message, DNS_SECTION_QUESTION); if (result != ISC_R_SUCCESS) { query_error(client, result, __LINE__); return; } dns_message_currentname(message, DNS_SECTION_QUESTION, &client->query.qname); client->query.origqname = client->query.qname; result = dns_message_nextname(message, DNS_SECTION_QUESTION); if (result != ISC_R_NOMORE) { if (result == ISC_R_SUCCESS) { /* * There's more than one QNAME in the question * section. */ query_error(client, DNS_R_FORMERR, __LINE__); } else query_error(client, result, __LINE__); return; } if (ns_g_server->log_queries) log_query(client, saved_flags, saved_extflags); /* * Check for multiple question queries, since edns1 is dead. */ if (message->counts[DNS_SECTION_QUESTION] > 1) { query_error(client, DNS_R_FORMERR, __LINE__); return; } /* * Check for meta-queries like IXFR and AXFR. */ rdataset = ISC_LIST_HEAD(client->query.qname->list); INSIST(rdataset != NULL); qtype = rdataset->type; dns_rdatatypestats_increment(ns_g_server->rcvquerystats, qtype); if (dns_rdatatype_ismeta(qtype)) { switch (qtype) { case dns_rdatatype_any: break; /* Let query_find handle it. */ case dns_rdatatype_ixfr: case dns_rdatatype_axfr: ns_xfr_start(client, rdataset->type); return; case dns_rdatatype_maila: case dns_rdatatype_mailb: query_error(client, DNS_R_NOTIMP, __LINE__); return; case dns_rdatatype_tkey: result = dns_tkey_processquery(client->message, ns_g_server->tkeyctx, client->view->dynamickeys); if (result == ISC_R_SUCCESS) query_send(client); else query_error(client, result, __LINE__); return; default: /* TSIG, etc. */ query_error(client, DNS_R_FORMERR, __LINE__); return; } } /* * Turn on minimal response for DNSKEY and DS queries. */ if (qtype == dns_rdatatype_dnskey || qtype == dns_rdatatype_ds) client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY | NS_QUERYATTR_NOADDITIONAL); /* * Turn on minimal responses for EDNS/UDP bufsize 512 queries. */ if (client->opt != NULL && client->udpsize <= 512U && (client->attributes & NS_CLIENTATTR_TCP) == 0) client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY | NS_QUERYATTR_NOADDITIONAL); /* * If the client has requested that DNSSEC checking be disabled, * allow lookups to return pending data and instruct the resolver * to return data before validation has completed. * * We don't need to set DNS_DBFIND_PENDINGOK when validation is * disabled as there will be no pending data. */ if (message->flags & DNS_MESSAGEFLAG_CD || qtype == dns_rdatatype_rrsig) { client->query.dboptions |= DNS_DBFIND_PENDINGOK; client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE; } else if (!client->view->enablevalidation) client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE; /* * Allow glue NS records to be added to the authority section * if the answer is secure. */ if (message->flags & DNS_MESSAGEFLAG_CD) client->query.attributes &= ~NS_QUERYATTR_SECURE; /* * Set NS_CLIENTATTR_WANTDNSSEC if the client has set AD in the query. * This allows AD to be returned on queries without DO set. */ if ((message->flags & DNS_MESSAGEFLAG_AD) != 0) client->attributes |= NS_CLIENTATTR_WANTAD; /* * This is an ordinary query. */ result = dns_message_reply(message, ISC_TRUE); if (result != ISC_R_SUCCESS) { query_next(client, result); return; } /* * Assume authoritative response until it is known to be * otherwise. * * If "-T noaa" has been set on the command line don't set * AA on authoritative answers. */ if (!ns_g_noaa) message->flags |= DNS_MESSAGEFLAG_AA; /* * Set AD. We must clear it if we add non-validated data to a * response. */ if (WANTDNSSEC(client) || WANTAD(client)) message->flags |= DNS_MESSAGEFLAG_AD; qclient = NULL; ns_client_attach(client, &qclient); (void)query_find(qclient, NULL, qtype); } Index: stable/9/contrib/bind9/bin/named/server.c =================================================================== --- stable/9/contrib/bind9/bin/named/server.c (revision 275668) +++ stable/9/contrib/bind9/bin/named/server.c (revision 275669) @@ -1,8755 +1,8760 @@ /* * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id$ */ /*! \file */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBSCF #include #include #endif #ifndef PATH_MAX #define PATH_MAX 1024 #endif #ifndef SIZE_MAX #define SIZE_MAX ((size_t)-1) #endif /*% * Check an operation for failure. Assumes that the function * using it has a 'result' variable and a 'cleanup' label. */ #define CHECK(op) \ do { result = (op); \ if (result != ISC_R_SUCCESS) goto cleanup; \ } while (0) #define CHECKM(op, msg) \ do { result = (op); \ if (result != ISC_R_SUCCESS) { \ isc_log_write(ns_g_lctx, \ NS_LOGCATEGORY_GENERAL, \ NS_LOGMODULE_SERVER, \ ISC_LOG_ERROR, \ "%s: %s", msg, \ isc_result_totext(result)); \ goto cleanup; \ } \ } while (0) \ #define CHECKMF(op, msg, file) \ do { result = (op); \ if (result != ISC_R_SUCCESS) { \ isc_log_write(ns_g_lctx, \ NS_LOGCATEGORY_GENERAL, \ NS_LOGMODULE_SERVER, \ ISC_LOG_ERROR, \ "%s '%s': %s", msg, file, \ isc_result_totext(result)); \ goto cleanup; \ } \ } while (0) \ #define CHECKFATAL(op, msg) \ do { result = (op); \ if (result != ISC_R_SUCCESS) \ fatal(msg, result); \ } while (0) \ /*% * Maximum ADB size for views that share a cache. Use this limit to suppress * the total of memory footprint, which should be the main reason for sharing * a cache. Only effective when a finite max-cache-size is specified. * This is currently defined to be 8MB. */ #define MAX_ADB_SIZE_FOR_CACHESHARE 8388608U struct ns_dispatch { isc_sockaddr_t addr; unsigned int dispatchgen; dns_dispatch_t *dispatch; ISC_LINK(struct ns_dispatch) link; }; struct ns_cache { dns_cache_t *cache; dns_view_t *primaryview; isc_boolean_t needflush; isc_boolean_t adbsizeadjusted; ISC_LINK(ns_cache_t) link; }; struct dumpcontext { isc_mem_t *mctx; isc_boolean_t dumpcache; isc_boolean_t dumpzones; FILE *fp; ISC_LIST(struct viewlistentry) viewlist; struct viewlistentry *view; struct zonelistentry *zone; dns_dumpctx_t *mdctx; dns_db_t *db; dns_db_t *cache; isc_task_t *task; dns_dbversion_t *version; }; struct viewlistentry { dns_view_t *view; ISC_LINK(struct viewlistentry) link; ISC_LIST(struct zonelistentry) zonelist; }; struct zonelistentry { dns_zone_t *zone; ISC_LINK(struct zonelistentry) link; }; /*% * Configuration context to retain for each view that allows * new zones to be added at runtime. */ struct cfg_context { isc_mem_t * mctx; cfg_parser_t * parser; cfg_obj_t * config; cfg_parser_t * nzparser; cfg_obj_t * nzconfig; cfg_aclconfctx_t * actx; }; /*% * Holds state information for the initial zone loading process. * Uses the isc_refcount structure to count the number of views * with pending zone loads, dereferencing as each view finishes. */ typedef struct { ns_server_t *server; isc_refcount_t refs; } ns_zoneload_t; /* * These zones should not leak onto the Internet. */ const char *empty_zones[] = { /* RFC 1918 */ "10.IN-ADDR.ARPA", "16.172.IN-ADDR.ARPA", "17.172.IN-ADDR.ARPA", "18.172.IN-ADDR.ARPA", "19.172.IN-ADDR.ARPA", "20.172.IN-ADDR.ARPA", "21.172.IN-ADDR.ARPA", "22.172.IN-ADDR.ARPA", "23.172.IN-ADDR.ARPA", "24.172.IN-ADDR.ARPA", "25.172.IN-ADDR.ARPA", "26.172.IN-ADDR.ARPA", "27.172.IN-ADDR.ARPA", "28.172.IN-ADDR.ARPA", "29.172.IN-ADDR.ARPA", "30.172.IN-ADDR.ARPA", "31.172.IN-ADDR.ARPA", "168.192.IN-ADDR.ARPA", /* RFC 6598 */ "64.100.IN-ADDR.ARPA", "65.100.IN-ADDR.ARPA", "66.100.IN-ADDR.ARPA", "67.100.IN-ADDR.ARPA", "68.100.IN-ADDR.ARPA", "69.100.IN-ADDR.ARPA", "70.100.IN-ADDR.ARPA", "71.100.IN-ADDR.ARPA", "72.100.IN-ADDR.ARPA", "73.100.IN-ADDR.ARPA", "74.100.IN-ADDR.ARPA", "75.100.IN-ADDR.ARPA", "76.100.IN-ADDR.ARPA", "77.100.IN-ADDR.ARPA", "78.100.IN-ADDR.ARPA", "79.100.IN-ADDR.ARPA", "80.100.IN-ADDR.ARPA", "81.100.IN-ADDR.ARPA", "82.100.IN-ADDR.ARPA", "83.100.IN-ADDR.ARPA", "84.100.IN-ADDR.ARPA", "85.100.IN-ADDR.ARPA", "86.100.IN-ADDR.ARPA", "87.100.IN-ADDR.ARPA", "88.100.IN-ADDR.ARPA", "89.100.IN-ADDR.ARPA", "90.100.IN-ADDR.ARPA", "91.100.IN-ADDR.ARPA", "92.100.IN-ADDR.ARPA", "93.100.IN-ADDR.ARPA", "94.100.IN-ADDR.ARPA", "95.100.IN-ADDR.ARPA", "96.100.IN-ADDR.ARPA", "97.100.IN-ADDR.ARPA", "98.100.IN-ADDR.ARPA", "99.100.IN-ADDR.ARPA", "100.100.IN-ADDR.ARPA", "101.100.IN-ADDR.ARPA", "102.100.IN-ADDR.ARPA", "103.100.IN-ADDR.ARPA", "104.100.IN-ADDR.ARPA", "105.100.IN-ADDR.ARPA", "106.100.IN-ADDR.ARPA", "107.100.IN-ADDR.ARPA", "108.100.IN-ADDR.ARPA", "109.100.IN-ADDR.ARPA", "110.100.IN-ADDR.ARPA", "111.100.IN-ADDR.ARPA", "112.100.IN-ADDR.ARPA", "113.100.IN-ADDR.ARPA", "114.100.IN-ADDR.ARPA", "115.100.IN-ADDR.ARPA", "116.100.IN-ADDR.ARPA", "117.100.IN-ADDR.ARPA", "118.100.IN-ADDR.ARPA", "119.100.IN-ADDR.ARPA", "120.100.IN-ADDR.ARPA", "121.100.IN-ADDR.ARPA", "122.100.IN-ADDR.ARPA", "123.100.IN-ADDR.ARPA", "124.100.IN-ADDR.ARPA", "125.100.IN-ADDR.ARPA", "126.100.IN-ADDR.ARPA", "127.100.IN-ADDR.ARPA", /* RFC 5735 and RFC 5737 */ "0.IN-ADDR.ARPA", /* THIS NETWORK */ "127.IN-ADDR.ARPA", /* LOOPBACK */ "254.169.IN-ADDR.ARPA", /* LINK LOCAL */ "2.0.192.IN-ADDR.ARPA", /* TEST NET */ "100.51.198.IN-ADDR.ARPA", /* TEST NET 2 */ "113.0.203.IN-ADDR.ARPA", /* TEST NET 3 */ "255.255.255.255.IN-ADDR.ARPA", /* BROADCAST */ /* Local IPv6 Unicast Addresses */ "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA", /* LOCALLY ASSIGNED LOCAL ADDRESS SCOPE */ "D.F.IP6.ARPA", "8.E.F.IP6.ARPA", /* LINK LOCAL */ "9.E.F.IP6.ARPA", /* LINK LOCAL */ "A.E.F.IP6.ARPA", /* LINK LOCAL */ "B.E.F.IP6.ARPA", /* LINK LOCAL */ /* Example Prefix, RFC 3849. */ "8.B.D.0.1.0.0.2.IP6.ARPA", NULL }; ISC_PLATFORM_NORETURN_PRE static void fatal(const char *msg, isc_result_t result) ISC_PLATFORM_NORETURN_POST; static void ns_server_reload(isc_task_t *task, isc_event_t *event); static isc_result_t ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, ns_listenelt_t **target); static isc_result_t ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, ns_listenlist_t **target); static isc_result_t configure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin, const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype); static isc_result_t configure_alternates(const cfg_obj_t *config, dns_view_t *view, const cfg_obj_t *alternates); static isc_result_t configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view, cfg_aclconfctx_t *aclconf, isc_boolean_t added); static isc_result_t add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx); static void end_reserved_dispatches(ns_server_t *server, isc_boolean_t all); static void newzone_cfgctx_destroy(void **cfgp); static isc_result_t putstr(isc_buffer_t *b, const char *str); isc_result_t add_comment(FILE *fp, const char *viewname); /*% * Configure a single view ACL at '*aclp'. Get its configuration from * 'vconfig' (for per-view configuration) and maybe from 'config' */ static isc_result_t configure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config, const char *aclname, const char *acltuplename, cfg_aclconfctx_t *actx, isc_mem_t *mctx, dns_acl_t **aclp) { isc_result_t result; const cfg_obj_t *maps[3]; const cfg_obj_t *aclobj = NULL; int i = 0; if (*aclp != NULL) dns_acl_detach(aclp); if (vconfig != NULL) maps[i++] = cfg_tuple_get(vconfig, "options"); if (config != NULL) { const cfg_obj_t *options = NULL; (void)cfg_map_get(config, "options", &options); if (options != NULL) maps[i++] = options; } maps[i] = NULL; (void)ns_config_get(maps, aclname, &aclobj); if (aclobj == NULL) /* * No value available. *aclp == NULL. */ return (ISC_R_SUCCESS); if (acltuplename != NULL) { /* * If the ACL is given in an optional tuple, retrieve it. * The parser should have ensured that a valid object be * returned. */ aclobj = cfg_tuple_get(aclobj, acltuplename); } result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx, actx, mctx, 0, aclp); return (result); } /*% * Configure a sortlist at '*aclp'. Essentially the same as * configure_view_acl() except it calls cfg_acl_fromconfig with a * nest_level value of 2. */ static isc_result_t configure_view_sortlist(const cfg_obj_t *vconfig, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, dns_acl_t **aclp) { isc_result_t result; const cfg_obj_t *maps[3]; const cfg_obj_t *aclobj = NULL; int i = 0; if (*aclp != NULL) dns_acl_detach(aclp); if (vconfig != NULL) maps[i++] = cfg_tuple_get(vconfig, "options"); if (config != NULL) { const cfg_obj_t *options = NULL; (void)cfg_map_get(config, "options", &options); if (options != NULL) maps[i++] = options; } maps[i] = NULL; (void)ns_config_get(maps, "sortlist", &aclobj); if (aclobj == NULL) return (ISC_R_SUCCESS); /* * Use a nest level of 3 for the "top level" of the sortlist; * this means each entry in the top three levels will be stored * as lists of separate, nested ACLs, rather than merged together * into IP tables as is usually done with ACLs. */ result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx, actx, mctx, 3, aclp); return (result); } static isc_result_t configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config, const char *confname, const char *conftuplename, isc_mem_t *mctx, dns_rbt_t **rbtp) { isc_result_t result; const cfg_obj_t *maps[3]; const cfg_obj_t *obj = NULL; const cfg_listelt_t *element; int i = 0; dns_fixedname_t fixed; dns_name_t *name; isc_buffer_t b; const char *str; const cfg_obj_t *nameobj; if (*rbtp != NULL) dns_rbt_destroy(rbtp); if (vconfig != NULL) maps[i++] = cfg_tuple_get(vconfig, "options"); if (config != NULL) { const cfg_obj_t *options = NULL; (void)cfg_map_get(config, "options", &options); if (options != NULL) maps[i++] = options; } maps[i] = NULL; (void)ns_config_get(maps, confname, &obj); if (obj == NULL) /* * No value available. *rbtp == NULL. */ return (ISC_R_SUCCESS); if (conftuplename != NULL) { obj = cfg_tuple_get(obj, conftuplename); if (cfg_obj_isvoid(obj)) return (ISC_R_SUCCESS); } result = dns_rbt_create(mctx, NULL, NULL, rbtp); if (result != ISC_R_SUCCESS) return (result); dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); for (element = cfg_list_first(obj); element != NULL; element = cfg_list_next(element)) { nameobj = cfg_listelt_value(element); str = cfg_obj_asstring(nameobj); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); /* * We don't need the node data, but need to set dummy data to * avoid a partial match with an empty node. For example, if * we have foo.example.com and bar.example.com, we'd get a match * for baz.example.com, which is not the expected result. * We simply use (void *)1 as the dummy data. */ result = dns_rbt_addname(*rbtp, name, (void *)1); if (result != ISC_R_SUCCESS) { cfg_obj_log(nameobj, ns_g_lctx, ISC_LOG_ERROR, "failed to add %s for %s: %s", str, confname, isc_result_totext(result)); goto cleanup; } } return (result); cleanup: dns_rbt_destroy(rbtp); return (result); } static isc_result_t dstkey_fromconfig(const cfg_obj_t *vconfig, const cfg_obj_t *key, isc_boolean_t managed, dst_key_t **target, isc_mem_t *mctx) { dns_rdataclass_t viewclass; dns_rdata_dnskey_t keystruct; isc_uint32_t flags, proto, alg; const char *keystr, *keynamestr; unsigned char keydata[4096]; isc_buffer_t keydatabuf; unsigned char rrdata[4096]; isc_buffer_t rrdatabuf; isc_region_t r; dns_fixedname_t fkeyname; dns_name_t *keyname; isc_buffer_t namebuf; isc_result_t result; dst_key_t *dstkey = NULL; INSIST(target != NULL && *target == NULL); flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags")); proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol")); alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm")); keyname = dns_fixedname_name(&fkeyname); keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); if (managed) { const char *initmethod; initmethod = cfg_obj_asstring(cfg_tuple_get(key, "init")); if (strcasecmp(initmethod, "initial-key") != 0) { cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR, "managed key '%s': " "invalid initialization method '%s'", keynamestr, initmethod); result = ISC_R_FAILURE; goto cleanup; } } if (vconfig == NULL) viewclass = dns_rdataclass_in; else { const cfg_obj_t *classobj = cfg_tuple_get(vconfig, "class"); CHECK(ns_config_getclass(classobj, dns_rdataclass_in, &viewclass)); } keystruct.common.rdclass = viewclass; keystruct.common.rdtype = dns_rdatatype_dnskey; /* * The key data in keystruct is not dynamically allocated. */ keystruct.mctx = NULL; ISC_LINK_INIT(&keystruct.common, link); if (flags > 0xffff) CHECKM(ISC_R_RANGE, "key flags"); if (proto > 0xff) CHECKM(ISC_R_RANGE, "key protocol"); if (alg > 0xff) CHECKM(ISC_R_RANGE, "key algorithm"); keystruct.flags = (isc_uint16_t)flags; keystruct.protocol = (isc_uint8_t)proto; keystruct.algorithm = (isc_uint8_t)alg; isc_buffer_init(&keydatabuf, keydata, sizeof(keydata)); isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); keystr = cfg_obj_asstring(cfg_tuple_get(key, "key")); CHECK(isc_base64_decodestring(keystr, &keydatabuf)); isc_buffer_usedregion(&keydatabuf, &r); keystruct.datalen = r.length; keystruct.data = r.base; if ((keystruct.algorithm == DST_ALG_RSASHA1 || keystruct.algorithm == DST_ALG_RSAMD5) && r.length > 1 && r.base[0] == 1 && r.base[1] == 3) cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING, "%s key '%s' has a weak exponent", managed ? "managed" : "trusted", keynamestr); CHECK(dns_rdata_fromstruct(NULL, keystruct.common.rdclass, keystruct.common.rdtype, &keystruct, &rrdatabuf)); dns_fixedname_init(&fkeyname); isc_buffer_constinit(&namebuf, keynamestr, strlen(keynamestr)); isc_buffer_add(&namebuf, strlen(keynamestr)); CHECK(dns_name_fromtext(keyname, &namebuf, dns_rootname, 0, NULL)); CHECK(dst_key_fromdns(keyname, viewclass, &rrdatabuf, mctx, &dstkey)); *target = dstkey; return (ISC_R_SUCCESS); cleanup: if (result == DST_R_NOCRYPTO) { cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR, "ignoring %s key for '%s': no crypto support", managed ? "managed" : "trusted", keynamestr); } else if (result == DST_R_UNSUPPORTEDALG) { cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING, "skipping %s key for '%s': %s", managed ? "managed" : "trusted", keynamestr, isc_result_totext(result)); } else { cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR, "configuring %s key for '%s': %s", managed ? "managed" : "trusted", keynamestr, isc_result_totext(result)); result = ISC_R_FAILURE; } if (dstkey != NULL) dst_key_free(&dstkey); return (result); } static isc_result_t load_view_keys(const cfg_obj_t *keys, const cfg_obj_t *vconfig, dns_view_t *view, isc_boolean_t managed, dns_name_t *keyname, isc_mem_t *mctx) { const cfg_listelt_t *elt, *elt2; const cfg_obj_t *key, *keylist; dst_key_t *dstkey = NULL; isc_result_t result; dns_keytable_t *secroots = NULL; CHECK(dns_view_getsecroots(view, &secroots)); for (elt = cfg_list_first(keys); elt != NULL; elt = cfg_list_next(elt)) { keylist = cfg_listelt_value(elt); for (elt2 = cfg_list_first(keylist); elt2 != NULL; elt2 = cfg_list_next(elt2)) { key = cfg_listelt_value(elt2); result = dstkey_fromconfig(vconfig, key, managed, &dstkey, mctx); if (result == DST_R_UNSUPPORTEDALG) { result = ISC_R_SUCCESS; continue; } if (result != ISC_R_SUCCESS) goto cleanup; /* * If keyname was specified, we only add that key. */ if (keyname != NULL && !dns_name_equal(keyname, dst_key_name(dstkey))) { dst_key_free(&dstkey); continue; } CHECK(dns_keytable_add(secroots, managed, &dstkey)); } } cleanup: if (dstkey != NULL) dst_key_free(&dstkey); if (secroots != NULL) dns_keytable_detach(&secroots); if (result == DST_R_NOCRYPTO) result = ISC_R_SUCCESS; return (result); } /*% * Configure DNSSEC keys for a view. * * The per-view configuration values and the server-global defaults are read * from 'vconfig' and 'config'. */ static isc_result_t configure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig, const cfg_obj_t *config, const cfg_obj_t *bindkeys, isc_boolean_t auto_dlv, isc_boolean_t auto_root, isc_mem_t *mctx) { isc_result_t result = ISC_R_SUCCESS; const cfg_obj_t *view_keys = NULL; const cfg_obj_t *global_keys = NULL; const cfg_obj_t *view_managed_keys = NULL; const cfg_obj_t *global_managed_keys = NULL; const cfg_obj_t *maps[4]; const cfg_obj_t *voptions = NULL; const cfg_obj_t *options = NULL; const cfg_obj_t *obj = NULL; const char *directory; int i = 0; /* We don't need trust anchors for the _bind view */ if (strcmp(view->name, "_bind") == 0 && view->rdclass == dns_rdataclass_chaos) { return (ISC_R_SUCCESS); } if (vconfig != NULL) { voptions = cfg_tuple_get(vconfig, "options"); if (voptions != NULL) { (void) cfg_map_get(voptions, "trusted-keys", &view_keys); (void) cfg_map_get(voptions, "managed-keys", &view_managed_keys); maps[i++] = voptions; } } if (config != NULL) { (void)cfg_map_get(config, "trusted-keys", &global_keys); (void)cfg_map_get(config, "managed-keys", &global_managed_keys); (void)cfg_map_get(config, "options", &options); if (options != NULL) { maps[i++] = options; } } maps[i++] = ns_g_defaults; maps[i] = NULL; result = dns_view_initsecroots(view, mctx); if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "couldn't create keytable"); return (ISC_R_UNEXPECTED); } if (auto_dlv && view->rdclass == dns_rdataclass_in) { const cfg_obj_t *builtin_keys = NULL; const cfg_obj_t *builtin_managed_keys = NULL; isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "using built-in DLV key for view %s", view->name); /* * If bind.keys exists, it overrides the managed-keys * clause hard-coded in ns_g_config. */ if (bindkeys != NULL) { (void)cfg_map_get(bindkeys, "trusted-keys", &builtin_keys); (void)cfg_map_get(bindkeys, "managed-keys", &builtin_managed_keys); } else { (void)cfg_map_get(ns_g_config, "trusted-keys", &builtin_keys); (void)cfg_map_get(ns_g_config, "managed-keys", &builtin_managed_keys); } if (builtin_keys != NULL) CHECK(load_view_keys(builtin_keys, vconfig, view, ISC_FALSE, view->dlv, mctx)); if (builtin_managed_keys != NULL) CHECK(load_view_keys(builtin_managed_keys, vconfig, view, ISC_TRUE, view->dlv, mctx)); } if (auto_root && view->rdclass == dns_rdataclass_in) { const cfg_obj_t *builtin_keys = NULL; const cfg_obj_t *builtin_managed_keys = NULL; isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "using built-in root key for view %s", view->name); /* * If bind.keys exists, it overrides the managed-keys * clause hard-coded in ns_g_config. */ if (bindkeys != NULL) { (void)cfg_map_get(bindkeys, "trusted-keys", &builtin_keys); (void)cfg_map_get(bindkeys, "managed-keys", &builtin_managed_keys); } else { (void)cfg_map_get(ns_g_config, "trusted-keys", &builtin_keys); (void)cfg_map_get(ns_g_config, "managed-keys", &builtin_managed_keys); } if (builtin_keys != NULL) CHECK(load_view_keys(builtin_keys, vconfig, view, ISC_FALSE, dns_rootname, mctx)); if (builtin_managed_keys != NULL) CHECK(load_view_keys(builtin_managed_keys, vconfig, view, ISC_TRUE, dns_rootname, mctx)); } CHECK(load_view_keys(view_keys, vconfig, view, ISC_FALSE, NULL, mctx)); CHECK(load_view_keys(view_managed_keys, vconfig, view, ISC_TRUE, NULL, mctx)); if (view->rdclass == dns_rdataclass_in) { CHECK(load_view_keys(global_keys, vconfig, view, ISC_FALSE, NULL, mctx)); CHECK(load_view_keys(global_managed_keys, vconfig, view, ISC_TRUE, NULL, mctx)); } /* * Add key zone for managed-keys. */ obj = NULL; (void)ns_config_get(maps, "managed-keys-directory", &obj); directory = (obj != NULL ? cfg_obj_asstring(obj) : NULL); if (directory != NULL) result = isc_file_isdirectory(directory); if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "invalid managed-keys-directory %s: %s", directory, isc_result_totext(result)); goto cleanup; } CHECK(add_keydata_zone(view, directory, ns_g_mctx)); cleanup: return (result); } static isc_result_t mustbesecure(const cfg_obj_t *mbs, dns_resolver_t *resolver) { const cfg_listelt_t *element; const cfg_obj_t *obj; const char *str; dns_fixedname_t fixed; dns_name_t *name; isc_boolean_t value; isc_result_t result; isc_buffer_t b; dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); for (element = cfg_list_first(mbs); element != NULL; element = cfg_list_next(element)) { obj = cfg_listelt_value(element); str = cfg_obj_asstring(cfg_tuple_get(obj, "name")); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); value = cfg_obj_asboolean(cfg_tuple_get(obj, "value")); CHECK(dns_resolver_setmustbesecure(resolver, name, value)); } result = ISC_R_SUCCESS; cleanup: return (result); } /*% * Get a dispatch appropriate for the resolver of a given view. */ static isc_result_t get_view_querysource_dispatch(const cfg_obj_t **maps, int af, dns_dispatch_t **dispatchp, isc_boolean_t is_firstview) { isc_result_t result = ISC_R_FAILURE; dns_dispatch_t *disp; isc_sockaddr_t sa; unsigned int attrs, attrmask; const cfg_obj_t *obj = NULL; unsigned int maxdispatchbuffers; switch (af) { case AF_INET: result = ns_config_get(maps, "query-source", &obj); INSIST(result == ISC_R_SUCCESS); break; case AF_INET6: result = ns_config_get(maps, "query-source-v6", &obj); INSIST(result == ISC_R_SUCCESS); break; default: INSIST(0); } sa = *(cfg_obj_assockaddr(obj)); INSIST(isc_sockaddr_pf(&sa) == af); /* * If we don't support this address family, we're done! */ switch (af) { case AF_INET: result = isc_net_probeipv4(); break; case AF_INET6: result = isc_net_probeipv6(); break; default: INSIST(0); } if (result != ISC_R_SUCCESS) return (ISC_R_SUCCESS); /* * Try to find a dispatcher that we can share. */ attrs = 0; attrs |= DNS_DISPATCHATTR_UDP; switch (af) { case AF_INET: attrs |= DNS_DISPATCHATTR_IPV4; break; case AF_INET6: attrs |= DNS_DISPATCHATTR_IPV6; break; } if (isc_sockaddr_getport(&sa) == 0) { attrs |= DNS_DISPATCHATTR_EXCLUSIVE; maxdispatchbuffers = 4096; } else { INSIST(obj != NULL); if (is_firstview) { cfg_obj_log(obj, ns_g_lctx, ISC_LOG_INFO, "using specific query-source port " "suppresses port randomization and can be " "insecure."); } maxdispatchbuffers = 1000; } attrmask = 0; attrmask |= DNS_DISPATCHATTR_UDP; attrmask |= DNS_DISPATCHATTR_TCP; attrmask |= DNS_DISPATCHATTR_IPV4; attrmask |= DNS_DISPATCHATTR_IPV6; disp = NULL; result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr, ns_g_taskmgr, &sa, 4096, maxdispatchbuffers, 32768, 16411, 16433, attrs, attrmask, &disp); if (result != ISC_R_SUCCESS) { isc_sockaddr_t any; char buf[ISC_SOCKADDR_FORMATSIZE]; switch (af) { case AF_INET: isc_sockaddr_any(&any); break; case AF_INET6: isc_sockaddr_any6(&any); break; } if (isc_sockaddr_equal(&sa, &any)) return (ISC_R_SUCCESS); isc_sockaddr_format(&sa, buf, sizeof(buf)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "could not get query source dispatcher (%s)", buf); return (result); } *dispatchp = disp; return (ISC_R_SUCCESS); } static isc_result_t configure_order(dns_order_t *order, const cfg_obj_t *ent) { dns_rdataclass_t rdclass; dns_rdatatype_t rdtype; const cfg_obj_t *obj; dns_fixedname_t fixed; unsigned int mode = 0; const char *str; isc_buffer_t b; isc_result_t result; isc_boolean_t addroot; result = ns_config_getclass(cfg_tuple_get(ent, "class"), dns_rdataclass_any, &rdclass); if (result != ISC_R_SUCCESS) return (result); result = ns_config_gettype(cfg_tuple_get(ent, "type"), dns_rdatatype_any, &rdtype); if (result != ISC_R_SUCCESS) return (result); obj = cfg_tuple_get(ent, "name"); if (cfg_obj_isstring(obj)) str = cfg_obj_asstring(obj); else str = "*"; addroot = ISC_TF(strcmp(str, "*") == 0); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); dns_fixedname_init(&fixed); result = dns_name_fromtext(dns_fixedname_name(&fixed), &b, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) return (result); obj = cfg_tuple_get(ent, "ordering"); INSIST(cfg_obj_isstring(obj)); str = cfg_obj_asstring(obj); if (!strcasecmp(str, "fixed")) mode = DNS_RDATASETATTR_FIXEDORDER; else if (!strcasecmp(str, "random")) mode = DNS_RDATASETATTR_RANDOMIZE; else if (!strcasecmp(str, "cyclic")) mode = 0; else INSIST(0); /* * "*" should match everything including the root (BIND 8 compat). * As dns_name_matcheswildcard(".", "*.") returns FALSE add a * explicit entry for "." when the name is "*". */ if (addroot) { result = dns_order_add(order, dns_rootname, rdtype, rdclass, mode); if (result != ISC_R_SUCCESS) return (result); } return (dns_order_add(order, dns_fixedname_name(&fixed), rdtype, rdclass, mode)); } static isc_result_t configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) { isc_netaddr_t na; dns_peer_t *peer; const cfg_obj_t *obj; const char *str; isc_result_t result; unsigned int prefixlen; cfg_obj_asnetprefix(cfg_map_getname(cpeer), &na, &prefixlen); peer = NULL; result = dns_peer_newprefix(mctx, &na, prefixlen, &peer); if (result != ISC_R_SUCCESS) return (result); obj = NULL; (void)cfg_map_get(cpeer, "bogus", &obj); if (obj != NULL) CHECK(dns_peer_setbogus(peer, cfg_obj_asboolean(obj))); obj = NULL; (void)cfg_map_get(cpeer, "provide-ixfr", &obj); if (obj != NULL) CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj))); obj = NULL; (void)cfg_map_get(cpeer, "request-ixfr", &obj); if (obj != NULL) CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj))); obj = NULL; (void)cfg_map_get(cpeer, "request-nsid", &obj); if (obj != NULL) CHECK(dns_peer_setrequestnsid(peer, cfg_obj_asboolean(obj))); obj = NULL; (void)cfg_map_get(cpeer, "edns", &obj); if (obj != NULL) CHECK(dns_peer_setsupportedns(peer, cfg_obj_asboolean(obj))); obj = NULL; (void)cfg_map_get(cpeer, "edns-udp-size", &obj); if (obj != NULL) { isc_uint32_t udpsize = cfg_obj_asuint32(obj); if (udpsize < 512) udpsize = 512; if (udpsize > 4096) udpsize = 4096; CHECK(dns_peer_setudpsize(peer, (isc_uint16_t)udpsize)); } obj = NULL; (void)cfg_map_get(cpeer, "max-udp-size", &obj); if (obj != NULL) { isc_uint32_t udpsize = cfg_obj_asuint32(obj); if (udpsize < 512) udpsize = 512; if (udpsize > 4096) udpsize = 4096; CHECK(dns_peer_setmaxudp(peer, (isc_uint16_t)udpsize)); } obj = NULL; (void)cfg_map_get(cpeer, "transfers", &obj); if (obj != NULL) CHECK(dns_peer_settransfers(peer, cfg_obj_asuint32(obj))); obj = NULL; (void)cfg_map_get(cpeer, "transfer-format", &obj); if (obj != NULL) { str = cfg_obj_asstring(obj); if (strcasecmp(str, "many-answers") == 0) CHECK(dns_peer_settransferformat(peer, dns_many_answers)); else if (strcasecmp(str, "one-answer") == 0) CHECK(dns_peer_settransferformat(peer, dns_one_answer)); else INSIST(0); } obj = NULL; (void)cfg_map_get(cpeer, "keys", &obj); if (obj != NULL) { result = dns_peer_setkeybycharp(peer, cfg_obj_asstring(obj)); if (result != ISC_R_SUCCESS) goto cleanup; } obj = NULL; if (na.family == AF_INET) (void)cfg_map_get(cpeer, "transfer-source", &obj); else (void)cfg_map_get(cpeer, "transfer-source-v6", &obj); if (obj != NULL) { result = dns_peer_settransfersource(peer, cfg_obj_assockaddr(obj)); if (result != ISC_R_SUCCESS) goto cleanup; ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj)); } obj = NULL; if (na.family == AF_INET) (void)cfg_map_get(cpeer, "notify-source", &obj); else (void)cfg_map_get(cpeer, "notify-source-v6", &obj); if (obj != NULL) { result = dns_peer_setnotifysource(peer, cfg_obj_assockaddr(obj)); if (result != ISC_R_SUCCESS) goto cleanup; ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj)); } obj = NULL; if (na.family == AF_INET) (void)cfg_map_get(cpeer, "query-source", &obj); else (void)cfg_map_get(cpeer, "query-source-v6", &obj); if (obj != NULL) { result = dns_peer_setquerysource(peer, cfg_obj_assockaddr(obj)); if (result != ISC_R_SUCCESS) goto cleanup; ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj)); } *peerp = peer; return (ISC_R_SUCCESS); cleanup: dns_peer_detach(&peer); return (result); } static isc_result_t disable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) { isc_result_t result; const cfg_obj_t *algorithms; const cfg_listelt_t *element; const char *str; dns_fixedname_t fixed; dns_name_t *name; isc_buffer_t b; dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); str = cfg_obj_asstring(cfg_tuple_get(disabled, "name")); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); algorithms = cfg_tuple_get(disabled, "algorithms"); for (element = cfg_list_first(algorithms); element != NULL; element = cfg_list_next(element)) { isc_textregion_t r; dns_secalg_t alg; DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base); r.length = strlen(r.base); result = dns_secalg_fromtext(&alg, &r); if (result != ISC_R_SUCCESS) { isc_uint8_t ui; result = isc_parse_uint8(&ui, r.base, 10); alg = ui; } if (result != ISC_R_SUCCESS) { cfg_obj_log(cfg_listelt_value(element), ns_g_lctx, ISC_LOG_ERROR, "invalid algorithm"); CHECK(result); } CHECK(dns_resolver_disable_algorithm(resolver, name, alg)); } cleanup: return (result); } static isc_boolean_t on_disable_list(const cfg_obj_t *disablelist, dns_name_t *zonename) { const cfg_listelt_t *element; dns_fixedname_t fixed; dns_name_t *name; isc_result_t result; const cfg_obj_t *value; const char *str; isc_buffer_t b; dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); for (element = cfg_list_first(disablelist); element != NULL; element = cfg_list_next(element)) { value = cfg_listelt_value(element); str = cfg_obj_asstring(value); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); if (dns_name_equal(name, zonename)) return (ISC_TRUE); } return (ISC_FALSE); } static isc_result_t check_dbtype(dns_zone_t *zone, unsigned int dbtypec, const char **dbargv, isc_mem_t *mctx) { char **argv = NULL; unsigned int i; isc_result_t result = ISC_R_SUCCESS; CHECK(dns_zone_getdbtype(zone, &argv, mctx)); /* * Check that all the arguments match. */ for (i = 0; i < dbtypec; i++) if (argv[i] == NULL || strcmp(argv[i], dbargv[i]) != 0) { CHECK(ISC_R_FAILURE); break; } /* * Check that there are not extra arguments. */ if (i == dbtypec && argv[i] != NULL) result = ISC_R_FAILURE; cleanup: isc_mem_free(mctx, argv); return (result); } static isc_result_t setquerystats(dns_zone_t *zone, isc_mem_t *mctx, dns_zonestat_level_t level) { isc_result_t result; isc_stats_t *zoneqrystats; dns_zone_setstatlevel(zone, level); zoneqrystats = NULL; if (level == dns_zonestat_full) { result = isc_stats_create(mctx, &zoneqrystats, dns_nsstatscounter_max); if (result != ISC_R_SUCCESS) return (result); } dns_zone_setrequeststats(zone, zoneqrystats); if (zoneqrystats != NULL) isc_stats_detach(&zoneqrystats); return (ISC_R_SUCCESS); } static ns_cache_t * cachelist_find(ns_cachelist_t *cachelist, const char *cachename) { ns_cache_t *nsc; for (nsc = ISC_LIST_HEAD(*cachelist); nsc != NULL; nsc = ISC_LIST_NEXT(nsc, link)) { if (strcmp(dns_cache_getname(nsc->cache), cachename) == 0) return (nsc); } return (NULL); } static isc_boolean_t cache_reusable(dns_view_t *originview, dns_view_t *view, isc_boolean_t new_zero_no_soattl) { if (originview->checknames != view->checknames || dns_resolver_getzeronosoattl(originview->resolver) != new_zero_no_soattl || originview->acceptexpired != view->acceptexpired || originview->enablevalidation != view->enablevalidation || originview->maxcachettl != view->maxcachettl || originview->maxncachettl != view->maxncachettl) { return (ISC_FALSE); } return (ISC_TRUE); } static isc_boolean_t cache_sharable(dns_view_t *originview, dns_view_t *view, isc_boolean_t new_zero_no_soattl, unsigned int new_cleaning_interval, isc_uint64_t new_max_cache_size) { /* * If the cache cannot even reused for the same view, it cannot be * shared with other views. */ if (!cache_reusable(originview, view, new_zero_no_soattl)) return (ISC_FALSE); /* * Check other cache related parameters that must be consistent among * the sharing views. */ if (dns_cache_getcleaninginterval(originview->cache) != new_cleaning_interval || dns_cache_getcachesize(originview->cache) != new_max_cache_size) { return (ISC_FALSE); } return (ISC_TRUE); } /* * Callback from DLZ configure when the driver sets up a writeable zone */ static isc_result_t dlzconfigure_callback(dns_view_t *view, dns_zone_t *zone) { dns_name_t *origin = dns_zone_getorigin(zone); dns_rdataclass_t zclass = view->rdclass; isc_result_t result; result = dns_zonemgr_managezone(ns_g_server->zonemgr, zone); if (result != ISC_R_SUCCESS) return (result); dns_zone_setstats(zone, ns_g_server->zonestats); return (ns_zone_configure_writeable_dlz(view->dlzdatabase, zone, zclass, origin)); } static isc_result_t dns64_reverse(dns_view_t *view, isc_mem_t *mctx, isc_netaddr_t *na, unsigned int prefixlen, const char *server, const char *contact) { char *cp; char reverse[48+sizeof("ip6.arpa.")]; const char *dns64_dbtype[4] = { "_dns64", "dns64", ".", "." }; const char *sep = ": view "; const char *viewname = view->name; const unsigned char *s6; dns_fixedname_t fixed; dns_name_t *name; dns_zone_t *zone = NULL; int dns64_dbtypec = 4; isc_buffer_t b; isc_result_t result; REQUIRE(prefixlen == 32 || prefixlen == 40 || prefixlen == 48 || prefixlen == 56 || prefixlen == 64 || prefixlen == 96); if (!strcmp(viewname, "_default")) { sep = ""; viewname = ""; } /* * Construct the reverse name of the zone. */ cp = reverse; s6 = na->type.in6.s6_addr; while (prefixlen > 0) { prefixlen -= 8; sprintf(cp, "%x.%x.", s6[prefixlen/8] & 0xf, (s6[prefixlen/8] >> 4) & 0xf); cp += 4; } strcat(cp, "ip6.arpa."); /* * Create the actual zone. */ if (server != NULL) dns64_dbtype[2] = server; if (contact != NULL) dns64_dbtype[3] = contact; dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); isc_buffer_constinit(&b, reverse, strlen(reverse)); isc_buffer_add(&b, strlen(reverse)); CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); CHECK(dns_zone_create(&zone, mctx)); CHECK(dns_zone_setorigin(zone, name)); dns_zone_setview(zone, view); CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone)); dns_zone_setclass(zone, view->rdclass); dns_zone_settype(zone, dns_zone_master); dns_zone_setstats(zone, ns_g_server->zonestats); CHECK(dns_zone_setdbtype(zone, dns64_dbtypec, dns64_dbtype)); if (view->queryacl != NULL) dns_zone_setqueryacl(zone, view->queryacl); if (view->queryonacl != NULL) dns_zone_setqueryonacl(zone, view->queryonacl); dns_zone_setdialup(zone, dns_dialuptype_no); dns_zone_setnotifytype(zone, dns_notifytype_no); dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE); CHECK(setquerystats(zone, mctx, dns_zonestat_none)); /* XXXMPA */ CHECK(dns_view_addzone(view, zone)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dns64 reverse zone%s%s: %s", sep, viewname, reverse); cleanup: if (zone != NULL) dns_zone_detach(&zone); return (result); } static isc_result_t configure_rpz_name(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name, const char *str, const char *msg) { isc_result_t result; result = dns_name_fromstring(name, str, DNS_NAME_DOWNCASE, view->mctx); if (result != ISC_R_SUCCESS) cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "invalid %s '%s'", msg, str); return (result); } static isc_result_t configure_rpz_name2(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name, const char *str, const dns_name_t *origin) { isc_result_t result; result = dns_name_fromstring2(name, str, origin, DNS_NAME_DOWNCASE, view->mctx); if (result != ISC_R_SUCCESS) cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "invalid zone '%s'", str); return (result); } static isc_result_t configure_rpz(dns_view_t *view, const cfg_listelt_t *element, isc_boolean_t recursive_only_def, dns_ttl_t ttl_def) { const cfg_obj_t *rpz_obj, *obj; const char *str; dns_rpz_zone_t *old, *new; isc_result_t result; rpz_obj = cfg_listelt_value(element); new = isc_mem_get(view->mctx, sizeof(*new)); if (new == NULL) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "no memory for response policy zones"); return (ISC_R_NOMEMORY); } memset(new, 0, sizeof(*new)); dns_name_init(&new->origin, NULL); dns_name_init(&new->nsdname, NULL); dns_name_init(&new->passthru, NULL); dns_name_init(&new->cname, NULL); ISC_LIST_INITANDAPPEND(view->rpz_zones, new, link); obj = cfg_tuple_get(rpz_obj, "recursive-only"); if (cfg_obj_isvoid(obj)) { new->recursive_only = recursive_only_def; } else { new->recursive_only = cfg_obj_asboolean(obj); } if (!new->recursive_only) view->rpz_recursive_only = ISC_FALSE; obj = cfg_tuple_get(rpz_obj, "max-policy-ttl"); if (cfg_obj_isuint32(obj)) { new->max_policy_ttl = cfg_obj_asuint32(obj); } else { new->max_policy_ttl = ttl_def; } str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "zone name")); result = configure_rpz_name(view, rpz_obj, &new->origin, str, "zone"); if (result != ISC_R_SUCCESS) return (result); if (dns_name_equal(&new->origin, dns_rootname)) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "invalid zone name '%s'", str); return (DNS_R_EMPTYLABEL); } for (old = ISC_LIST_HEAD(view->rpz_zones); old != new; old = ISC_LIST_NEXT(old, link)) { ++new->num; if (dns_name_equal(&old->origin, &new->origin)) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "duplicate '%s'", str); result = DNS_R_DUPLICATE; return (result); } } result = configure_rpz_name2(view, rpz_obj, &new->nsdname, DNS_RPZ_NSDNAME_ZONE, &new->origin); if (result != ISC_R_SUCCESS) return (result); result = configure_rpz_name(view, rpz_obj, &new->passthru, DNS_RPZ_PASSTHRU_ZONE, "zone"); if (result != ISC_R_SUCCESS) return (result); obj = cfg_tuple_get(rpz_obj, "policy"); if (cfg_obj_isvoid(obj)) { new->policy = DNS_RPZ_POLICY_GIVEN; } else { str = cfg_obj_asstring(cfg_tuple_get(obj, "policy name")); new->policy = dns_rpz_str2policy(str); INSIST(new->policy != DNS_RPZ_POLICY_ERROR); if (new->policy == DNS_RPZ_POLICY_CNAME) { str = cfg_obj_asstring(cfg_tuple_get(obj, "cname")); result = configure_rpz_name(view, rpz_obj, &new->cname, str, "cname"); if (result != ISC_R_SUCCESS) return (result); } } return (ISC_R_SUCCESS); } #ifdef USE_RRL #define CHECK_RRL(cond, pat, val1, val2) \ do { \ if (!(cond)) { \ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, \ pat, val1, val2); \ result = ISC_R_RANGE; \ goto cleanup; \ } \ } while (0) #define CHECK_RRL_RATE(rate, def, max_rate, name) \ do { \ obj = NULL; \ rrl->rate.str = name; \ result = cfg_map_get(map, name, &obj); \ if (result == ISC_R_SUCCESS) { \ rrl->rate.r = cfg_obj_asuint32(obj); \ CHECK_RRL(rrl->rate.r <= max_rate, \ name" %d > %d", \ rrl->rate.r, max_rate); \ } else { \ rrl->rate.r = def; \ } \ rrl->rate.scaled = rrl->rate.r; \ } while (0) static isc_result_t configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) { const cfg_obj_t *obj; dns_rrl_t *rrl; isc_result_t result; int min_entries, i, j; /* * Most DNS servers have few clients, but intentinally open * recursive and authoritative servers often have many. * So start with a small number of entries unless told otherwise * to reduce cold-start costs. */ min_entries = 500; obj = NULL; result = cfg_map_get(map, "min-table-size", &obj); if (result == ISC_R_SUCCESS) { min_entries = cfg_obj_asuint32(obj); if (min_entries < 1) min_entries = 1; } result = dns_rrl_init(&rrl, view, min_entries); if (result != ISC_R_SUCCESS) return (result); i = ISC_MAX(20000, min_entries); obj = NULL; result = cfg_map_get(map, "max-table-size", &obj); if (result == ISC_R_SUCCESS) { i = cfg_obj_asuint32(obj); CHECK_RRL(i >= min_entries, "max-table-size %d < min-table-size %d", i, min_entries); } rrl->max_entries = i; CHECK_RRL_RATE(responses_per_second, 0, DNS_RRL_MAX_RATE, "responses-per-second"); CHECK_RRL_RATE(referrals_per_second, rrl->responses_per_second.r, DNS_RRL_MAX_RATE, "referrals-per-second"); CHECK_RRL_RATE(nodata_per_second, rrl->responses_per_second.r, DNS_RRL_MAX_RATE, "nodata-per-second"); CHECK_RRL_RATE(nxdomains_per_second, rrl->responses_per_second.r, DNS_RRL_MAX_RATE, "nxdomains-per-second"); CHECK_RRL_RATE(errors_per_second, rrl->responses_per_second.r, DNS_RRL_MAX_RATE, "errors-per-second"); CHECK_RRL_RATE(all_per_second, 0, DNS_RRL_MAX_RATE, "all-per-second"); CHECK_RRL_RATE(slip, 2, DNS_RRL_MAX_SLIP, "slip"); i = 15; obj = NULL; result = cfg_map_get(map, "window", &obj); if (result == ISC_R_SUCCESS) { i = cfg_obj_asuint32(obj); CHECK_RRL(i >= 1 && i <= DNS_RRL_MAX_WINDOW, "window %d < 1 or > %d", i, DNS_RRL_MAX_WINDOW); } rrl->window = i; i = 0; obj = NULL; result = cfg_map_get(map, "qps-scale", &obj); if (result == ISC_R_SUCCESS) { i = cfg_obj_asuint32(obj); CHECK_RRL(i >= 1, "invalid 'qps-scale %d'%s", i, ""); } rrl->qps_scale = i; rrl->qps = 1.0; i = 24; obj = NULL; result = cfg_map_get(map, "ipv4-prefix-length", &obj); if (result == ISC_R_SUCCESS) { i = cfg_obj_asuint32(obj); CHECK_RRL(i >= 8 && i <= 32, "invalid 'ipv4-prefix-length %d'%s", i, ""); } rrl->ipv4_prefixlen = i; if (i == 32) rrl->ipv4_mask = 0xffffffff; else rrl->ipv4_mask = htonl(0xffffffff << (32-i)); i = 56; obj = NULL; result = cfg_map_get(map, "ipv6-prefix-length", &obj); if (result == ISC_R_SUCCESS) { i = cfg_obj_asuint32(obj); CHECK_RRL(i >= 16 && i <= DNS_RRL_MAX_PREFIX, "ipv6-prefix-length %d < 16 or > %d", i, DNS_RRL_MAX_PREFIX); } rrl->ipv6_prefixlen = i; for (j = 0; j < 4; ++j) { if (i <= 0) { rrl->ipv6_mask[j] = 0; } else if (i < 32) { rrl->ipv6_mask[j] = htonl(0xffffffff << (32-i)); } else { rrl->ipv6_mask[j] = 0xffffffff; } i -= 32; } obj = NULL; result = cfg_map_get(map, "exempt-clients", &obj); if (result == ISC_R_SUCCESS) { result = cfg_acl_fromconfig(obj, config, ns_g_lctx, ns_g_aclconfctx, ns_g_mctx, 0, &rrl->exempt); CHECK_RRL(result == ISC_R_SUCCESS, "invalid %s%s", "address match list", ""); } obj = NULL; result = cfg_map_get(map, "log-only", &obj); if (result == ISC_R_SUCCESS && cfg_obj_asboolean(obj)) rrl->log_only = ISC_TRUE; else rrl->log_only = ISC_FALSE; return (ISC_R_SUCCESS); cleanup: dns_rrl_view_destroy(view); return (result); } #endif /* USE_RRL */ static isc_result_t add_soa(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, dns_name_t *origin, dns_name_t *contact) { dns_dbnode_t *node = NULL; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdatalist_t rdatalist; dns_rdataset_t rdataset; isc_result_t result; unsigned char buf[DNS_SOA_BUFFERSIZE]; dns_rdataset_init(&rdataset); dns_rdatalist_init(&rdatalist); CHECK(dns_soa_buildrdata(origin, contact, dns_db_class(db), 0, 28800, 7200, 604800, 86400, buf, &rdata)); rdatalist.type = rdata.type; rdatalist.covers = 0; rdatalist.rdclass = rdata.rdclass; rdatalist.ttl = 86400; ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset)); CHECK(dns_db_findnode(db, name, ISC_TRUE, &node)); CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL)); cleanup: if (node != NULL) dns_db_detachnode(db, &node); return (result); } static isc_result_t add_ns(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, dns_name_t *nsname) { dns_dbnode_t *node = NULL; dns_rdata_ns_t ns; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdatalist_t rdatalist; dns_rdataset_t rdataset; isc_result_t result; isc_buffer_t b; unsigned char buf[DNS_NAME_MAXWIRE]; isc_buffer_init(&b, buf, sizeof(buf)); dns_rdataset_init(&rdataset); dns_rdatalist_init(&rdatalist); ns.common.rdtype = dns_rdatatype_ns; ns.common.rdclass = dns_db_class(db); ns.mctx = NULL; dns_name_init(&ns.name, NULL); dns_name_clone(nsname, &ns.name); CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db), dns_rdatatype_ns, &ns, &b)); rdatalist.type = rdata.type; rdatalist.covers = 0; rdatalist.rdclass = rdata.rdclass; rdatalist.ttl = 86400; ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset)); CHECK(dns_db_findnode(db, name, ISC_TRUE, &node)); CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL)); cleanup: if (node != NULL) dns_db_detachnode(db, &node); return (result); } static isc_result_t create_empty_zone(dns_zone_t *zone, dns_name_t *name, dns_view_t *view, const cfg_obj_t *zonelist, const char **empty_dbtype, int empty_dbtypec, dns_zonestat_level_t statlevel) { char namebuf[DNS_NAME_FORMATSIZE]; const cfg_listelt_t *element; const cfg_obj_t *obj; const cfg_obj_t *zconfig; const cfg_obj_t *zoptions; const char *rbt_dbtype[4] = { "rbt" }; const char *sep = ": view "; const char *str; const char *viewname = view->name; dns_db_t *db = NULL; dns_dbversion_t *version = NULL; dns_fixedname_t cfixed; dns_fixedname_t fixed; dns_fixedname_t nsfixed; dns_name_t *contact; dns_name_t *ns; dns_name_t *zname; dns_zone_t *myzone = NULL; int rbt_dbtypec = 1; isc_result_t result; dns_namereln_t namereln; int order; unsigned int nlabels; dns_fixedname_init(&fixed); zname = dns_fixedname_name(&fixed); dns_fixedname_init(&nsfixed); ns = dns_fixedname_name(&nsfixed); dns_fixedname_init(&cfixed); contact = dns_fixedname_name(&cfixed); /* * Look for forward "zones" beneath this empty zone and if so * create a custom db for the empty zone. */ for (element = cfg_list_first(zonelist); element != NULL; element = cfg_list_next(element)) { zconfig = cfg_listelt_value(element); str = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); CHECK(dns_name_fromstring(zname, str, 0, NULL)); namereln = dns_name_fullcompare(zname, name, &order, &nlabels); if (namereln != dns_namereln_subdomain) continue; zoptions = cfg_tuple_get(zconfig, "options"); obj = NULL; (void)cfg_map_get(zoptions, "type", &obj); INSIST(obj != NULL); if (strcasecmp(cfg_obj_asstring(obj), "forward") == 0) { obj = NULL; (void)cfg_map_get(zoptions, "forward", &obj); if (obj == NULL) continue; if (strcasecmp(cfg_obj_asstring(obj), "only") != 0) continue; } if (db == NULL) { CHECK(dns_db_create(view->mctx, "rbt", name, dns_dbtype_zone, view->rdclass, 0, NULL, &db)); CHECK(dns_db_newversion(db, &version)); if (strcmp(empty_dbtype[2], "@") == 0) dns_name_clone(name, ns); else CHECK(dns_name_fromstring(ns, empty_dbtype[2], 0, NULL)); CHECK(dns_name_fromstring(contact, empty_dbtype[3], 0, NULL)); CHECK(add_soa(db, version, name, ns, contact)); CHECK(add_ns(db, version, name, ns)); } CHECK(add_ns(db, version, zname, dns_rootname)); } /* * Is the existing zone the ok to use? */ if (zone != NULL) { unsigned int typec; const char **dbargv; if (db != NULL) { typec = rbt_dbtypec; dbargv = rbt_dbtype; } else { typec = empty_dbtypec; dbargv = empty_dbtype; } result = check_dbtype(zone, typec, dbargv, view->mctx); if (result != ISC_R_SUCCESS) zone = NULL; if (zone != NULL && dns_zone_gettype(zone) != dns_zone_master) zone = NULL; if (zone != NULL && dns_zone_getfile(zone) != NULL) zone = NULL; if (zone != NULL) { dns_zone_getraw(zone, &myzone); if (myzone != NULL) { dns_zone_detach(&myzone); zone = NULL; } } } if (zone == NULL) { CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr, &myzone)); zone = myzone; CHECK(dns_zone_setorigin(zone, name)); CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone)); if (db == NULL) CHECK(dns_zone_setdbtype(zone, empty_dbtypec, empty_dbtype)); dns_zone_setclass(zone, view->rdclass); dns_zone_settype(zone, dns_zone_master); dns_zone_setstats(zone, ns_g_server->zonestats); } dns_zone_setoption(zone, ~DNS_ZONEOPT_NOCHECKNS, ISC_FALSE); dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE); dns_zone_setnotifytype(zone, dns_notifytype_no); dns_zone_setdialup(zone, dns_dialuptype_no); if (view->queryacl) dns_zone_setqueryacl(zone, view->queryacl); else dns_zone_clearqueryacl(zone); if (view->queryonacl) dns_zone_setqueryonacl(zone, view->queryonacl); else dns_zone_clearqueryonacl(zone); dns_zone_clearupdateacl(zone); dns_zone_clearxfracl(zone); CHECK(setquerystats(zone, view->mctx, statlevel)); if (db != NULL) { dns_db_closeversion(db, &version, ISC_TRUE); CHECK(dns_zone_replacedb(zone, db, ISC_FALSE)); } dns_zone_setview(zone, view); CHECK(dns_view_addzone(view, zone)); if (!strcmp(viewname, "_default")) { sep = ""; viewname = ""; } dns_name_format(name, namebuf, sizeof(namebuf)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "automatic empty zone%s%s: %s", sep, viewname, namebuf); cleanup: if (myzone != NULL) dns_zone_detach(&myzone); if (version != NULL) dns_db_closeversion(db, &version, ISC_FALSE); if (db != NULL) dns_db_detach(&db); return (result); } /* * Configure 'view' according to 'vconfig', taking defaults from 'config' * where values are missing in 'vconfig'. * * When configuring the default view, 'vconfig' will be NULL and the * global defaults in 'config' used exclusively. */ static isc_result_t configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, ns_cachelist_t *cachelist, const cfg_obj_t *bindkeys, isc_mem_t *mctx, cfg_aclconfctx_t *actx, isc_boolean_t need_hints) { const cfg_obj_t *maps[4]; const cfg_obj_t *cfgmaps[3]; const cfg_obj_t *optionmaps[3]; const cfg_obj_t *options = NULL; const cfg_obj_t *voptions = NULL; const cfg_obj_t *forwardtype; const cfg_obj_t *forwarders; const cfg_obj_t *alternates; const cfg_obj_t *zonelist; const cfg_obj_t *dlz; unsigned int dlzargc; char **dlzargv; const cfg_obj_t *disabled; const cfg_obj_t *obj; const cfg_listelt_t *element; in_port_t port; dns_cache_t *cache = NULL; isc_result_t result; unsigned int cleaning_interval; size_t max_cache_size; size_t max_acache_size; size_t max_adb_size; isc_uint32_t lame_ttl; dns_tsig_keyring_t *ring = NULL; dns_view_t *pview = NULL; /* Production view */ isc_mem_t *cmctx = NULL, *hmctx = NULL; dns_dispatch_t *dispatch4 = NULL; dns_dispatch_t *dispatch6 = NULL; isc_boolean_t reused_cache = ISC_FALSE; isc_boolean_t shared_cache = ISC_FALSE; int i = 0, j = 0, k = 0; const char *str; const char *cachename = NULL; dns_order_t *order = NULL; isc_uint32_t udpsize; isc_uint32_t maxbits; unsigned int resopts = 0; dns_zone_t *zone = NULL; isc_uint32_t max_clients_per_query; isc_boolean_t empty_zones_enable; const cfg_obj_t *disablelist = NULL; isc_stats_t *resstats = NULL; dns_stats_t *resquerystats = NULL; isc_boolean_t auto_dlv = ISC_FALSE; isc_boolean_t auto_root = ISC_FALSE; ns_cache_t *nsc; isc_boolean_t zero_no_soattl; dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL; unsigned int query_timeout, ndisp; struct cfg_context *nzctx; dns_rpz_zone_t *rpz; REQUIRE(DNS_VIEW_VALID(view)); if (config != NULL) (void)cfg_map_get(config, "options", &options); /* * maps: view options, options, defaults * cfgmaps: view options, config * optionmaps: view options, options */ if (vconfig != NULL) { voptions = cfg_tuple_get(vconfig, "options"); maps[i++] = voptions; optionmaps[j++] = voptions; cfgmaps[k++] = voptions; } if (options != NULL) { maps[i++] = options; optionmaps[j++] = options; } maps[i++] = ns_g_defaults; maps[i] = NULL; optionmaps[j] = NULL; if (config != NULL) cfgmaps[k++] = config; cfgmaps[k] = NULL; /* * Set the view's port number for outgoing queries. */ CHECKM(ns_config_getport(config, &port), "port"); dns_view_setdstport(view, port); /* * Create additional cache for this view and zones under the view * if explicitly enabled. * XXX950 default to on. */ obj = NULL; (void)ns_config_get(maps, "acache-enable", &obj); if (obj != NULL && cfg_obj_asboolean(obj)) { cmctx = NULL; CHECK(isc_mem_create(0, 0, &cmctx)); CHECK(dns_acache_create(&view->acache, cmctx, ns_g_taskmgr, ns_g_timermgr)); isc_mem_setname(cmctx, "acache", NULL); isc_mem_detach(&cmctx); } if (view->acache != NULL) { obj = NULL; result = ns_config_get(maps, "acache-cleaning-interval", &obj); INSIST(result == ISC_R_SUCCESS); dns_acache_setcleaninginterval(view->acache, cfg_obj_asuint32(obj) * 60); obj = NULL; result = ns_config_get(maps, "max-acache-size", &obj); INSIST(result == ISC_R_SUCCESS); if (cfg_obj_isstring(obj)) { str = cfg_obj_asstring(obj); INSIST(strcasecmp(str, "unlimited") == 0); max_acache_size = ISC_UINT32_MAX; } else { isc_resourcevalue_t value; value = cfg_obj_asuint64(obj); if (value > SIZE_MAX) { cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING, "'max-acache-size " "%" ISC_PRINT_QUADFORMAT "u' " "is too large for this " "system; reducing to %lu", value, (unsigned long)SIZE_MAX); value = SIZE_MAX; } max_acache_size = (size_t) value; } dns_acache_setcachesize(view->acache, max_acache_size); } CHECK(configure_view_acl(vconfig, config, "allow-query", NULL, actx, ns_g_mctx, &view->queryacl)); if (view->queryacl == NULL) { CHECK(configure_view_acl(NULL, ns_g_config, "allow-query", NULL, actx, ns_g_mctx, &view->queryacl)); } /* * Make the list of response policy zone names for a view that * is used for real lookups and so cares about hints. */ obj = NULL; if (view->rdclass == dns_rdataclass_in && need_hints && ns_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) { const cfg_obj_t *rpz_obj; isc_boolean_t recursive_only_def; dns_ttl_t ttl_def; rpz_obj = cfg_tuple_get(obj, "recursive-only"); if (!cfg_obj_isvoid(rpz_obj) && !cfg_obj_asboolean(rpz_obj)) recursive_only_def = ISC_FALSE; else recursive_only_def = ISC_TRUE; rpz_obj = cfg_tuple_get(obj, "break-dnssec"); if (!cfg_obj_isvoid(rpz_obj) && cfg_obj_asboolean(rpz_obj)) view->rpz_break_dnssec = ISC_TRUE; else view->rpz_break_dnssec = ISC_FALSE; rpz_obj = cfg_tuple_get(obj, "max-policy-ttl"); if (cfg_obj_isuint32(rpz_obj)) ttl_def = cfg_obj_asuint32(rpz_obj); else ttl_def = DNS_RPZ_MAX_TTL_DEFAULT; rpz_obj = cfg_tuple_get(obj, "min-ns-dots"); if (cfg_obj_isuint32(rpz_obj)) view->rpz_min_ns_labels = cfg_obj_asuint32(rpz_obj) + 1; else view->rpz_min_ns_labels = 2; element = cfg_list_first(cfg_tuple_get(obj, "zone list")); while (element != NULL) { result = configure_rpz(view, element, recursive_only_def, ttl_def); if (result != ISC_R_SUCCESS) goto cleanup; element = cfg_list_next(element); } } /* * Configure the zones. */ zonelist = NULL; if (voptions != NULL) (void)cfg_map_get(voptions, "zone", &zonelist); else (void)cfg_map_get(config, "zone", &zonelist); /* * Load zone configuration */ for (element = cfg_list_first(zonelist); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *zconfig = cfg_listelt_value(element); CHECK(configure_zone(config, zconfig, vconfig, mctx, view, actx, ISC_FALSE)); } for (rpz = ISC_LIST_HEAD(view->rpz_zones); rpz != NULL; rpz = ISC_LIST_NEXT(rpz, link)) { if (!rpz->defined) { char namebuf[DNS_NAME_FORMATSIZE]; dns_name_format(&rpz->origin, namebuf, sizeof(namebuf)); cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "'%s' is not a master or slave zone", namebuf); result = ISC_R_NOTFOUND; goto cleanup; } } /* * If we're allowing added zones, then load zone configuration * from the newzone file for zones that were added during previous * runs. */ nzctx = view->new_zone_config; if (nzctx != NULL && nzctx->nzconfig != NULL) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "loading additional zones for view '%s'", view->name); zonelist = NULL; cfg_map_get(nzctx->nzconfig, "zone", &zonelist); for (element = cfg_list_first(zonelist); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *zconfig = cfg_listelt_value(element); CHECK(configure_zone(config, zconfig, vconfig, mctx, view, actx, ISC_TRUE)); } } /* * Create Dynamically Loadable Zone driver. */ dlz = NULL; if (voptions != NULL) (void)cfg_map_get(voptions, "dlz", &dlz); else (void)cfg_map_get(config, "dlz", &dlz); obj = NULL; if (dlz != NULL) { (void)cfg_map_get(cfg_tuple_get(dlz, "options"), "database", &obj); if (obj != NULL) { char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj)); if (s == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } result = dns_dlzstrtoargv(mctx, s, &dlzargc, &dlzargv); if (result != ISC_R_SUCCESS) { isc_mem_free(mctx, s); goto cleanup; } obj = cfg_tuple_get(dlz, "name"); result = dns_dlzcreate(mctx, cfg_obj_asstring(obj), dlzargv[0], dlzargc, dlzargv, &view->dlzdatabase); isc_mem_free(mctx, s); isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv)); if (result != ISC_R_SUCCESS) goto cleanup; /* * If the dlz backend supports configuration, * then call its configure method now. */ result = dns_dlzconfigure(view, dlzconfigure_callback); if (result != ISC_R_SUCCESS) goto cleanup; } } /* * Obtain configuration parameters that affect the decision of whether * we can reuse/share an existing cache. */ obj = NULL; result = ns_config_get(maps, "cleaning-interval", &obj); INSIST(result == ISC_R_SUCCESS); cleaning_interval = cfg_obj_asuint32(obj) * 60; obj = NULL; result = ns_config_get(maps, "max-cache-size", &obj); INSIST(result == ISC_R_SUCCESS); if (cfg_obj_isstring(obj)) { str = cfg_obj_asstring(obj); INSIST(strcasecmp(str, "unlimited") == 0); max_cache_size = ISC_UINT32_MAX; } else { isc_resourcevalue_t value; value = cfg_obj_asuint64(obj); if (value > SIZE_MAX) { cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING, "'max-cache-size " "%" ISC_PRINT_QUADFORMAT "u' " "is too large for this " "system; reducing to %lu", value, (unsigned long)SIZE_MAX); value = SIZE_MAX; } max_cache_size = (size_t) value; } /* Check-names. */ obj = NULL; result = ns_checknames_get(maps, "response", &obj); INSIST(result == ISC_R_SUCCESS); str = cfg_obj_asstring(obj); if (strcasecmp(str, "fail") == 0) { resopts |= DNS_RESOLVER_CHECKNAMES | DNS_RESOLVER_CHECKNAMESFAIL; view->checknames = ISC_TRUE; } else if (strcasecmp(str, "warn") == 0) { resopts |= DNS_RESOLVER_CHECKNAMES; view->checknames = ISC_FALSE; } else if (strcasecmp(str, "ignore") == 0) { view->checknames = ISC_FALSE; } else INSIST(0); obj = NULL; result = ns_config_get(maps, "zero-no-soa-ttl-cache", &obj); INSIST(result == ISC_R_SUCCESS); zero_no_soattl = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "dns64", &obj); if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") && strcmp(view->name, "_meta")) { const cfg_listelt_t *element; isc_netaddr_t na, suffix, *sp; unsigned int prefixlen; const char *server, *contact; const cfg_obj_t *myobj; myobj = NULL; result = ns_config_get(maps, "dns64-server", &myobj); if (result == ISC_R_SUCCESS) server = cfg_obj_asstring(myobj); else server = NULL; myobj = NULL; result = ns_config_get(maps, "dns64-contact", &myobj); if (result == ISC_R_SUCCESS) contact = cfg_obj_asstring(myobj); else contact = NULL; for (element = cfg_list_first(obj); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *map = cfg_listelt_value(element); dns_dns64_t *dns64 = NULL; unsigned int dns64options = 0; cfg_obj_asnetprefix(cfg_map_getname(map), &na, &prefixlen); obj = NULL; (void)cfg_map_get(map, "suffix", &obj); if (obj != NULL) { sp = &suffix; isc_netaddr_fromsockaddr(sp, cfg_obj_assockaddr(obj)); } else sp = NULL; clients = mapped = excluded = NULL; obj = NULL; (void)cfg_map_get(map, "clients", &obj); if (obj != NULL) { result = cfg_acl_fromconfig(obj, config, ns_g_lctx, actx, mctx, 0, &clients); if (result != ISC_R_SUCCESS) goto cleanup; } obj = NULL; (void)cfg_map_get(map, "mapped", &obj); if (obj != NULL) { result = cfg_acl_fromconfig(obj, config, ns_g_lctx, actx, mctx, 0, &mapped); if (result != ISC_R_SUCCESS) goto cleanup; } obj = NULL; (void)cfg_map_get(map, "exclude", &obj); if (obj != NULL) { result = cfg_acl_fromconfig(obj, config, ns_g_lctx, actx, mctx, 0, &excluded); if (result != ISC_R_SUCCESS) goto cleanup; } obj = NULL; (void)cfg_map_get(map, "recursive-only", &obj); if (obj != NULL && cfg_obj_asboolean(obj)) dns64options |= DNS_DNS64_RECURSIVE_ONLY; obj = NULL; (void)cfg_map_get(map, "break-dnssec", &obj); if (obj != NULL && cfg_obj_asboolean(obj)) dns64options |= DNS_DNS64_BREAK_DNSSEC; result = dns_dns64_create(mctx, &na, prefixlen, sp, clients, mapped, excluded, dns64options, &dns64); if (result != ISC_R_SUCCESS) goto cleanup; dns_dns64_append(&view->dns64, dns64); view->dns64cnt++; result = dns64_reverse(view, mctx, &na, prefixlen, server, contact); if (result != ISC_R_SUCCESS) goto cleanup; if (clients != NULL) dns_acl_detach(&clients); if (mapped != NULL) dns_acl_detach(&mapped); if (excluded != NULL) dns_acl_detach(&excluded); } } obj = NULL; result = ns_config_get(maps, "dnssec-accept-expired", &obj); INSIST(result == ISC_R_SUCCESS); view->acceptexpired = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "dnssec-validation", &obj); INSIST(result == ISC_R_SUCCESS); if (cfg_obj_isboolean(obj)) { view->enablevalidation = cfg_obj_asboolean(obj); } else { /* If dnssec-validation is not boolean, it must be "auto" */ view->enablevalidation = ISC_TRUE; auto_root = ISC_TRUE; } obj = NULL; result = ns_config_get(maps, "max-cache-ttl", &obj); INSIST(result == ISC_R_SUCCESS); view->maxcachettl = cfg_obj_asuint32(obj); obj = NULL; result = ns_config_get(maps, "max-ncache-ttl", &obj); INSIST(result == ISC_R_SUCCESS); view->maxncachettl = cfg_obj_asuint32(obj); if (view->maxncachettl > 7 * 24 * 3600) view->maxncachettl = 7 * 24 * 3600; /* * Configure the view's cache. * * First, check to see if there are any attach-cache options. If yes, * attempt to lookup an existing cache at attach it to the view. If * there is not one, then try to reuse an existing cache if possible; * otherwise create a new cache. * * Note that the ADB is not preserved or shared in either case. * * When a matching view is found, the associated statistics are also * retrieved and reused. * * XXX Determining when it is safe to reuse or share a cache is tricky. * When the view's configuration changes, the cached data may become * invalid because it reflects our old view of the world. We check * some of the configuration parameters that could invalidate the cache * or otherwise make it unsharable, but there are other configuration * options that should be checked. For example, if a view uses a * forwarder, changes in the forwarder configuration may invalidate * the cache. At the moment, it's the administrator's responsibility to * ensure these configuration options don't invalidate reusing/sharing. */ obj = NULL; result = ns_config_get(maps, "attach-cache", &obj); if (result == ISC_R_SUCCESS) cachename = cfg_obj_asstring(obj); else cachename = view->name; cache = NULL; nsc = cachelist_find(cachelist, cachename); if (nsc != NULL) { if (!cache_sharable(nsc->primaryview, view, zero_no_soattl, cleaning_interval, max_cache_size)) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "views %s and %s can't share the cache " "due to configuration parameter mismatch", nsc->primaryview->name, view->name); result = ISC_R_FAILURE; goto cleanup; } dns_cache_attach(nsc->cache, &cache); shared_cache = ISC_TRUE; } else { if (strcmp(cachename, view->name) == 0) { result = dns_viewlist_find(&ns_g_server->viewlist, cachename, view->rdclass, &pview); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) goto cleanup; if (pview != NULL) { if (!cache_reusable(pview, view, zero_no_soattl)) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), "cache cannot be reused " "for view %s due to " "configuration parameter " "mismatch", view->name); } else { INSIST(pview->cache != NULL); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(3), "reusing existing cache"); reused_cache = ISC_TRUE; dns_cache_attach(pview->cache, &cache); } dns_view_getresstats(pview, &resstats); dns_view_getresquerystats(pview, &resquerystats); dns_view_detach(&pview); } } if (cache == NULL) { /* * Create a cache with the desired name. This normally * equals the view name, but may also be a forward * reference to a view that share the cache with this * view but is not yet configured. If it is not the * view name but not a forward reference either, then it * is simply a named cache that is not shared. * * We use two separate memory contexts for the * cache, for the main cache memory and the heap * memory. */ CHECK(isc_mem_create(0, 0, &cmctx)); isc_mem_setname(cmctx, "cache", NULL); CHECK(isc_mem_create(0, 0, &hmctx)); isc_mem_setname(hmctx, "cache_heap", NULL); CHECK(dns_cache_create3(cmctx, hmctx, ns_g_taskmgr, ns_g_timermgr, view->rdclass, cachename, "rbt", 0, NULL, &cache)); isc_mem_detach(&cmctx); isc_mem_detach(&hmctx); } nsc = isc_mem_get(mctx, sizeof(*nsc)); if (nsc == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } nsc->cache = NULL; dns_cache_attach(cache, &nsc->cache); nsc->primaryview = view; nsc->needflush = ISC_FALSE; nsc->adbsizeadjusted = ISC_FALSE; ISC_LINK_INIT(nsc, link); ISC_LIST_APPEND(*cachelist, nsc, link); } dns_view_setcache2(view, cache, shared_cache); /* * cache-file cannot be inherited if views are present, but this * should be caught by the configuration checking stage. */ obj = NULL; result = ns_config_get(maps, "cache-file", &obj); if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) { CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj))); if (!reused_cache && !shared_cache) CHECK(dns_cache_load(cache)); } dns_cache_setcleaninginterval(cache, cleaning_interval); dns_cache_setcachesize(cache, max_cache_size); dns_cache_detach(&cache); /* * Resolver. * * XXXRTH Hardwired number of tasks. */ CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4, ISC_TF(ISC_LIST_PREV(view, link) == NULL))); CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6, ISC_TF(ISC_LIST_PREV(view, link) == NULL))); if (dispatch4 == NULL && dispatch6 == NULL) { UNEXPECTED_ERROR(__FILE__, __LINE__, "unable to obtain neither an IPv4 nor" " an IPv6 dispatch"); result = ISC_R_UNEXPECTED; goto cleanup; } ndisp = 4 * ISC_MIN(ns_g_udpdisp, MAX_UDP_DISPATCH); CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31, ndisp, ns_g_socketmgr, ns_g_timermgr, resopts, ns_g_dispatchmgr, dispatch4, dispatch6)); if (resstats == NULL) { CHECK(isc_stats_create(mctx, &resstats, dns_resstatscounter_max)); } dns_view_setresstats(view, resstats); if (resquerystats == NULL) CHECK(dns_rdatatypestats_create(mctx, &resquerystats)); dns_view_setresquerystats(view, resquerystats); /* * Set the ADB cache size to 1/8th of the max-cache-size or * MAX_ADB_SIZE_FOR_CACHESHARE when the cache is shared. */ max_adb_size = 0; if (max_cache_size != 0U) { max_adb_size = max_cache_size / 8; if (max_adb_size == 0U) max_adb_size = 1; /* Force minimum. */ if (view != nsc->primaryview && max_adb_size > MAX_ADB_SIZE_FOR_CACHESHARE) { max_adb_size = MAX_ADB_SIZE_FOR_CACHESHARE; if (!nsc->adbsizeadjusted) { dns_adb_setadbsize(nsc->primaryview->adb, MAX_ADB_SIZE_FOR_CACHESHARE); nsc->adbsizeadjusted = ISC_TRUE; } } } dns_adb_setadbsize(view->adb, max_adb_size); /* * Set resolver's lame-ttl. */ obj = NULL; result = ns_config_get(maps, "lame-ttl", &obj); INSIST(result == ISC_R_SUCCESS); lame_ttl = cfg_obj_asuint32(obj); if (lame_ttl > 1800) lame_ttl = 1800; dns_resolver_setlamettl(view->resolver, lame_ttl); /* * Set the resolver's query timeout. */ obj = NULL; result = ns_config_get(maps, "resolver-query-timeout", &obj); INSIST(result == ISC_R_SUCCESS); query_timeout = cfg_obj_asuint32(obj); dns_resolver_settimeout(view->resolver, query_timeout); /* Specify whether to use 0-TTL for negative response for SOA query */ dns_resolver_setzeronosoattl(view->resolver, zero_no_soattl); /* * Set the resolver's EDNS UDP size. */ obj = NULL; result = ns_config_get(maps, "edns-udp-size", &obj); INSIST(result == ISC_R_SUCCESS); udpsize = cfg_obj_asuint32(obj); if (udpsize < 512) udpsize = 512; if (udpsize > 4096) udpsize = 4096; dns_resolver_setudpsize(view->resolver, (isc_uint16_t)udpsize); /* * Set the maximum UDP response size. */ obj = NULL; result = ns_config_get(maps, "max-udp-size", &obj); INSIST(result == ISC_R_SUCCESS); udpsize = cfg_obj_asuint32(obj); if (udpsize < 512) udpsize = 512; if (udpsize > 4096) udpsize = 4096; view->maxudp = udpsize; /* * Set the maximum rsa exponent bits. */ obj = NULL; result = ns_config_get(maps, "max-rsa-exponent-size", &obj); INSIST(result == ISC_R_SUCCESS); maxbits = cfg_obj_asuint32(obj); if (maxbits != 0 && maxbits < 35) maxbits = 35; if (maxbits > 4096) maxbits = 4096; view->maxbits = maxbits; /* * Set supported DNSSEC algorithms. */ dns_resolver_reset_algorithms(view->resolver); disabled = NULL; (void)ns_config_get(maps, "disable-algorithms", &disabled); if (disabled != NULL) { for (element = cfg_list_first(disabled); element != NULL; element = cfg_list_next(element)) CHECK(disable_algorithms(cfg_listelt_value(element), view->resolver)); } /* * A global or view "forwarders" option, if present, * creates an entry for "." in the forwarding table. */ forwardtype = NULL; forwarders = NULL; (void)ns_config_get(maps, "forward", &forwardtype); (void)ns_config_get(maps, "forwarders", &forwarders); if (forwarders != NULL) CHECK(configure_forward(config, view, dns_rootname, forwarders, forwardtype)); /* * Dual Stack Servers. */ alternates = NULL; (void)ns_config_get(maps, "dual-stack-servers", &alternates); if (alternates != NULL) CHECK(configure_alternates(config, view, alternates)); /* * We have default hints for class IN if we need them. */ if (view->rdclass == dns_rdataclass_in && view->hints == NULL) dns_view_sethints(view, ns_g_server->in_roothints); /* * If we still have no hints, this is a non-IN view with no * "hints zone" configured. Issue a warning, except if this * is a root server. Root servers never need to consult * their hints, so it's no point requiring users to configure * them. */ if (view->hints == NULL) { dns_zone_t *rootzone = NULL; (void)dns_view_findzone(view, dns_rootname, &rootzone); if (rootzone != NULL) { dns_zone_detach(&rootzone); need_hints = ISC_FALSE; } if (need_hints) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "no root hints for view '%s'", view->name); } /* * Configure the view's TSIG keys. */ CHECK(ns_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring)); if (ns_g_server->sessionkey != NULL) { CHECK(dns_tsigkeyring_add(ring, ns_g_server->session_keyname, ns_g_server->sessionkey)); } dns_view_setkeyring(view, ring); dns_tsigkeyring_detach(&ring); /* * See if we can re-use a dynamic key ring. */ result = dns_viewlist_find(&ns_g_server->viewlist, view->name, view->rdclass, &pview); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) goto cleanup; if (pview != NULL) { dns_view_getdynamickeyring(pview, &ring); if (ring != NULL) dns_view_setdynamickeyring(view, ring); dns_tsigkeyring_detach(&ring); dns_view_detach(&pview); } else dns_view_restorekeyring(view); /* * Configure the view's peer list. */ { const cfg_obj_t *peers = NULL; const cfg_listelt_t *element; dns_peerlist_t *newpeers = NULL; (void)ns_config_get(cfgmaps, "server", &peers); CHECK(dns_peerlist_new(mctx, &newpeers)); for (element = cfg_list_first(peers); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *cpeer = cfg_listelt_value(element); dns_peer_t *peer; CHECK(configure_peer(cpeer, mctx, &peer)); dns_peerlist_addpeer(newpeers, peer); dns_peer_detach(&peer); } dns_peerlist_detach(&view->peers); view->peers = newpeers; /* Transfer ownership. */ } /* * Configure the views rrset-order. */ { const cfg_obj_t *rrsetorder = NULL; const cfg_listelt_t *element; (void)ns_config_get(maps, "rrset-order", &rrsetorder); CHECK(dns_order_create(mctx, &order)); for (element = cfg_list_first(rrsetorder); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *ent = cfg_listelt_value(element); CHECK(configure_order(order, ent)); } if (view->order != NULL) dns_order_detach(&view->order); dns_order_attach(order, &view->order); dns_order_detach(&order); } /* * Copy the aclenv object. */ dns_aclenv_copy(&view->aclenv, &ns_g_server->aclenv); /* * Configure the "match-clients" and "match-destinations" ACL. */ CHECK(configure_view_acl(vconfig, config, "match-clients", NULL, actx, ns_g_mctx, &view->matchclients)); CHECK(configure_view_acl(vconfig, config, "match-destinations", NULL, actx, ns_g_mctx, &view->matchdestinations)); /* * Configure the "match-recursive-only" option. */ obj = NULL; (void)ns_config_get(maps, "match-recursive-only", &obj); if (obj != NULL && cfg_obj_asboolean(obj)) view->matchrecursiveonly = ISC_TRUE; else view->matchrecursiveonly = ISC_FALSE; /* * Configure other configurable data. */ obj = NULL; result = ns_config_get(maps, "recursion", &obj); INSIST(result == ISC_R_SUCCESS); view->recursion = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "auth-nxdomain", &obj); INSIST(result == ISC_R_SUCCESS); view->auth_nxdomain = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "minimal-responses", &obj); INSIST(result == ISC_R_SUCCESS); view->minimalresponses = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "transfer-format", &obj); INSIST(result == ISC_R_SUCCESS); str = cfg_obj_asstring(obj); if (strcasecmp(str, "many-answers") == 0) view->transfer_format = dns_many_answers; else if (strcasecmp(str, "one-answer") == 0) view->transfer_format = dns_one_answer; else INSIST(0); /* * Set sources where additional data and CNAME/DNAME * targets for authoritative answers may be found. */ obj = NULL; result = ns_config_get(maps, "additional-from-auth", &obj); INSIST(result == ISC_R_SUCCESS); view->additionalfromauth = cfg_obj_asboolean(obj); if (view->recursion && ! view->additionalfromauth) { cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING, "'additional-from-auth no' is only supported " "with 'recursion no'"); view->additionalfromauth = ISC_TRUE; } obj = NULL; result = ns_config_get(maps, "additional-from-cache", &obj); INSIST(result == ISC_R_SUCCESS); view->additionalfromcache = cfg_obj_asboolean(obj); if (view->recursion && ! view->additionalfromcache) { cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING, "'additional-from-cache no' is only supported " "with 'recursion no'"); view->additionalfromcache = ISC_TRUE; } /* * Set "allow-query-cache", "allow-query-cache-on", * "allow-recursion", and "allow-recursion-on" acls if * configured in named.conf. */ CHECK(configure_view_acl(vconfig, config, "allow-query-cache", NULL, actx, ns_g_mctx, &view->cacheacl)); CHECK(configure_view_acl(vconfig, config, "allow-query-cache-on", NULL, actx, ns_g_mctx, &view->cacheonacl)); if (view->cacheonacl == NULL) CHECK(configure_view_acl(NULL, ns_g_config, "allow-query-cache-on", NULL, actx, ns_g_mctx, &view->cacheonacl)); if (strcmp(view->name, "_bind") != 0) { CHECK(configure_view_acl(vconfig, config, "allow-recursion", NULL, actx, ns_g_mctx, &view->recursionacl)); CHECK(configure_view_acl(vconfig, config, "allow-recursion-on", NULL, actx, ns_g_mctx, &view->recursiononacl)); } /* * "allow-query-cache" inherits from "allow-recursion" if set, * otherwise from "allow-query" if set. * "allow-recursion" inherits from "allow-query-cache" if set, * otherwise from "allow-query" if set. */ if (view->cacheacl == NULL && view->recursionacl != NULL) dns_acl_attach(view->recursionacl, &view->cacheacl); /* * XXXEACH: This call to configure_view_acl() is redundant. We * are leaving it as it is because we are making a minimal change * for a patch release. In the future this should be changed to * dns_acl_attach(view->queryacl, &view->cacheacl). */ if (view->cacheacl == NULL && view->recursion) CHECK(configure_view_acl(vconfig, config, "allow-query", NULL, actx, ns_g_mctx, &view->cacheacl)); if (view->recursion && view->recursionacl == NULL && view->cacheacl != NULL) dns_acl_attach(view->cacheacl, &view->recursionacl); /* * Set default "allow-recursion", "allow-recursion-on" and * "allow-query-cache" acls. */ if (view->recursionacl == NULL && view->recursion) CHECK(configure_view_acl(NULL, ns_g_config, "allow-recursion", NULL, actx, ns_g_mctx, &view->recursionacl)); if (view->recursiononacl == NULL && view->recursion) CHECK(configure_view_acl(NULL, ns_g_config, "allow-recursion-on", NULL, actx, ns_g_mctx, &view->recursiononacl)); if (view->cacheacl == NULL) { if (view->recursion) CHECK(configure_view_acl(NULL, ns_g_config, "allow-query-cache", NULL, actx, ns_g_mctx, &view->cacheacl)); else CHECK(dns_acl_none(mctx, &view->cacheacl)); } /* * Filter setting on addresses in the answer section. */ CHECK(configure_view_acl(vconfig, config, "deny-answer-addresses", "acl", actx, ns_g_mctx, &view->denyansweracl)); CHECK(configure_view_nametable(vconfig, config, "deny-answer-addresses", "except-from", ns_g_mctx, &view->answeracl_exclude)); /* * Filter setting on names (CNAME/DNAME targets) in the answer section. */ CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases", "name", ns_g_mctx, &view->denyanswernames)); CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases", "except-from", ns_g_mctx, &view->answernames_exclude)); /* * Configure sortlist, if set */ CHECK(configure_view_sortlist(vconfig, config, actx, ns_g_mctx, &view->sortlist)); /* * Configure default allow-transfer, allow-notify, allow-update * and allow-update-forwarding ACLs, if set, so they can be * inherited by zones. */ if (view->notifyacl == NULL) CHECK(configure_view_acl(NULL, ns_g_config, "allow-notify", NULL, actx, ns_g_mctx, &view->notifyacl)); if (view->transferacl == NULL) CHECK(configure_view_acl(NULL, ns_g_config, "allow-transfer", NULL, actx, ns_g_mctx, &view->transferacl)); if (view->updateacl == NULL) CHECK(configure_view_acl(NULL, ns_g_config, "allow-update", NULL, actx, ns_g_mctx, &view->updateacl)); if (view->upfwdacl == NULL) CHECK(configure_view_acl(NULL, ns_g_config, "allow-update-forwarding", NULL, actx, ns_g_mctx, &view->upfwdacl)); obj = NULL; result = ns_config_get(maps, "provide-ixfr", &obj); INSIST(result == ISC_R_SUCCESS); view->provideixfr = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "request-nsid", &obj); INSIST(result == ISC_R_SUCCESS); view->requestnsid = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(maps, "max-clients-per-query", &obj); INSIST(result == ISC_R_SUCCESS); max_clients_per_query = cfg_obj_asuint32(obj); obj = NULL; result = ns_config_get(maps, "clients-per-query", &obj); INSIST(result == ISC_R_SUCCESS); dns_resolver_setclientsperquery(view->resolver, cfg_obj_asuint32(obj), max_clients_per_query); + obj = NULL; + result = ns_config_get(maps, "max-recursion-depth", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_resolver_setmaxdepth(view->resolver, cfg_obj_asuint32(obj)); + #ifdef ALLOW_FILTER_AAAA_ON_V4 obj = NULL; result = ns_config_get(maps, "filter-aaaa-on-v4", &obj); INSIST(result == ISC_R_SUCCESS); if (cfg_obj_isboolean(obj)) { if (cfg_obj_asboolean(obj)) view->v4_aaaa = dns_v4_aaaa_filter; else view->v4_aaaa = dns_v4_aaaa_ok; } else { const char *v4_aaaastr = cfg_obj_asstring(obj); if (strcasecmp(v4_aaaastr, "break-dnssec") == 0) view->v4_aaaa = dns_v4_aaaa_break_dnssec; else INSIST(0); } CHECK(configure_view_acl(vconfig, config, "filter-aaaa", NULL, actx, ns_g_mctx, &view->v4_aaaa_acl)); #endif obj = NULL; result = ns_config_get(maps, "dnssec-enable", &obj); INSIST(result == ISC_R_SUCCESS); view->enablednssec = cfg_obj_asboolean(obj); obj = NULL; result = ns_config_get(optionmaps, "dnssec-lookaside", &obj); if (result == ISC_R_SUCCESS) { /* If set to "auto", use the version from the defaults */ const cfg_obj_t *dlvobj; const char *dom; dlvobj = cfg_listelt_value(cfg_list_first(obj)); dom = cfg_obj_asstring(cfg_tuple_get(dlvobj, "domain")); if (cfg_obj_isvoid(cfg_tuple_get(dlvobj, "trust-anchor"))) { /* If "no", skip; if "auto", use global default */ if (!strcasecmp(dom, "no")) result = ISC_R_NOTFOUND; else if (!strcasecmp(dom, "auto")) { auto_dlv = ISC_TRUE; obj = NULL; result = cfg_map_get(ns_g_defaults, "dnssec-lookaside", &obj); } } } if (result == ISC_R_SUCCESS) { for (element = cfg_list_first(obj); element != NULL; element = cfg_list_next(element)) { const char *str; isc_buffer_t b; dns_name_t *dlv; obj = cfg_listelt_value(element); str = cfg_obj_asstring(cfg_tuple_get(obj, "trust-anchor")); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); dlv = dns_fixedname_name(&view->dlv_fixed); CHECK(dns_name_fromtext(dlv, &b, dns_rootname, DNS_NAME_DOWNCASE, NULL)); view->dlv = dns_fixedname_name(&view->dlv_fixed); } } else view->dlv = NULL; /* * For now, there is only one kind of trusted keys, the * "security roots". */ CHECK(configure_view_dnsseckeys(view, vconfig, config, bindkeys, auto_dlv, auto_root, mctx)); dns_resolver_resetmustbesecure(view->resolver); obj = NULL; result = ns_config_get(maps, "dnssec-must-be-secure", &obj); if (result == ISC_R_SUCCESS) CHECK(mustbesecure(obj, view->resolver)); obj = NULL; result = ns_config_get(maps, "preferred-glue", &obj); if (result == ISC_R_SUCCESS) { str = cfg_obj_asstring(obj); if (strcasecmp(str, "a") == 0) view->preferred_glue = dns_rdatatype_a; else if (strcasecmp(str, "aaaa") == 0) view->preferred_glue = dns_rdatatype_aaaa; else view->preferred_glue = 0; } else view->preferred_glue = 0; obj = NULL; result = ns_config_get(maps, "root-delegation-only", &obj); if (result == ISC_R_SUCCESS) { dns_view_setrootdelonly(view, ISC_TRUE); if (!cfg_obj_isvoid(obj)) { dns_fixedname_t fixed; dns_name_t *name; isc_buffer_t b; const char *str; const cfg_obj_t *exclude; dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); for (element = cfg_list_first(obj); element != NULL; element = cfg_list_next(element)) { exclude = cfg_listelt_value(element); str = cfg_obj_asstring(exclude); isc_buffer_constinit(&b, str, strlen(str)); isc_buffer_add(&b, strlen(str)); CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); CHECK(dns_view_excludedelegationonly(view, name)); } } } else dns_view_setrootdelonly(view, ISC_FALSE); /* * Setup automatic empty zones. If recursion is off then * they are disabled by default. */ obj = NULL; (void)ns_config_get(maps, "empty-zones-enable", &obj); (void)ns_config_get(maps, "disable-empty-zone", &disablelist); if (obj == NULL && disablelist == NULL && view->rdclass == dns_rdataclass_in) { empty_zones_enable = view->recursion; } else if (view->rdclass == dns_rdataclass_in) { if (obj != NULL) empty_zones_enable = cfg_obj_asboolean(obj); else empty_zones_enable = view->recursion; } else { empty_zones_enable = ISC_FALSE; } if (empty_zones_enable && !lwresd_g_useresolvconf) { const char *empty; int empty_zone = 0; dns_fixedname_t fixed; dns_name_t *name; isc_buffer_t buffer; const char *str; char server[DNS_NAME_FORMATSIZE + 1]; char contact[DNS_NAME_FORMATSIZE + 1]; const char *empty_dbtype[4] = { "_builtin", "empty", NULL, NULL }; int empty_dbtypec = 4; dns_zonestat_level_t statlevel; dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); obj = NULL; result = ns_config_get(maps, "empty-server", &obj); if (result == ISC_R_SUCCESS) { str = cfg_obj_asstring(obj); isc_buffer_constinit(&buffer, str, strlen(str)); isc_buffer_add(&buffer, strlen(str)); CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL)); isc_buffer_init(&buffer, server, sizeof(server) - 1); CHECK(dns_name_totext(name, ISC_FALSE, &buffer)); server[isc_buffer_usedlength(&buffer)] = 0; empty_dbtype[2] = server; } else empty_dbtype[2] = "@"; obj = NULL; result = ns_config_get(maps, "empty-contact", &obj); if (result == ISC_R_SUCCESS) { str = cfg_obj_asstring(obj); isc_buffer_constinit(&buffer, str, strlen(str)); isc_buffer_add(&buffer, strlen(str)); CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL)); isc_buffer_init(&buffer, contact, sizeof(contact) - 1); CHECK(dns_name_totext(name, ISC_FALSE, &buffer)); contact[isc_buffer_usedlength(&buffer)] = 0; empty_dbtype[3] = contact; } else empty_dbtype[3] = "."; obj = NULL; result = ns_config_get(maps, "zone-statistics", &obj); INSIST(result == ISC_R_SUCCESS); if (cfg_obj_isboolean(obj)) { if (cfg_obj_asboolean(obj)) statlevel = dns_zonestat_full; else statlevel = dns_zonestat_terse; /* XXX */ } else { const char *levelstr = cfg_obj_asstring(obj); if (strcasecmp(levelstr, "full") == 0) statlevel = dns_zonestat_full; else if (strcasecmp(levelstr, "terse") == 0) statlevel = dns_zonestat_terse; else if (strcasecmp(levelstr, "none") == 0) statlevel = dns_zonestat_none; else INSIST(0); } for (empty = empty_zones[empty_zone]; empty != NULL; empty = empty_zones[++empty_zone]) { dns_forwarders_t *forwarders = NULL; dns_view_t *pview = NULL; isc_buffer_constinit(&buffer, empty, strlen(empty)); isc_buffer_add(&buffer, strlen(empty)); /* * Look for zone on drop list. */ CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL)); if (disablelist != NULL && on_disable_list(disablelist, name)) continue; /* * This zone already exists. */ (void)dns_view_findzone(view, name, &zone); if (zone != NULL) { dns_zone_detach(&zone); continue; } /* * If we would forward this name don't add a * empty zone for it. */ result = dns_fwdtable_find(view->fwdtable, name, &forwarders); if (result == ISC_R_SUCCESS && forwarders->fwdpolicy == dns_fwdpolicy_only) continue; /* * See if we can re-use a existing zone. */ result = dns_viewlist_find(&ns_g_server->viewlist, view->name, view->rdclass, &pview); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) goto cleanup; if (pview != NULL) { (void)dns_view_findzone(pview, name, &zone); dns_view_detach(&pview); } CHECK(create_empty_zone(zone, name, view, zonelist, empty_dbtype, empty_dbtypec, statlevel)); if (zone != NULL) dns_zone_detach(&zone); } } #ifdef USE_RRL obj = NULL; result = ns_config_get(maps, "rate-limit", &obj); if (result == ISC_R_SUCCESS) { result = configure_rrl(view, config, obj); if (result != ISC_R_SUCCESS) goto cleanup; } #endif /* USE_RRL */ result = ISC_R_SUCCESS; cleanup: if (clients != NULL) dns_acl_detach(&clients); if (mapped != NULL) dns_acl_detach(&mapped); if (excluded != NULL) dns_acl_detach(&excluded); if (ring != NULL) dns_tsigkeyring_detach(&ring); if (zone != NULL) dns_zone_detach(&zone); if (dispatch4 != NULL) dns_dispatch_detach(&dispatch4); if (dispatch6 != NULL) dns_dispatch_detach(&dispatch6); if (resstats != NULL) isc_stats_detach(&resstats); if (resquerystats != NULL) dns_stats_detach(&resquerystats); if (order != NULL) dns_order_detach(&order); if (cmctx != NULL) isc_mem_detach(&cmctx); if (hmctx != NULL) isc_mem_detach(&hmctx); if (cache != NULL) dns_cache_detach(&cache); return (result); } static isc_result_t configure_hints(dns_view_t *view, const char *filename) { isc_result_t result; dns_db_t *db; db = NULL; result = dns_rootns_create(view->mctx, view->rdclass, filename, &db); if (result == ISC_R_SUCCESS) { dns_view_sethints(view, db); dns_db_detach(&db); } return (result); } static isc_result_t configure_alternates(const cfg_obj_t *config, dns_view_t *view, const cfg_obj_t *alternates) { const cfg_obj_t *portobj; const cfg_obj_t *addresses; const cfg_listelt_t *element; isc_result_t result = ISC_R_SUCCESS; in_port_t port; /* * Determine which port to send requests to. */ if (ns_g_lwresdonly && ns_g_port != 0) port = ns_g_port; else CHECKM(ns_config_getport(config, &port), "port"); if (alternates != NULL) { portobj = cfg_tuple_get(alternates, "port"); if (cfg_obj_isuint32(portobj)) { isc_uint32_t val = cfg_obj_asuint32(portobj); if (val > ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port '%u' out of range", val); return (ISC_R_RANGE); } port = (in_port_t) val; } } addresses = NULL; if (alternates != NULL) addresses = cfg_tuple_get(alternates, "addresses"); for (element = cfg_list_first(addresses); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *alternate = cfg_listelt_value(element); isc_sockaddr_t sa; if (!cfg_obj_issockaddr(alternate)) { dns_fixedname_t fixed; dns_name_t *name; const char *str = cfg_obj_asstring(cfg_tuple_get( alternate, "name")); isc_buffer_t buffer; in_port_t myport = port; isc_buffer_constinit(&buffer, str, strlen(str)); isc_buffer_add(&buffer, strlen(str)); dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL)); portobj = cfg_tuple_get(alternate, "port"); if (cfg_obj_isuint32(portobj)) { isc_uint32_t val = cfg_obj_asuint32(portobj); if (val > ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port '%u' out of range", val); return (ISC_R_RANGE); } myport = (in_port_t) val; } CHECK(dns_resolver_addalternate(view->resolver, NULL, name, myport)); continue; } sa = *cfg_obj_assockaddr(alternate); if (isc_sockaddr_getport(&sa) == 0) isc_sockaddr_setport(&sa, port); CHECK(dns_resolver_addalternate(view->resolver, &sa, NULL, 0)); } cleanup: return (result); } static isc_result_t configure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin, const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype) { const cfg_obj_t *portobj; const cfg_obj_t *faddresses; const cfg_listelt_t *element; dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none; isc_sockaddrlist_t addresses; isc_sockaddr_t *sa; isc_result_t result; in_port_t port; ISC_LIST_INIT(addresses); /* * Determine which port to send forwarded requests to. */ if (ns_g_lwresdonly && ns_g_port != 0) port = ns_g_port; else CHECKM(ns_config_getport(config, &port), "port"); if (forwarders != NULL) { portobj = cfg_tuple_get(forwarders, "port"); if (cfg_obj_isuint32(portobj)) { isc_uint32_t val = cfg_obj_asuint32(portobj); if (val > ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port '%u' out of range", val); return (ISC_R_RANGE); } port = (in_port_t) val; } } faddresses = NULL; if (forwarders != NULL) faddresses = cfg_tuple_get(forwarders, "addresses"); for (element = cfg_list_first(faddresses); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *forwarder = cfg_listelt_value(element); sa = isc_mem_get(view->mctx, sizeof(isc_sockaddr_t)); if (sa == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } *sa = *cfg_obj_assockaddr(forwarder); if (isc_sockaddr_getport(sa) == 0) isc_sockaddr_setport(sa, port); ISC_LINK_INIT(sa, link); ISC_LIST_APPEND(addresses, sa, link); } if (ISC_LIST_EMPTY(addresses)) { if (forwardtype != NULL) cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING, "no forwarders seen; disabling " "forwarding"); fwdpolicy = dns_fwdpolicy_none; } else { if (forwardtype == NULL) fwdpolicy = dns_fwdpolicy_first; else { const char *forwardstr = cfg_obj_asstring(forwardtype); if (strcasecmp(forwardstr, "first") == 0) fwdpolicy = dns_fwdpolicy_first; else if (strcasecmp(forwardstr, "only") == 0) fwdpolicy = dns_fwdpolicy_only; else INSIST(0); } } result = dns_fwdtable_add(view->fwdtable, origin, &addresses, fwdpolicy); if (result != ISC_R_SUCCESS) { char namebuf[DNS_NAME_FORMATSIZE]; dns_name_format(origin, namebuf, sizeof(namebuf)); cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING, "could not set up forwarding for domain '%s': %s", namebuf, isc_result_totext(result)); goto cleanup; } result = ISC_R_SUCCESS; cleanup: while (!ISC_LIST_EMPTY(addresses)) { sa = ISC_LIST_HEAD(addresses); ISC_LIST_UNLINK(addresses, sa, link); isc_mem_put(view->mctx, sa, sizeof(isc_sockaddr_t)); } return (result); } static isc_result_t get_viewinfo(const cfg_obj_t *vconfig, const char **namep, dns_rdataclass_t *classp) { isc_result_t result = ISC_R_SUCCESS; const char *viewname; dns_rdataclass_t viewclass; REQUIRE(namep != NULL && *namep == NULL); REQUIRE(classp != NULL); if (vconfig != NULL) { const cfg_obj_t *classobj = NULL; viewname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name")); classobj = cfg_tuple_get(vconfig, "class"); result = ns_config_getclass(classobj, dns_rdataclass_in, &viewclass); } else { viewname = "_default"; viewclass = dns_rdataclass_in; } *namep = viewname; *classp = viewclass; return (result); } /* * Find a view based on its configuration info and attach to it. * * If 'vconfig' is NULL, attach to the default view. */ static isc_result_t find_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist, dns_view_t **viewp) { isc_result_t result; const char *viewname = NULL; dns_rdataclass_t viewclass; dns_view_t *view = NULL; result = get_viewinfo(vconfig, &viewname, &viewclass); if (result != ISC_R_SUCCESS) return (result); result = dns_viewlist_find(viewlist, viewname, viewclass, &view); if (result != ISC_R_SUCCESS) return (result); *viewp = view; return (ISC_R_SUCCESS); } /* * Create a new view and add it to the list. * * If 'vconfig' is NULL, create the default view. * * The view created is attached to '*viewp'. */ static isc_result_t create_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist, dns_view_t **viewp) { isc_result_t result; const char *viewname = NULL; dns_rdataclass_t viewclass; dns_view_t *view = NULL; result = get_viewinfo(vconfig, &viewname, &viewclass); if (result != ISC_R_SUCCESS) return (result); result = dns_viewlist_find(viewlist, viewname, viewclass, &view); if (result == ISC_R_SUCCESS) return (ISC_R_EXISTS); if (result != ISC_R_NOTFOUND) return (result); INSIST(view == NULL); result = dns_view_create(ns_g_mctx, viewclass, viewname, &view); if (result != ISC_R_SUCCESS) return (result); ISC_LIST_APPEND(*viewlist, view, link); dns_view_attach(view, viewp); return (ISC_R_SUCCESS); } /* * Configure or reconfigure a zone. */ static isc_result_t configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view, cfg_aclconfctx_t *aclconf, isc_boolean_t added) { dns_view_t *pview = NULL; /* Production view */ dns_zone_t *zone = NULL; /* New or reused zone */ dns_zone_t *raw = NULL; /* New or reused raw zone */ dns_zone_t *dupzone = NULL; const cfg_obj_t *options = NULL; const cfg_obj_t *zoptions = NULL; const cfg_obj_t *typeobj = NULL; const cfg_obj_t *forwarders = NULL; const cfg_obj_t *forwardtype = NULL; const cfg_obj_t *only = NULL; const cfg_obj_t *signing = NULL; isc_result_t result; isc_result_t tresult; isc_buffer_t buffer; dns_fixedname_t fixorigin; dns_name_t *origin; const char *zname; dns_rdataclass_t zclass; const char *ztypestr; isc_boolean_t is_rpz; dns_rpz_zone_t *rpz; options = NULL; (void)cfg_map_get(config, "options", &options); zoptions = cfg_tuple_get(zconfig, "options"); /* * Get the zone origin as a dns_name_t. */ zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); isc_buffer_constinit(&buffer, zname, strlen(zname)); isc_buffer_add(&buffer, strlen(zname)); dns_fixedname_init(&fixorigin); CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin), &buffer, dns_rootname, 0, NULL)); origin = dns_fixedname_name(&fixorigin); CHECK(ns_config_getclass(cfg_tuple_get(zconfig, "class"), view->rdclass, &zclass)); if (zclass != view->rdclass) { const char *vname = NULL; if (vconfig != NULL) vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name")); else vname = ""; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "zone '%s': wrong class for view '%s'", zname, vname); result = ISC_R_FAILURE; goto cleanup; } (void)cfg_map_get(zoptions, "type", &typeobj); if (typeobj == NULL) { cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR, "zone '%s' 'type' not specified", zname); return (ISC_R_FAILURE); } ztypestr = cfg_obj_asstring(typeobj); /* * "hints zones" aren't zones. If we've got one, * configure it and return. */ if (strcasecmp(ztypestr, "hint") == 0) { const cfg_obj_t *fileobj = NULL; if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "zone '%s': 'file' not specified", zname); result = ISC_R_FAILURE; goto cleanup; } if (dns_name_equal(origin, dns_rootname)) { const char *hintsfile = cfg_obj_asstring(fileobj); result = configure_hints(view, hintsfile); if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "could not configure root hints " "from '%s': %s", hintsfile, isc_result_totext(result)); goto cleanup; } /* * Hint zones may also refer to delegation only points. */ only = NULL; tresult = cfg_map_get(zoptions, "delegation-only", &only); if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only)) CHECK(dns_view_adddelegationonly(view, origin)); } else { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "ignoring non-root hint zone '%s'", zname); result = ISC_R_SUCCESS; } /* Skip ordinary zone processing. */ goto cleanup; } /* * "forward zones" aren't zones either. Translate this syntax into * the appropriate selective forwarding configuration and return. */ if (strcasecmp(ztypestr, "forward") == 0) { forwardtype = NULL; forwarders = NULL; (void)cfg_map_get(zoptions, "forward", &forwardtype); (void)cfg_map_get(zoptions, "forwarders", &forwarders); result = configure_forward(config, view, origin, forwarders, forwardtype); goto cleanup; } /* * "delegation-only zones" aren't zones either. */ if (strcasecmp(ztypestr, "delegation-only") == 0) { result = dns_view_adddelegationonly(view, origin); goto cleanup; } /* * Redirect zones only require minimal configuration. */ if (strcasecmp(ztypestr, "redirect") == 0) { if (view->redirect != NULL) { cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR, "redirect zone already exists"); result = ISC_R_EXISTS; goto cleanup; } result = dns_viewlist_find(&ns_g_server->viewlist, view->name, view->rdclass, &pview); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) goto cleanup; if (pview != NULL && pview->redirect != NULL) { dns_zone_attach(pview->redirect, &zone); dns_zone_setview(zone, view); } else { CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr, &zone)); CHECK(dns_zone_setorigin(zone, origin)); dns_zone_setview(zone, view); CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone)); dns_zone_setstats(zone, ns_g_server->zonestats); } CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone, NULL)); dns_zone_attach(zone, &view->redirect); goto cleanup; } /* * Check for duplicates in the new zone table. */ result = dns_view_findzone(view, origin, &dupzone); if (result == ISC_R_SUCCESS) { /* * We already have this zone! */ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR, "zone '%s' already exists", zname); dns_zone_detach(&dupzone); result = ISC_R_EXISTS; goto cleanup; } INSIST(dupzone == NULL); /* * Note whether this is a response policy zone. */ is_rpz = ISC_FALSE; for (rpz = ISC_LIST_HEAD(view->rpz_zones); rpz != NULL; rpz = ISC_LIST_NEXT(rpz, link)) { if (dns_name_equal(&rpz->origin, origin)) { is_rpz = ISC_TRUE; rpz->defined = ISC_TRUE; break; } } /* * See if we can reuse an existing zone. This is * only possible if all of these are true: * - The zone's view exists * - A zone with the right name exists in the view * - The zone is compatible with the config * options (e.g., an existing master zone cannot * be reused if the options specify a slave zone) * - The zone was and is or was not and is not a policy zone */ result = dns_viewlist_find(&ns_g_server->viewlist, view->name, view->rdclass, &pview); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) goto cleanup; if (pview != NULL) result = dns_view_findzone(pview, origin, &zone); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) goto cleanup; if (zone != NULL && !ns_zone_reusable(zone, zconfig)) dns_zone_detach(&zone); if (zone != NULL && is_rpz != dns_zone_get_rpz(zone)) dns_zone_detach(&zone); if (zone != NULL) { /* * We found a reusable zone. Make it use the * new view. */ dns_zone_setview(zone, view); if (view->acache != NULL) dns_zone_setacache(zone, view->acache); } else { /* * We cannot reuse an existing zone, we have * to create a new one. */ CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr, &zone)); CHECK(dns_zone_setorigin(zone, origin)); dns_zone_setview(zone, view); if (view->acache != NULL) dns_zone_setacache(zone, view->acache); CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone)); dns_zone_setstats(zone, ns_g_server->zonestats); } if (is_rpz) { result = dns_zone_rpz_enable(zone); if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "zone '%s': incompatible" " masterfile-format or database" " for a response policy zone", zname); goto cleanup; } } /* * If the zone contains a 'forwarders' statement, configure * selective forwarding. */ forwarders = NULL; if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS) { forwardtype = NULL; (void)cfg_map_get(zoptions, "forward", &forwardtype); CHECK(configure_forward(config, view, origin, forwarders, forwardtype)); } /* * Stub and forward zones may also refer to delegation only points. */ only = NULL; if (cfg_map_get(zoptions, "delegation-only", &only) == ISC_R_SUCCESS) { if (cfg_obj_asboolean(only)) CHECK(dns_view_adddelegationonly(view, origin)); } /* * Mark whether the zone was originally added at runtime or not */ dns_zone_setadded(zone, added); signing = NULL; if ((strcasecmp(ztypestr, "master") == 0 || strcasecmp(ztypestr, "slave") == 0) && cfg_map_get(zoptions, "inline-signing", &signing) == ISC_R_SUCCESS && cfg_obj_asboolean(signing)) { dns_zone_getraw(zone, &raw); if (raw == NULL) { CHECK(dns_zone_create(&raw, mctx)); CHECK(dns_zone_setorigin(raw, origin)); dns_zone_setview(raw, view); if (view->acache != NULL) dns_zone_setacache(raw, view->acache); dns_zone_setstats(raw, ns_g_server->zonestats); CHECK(dns_zone_link(zone, raw)); } } /* * Configure the zone. */ CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone, raw)); /* * Add the zone to its view in the new view list. */ CHECK(dns_view_addzone(view, zone)); /* * Ensure that zone keys are reloaded on reconfig */ if ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0) dns_zone_rekey(zone, ISC_FALSE); cleanup: if (zone != NULL) dns_zone_detach(&zone); if (raw != NULL) dns_zone_detach(&raw); if (pview != NULL) dns_view_detach(&pview); return (result); } /* * Configure built-in zone for storing managed-key data. */ #define KEYZONE "managed-keys.bind" #define MKEYS ".mkeys" static isc_result_t add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) { isc_result_t result; dns_view_t *pview = NULL; dns_zone_t *zone = NULL; dns_acl_t *none = NULL; char filename[PATH_MAX]; char buffer[ISC_SHA256_DIGESTSTRINGLENGTH + sizeof(MKEYS)]; int n; REQUIRE(view != NULL); /* See if we can re-use an existing keydata zone. */ result = dns_viewlist_find(&ns_g_server->viewlist, view->name, view->rdclass, &pview); if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) return (result); if (pview != NULL && pview->managed_keys != NULL) { dns_zone_attach(pview->managed_keys, &view->managed_keys); dns_zone_setview(pview->managed_keys, view); dns_view_detach(&pview); dns_zone_synckeyzone(view->managed_keys); return (ISC_R_SUCCESS); } /* No existing keydata zone was found; create one */ CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr, &zone)); CHECK(dns_zone_setorigin(zone, dns_rootname)); isc_sha256_data((void *)view->name, strlen(view->name), buffer); strcat(buffer, MKEYS); n = snprintf(filename, sizeof(filename), "%s%s%s", directory ? directory : "", directory ? "/" : "", strcmp(view->name, "_default") == 0 ? KEYZONE : buffer); if (n < 0 || (size_t)n >= sizeof(filename)) { result = (n < 0) ? ISC_R_FAILURE : ISC_R_NOSPACE; goto cleanup; } CHECK(dns_zone_setfile(zone, filename)); dns_zone_setview(zone, view); dns_zone_settype(zone, dns_zone_key); dns_zone_setclass(zone, view->rdclass); CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone)); if (view->acache != NULL) dns_zone_setacache(zone, view->acache); CHECK(dns_acl_none(mctx, &none)); dns_zone_setqueryacl(zone, none); dns_zone_setqueryonacl(zone, none); dns_acl_detach(&none); dns_zone_setdialup(zone, dns_dialuptype_no); dns_zone_setnotifytype(zone, dns_notifytype_no); dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE); dns_zone_setjournalsize(zone, 0); dns_zone_setstats(zone, ns_g_server->zonestats); CHECK(setquerystats(zone, mctx, dns_zonestat_none)); if (view->managed_keys != NULL) dns_zone_detach(&view->managed_keys); dns_zone_attach(zone, &view->managed_keys); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "set up managed keys zone for view %s, file '%s'", view->name, filename); cleanup: if (zone != NULL) dns_zone_detach(&zone); if (none != NULL) dns_acl_detach(&none); return (result); } /* * Configure a single server quota. */ static void configure_server_quota(const cfg_obj_t **maps, const char *name, isc_quota_t *quota) { const cfg_obj_t *obj = NULL; isc_result_t result; result = ns_config_get(maps, name, &obj); INSIST(result == ISC_R_SUCCESS); isc_quota_max(quota, cfg_obj_asuint32(obj)); } /* * This function is called as soon as the 'directory' statement has been * parsed. This can be extended to support other options if necessary. */ static isc_result_t directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) { isc_result_t result; const char *directory; REQUIRE(strcasecmp("directory", clausename) == 0); UNUSED(arg); UNUSED(clausename); /* * Change directory. */ directory = cfg_obj_asstring(obj); if (! isc_file_ischdiridempotent(directory)) cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING, "option 'directory' contains relative path '%s'", directory); result = isc_dir_chdir(directory); if (result != ISC_R_SUCCESS) { cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, "change directory to '%s' failed: %s", directory, isc_result_totext(result)); return (result); } return (ISC_R_SUCCESS); } static void scan_interfaces(ns_server_t *server, isc_boolean_t verbose) { isc_boolean_t match_mapped = server->aclenv.match_mapped; ns_interfacemgr_scan(server->interfacemgr, verbose); /* * Update the "localhost" and "localnets" ACLs to match the * current set of network interfaces. */ dns_aclenv_copy(&server->aclenv, ns_interfacemgr_getaclenv(server->interfacemgr)); server->aclenv.match_mapped = match_mapped; } static isc_result_t add_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr, isc_boolean_t wcardport_ok) { ns_listenelt_t *lelt = NULL; dns_acl_t *src_acl = NULL; isc_result_t result; isc_sockaddr_t any_sa6; isc_netaddr_t netaddr; REQUIRE(isc_sockaddr_pf(addr) == AF_INET6); isc_sockaddr_any6(&any_sa6); if (!isc_sockaddr_equal(&any_sa6, addr) && (wcardport_ok || isc_sockaddr_getport(addr) != 0)) { isc_netaddr_fromin6(&netaddr, &addr->type.sin6.sin6_addr); result = dns_acl_create(mctx, 0, &src_acl); if (result != ISC_R_SUCCESS) return (result); result = dns_iptable_addprefix(src_acl->iptable, &netaddr, 128, ISC_TRUE); if (result != ISC_R_SUCCESS) goto clean; result = ns_listenelt_create(mctx, isc_sockaddr_getport(addr), src_acl, &lelt); if (result != ISC_R_SUCCESS) goto clean; ISC_LIST_APPEND(list->elts, lelt, link); } return (ISC_R_SUCCESS); clean: INSIST(lelt == NULL); dns_acl_detach(&src_acl); return (result); } /* * Make a list of xxx-source addresses and call ns_interfacemgr_adjust() * to update the listening interfaces accordingly. * We currently only consider IPv6, because this only affects IPv6 wildcard * sockets. */ static void adjust_interfaces(ns_server_t *server, isc_mem_t *mctx) { isc_result_t result; ns_listenlist_t *list = NULL; dns_view_t *view; dns_zone_t *zone, *next; isc_sockaddr_t addr, *addrp; result = ns_listenlist_create(mctx, &list); if (result != ISC_R_SUCCESS) return; for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { dns_dispatch_t *dispatch6; dispatch6 = dns_resolver_dispatchv6(view->resolver); if (dispatch6 == NULL) continue; result = dns_dispatch_getlocaladdress(dispatch6, &addr); if (result != ISC_R_SUCCESS) goto fail; /* * We always add non-wildcard address regardless of whether * the port is 'any' (the fourth arg is TRUE): if the port is * specific, we need to add it since it may conflict with a * listening interface; if it's zero, we'll dynamically open * query ports, and some of them may override an existing * wildcard IPv6 port. */ result = add_listenelt(mctx, list, &addr, ISC_TRUE); if (result != ISC_R_SUCCESS) goto fail; } zone = NULL; for (result = dns_zone_first(server->zonemgr, &zone); result == ISC_R_SUCCESS; next = NULL, result = dns_zone_next(zone, &next), zone = next) { dns_view_t *zoneview; /* * At this point the zone list may contain a stale zone * just removed from the configuration. To see the validity, * check if the corresponding view is in our current view list. * There may also be old zones that are still in the process * of shutting down and have detached from their old view * (zoneview == NULL). */ zoneview = dns_zone_getview(zone); if (zoneview == NULL) continue; for (view = ISC_LIST_HEAD(server->viewlist); view != NULL && view != zoneview; view = ISC_LIST_NEXT(view, link)) ; if (view == NULL) continue; addrp = dns_zone_getnotifysrc6(zone); result = add_listenelt(mctx, list, addrp, ISC_FALSE); if (result != ISC_R_SUCCESS) goto fail; addrp = dns_zone_getxfrsource6(zone); result = add_listenelt(mctx, list, addrp, ISC_FALSE); if (result != ISC_R_SUCCESS) goto fail; } ns_interfacemgr_adjust(server->interfacemgr, list, ISC_TRUE); clean: ns_listenlist_detach(&list); return; fail: /* * Even when we failed the procedure, most of other interfaces * should work correctly. We therefore just warn it. */ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "could not adjust the listen-on list; " "some interfaces may not work"); goto clean; } /* * This event callback is invoked to do periodic network * interface scanning. */ static void interface_timer_tick(isc_task_t *task, isc_event_t *event) { isc_result_t result; ns_server_t *server = (ns_server_t *) event->ev_arg; INSIST(task == server->task); UNUSED(task); isc_event_free(&event); /* * XXX should scan interfaces unlocked and get exclusive access * only to replace ACLs. */ result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); scan_interfaces(server, ISC_FALSE); isc_task_endexclusive(server->task); } static void heartbeat_timer_tick(isc_task_t *task, isc_event_t *event) { ns_server_t *server = (ns_server_t *) event->ev_arg; dns_view_t *view; UNUSED(task); isc_event_free(&event); view = ISC_LIST_HEAD(server->viewlist); while (view != NULL) { dns_view_dialup(view); view = ISC_LIST_NEXT(view, link); } } static void pps_timer_tick(isc_task_t *task, isc_event_t *event) { static unsigned int oldrequests = 0; unsigned int requests = ns_client_requests; UNUSED(task); isc_event_free(&event); /* * Don't worry about wrapping as the overflow result will be right. */ dns_pps = (requests - oldrequests) / 1200; oldrequests = requests; } /* * Replace the current value of '*field', a dynamically allocated * string or NULL, with a dynamically allocated copy of the * null-terminated string pointed to by 'value', or NULL. */ static isc_result_t setstring(ns_server_t *server, char **field, const char *value) { char *copy; if (value != NULL) { copy = isc_mem_strdup(server->mctx, value); if (copy == NULL) return (ISC_R_NOMEMORY); } else { copy = NULL; } if (*field != NULL) isc_mem_free(server->mctx, *field); *field = copy; return (ISC_R_SUCCESS); } /* * Replace the current value of '*field', a dynamically allocated * string or NULL, with another dynamically allocated string * or NULL if whether 'obj' is a string or void value, respectively. */ static isc_result_t setoptstring(ns_server_t *server, char **field, const cfg_obj_t *obj) { if (cfg_obj_isvoid(obj)) return (setstring(server, field, NULL)); else return (setstring(server, field, cfg_obj_asstring(obj))); } static void set_limit(const cfg_obj_t **maps, const char *configname, const char *description, isc_resource_t resourceid, isc_resourcevalue_t defaultvalue) { const cfg_obj_t *obj = NULL; const char *resource; isc_resourcevalue_t value; isc_result_t result; if (ns_config_get(maps, configname, &obj) != ISC_R_SUCCESS) return; if (cfg_obj_isstring(obj)) { resource = cfg_obj_asstring(obj); if (strcasecmp(resource, "unlimited") == 0) value = ISC_RESOURCE_UNLIMITED; else { INSIST(strcasecmp(resource, "default") == 0); value = defaultvalue; } } else value = cfg_obj_asuint64(obj); result = isc_resource_setlimit(resourceid, value); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, result == ISC_R_SUCCESS ? ISC_LOG_DEBUG(3) : ISC_LOG_WARNING, "set maximum %s to %" ISC_PRINT_QUADFORMAT "u: %s", description, value, isc_result_totext(result)); } #define SETLIMIT(cfgvar, resource, description) \ set_limit(maps, cfgvar, description, isc_resource_ ## resource, \ ns_g_init ## resource) static void set_limits(const cfg_obj_t **maps) { SETLIMIT("stacksize", stacksize, "stack size"); SETLIMIT("datasize", datasize, "data size"); SETLIMIT("coresize", coresize, "core size"); SETLIMIT("files", openfiles, "open files"); } static void portset_fromconf(isc_portset_t *portset, const cfg_obj_t *ports, isc_boolean_t positive) { const cfg_listelt_t *element; for (element = cfg_list_first(ports); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *obj = cfg_listelt_value(element); if (cfg_obj_isuint32(obj)) { in_port_t port = (in_port_t)cfg_obj_asuint32(obj); if (positive) isc_portset_add(portset, port); else isc_portset_remove(portset, port); } else { const cfg_obj_t *obj_loport, *obj_hiport; in_port_t loport, hiport; obj_loport = cfg_tuple_get(obj, "loport"); loport = (in_port_t)cfg_obj_asuint32(obj_loport); obj_hiport = cfg_tuple_get(obj, "hiport"); hiport = (in_port_t)cfg_obj_asuint32(obj_hiport); if (positive) isc_portset_addrange(portset, loport, hiport); else { isc_portset_removerange(portset, loport, hiport); } } } } static isc_result_t removed(dns_zone_t *zone, void *uap) { const char *type; if (dns_zone_getview(zone) != uap) return (ISC_R_SUCCESS); switch (dns_zone_gettype(zone)) { case dns_zone_master: type = "master"; break; case dns_zone_slave: type = "slave"; break; case dns_zone_stub: type = "stub"; break; case dns_zone_redirect: type = "redirect"; break; default: type = "other"; break; } dns_zone_log(zone, ISC_LOG_INFO, "(%s) removed", type); return (ISC_R_SUCCESS); } static void cleanup_session_key(ns_server_t *server, isc_mem_t *mctx) { if (server->session_keyfile != NULL) { isc_file_remove(server->session_keyfile); isc_mem_free(mctx, server->session_keyfile); server->session_keyfile = NULL; } if (server->session_keyname != NULL) { if (dns_name_dynamic(server->session_keyname)) dns_name_free(server->session_keyname, mctx); isc_mem_put(mctx, server->session_keyname, sizeof(dns_name_t)); server->session_keyname = NULL; } if (server->sessionkey != NULL) dns_tsigkey_detach(&server->sessionkey); server->session_keyalg = DST_ALG_UNKNOWN; server->session_keybits = 0; } static isc_result_t generate_session_key(const char *filename, const char *keynamestr, dns_name_t *keyname, const char *algstr, dns_name_t *algname, unsigned int algtype, isc_uint16_t bits, isc_mem_t *mctx, dns_tsigkey_t **tsigkeyp) { isc_result_t result = ISC_R_SUCCESS; dst_key_t *key = NULL; isc_buffer_t key_txtbuffer; isc_buffer_t key_rawbuffer; char key_txtsecret[256]; char key_rawsecret[64]; isc_region_t key_rawregion; isc_stdtime_t now; dns_tsigkey_t *tsigkey = NULL; FILE *fp = NULL; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "generating session key for dynamic DNS"); /* generate key */ result = dst_key_generate(keyname, algtype, bits, 1, 0, DNS_KEYPROTO_ANY, dns_rdataclass_in, mctx, &key); if (result != ISC_R_SUCCESS) return (result); /* * Dump the key to the buffer for later use. Should be done before * we transfer the ownership of key to tsigkey. */ isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret)); CHECK(dst_key_tobuffer(key, &key_rawbuffer)); isc_buffer_usedregion(&key_rawbuffer, &key_rawregion); isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret)); CHECK(isc_base64_totext(&key_rawregion, -1, "", &key_txtbuffer)); /* Store the key in tsigkey. */ isc_stdtime_get(&now); CHECK(dns_tsigkey_createfromkey(dst_key_name(key), algname, key, ISC_FALSE, NULL, now, now, mctx, NULL, &tsigkey)); /* Dump the key to the key file. */ fp = ns_os_openfile(filename, S_IRUSR|S_IWUSR, ISC_TRUE); if (fp == NULL) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "could not create %s", filename); result = ISC_R_NOPERM; goto cleanup; } fprintf(fp, "key \"%s\" {\n" "\talgorithm %s;\n" "\tsecret \"%.*s\";\n};\n", keynamestr, algstr, (int) isc_buffer_usedlength(&key_txtbuffer), (char*) isc_buffer_base(&key_txtbuffer)); RUNTIME_CHECK(isc_stdio_flush(fp) == ISC_R_SUCCESS); RUNTIME_CHECK(isc_stdio_close(fp) == ISC_R_SUCCESS); dst_key_free(&key); *tsigkeyp = tsigkey; return (ISC_R_SUCCESS); cleanup: isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "failed to generate session key " "for dynamic DNS: %s", isc_result_totext(result)); if (tsigkey != NULL) dns_tsigkey_detach(&tsigkey); if (key != NULL) dst_key_free(&key); return (result); } static isc_result_t configure_session_key(const cfg_obj_t **maps, ns_server_t *server, isc_mem_t *mctx) { const char *keyfile, *keynamestr, *algstr; unsigned int algtype; dns_fixedname_t fname; dns_name_t *keyname, *algname; isc_buffer_t buffer; isc_uint16_t bits; const cfg_obj_t *obj; isc_boolean_t need_deleteold = ISC_FALSE; isc_boolean_t need_createnew = ISC_FALSE; isc_result_t result; obj = NULL; result = ns_config_get(maps, "session-keyfile", &obj); if (result == ISC_R_SUCCESS) { if (cfg_obj_isvoid(obj)) keyfile = NULL; /* disable it */ else keyfile = cfg_obj_asstring(obj); } else keyfile = ns_g_defaultsessionkeyfile; obj = NULL; result = ns_config_get(maps, "session-keyname", &obj); INSIST(result == ISC_R_SUCCESS); keynamestr = cfg_obj_asstring(obj); dns_fixedname_init(&fname); isc_buffer_constinit(&buffer, keynamestr, strlen(keynamestr)); isc_buffer_add(&buffer, strlen(keynamestr)); keyname = dns_fixedname_name(&fname); result = dns_name_fromtext(keyname, &buffer, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) return (result); obj = NULL; result = ns_config_get(maps, "session-keyalg", &obj); INSIST(result == ISC_R_SUCCESS); algstr = cfg_obj_asstring(obj); algname = NULL; result = ns_config_getkeyalgorithm2(algstr, &algname, &algtype, &bits); if (result != ISC_R_SUCCESS) { const char *s = " (keeping current key)"; cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, "session-keyalg: " "unsupported or unknown algorithm '%s'%s", algstr, server->session_keyfile != NULL ? s : ""); return (result); } /* See if we need to (re)generate a new key. */ if (keyfile == NULL) { if (server->session_keyfile != NULL) need_deleteold = ISC_TRUE; } else if (server->session_keyfile == NULL) need_createnew = ISC_TRUE; else if (strcmp(keyfile, server->session_keyfile) != 0 || !dns_name_equal(server->session_keyname, keyname) || server->session_keyalg != algtype || server->session_keybits != bits) { need_deleteold = ISC_TRUE; need_createnew = ISC_TRUE; } if (need_deleteold) { INSIST(server->session_keyfile != NULL); INSIST(server->session_keyname != NULL); INSIST(server->sessionkey != NULL); cleanup_session_key(server, mctx); } if (need_createnew) { INSIST(server->sessionkey == NULL); INSIST(server->session_keyfile == NULL); INSIST(server->session_keyname == NULL); INSIST(server->session_keyalg == DST_ALG_UNKNOWN); INSIST(server->session_keybits == 0); server->session_keyname = isc_mem_get(mctx, sizeof(dns_name_t)); if (server->session_keyname == NULL) goto cleanup; dns_name_init(server->session_keyname, NULL); CHECK(dns_name_dup(keyname, mctx, server->session_keyname)); server->session_keyfile = isc_mem_strdup(mctx, keyfile); if (server->session_keyfile == NULL) goto cleanup; server->session_keyalg = algtype; server->session_keybits = bits; CHECK(generate_session_key(keyfile, keynamestr, keyname, algstr, algname, algtype, bits, mctx, &server->sessionkey)); } return (result); cleanup: cleanup_session_key(server, mctx); return (result); } static isc_result_t setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, cfg_parser_t *parser, cfg_aclconfctx_t *actx) { isc_result_t result = ISC_R_SUCCESS; isc_boolean_t allow = ISC_FALSE; struct cfg_context *nzcfg = NULL; cfg_parser_t *nzparser = NULL; cfg_obj_t *nzconfig = NULL; const cfg_obj_t *maps[4]; const cfg_obj_t *options = NULL, *voptions = NULL; const cfg_obj_t *nz = NULL; int i = 0; REQUIRE (config != NULL); if (vconfig != NULL) voptions = cfg_tuple_get(vconfig, "options"); if (voptions != NULL) maps[i++] = voptions; result = cfg_map_get(config, "options", &options); if (result == ISC_R_SUCCESS) maps[i++] = options; maps[i++] = ns_g_defaults; maps[i] = NULL; result = ns_config_get(maps, "allow-new-zones", &nz); if (result == ISC_R_SUCCESS) allow = cfg_obj_asboolean(nz); if (!allow) { dns_view_setnewzones(view, ISC_FALSE, NULL, NULL); return (ISC_R_SUCCESS); } nzcfg = isc_mem_get(view->mctx, sizeof(*nzcfg)); if (nzcfg == NULL) { dns_view_setnewzones(view, ISC_FALSE, NULL, NULL); return (ISC_R_NOMEMORY); } dns_view_setnewzones(view, allow, nzcfg, newzone_cfgctx_destroy); memset(nzcfg, 0, sizeof(*nzcfg)); isc_mem_attach(view->mctx, &nzcfg->mctx); cfg_obj_attach(config, &nzcfg->config); cfg_parser_attach(parser, &nzcfg->parser); cfg_aclconfctx_attach(actx, &nzcfg->actx); /* * Attempt to create a parser and parse the newzones * file. If successful, preserve both; otherwise leave * them NULL. */ result = cfg_parser_create(view->mctx, ns_g_lctx, &nzparser); if (result == ISC_R_SUCCESS) result = cfg_parse_file(nzparser, view->new_zone_file, &cfg_type_newzones, &nzconfig); if (result == ISC_R_SUCCESS) { cfg_parser_attach(nzparser, &nzcfg->nzparser); cfg_obj_attach(nzconfig, &nzcfg->nzconfig); } if (nzparser != NULL) { if (nzconfig != NULL) cfg_obj_destroy(nzparser, &nzconfig); cfg_parser_destroy(&nzparser); } return (ISC_R_SUCCESS); } static int count_zones(const cfg_obj_t *conf) { const cfg_obj_t *zonelist = NULL; const cfg_listelt_t *element; int n = 0; REQUIRE(conf != NULL); cfg_map_get(conf, "zone", &zonelist); for (element = cfg_list_first(zonelist); element != NULL; element = cfg_list_next(element)) n++; return (n); } static isc_result_t load_configuration(const char *filename, ns_server_t *server, isc_boolean_t first_time) { cfg_obj_t *config = NULL, *bindkeys = NULL; cfg_parser_t *conf_parser = NULL, *bindkeys_parser = NULL; const cfg_listelt_t *element; const cfg_obj_t *builtin_views; const cfg_obj_t *maps[3]; const cfg_obj_t *obj; const cfg_obj_t *options; const cfg_obj_t *usev4ports, *avoidv4ports, *usev6ports, *avoidv6ports; const cfg_obj_t *views; dns_view_t *view = NULL; dns_view_t *view_next; dns_viewlist_t tmpviewlist; dns_viewlist_t viewlist, builtin_viewlist; in_port_t listen_port, udpport_low, udpport_high; int i; int num_zones = 0; isc_boolean_t exclusive = ISC_FALSE; isc_interval_t interval; isc_logconfig_t *logc = NULL; isc_portset_t *v4portset = NULL; isc_portset_t *v6portset = NULL; isc_resourcevalue_t nfiles; isc_result_t result; isc_uint32_t heartbeat_interval; isc_uint32_t interface_interval; isc_uint32_t reserved; isc_uint32_t udpsize; ns_cache_t *nsc; ns_cachelist_t cachelist, tmpcachelist; struct cfg_context *nzctx; unsigned int maxsocks; ISC_LIST_INIT(viewlist); ISC_LIST_INIT(builtin_viewlist); ISC_LIST_INIT(cachelist); /* Create the ACL configuration context */ if (ns_g_aclconfctx != NULL) cfg_aclconfctx_detach(&ns_g_aclconfctx); CHECK(cfg_aclconfctx_create(ns_g_mctx, &ns_g_aclconfctx)); /* * Parse the global default pseudo-config file. */ if (first_time) { CHECK(ns_config_parsedefaults(ns_g_parser, &ns_g_config)); RUNTIME_CHECK(cfg_map_get(ns_g_config, "options", &ns_g_defaults) == ISC_R_SUCCESS); } /* * Parse the configuration file using the new config code. */ result = ISC_R_FAILURE; config = NULL; /* * Unless this is lwresd with the -C option, parse the config file. */ if (!(ns_g_lwresdonly && lwresd_g_useresolvconf)) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "loading configuration from '%s'", filename); CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser)); cfg_parser_setcallback(conf_parser, directory_callback, NULL); result = cfg_parse_file(conf_parser, filename, &cfg_type_namedconf, &config); } /* * If this is lwresd with the -C option, or lwresd with no -C or -c * option where the above parsing failed, parse resolv.conf. */ if (ns_g_lwresdonly && (lwresd_g_useresolvconf || (!ns_g_conffileset && result == ISC_R_FILENOTFOUND))) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "loading configuration from '%s'", lwresd_g_resolvconffile); if (conf_parser != NULL) cfg_parser_destroy(&conf_parser); CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser)); result = ns_lwresd_parseeresolvconf(ns_g_mctx, conf_parser, &config); } CHECK(result); /* * Check the validity of the configuration. */ CHECK(bind9_check_namedconf(config, ns_g_lctx, ns_g_mctx)); /* * Fill in the maps array, used for resolving defaults. */ i = 0; options = NULL; result = cfg_map_get(config, "options", &options); if (result == ISC_R_SUCCESS) maps[i++] = options; maps[i++] = ns_g_defaults; maps[i] = NULL; /* * If bind.keys exists, load it. If "dnssec-lookaside auto" * is turned on, the keys found there will be used as default * trust anchors. */ obj = NULL; result = ns_config_get(maps, "bindkeys-file", &obj); INSIST(result == ISC_R_SUCCESS); CHECKM(setstring(server, &server->bindkeysfile, cfg_obj_asstring(obj)), "strdup"); if (access(server->bindkeysfile, R_OK) == 0) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "reading built-in trusted " "keys from file '%s'", server->bindkeysfile); CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &bindkeys_parser)); result = cfg_parse_file(bindkeys_parser, server->bindkeysfile, &cfg_type_bindkeys, &bindkeys); CHECK(result); } /* Ensure exclusive access to configuration data. */ if (!exclusive) { result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); exclusive = ISC_TRUE; } /* * Set process limits, which (usually) needs to be done as root. */ set_limits(maps); /* * Check if max number of open sockets that the system allows is * sufficiently large. Failing this condition is not necessarily fatal, * but may cause subsequent runtime failures for a busy recursive * server. */ result = isc_socketmgr_getmaxsockets(ns_g_socketmgr, &maxsocks); if (result != ISC_R_SUCCESS) maxsocks = 0; result = isc_resource_getcurlimit(isc_resource_openfiles, &nfiles); if (result == ISC_R_SUCCESS && (isc_resourcevalue_t)maxsocks > nfiles) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "max open files (%" ISC_PRINT_QUADFORMAT "u)" " is smaller than max sockets (%u)", nfiles, maxsocks); } /* * Set the number of socket reserved for TCP, stdio etc. */ obj = NULL; result = ns_config_get(maps, "reserved-sockets", &obj); INSIST(result == ISC_R_SUCCESS); reserved = cfg_obj_asuint32(obj); if (maxsocks != 0) { if (maxsocks < 128U) /* Prevent underflow. */ reserved = 0; else if (reserved > maxsocks - 128U) /* Minimum UDP space. */ reserved = maxsocks - 128; } /* Minimum TCP/stdio space. */ if (reserved < 128U) reserved = 128; if (reserved + 128U > maxsocks && maxsocks != 0) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "less than 128 UDP sockets available after " "applying 'reserved-sockets' and 'maxsockets'"); } isc__socketmgr_setreserved(ns_g_socketmgr, reserved); /* * Configure various server options. */ configure_server_quota(maps, "transfers-out", &server->xfroutquota); configure_server_quota(maps, "tcp-clients", &server->tcpquota); configure_server_quota(maps, "recursive-clients", &server->recursionquota); if (server->recursionquota.max > 1000) isc_quota_soft(&server->recursionquota, server->recursionquota.max - 100); else isc_quota_soft(&server->recursionquota, 0); CHECK(configure_view_acl(NULL, config, "blackhole", NULL, ns_g_aclconfctx, ns_g_mctx, &server->blackholeacl)); if (server->blackholeacl != NULL) dns_dispatchmgr_setblackhole(ns_g_dispatchmgr, server->blackholeacl); obj = NULL; result = ns_config_get(maps, "match-mapped-addresses", &obj); INSIST(result == ISC_R_SUCCESS); server->aclenv.match_mapped = cfg_obj_asboolean(obj); CHECKM(ns_statschannels_configure(ns_g_server, config, ns_g_aclconfctx), "configuring statistics server(s)"); /* * Configure sets of UDP query source ports. */ CHECKM(isc_portset_create(ns_g_mctx, &v4portset), "creating UDP port set"); CHECKM(isc_portset_create(ns_g_mctx, &v6portset), "creating UDP port set"); usev4ports = NULL; usev6ports = NULL; avoidv4ports = NULL; avoidv6ports = NULL; (void)ns_config_get(maps, "use-v4-udp-ports", &usev4ports); if (usev4ports != NULL) portset_fromconf(v4portset, usev4ports, ISC_TRUE); else { CHECKM(isc_net_getudpportrange(AF_INET, &udpport_low, &udpport_high), "get the default UDP/IPv4 port range"); if (udpport_low == udpport_high) isc_portset_add(v4portset, udpport_low); else { isc_portset_addrange(v4portset, udpport_low, udpport_high); } isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "using default UDP/IPv4 port range: [%d, %d]", udpport_low, udpport_high); } (void)ns_config_get(maps, "avoid-v4-udp-ports", &avoidv4ports); if (avoidv4ports != NULL) portset_fromconf(v4portset, avoidv4ports, ISC_FALSE); (void)ns_config_get(maps, "use-v6-udp-ports", &usev6ports); if (usev6ports != NULL) portset_fromconf(v6portset, usev6ports, ISC_TRUE); else { CHECKM(isc_net_getudpportrange(AF_INET6, &udpport_low, &udpport_high), "get the default UDP/IPv6 port range"); if (udpport_low == udpport_high) isc_portset_add(v6portset, udpport_low); else { isc_portset_addrange(v6portset, udpport_low, udpport_high); } isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "using default UDP/IPv6 port range: [%d, %d]", udpport_low, udpport_high); } (void)ns_config_get(maps, "avoid-v6-udp-ports", &avoidv6ports); if (avoidv6ports != NULL) portset_fromconf(v6portset, avoidv6ports, ISC_FALSE); dns_dispatchmgr_setavailports(ns_g_dispatchmgr, v4portset, v6portset); /* * Set the EDNS UDP size when we don't match a view. */ obj = NULL; result = ns_config_get(maps, "edns-udp-size", &obj); INSIST(result == ISC_R_SUCCESS); udpsize = cfg_obj_asuint32(obj); if (udpsize < 512) udpsize = 512; if (udpsize > 4096) udpsize = 4096; ns_g_udpsize = (isc_uint16_t)udpsize; /* * Configure the zone manager. */ obj = NULL; result = ns_config_get(maps, "transfers-in", &obj); INSIST(result == ISC_R_SUCCESS); dns_zonemgr_settransfersin(server->zonemgr, cfg_obj_asuint32(obj)); obj = NULL; result = ns_config_get(maps, "transfers-per-ns", &obj); INSIST(result == ISC_R_SUCCESS); dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj)); obj = NULL; result = ns_config_get(maps, "serial-query-rate", &obj); INSIST(result == ISC_R_SUCCESS); dns_zonemgr_setserialqueryrate(server->zonemgr, cfg_obj_asuint32(obj)); /* * Determine which port to use for listening for incoming connections. */ if (ns_g_port != 0) listen_port = ns_g_port; else CHECKM(ns_config_getport(config, &listen_port), "port"); /* * Find the listen queue depth. */ obj = NULL; result = ns_config_get(maps, "tcp-listen-queue", &obj); INSIST(result == ISC_R_SUCCESS); ns_g_listen = cfg_obj_asuint32(obj); if ((ns_g_listen > 0) && (ns_g_listen < 10)) ns_g_listen = 10; /* * Configure the interface manager according to the "listen-on" * statement. */ { const cfg_obj_t *clistenon = NULL; ns_listenlist_t *listenon = NULL; clistenon = NULL; /* * Even though listen-on is present in the default * configuration, we can't use it here, since it isn't * used if we're in lwresd mode. This way is easier. */ if (options != NULL) (void)cfg_map_get(options, "listen-on", &clistenon); if (clistenon != NULL) { /* check return code? */ (void)ns_listenlist_fromconfig(clistenon, config, ns_g_aclconfctx, ns_g_mctx, &listenon); } else if (!ns_g_lwresdonly) { /* * Not specified, use default. */ CHECK(ns_listenlist_default(ns_g_mctx, listen_port, ISC_TRUE, &listenon)); } if (listenon != NULL) { ns_interfacemgr_setlistenon4(server->interfacemgr, listenon); ns_listenlist_detach(&listenon); } } /* * Ditto for IPv6. */ { const cfg_obj_t *clistenon = NULL; ns_listenlist_t *listenon = NULL; if (options != NULL) (void)cfg_map_get(options, "listen-on-v6", &clistenon); if (clistenon != NULL) { /* check return code? */ (void)ns_listenlist_fromconfig(clistenon, config, ns_g_aclconfctx, ns_g_mctx, &listenon); } else if (!ns_g_lwresdonly) { isc_boolean_t enable; /* * Not specified, use default. */ enable = ISC_TF(isc_net_probeipv4() != ISC_R_SUCCESS); CHECK(ns_listenlist_default(ns_g_mctx, listen_port, enable, &listenon)); } if (listenon != NULL) { ns_interfacemgr_setlistenon6(server->interfacemgr, listenon); ns_listenlist_detach(&listenon); } } /* * Rescan the interface list to pick up changes in the * listen-on option. It's important that we do this before we try * to configure the query source, since the dispatcher we use might * be shared with an interface. */ scan_interfaces(server, ISC_TRUE); /* * Arrange for further interface scanning to occur periodically * as specified by the "interface-interval" option. */ obj = NULL; result = ns_config_get(maps, "interface-interval", &obj); INSIST(result == ISC_R_SUCCESS); interface_interval = cfg_obj_asuint32(obj) * 60; if (interface_interval == 0) { CHECK(isc_timer_reset(server->interface_timer, isc_timertype_inactive, NULL, NULL, ISC_TRUE)); } else if (server->interface_interval != interface_interval) { isc_interval_set(&interval, interface_interval, 0); CHECK(isc_timer_reset(server->interface_timer, isc_timertype_ticker, NULL, &interval, ISC_FALSE)); } server->interface_interval = interface_interval; /* * Configure the dialup heartbeat timer. */ obj = NULL; result = ns_config_get(maps, "heartbeat-interval", &obj); INSIST(result == ISC_R_SUCCESS); heartbeat_interval = cfg_obj_asuint32(obj) * 60; if (heartbeat_interval == 0) { CHECK(isc_timer_reset(server->heartbeat_timer, isc_timertype_inactive, NULL, NULL, ISC_TRUE)); } else if (server->heartbeat_interval != heartbeat_interval) { isc_interval_set(&interval, heartbeat_interval, 0); CHECK(isc_timer_reset(server->heartbeat_timer, isc_timertype_ticker, NULL, &interval, ISC_FALSE)); } server->heartbeat_interval = heartbeat_interval; isc_interval_set(&interval, 1200, 0); CHECK(isc_timer_reset(server->pps_timer, isc_timertype_ticker, NULL, &interval, ISC_FALSE)); /* * Write the PID file. */ obj = NULL; if (ns_config_get(maps, "pid-file", &obj) == ISC_R_SUCCESS) if (cfg_obj_isvoid(obj)) ns_os_writepidfile(NULL, first_time); else ns_os_writepidfile(cfg_obj_asstring(obj), first_time); else if (ns_g_lwresdonly) ns_os_writepidfile(lwresd_g_defaultpidfile, first_time); else ns_os_writepidfile(ns_g_defaultpidfile, first_time); /* * Configure the server-wide session key. This must be done before * configure views because zone configuration may need to know * session-keyname. * * Failure of session key generation isn't fatal at this time; if it * turns out that a session key is really needed but doesn't exist, * we'll treat it as a fatal error then. */ (void)configure_session_key(maps, server, ns_g_mctx); views = NULL; (void)cfg_map_get(config, "view", &views); /* * Create the views and count all the configured zones in * order to correctly size the zone manager's task table. * (We only count zones for configured views; the built-in * "bind" view can be ignored as it only adds a negligible * number of zones.) * * If we're allowing new zones, we need to be able to find the * new zone file and count those as well. So we setup the new * zone configuration context, but otherwise view configuration * waits until after the zone manager's task list has been sized. */ for (element = cfg_list_first(views); element != NULL; element = cfg_list_next(element)) { cfg_obj_t *vconfig = cfg_listelt_value(element); const cfg_obj_t *voptions = cfg_tuple_get(vconfig, "options"); view = NULL; CHECK(create_view(vconfig, &viewlist, &view)); INSIST(view != NULL); num_zones += count_zones(voptions); CHECK(setup_newzones(view, config, vconfig, conf_parser, ns_g_aclconfctx)); nzctx = view->new_zone_config; if (nzctx != NULL && nzctx->nzconfig != NULL) num_zones += count_zones(nzctx->nzconfig); dns_view_detach(&view); } /* * If there were no explicit views then we do the default * view here. */ if (views == NULL) { CHECK(create_view(NULL, &viewlist, &view)); INSIST(view != NULL); num_zones = count_zones(config); CHECK(setup_newzones(view, config, NULL, conf_parser, ns_g_aclconfctx)); nzctx = view->new_zone_config; if (nzctx != NULL && nzctx->nzconfig != NULL) num_zones += count_zones(nzctx->nzconfig); dns_view_detach(&view); } /* * Zones have been counted; set the zone manager task pool size. */ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "sizing zone task pool based on %d zones", num_zones); CHECK(dns_zonemgr_setsize(ns_g_server->zonemgr, num_zones)); /* * Configure and freeze all explicit views. Explicit * views that have zones were already created at parsing * time, but views with no zones must be created here. */ for (element = cfg_list_first(views); element != NULL; element = cfg_list_next(element)) { cfg_obj_t *vconfig = cfg_listelt_value(element); view = NULL; CHECK(find_view(vconfig, &viewlist, &view)); CHECK(configure_view(view, config, vconfig, &cachelist, bindkeys, ns_g_mctx, ns_g_aclconfctx, ISC_TRUE)); dns_view_freeze(view); dns_view_detach(&view); } /* * Make sure we have a default view if and only if there * were no explicit views. */ if (views == NULL) { view = NULL; CHECK(find_view(NULL, &viewlist, &view)); CHECK(configure_view(view, config, NULL, &cachelist, bindkeys, ns_g_mctx, ns_g_aclconfctx, ISC_TRUE)); dns_view_freeze(view); dns_view_detach(&view); } /* * Create (or recreate) the built-in views. */ builtin_views = NULL; RUNTIME_CHECK(cfg_map_get(ns_g_config, "view", &builtin_views) == ISC_R_SUCCESS); for (element = cfg_list_first(builtin_views); element != NULL; element = cfg_list_next(element)) { cfg_obj_t *vconfig = cfg_listelt_value(element); CHECK(create_view(vconfig, &builtin_viewlist, &view)); CHECK(configure_view(view, config, vconfig, &cachelist, bindkeys, ns_g_mctx, ns_g_aclconfctx, ISC_FALSE)); dns_view_freeze(view); dns_view_detach(&view); view = NULL; } /* Now combine the two viewlists into one */ ISC_LIST_APPENDLIST(viewlist, builtin_viewlist, link); /* Swap our new view list with the production one. */ tmpviewlist = server->viewlist; server->viewlist = viewlist; viewlist = tmpviewlist; /* Make the view list available to each of the views */ view = ISC_LIST_HEAD(server->viewlist); while (view != NULL) { view->viewlist = &server->viewlist; view = ISC_LIST_NEXT(view, link); } /* Swap our new cache list with the production one. */ tmpcachelist = server->cachelist; server->cachelist = cachelist; cachelist = tmpcachelist; /* Load the TKEY information from the configuration. */ if (options != NULL) { dns_tkeyctx_t *t = NULL; CHECKM(ns_tkeyctx_fromconfig(options, ns_g_mctx, ns_g_entropy, &t), "configuring TKEY"); if (server->tkeyctx != NULL) dns_tkeyctx_destroy(&server->tkeyctx); server->tkeyctx = t; } /* * Bind the control port(s). */ CHECKM(ns_controls_configure(ns_g_server->controls, config, ns_g_aclconfctx), "binding control channel(s)"); /* * Bind the lwresd port(s). */ CHECKM(ns_lwresd_configure(ns_g_mctx, config), "binding lightweight resolver ports"); /* * Open the source of entropy. */ if (first_time) { obj = NULL; result = ns_config_get(maps, "random-device", &obj); if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "no source of entropy found"); } else { const char *randomdev = cfg_obj_asstring(obj); result = isc_entropy_createfilesource(ns_g_entropy, randomdev); if (result != ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "could not open entropy source " "%s: %s", randomdev, isc_result_totext(result)); #ifdef PATH_RANDOMDEV if (ns_g_fallbackentropy != NULL) { if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "using pre-chroot entropy source " "%s", PATH_RANDOMDEV); isc_entropy_detach(&ns_g_entropy); isc_entropy_attach(ns_g_fallbackentropy, &ns_g_entropy); } isc_entropy_detach(&ns_g_fallbackentropy); } #endif } } /* * Relinquish root privileges. */ if (first_time) ns_os_changeuser(); /* * Check that the working directory is writable. */ if (access(".", W_OK) != 0) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "the working directory is not writable"); } /* * Configure the logging system. * * Do this after changing UID to make sure that any log * files specified in named.conf get created by the * unprivileged user, not root. */ if (ns_g_logstderr) { const cfg_obj_t *logobj = NULL; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "not using config file logging " "statement for logging due to " "-g option"); (void)cfg_map_get(config, "logging", &logobj); if (logobj != NULL) { result = ns_log_configure(NULL, logobj); if (result != ISC_R_SUCCESS) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "checking logging configuration " "failed: %s", isc_result_totext(result)); goto cleanup; } } } else { const cfg_obj_t *logobj = NULL; CHECKM(isc_logconfig_create(ns_g_lctx, &logc), "creating new logging configuration"); logobj = NULL; (void)cfg_map_get(config, "logging", &logobj); if (logobj != NULL) { CHECKM(ns_log_configure(logc, logobj), "configuring logging"); } else { CHECKM(ns_log_setdefaultchannels(logc), "setting up default logging channels"); CHECKM(ns_log_setunmatchedcategory(logc), "setting up default 'category unmatched'"); CHECKM(ns_log_setdefaultcategory(logc), "setting up default 'category default'"); } CHECKM(isc_logconfig_use(ns_g_lctx, logc), "installing logging configuration"); logc = NULL; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), "now using logging configuration from " "config file"); } /* * Set the default value of the query logging flag depending * whether a "queries" category has been defined. This is * a disgusting hack, but we need to do this for BIND 8 * compatibility. */ if (first_time) { const cfg_obj_t *logobj = NULL; const cfg_obj_t *categories = NULL; obj = NULL; if (ns_config_get(maps, "querylog", &obj) == ISC_R_SUCCESS) { server->log_queries = cfg_obj_asboolean(obj); } else { (void)cfg_map_get(config, "logging", &logobj); if (logobj != NULL) (void)cfg_map_get(logobj, "category", &categories); if (categories != NULL) { const cfg_listelt_t *element; for (element = cfg_list_first(categories); element != NULL; element = cfg_list_next(element)) { const cfg_obj_t *catobj; const char *str; obj = cfg_listelt_value(element); catobj = cfg_tuple_get(obj, "name"); str = cfg_obj_asstring(catobj); if (strcasecmp(str, "queries") == 0) server->log_queries = ISC_TRUE; } } } } obj = NULL; if (options != NULL && cfg_map_get(options, "memstatistics", &obj) == ISC_R_SUCCESS) ns_g_memstatistics = cfg_obj_asboolean(obj); else ns_g_memstatistics = ISC_TF((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0); obj = NULL; if (ns_config_get(maps, "memstatistics-file", &obj) == ISC_R_SUCCESS) ns_main_setmemstats(cfg_obj_asstring(obj)); else if (ns_g_memstatistics) ns_main_setmemstats("named.memstats"); else ns_main_setmemstats(NULL); obj = NULL; result = ns_config_get(maps, "statistics-file", &obj); INSIST(result == ISC_R_SUCCESS); CHECKM(setstring(server, &server->statsfile, cfg_obj_asstring(obj)), "strdup"); obj = NULL; result = ns_config_get(maps, "dump-file", &obj); INSIST(result == ISC_R_SUCCESS); CHECKM(setstring(server, &server->dumpfile, cfg_obj_asstring(obj)), "strdup"); obj = NULL; result = ns_config_get(maps, "secroots-file", &obj); INSIST(result == ISC_R_SUCCESS); CHECKM(setstring(server, &server->secrootsfile, cfg_obj_asstring(obj)), "strdup"); obj = NULL; result = ns_config_get(maps, "recursing-file", &obj); INSIST(result == ISC_R_SUCCESS); CHECKM(setstring(server, &server->recfile, cfg_obj_asstring(obj)), "strdup"); obj = NULL; result = ns_config_get(maps, "version", &obj); if (result == ISC_R_SUCCESS) { CHECKM(setoptstring(server, &server->version, obj), "strdup"); server->version_set = ISC_TRUE; } else { server->version_set = ISC_FALSE; } obj = NULL; result = ns_config_get(maps, "hostname", &obj); if (result == ISC_R_SUCCESS) { CHECKM(setoptstring(server, &server->hostname, obj), "strdup"); server->hostname_set = ISC_TRUE; } else { server->hostname_set = ISC_FALSE; } obj = NULL; result = ns_config_get(maps, "server-id", &obj); server->server_usehostname = ISC_FALSE; if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) { /* The parser translates "hostname" to ISC_TRUE */ server->server_usehostname = cfg_obj_asboolean(obj); result = setstring(server, &server->server_id, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); } else if (result == ISC_R_SUCCESS) { /* Found a quoted string */ CHECKM(setoptstring(server, &server->server_id, obj), "strdup"); } else { result = setstring(server, &server->server_id, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); } obj = NULL; result = ns_config_get(maps, "flush-zones-on-shutdown", &obj); if (result == ISC_R_SUCCESS) { server->flushonshutdown = cfg_obj_asboolean(obj); } else { server->flushonshutdown = ISC_FALSE; } result = ISC_R_SUCCESS; cleanup: if (logc != NULL) isc_logconfig_destroy(&logc); if (v4portset != NULL) isc_portset_destroy(ns_g_mctx, &v4portset); if (v6portset != NULL) isc_portset_destroy(ns_g_mctx, &v6portset); if (conf_parser != NULL) { if (config != NULL) cfg_obj_destroy(conf_parser, &config); cfg_parser_destroy(&conf_parser); } if (bindkeys_parser != NULL) { if (bindkeys != NULL) cfg_obj_destroy(bindkeys_parser, &bindkeys); cfg_parser_destroy(&bindkeys_parser); } if (view != NULL) dns_view_detach(&view); /* * This cleans up either the old production view list * or our temporary list depending on whether they * were swapped above or not. */ for (view = ISC_LIST_HEAD(viewlist); view != NULL; view = view_next) { view_next = ISC_LIST_NEXT(view, link); ISC_LIST_UNLINK(viewlist, view, link); if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) (void)dns_zt_apply(view->zonetable, ISC_FALSE, removed, view); dns_view_detach(&view); } /* Same cleanup for cache list. */ while ((nsc = ISC_LIST_HEAD(cachelist)) != NULL) { ISC_LIST_UNLINK(cachelist, nsc, link); dns_cache_detach(&nsc->cache); isc_mem_put(server->mctx, nsc, sizeof(*nsc)); } /* * Adjust the listening interfaces in accordance with the source * addresses specified in views and zones. */ if (isc_net_probeipv6() == ISC_R_SUCCESS) adjust_interfaces(server, ns_g_mctx); /* Relinquish exclusive access to configuration data. */ if (exclusive) isc_task_endexclusive(server->task); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), "load_configuration: %s", isc_result_totext(result)); return (result); } static isc_result_t view_loaded(void *arg) { isc_result_t result; ns_zoneload_t *zl = (ns_zoneload_t *) arg; ns_server_t *server = zl->server; unsigned int refs; /* * Force zone maintenance. Do this after loading * so that we know when we need to force AXFR of * slave zones whose master files are missing. * * We use the zoneload reference counter to let us * know when all views are finished. */ isc_refcount_decrement(&zl->refs, &refs); if (refs != 0) return (ISC_R_SUCCESS); isc_refcount_destroy(&zl->refs); isc_mem_put(server->mctx, zl, sizeof (*zl)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_NOTICE, "all zones loaded"); CHECKFATAL(dns_zonemgr_forcemaint(server->zonemgr), "forcing zone maintenance"); ns_os_started(); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_NOTICE, "running"); return (ISC_R_SUCCESS); } static isc_result_t load_zones(ns_server_t *server, isc_boolean_t init) { isc_result_t result; dns_view_t *view; ns_zoneload_t *zl; unsigned int refs = 0; zl = isc_mem_get(server->mctx, sizeof (*zl)); if (zl == NULL) return (ISC_R_NOMEMORY); zl->server = server; result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); isc_refcount_init(&zl->refs, 1); /* * Schedule zones to be loaded from disk. */ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (view->managed_keys != NULL) { result = dns_zone_load(view->managed_keys); if (result != ISC_R_SUCCESS && result != DNS_R_UPTODATE && result != DNS_R_CONTINUE) goto cleanup; } if (view->redirect != NULL) { result = dns_zone_load(view->redirect); if (result != ISC_R_SUCCESS && result != DNS_R_UPTODATE && result != DNS_R_CONTINUE) goto cleanup; } /* * 'dns_view_asyncload' calls view_loaded if there are no * zones. */ isc_refcount_increment(&zl->refs, NULL); CHECK(dns_view_asyncload(view, view_loaded, zl)); } cleanup: isc_refcount_decrement(&zl->refs, &refs); if (refs == 0) { isc_refcount_destroy(&zl->refs); isc_mem_put(server->mctx, zl, sizeof (*zl)); } else if (init) { /* * Place the task manager into privileged mode. This * ensures that after we leave task-exclusive mode, no * other tasks will be able to run except for the ones * that are loading zones. (This should only be done during * the initial server setup; it isn't necessary during * a reload.) */ isc_taskmgr_setmode(ns_g_taskmgr, isc_taskmgrmode_privileged); } isc_task_endexclusive(server->task); return (result); } static isc_result_t load_new_zones(ns_server_t *server, isc_boolean_t stop) { isc_result_t result; dns_view_t *view; result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); /* * Load zone data from disk. */ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { CHECK(dns_view_loadnew(view, stop)); /* Load managed-keys data */ if (view->managed_keys != NULL) CHECK(dns_zone_loadnew(view->managed_keys)); if (view->redirect != NULL) CHECK(dns_zone_loadnew(view->redirect)); } /* * Resume zone XFRs. */ dns_zonemgr_resumexfrs(server->zonemgr); cleanup: isc_task_endexclusive(server->task); return (result); } static void run_server(isc_task_t *task, isc_event_t *event) { isc_result_t result; ns_server_t *server = (ns_server_t *)event->ev_arg; INSIST(task == server->task); isc_event_free(&event); CHECKFATAL(dns_dispatchmgr_create(ns_g_mctx, ns_g_entropy, &ns_g_dispatchmgr), "creating dispatch manager"); dns_dispatchmgr_setstats(ns_g_dispatchmgr, server->resolverstats); CHECKFATAL(ns_interfacemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_socketmgr, ns_g_dispatchmgr, &server->interfacemgr), "creating interface manager"); CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive, NULL, NULL, server->task, interface_timer_tick, server, &server->interface_timer), "creating interface timer"); CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive, NULL, NULL, server->task, heartbeat_timer_tick, server, &server->heartbeat_timer), "creating heartbeat timer"); CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive, NULL, NULL, server->task, pps_timer_tick, server, &server->pps_timer), "creating pps timer"); CHECKFATAL(cfg_parser_create(ns_g_mctx, NULL, &ns_g_parser), "creating default configuration parser"); if (ns_g_lwresdonly) CHECKFATAL(load_configuration(lwresd_g_conffile, server, ISC_TRUE), "loading configuration"); else CHECKFATAL(load_configuration(ns_g_conffile, server, ISC_TRUE), "loading configuration"); isc_hash_init(); CHECKFATAL(load_zones(server, ISC_TRUE), "loading zones"); } void ns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush) { REQUIRE(NS_SERVER_VALID(server)); server->flushonshutdown = flush; } static void shutdown_server(isc_task_t *task, isc_event_t *event) { isc_result_t result; dns_view_t *view, *view_next; ns_server_t *server = (ns_server_t *)event->ev_arg; isc_boolean_t flush = server->flushonshutdown; ns_cache_t *nsc; UNUSED(task); INSIST(task == server->task); result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "shutting down%s", flush ? ": flushing changes" : ""); ns_statschannels_shutdown(server); ns_controls_shutdown(server->controls); end_reserved_dispatches(server, ISC_TRUE); cleanup_session_key(server, server->mctx); if (ns_g_aclconfctx != NULL) cfg_aclconfctx_detach(&ns_g_aclconfctx); cfg_obj_destroy(ns_g_parser, &ns_g_config); cfg_parser_destroy(&ns_g_parser); for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = view_next) { view_next = ISC_LIST_NEXT(view, link); ISC_LIST_UNLINK(server->viewlist, view, link); if (flush) dns_view_flushanddetach(&view); else dns_view_detach(&view); } while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) { ISC_LIST_UNLINK(server->cachelist, nsc, link); dns_cache_detach(&nsc->cache); isc_mem_put(server->mctx, nsc, sizeof(*nsc)); } isc_timer_detach(&server->interface_timer); isc_timer_detach(&server->heartbeat_timer); isc_timer_detach(&server->pps_timer); ns_interfacemgr_shutdown(server->interfacemgr); ns_interfacemgr_detach(&server->interfacemgr); dns_dispatchmgr_destroy(&ns_g_dispatchmgr); dns_zonemgr_shutdown(server->zonemgr); if (ns_g_sessionkey != NULL) { dns_tsigkey_detach(&ns_g_sessionkey); dns_name_free(&ns_g_sessionkeyname, server->mctx); } if (server->blackholeacl != NULL) dns_acl_detach(&server->blackholeacl); dns_db_detach(&server->in_roothints); isc_task_endexclusive(server->task); isc_task_detach(&server->task); isc_event_free(&event); } void ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) { isc_result_t result; ns_server_t *server = isc_mem_get(mctx, sizeof(*server)); if (server == NULL) fatal("allocating server object", ISC_R_NOMEMORY); server->mctx = mctx; server->task = NULL; /* Initialize configuration data with default values. */ result = isc_quota_init(&server->xfroutquota, 10); RUNTIME_CHECK(result == ISC_R_SUCCESS); result = isc_quota_init(&server->tcpquota, 10); RUNTIME_CHECK(result == ISC_R_SUCCESS); result = isc_quota_init(&server->recursionquota, 100); RUNTIME_CHECK(result == ISC_R_SUCCESS); result = dns_aclenv_init(mctx, &server->aclenv); RUNTIME_CHECK(result == ISC_R_SUCCESS); /* Initialize server data structures. */ server->zonemgr = NULL; server->interfacemgr = NULL; ISC_LIST_INIT(server->viewlist); server->in_roothints = NULL; server->blackholeacl = NULL; CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL, &server->in_roothints), "setting up root hints"); CHECKFATAL(isc_mutex_init(&server->reload_event_lock), "initializing reload event lock"); server->reload_event = isc_event_allocate(ns_g_mctx, server, NS_EVENT_RELOAD, ns_server_reload, server, sizeof(isc_event_t)); CHECKFATAL(server->reload_event == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, "allocating reload event"); CHECKFATAL(dst_lib_init2(ns_g_mctx, ns_g_entropy, ns_g_engine, ISC_ENTROPY_GOODONLY), "initializing DST"); server->tkeyctx = NULL; CHECKFATAL(dns_tkeyctx_create(ns_g_mctx, ns_g_entropy, &server->tkeyctx), "creating TKEY context"); /* * Setup the server task, which is responsible for coordinating * startup and shutdown of the server, as well as all exclusive * tasks. */ CHECKFATAL(isc_task_create(ns_g_taskmgr, 0, &server->task), "creating server task"); isc_task_setname(server->task, "server", server); isc_taskmgr_setexcltask(ns_g_taskmgr, server->task); CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server), "isc_task_onshutdown"); CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server), "isc_app_onrun"); server->interface_timer = NULL; server->heartbeat_timer = NULL; server->pps_timer = NULL; server->interface_interval = 0; server->heartbeat_interval = 0; CHECKFATAL(dns_zonemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_timermgr, ns_g_socketmgr, &server->zonemgr), "dns_zonemgr_create"); CHECKFATAL(dns_zonemgr_setsize(server->zonemgr, 1000), "dns_zonemgr_setsize"); server->statsfile = isc_mem_strdup(server->mctx, "named.stats"); CHECKFATAL(server->statsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, "isc_mem_strdup"); server->nsstats = NULL; server->rcvquerystats = NULL; server->opcodestats = NULL; server->zonestats = NULL; server->resolverstats = NULL; server->sockstats = NULL; CHECKFATAL(isc_stats_create(server->mctx, &server->sockstats, isc_sockstatscounter_max), "isc_stats_create"); isc_socketmgr_setstats(ns_g_socketmgr, server->sockstats); server->bindkeysfile = isc_mem_strdup(server->mctx, "bind.keys"); CHECKFATAL(server->bindkeysfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, "isc_mem_strdup"); server->dumpfile = isc_mem_strdup(server->mctx, "named_dump.db"); CHECKFATAL(server->dumpfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, "isc_mem_strdup"); server->secrootsfile = isc_mem_strdup(server->mctx, "named.secroots"); CHECKFATAL(server->secrootsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, "isc_mem_strdup"); server->recfile = isc_mem_strdup(server->mctx, "named.recursing"); CHECKFATAL(server->recfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS, "isc_mem_strdup"); server->hostname_set = ISC_FALSE; server->hostname = NULL; server->version_set = ISC_FALSE; server->version = NULL; server->server_usehostname = ISC_FALSE; server->server_id = NULL; CHECKFATAL(isc_stats_create(ns_g_mctx, &server->nsstats, dns_nsstatscounter_max), "dns_stats_create (server)"); CHECKFATAL(dns_rdatatypestats_create(ns_g_mctx, &server->rcvquerystats), "dns_stats_create (rcvquery)"); CHECKFATAL(dns_opcodestats_create(ns_g_mctx, &server->opcodestats), "dns_stats_create (opcode)"); CHECKFATAL(isc_stats_create(ns_g_mctx, &server->zonestats, dns_zonestatscounter_max), "dns_stats_create (zone)"); CHECKFATAL(isc_stats_create(ns_g_mctx, &server->resolverstats, dns_resstatscounter_max), "dns_stats_create (resolver)"); server->flushonshutdown = ISC_FALSE; server->log_queries = ISC_FALSE; server->controls = NULL; CHECKFATAL(ns_controls_create(server, &server->controls), "ns_controls_create"); server->dispatchgen = 0; ISC_LIST_INIT(server->dispatches); ISC_LIST_INIT(server->statschannels); ISC_LIST_INIT(server->cachelist); server->sessionkey = NULL; server->session_keyfile = NULL; server->session_keyname = NULL; server->session_keyalg = DST_ALG_UNKNOWN; server->session_keybits = 0; server->magic = NS_SERVER_MAGIC; *serverp = server; } void ns_server_destroy(ns_server_t **serverp) { ns_server_t *server = *serverp; REQUIRE(NS_SERVER_VALID(server)); ns_controls_destroy(&server->controls); isc_stats_detach(&server->nsstats); dns_stats_detach(&server->rcvquerystats); dns_stats_detach(&server->opcodestats); isc_stats_detach(&server->zonestats); isc_stats_detach(&server->resolverstats); isc_stats_detach(&server->sockstats); isc_mem_free(server->mctx, server->statsfile); isc_mem_free(server->mctx, server->bindkeysfile); isc_mem_free(server->mctx, server->dumpfile); isc_mem_free(server->mctx, server->secrootsfile); isc_mem_free(server->mctx, server->recfile); if (server->version != NULL) isc_mem_free(server->mctx, server->version); if (server->hostname != NULL) isc_mem_free(server->mctx, server->hostname); if (server->server_id != NULL) isc_mem_free(server->mctx, server->server_id); if (server->zonemgr != NULL) dns_zonemgr_detach(&server->zonemgr); if (server->tkeyctx != NULL) dns_tkeyctx_destroy(&server->tkeyctx); dst_lib_destroy(); isc_event_free(&server->reload_event); INSIST(ISC_LIST_EMPTY(server->viewlist)); INSIST(ISC_LIST_EMPTY(server->cachelist)); dns_aclenv_destroy(&server->aclenv); isc_quota_destroy(&server->recursionquota); isc_quota_destroy(&server->tcpquota); isc_quota_destroy(&server->xfroutquota); server->magic = 0; isc_mem_put(server->mctx, server, sizeof(*server)); *serverp = NULL; } static void fatal(const char *msg, isc_result_t result) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_CRITICAL, "%s: %s", msg, isc_result_totext(result)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_CRITICAL, "exiting (due to fatal error)"); exit(1); } static void start_reserved_dispatches(ns_server_t *server) { REQUIRE(NS_SERVER_VALID(server)); server->dispatchgen++; } static void end_reserved_dispatches(ns_server_t *server, isc_boolean_t all) { ns_dispatch_t *dispatch, *nextdispatch; REQUIRE(NS_SERVER_VALID(server)); for (dispatch = ISC_LIST_HEAD(server->dispatches); dispatch != NULL; dispatch = nextdispatch) { nextdispatch = ISC_LIST_NEXT(dispatch, link); if (!all && server->dispatchgen == dispatch-> dispatchgen) continue; ISC_LIST_UNLINK(server->dispatches, dispatch, link); dns_dispatch_detach(&dispatch->dispatch); isc_mem_put(server->mctx, dispatch, sizeof(*dispatch)); } } void ns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr) { ns_dispatch_t *dispatch; in_port_t port; char addrbuf[ISC_SOCKADDR_FORMATSIZE]; isc_result_t result; unsigned int attrs, attrmask; REQUIRE(NS_SERVER_VALID(server)); port = isc_sockaddr_getport(addr); if (port == 0 || port >= 1024) return; for (dispatch = ISC_LIST_HEAD(server->dispatches); dispatch != NULL; dispatch = ISC_LIST_NEXT(dispatch, link)) { if (isc_sockaddr_equal(&dispatch->addr, addr)) break; } if (dispatch != NULL) { dispatch->dispatchgen = server->dispatchgen; return; } dispatch = isc_mem_get(server->mctx, sizeof(*dispatch)); if (dispatch == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } dispatch->addr = *addr; dispatch->dispatchgen = server->dispatchgen; dispatch->dispatch = NULL; attrs = 0; attrs |= DNS_DISPATCHATTR_UDP; switch (isc_sockaddr_pf(addr)) { case AF_INET: attrs |= DNS_DISPATCHATTR_IPV4; break; case AF_INET6: attrs |= DNS_DISPATCHATTR_IPV6; break; default: result = ISC_R_NOTIMPLEMENTED; goto cleanup; } attrmask = 0; attrmask |= DNS_DISPATCHATTR_UDP; attrmask |= DNS_DISPATCHATTR_TCP; attrmask |= DNS_DISPATCHATTR_IPV4; attrmask |= DNS_DISPATCHATTR_IPV6; result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr, ns_g_taskmgr, &dispatch->addr, 4096, 1000, 32768, 16411, 16433, attrs, attrmask, &dispatch->dispatch); if (result != ISC_R_SUCCESS) goto cleanup; ISC_LIST_INITANDPREPEND(server->dispatches, dispatch, link); return; cleanup: if (dispatch != NULL) isc_mem_put(server->mctx, dispatch, sizeof(*dispatch)); isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "unable to create dispatch for reserved port %s: %s", addrbuf, isc_result_totext(result)); } static isc_result_t loadconfig(ns_server_t *server) { isc_result_t result; start_reserved_dispatches(server); result = load_configuration(ns_g_lwresdonly ? lwresd_g_conffile : ns_g_conffile, server, ISC_FALSE); if (result == ISC_R_SUCCESS) { end_reserved_dispatches(server, ISC_FALSE); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "reloading configuration succeeded"); } else { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "reloading configuration failed: %s", isc_result_totext(result)); } return (result); } static isc_result_t reload(ns_server_t *server) { isc_result_t result; CHECK(loadconfig(server)); result = load_zones(server, ISC_FALSE); if (result == ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "reloading zones succeeded"); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "reloading zones failed: %s", isc_result_totext(result)); cleanup: return (result); } static void reconfig(ns_server_t *server) { isc_result_t result; CHECK(loadconfig(server)); result = load_new_zones(server, ISC_FALSE); if (result == ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "any newly configured zones are now loaded"); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "loading new zones failed: %s", isc_result_totext(result)); cleanup: ; } /* * Handle a reload event (from SIGHUP). */ static void ns_server_reload(isc_task_t *task, isc_event_t *event) { ns_server_t *server = (ns_server_t *)event->ev_arg; INSIST(task = server->task); UNUSED(task); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "received SIGHUP signal to reload zones"); (void)reload(server); LOCK(&server->reload_event_lock); INSIST(server->reload_event == NULL); server->reload_event = event; UNLOCK(&server->reload_event_lock); } void ns_server_reloadwanted(ns_server_t *server) { LOCK(&server->reload_event_lock); if (server->reload_event != NULL) isc_task_send(server->task, &server->reload_event); UNLOCK(&server->reload_event_lock); } static char * next_token(char **stringp, const char *delim) { char *res; do { res = strsep(stringp, delim); if (res == NULL) break; } while (*res == '\0'); return (res); } /* * Find the zone specified in the control channel command 'args', * if any. If a zone is specified, point '*zonep' at it, otherwise * set '*zonep' to NULL. */ static isc_result_t zone_from_args(ns_server_t *server, char *args, const char *zonetxt, dns_zone_t **zonep, const char **zonename, isc_buffer_t *text, isc_boolean_t skip) { char *input, *ptr; char *classtxt; const char *viewtxt = NULL; dns_fixedname_t fname; dns_name_t *name; isc_result_t result; dns_view_t *view = NULL; dns_rdataclass_t rdclass; char problem[DNS_NAME_FORMATSIZE + 500] = ""; REQUIRE(zonep != NULL && *zonep == NULL); REQUIRE(zonename == NULL || *zonename == NULL); input = args; if (skip) { /* Skip the command name. */ ptr = next_token(&input, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); } /* Look for the zone name. */ if (zonetxt == NULL) zonetxt = next_token(&input, " \t"); if (zonetxt == NULL) return (ISC_R_SUCCESS); if (zonename != NULL) *zonename = zonetxt; /* Look for the optional class name. */ classtxt = next_token(&input, " \t"); if (classtxt != NULL) { /* Look for the optional view name. */ viewtxt = next_token(&input, " \t"); } dns_fixedname_init(&fname); name = dns_fixedname_name(&fname); CHECK(dns_name_fromstring(name, zonetxt, 0, NULL)); if (classtxt != NULL) { isc_textregion_t r; r.base = classtxt; r.length = strlen(classtxt); CHECK(dns_rdataclass_fromtext(&rdclass, &r)); } else rdclass = dns_rdataclass_in; if (viewtxt == NULL) { result = dns_viewlist_findzone(&server->viewlist, name, ISC_TF(classtxt == NULL), rdclass, zonep); if (result == ISC_R_NOTFOUND) snprintf(problem, sizeof(problem), "no matching zone '%s' in any view", zonetxt); } else { result = dns_viewlist_find(&server->viewlist, viewtxt, rdclass, &view); if (result != ISC_R_SUCCESS) { snprintf(problem, sizeof(problem), "no matching view '%s'", viewtxt); goto report; } result = dns_zt_find(view->zonetable, name, 0, NULL, zonep); if (result != ISC_R_SUCCESS) snprintf(problem, sizeof(problem), "no matching zone '%s' in view '%s'", zonetxt, viewtxt); } /* Partial match? */ if (result != ISC_R_SUCCESS && *zonep != NULL) dns_zone_detach(zonep); if (result == DNS_R_PARTIALMATCH) result = ISC_R_NOTFOUND; report: if (result != ISC_R_SUCCESS) { isc_result_t tresult; tresult = putstr(text, problem); if (tresult == ISC_R_SUCCESS && isc_buffer_availablelength(text) > 0U) isc_buffer_putuint8(text, 0); } cleanup: if (view != NULL) dns_view_detach(&view); return (result); } /* * Act on a "retransfer" command from the command channel. */ isc_result_t ns_server_retransfercommand(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result; dns_zone_t *zone = NULL; dns_zone_t *raw = NULL; dns_zonetype_t type; result = zone_from_args(server, args, NULL, &zone, NULL, text, ISC_TRUE); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) return (ISC_R_UNEXPECTEDEND); dns_zone_getraw(zone, &raw); if (raw != NULL) { dns_zone_detach(&zone); dns_zone_attach(raw, &zone); dns_zone_detach(&raw); } type = dns_zone_gettype(zone); if (type == dns_zone_slave || type == dns_zone_stub) dns_zone_forcereload(zone); else result = ISC_R_NOTFOUND; dns_zone_detach(&zone); return (result); } /* * Act on a "reload" command from the command channel. */ isc_result_t ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result; dns_zone_t *zone = NULL; dns_zonetype_t type; const char *msg = NULL; result = zone_from_args(server, args, NULL, &zone, NULL, text, ISC_TRUE); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) { result = reload(server); if (result == ISC_R_SUCCESS) msg = "server reload successful"; } else { type = dns_zone_gettype(zone); if (type == dns_zone_slave || type == dns_zone_stub) { dns_zone_refresh(zone); dns_zone_detach(&zone); msg = "zone refresh queued"; } else { result = dns_zone_load(zone); dns_zone_detach(&zone); switch (result) { case ISC_R_SUCCESS: msg = "zone reload successful"; break; case DNS_R_CONTINUE: msg = "zone reload queued"; result = ISC_R_SUCCESS; break; case DNS_R_UPTODATE: msg = "zone reload up-to-date"; result = ISC_R_SUCCESS; break; default: /* failure message will be generated by rndc */ break; } } } if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text)) isc_buffer_putmem(text, (const unsigned char *)msg, strlen(msg) + 1); return (result); } /* * Act on a "reconfig" command from the command channel. */ isc_result_t ns_server_reconfigcommand(ns_server_t *server, char *args) { UNUSED(args); reconfig(server); return (ISC_R_SUCCESS); } /* * Act on a "notify" command from the command channel. */ isc_result_t ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result; dns_zone_t *zone = NULL; const unsigned char msg[] = "zone notify queued"; result = zone_from_args(server, args, NULL, &zone, NULL, text, ISC_TRUE); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) return (ISC_R_UNEXPECTEDEND); dns_zone_notify(zone); dns_zone_detach(&zone); if (sizeof(msg) <= isc_buffer_availablelength(text)) isc_buffer_putmem(text, msg, sizeof(msg)); return (ISC_R_SUCCESS); } /* * Act on a "refresh" command from the command channel. */ isc_result_t ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result; dns_zone_t *zone = NULL, *raw = NULL; const unsigned char msg1[] = "zone refresh queued"; const unsigned char msg2[] = "not a slave or stub zone"; dns_zonetype_t type; result = zone_from_args(server, args, NULL, &zone, NULL, text, ISC_TRUE); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) return (ISC_R_UNEXPECTEDEND); dns_zone_getraw(zone, &raw); if (raw != NULL) { dns_zone_detach(&zone); dns_zone_attach(raw, &zone); dns_zone_detach(&raw); } type = dns_zone_gettype(zone); if (type == dns_zone_slave || type == dns_zone_stub) { dns_zone_refresh(zone); dns_zone_detach(&zone); if (sizeof(msg1) <= isc_buffer_availablelength(text)) isc_buffer_putmem(text, msg1, sizeof(msg1)); return (ISC_R_SUCCESS); } dns_zone_detach(&zone); if (sizeof(msg2) <= isc_buffer_availablelength(text)) isc_buffer_putmem(text, msg2, sizeof(msg2)); return (ISC_R_FAILURE); } isc_result_t ns_server_togglequerylog(ns_server_t *server, char *args) { isc_boolean_t value; char *ptr; /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); ptr = next_token(&args, " \t"); if (ptr == NULL) value = server->log_queries ? ISC_FALSE : ISC_TRUE; else if (strcasecmp(ptr, "yes") == 0 || strcasecmp(ptr, "on") == 0) value = ISC_TRUE; else if (strcasecmp(ptr, "no") == 0 || strcasecmp(ptr, "off") == 0) value = ISC_FALSE; else return (ISC_R_NOTFOUND); if (server->log_queries == value) return (ISC_R_SUCCESS); server->log_queries = value; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "query logging is now %s", server->log_queries ? "on" : "off"); return (ISC_R_SUCCESS); } static isc_result_t ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, ns_listenlist_t **target) { isc_result_t result; const cfg_listelt_t *element; ns_listenlist_t *dlist = NULL; REQUIRE(target != NULL && *target == NULL); result = ns_listenlist_create(mctx, &dlist); if (result != ISC_R_SUCCESS) return (result); for (element = cfg_list_first(listenlist); element != NULL; element = cfg_list_next(element)) { ns_listenelt_t *delt = NULL; const cfg_obj_t *listener = cfg_listelt_value(element); result = ns_listenelt_fromconfig(listener, config, actx, mctx, &delt); if (result != ISC_R_SUCCESS) goto cleanup; ISC_LIST_APPEND(dlist->elts, delt, link); } *target = dlist; return (ISC_R_SUCCESS); cleanup: ns_listenlist_detach(&dlist); return (result); } /* * Create a listen list from the corresponding configuration * data structure. */ static isc_result_t ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, ns_listenelt_t **target) { isc_result_t result; const cfg_obj_t *portobj; in_port_t port; ns_listenelt_t *delt = NULL; REQUIRE(target != NULL && *target == NULL); portobj = cfg_tuple_get(listener, "port"); if (!cfg_obj_isuint32(portobj)) { if (ns_g_port != 0) { port = ns_g_port; } else { result = ns_config_getport(config, &port); if (result != ISC_R_SUCCESS) return (result); } } else { if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) { cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR, "port value '%u' is out of range", cfg_obj_asuint32(portobj)); return (ISC_R_RANGE); } port = (in_port_t)cfg_obj_asuint32(portobj); } result = ns_listenelt_create(mctx, port, NULL, &delt); if (result != ISC_R_SUCCESS) return (result); result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"), config, ns_g_lctx, actx, mctx, 0, &delt->acl); if (result != ISC_R_SUCCESS) { ns_listenelt_destroy(delt); return (result); } *target = delt; return (ISC_R_SUCCESS); } isc_result_t ns_server_dumpstats(ns_server_t *server) { isc_result_t result; FILE *fp = NULL; CHECKMF(isc_stdio_open(server->statsfile, "a", &fp), "could not open statistics dump file", server->statsfile); result = ns_stats_dump(server, fp); cleanup: if (fp != NULL) (void)isc_stdio_close(fp); if (result == ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dumpstats complete"); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "dumpstats failed: %s", dns_result_totext(result)); return (result); } static isc_result_t add_zone_tolist(dns_zone_t *zone, void *uap) { struct dumpcontext *dctx = uap; struct zonelistentry *zle; zle = isc_mem_get(dctx->mctx, sizeof *zle); if (zle == NULL) return (ISC_R_NOMEMORY); zle->zone = NULL; dns_zone_attach(zone, &zle->zone); ISC_LINK_INIT(zle, link); ISC_LIST_APPEND(ISC_LIST_TAIL(dctx->viewlist)->zonelist, zle, link); return (ISC_R_SUCCESS); } static isc_result_t add_view_tolist(struct dumpcontext *dctx, dns_view_t *view) { struct viewlistentry *vle; isc_result_t result = ISC_R_SUCCESS; /* * Prevent duplicate views. */ for (vle = ISC_LIST_HEAD(dctx->viewlist); vle != NULL; vle = ISC_LIST_NEXT(vle, link)) if (vle->view == view) return (ISC_R_SUCCESS); vle = isc_mem_get(dctx->mctx, sizeof *vle); if (vle == NULL) return (ISC_R_NOMEMORY); vle->view = NULL; dns_view_attach(view, &vle->view); ISC_LINK_INIT(vle, link); ISC_LIST_INIT(vle->zonelist); ISC_LIST_APPEND(dctx->viewlist, vle, link); if (dctx->dumpzones) result = dns_zt_apply(view->zonetable, ISC_TRUE, add_zone_tolist, dctx); return (result); } static void dumpcontext_destroy(struct dumpcontext *dctx) { struct viewlistentry *vle; struct zonelistentry *zle; vle = ISC_LIST_HEAD(dctx->viewlist); while (vle != NULL) { ISC_LIST_UNLINK(dctx->viewlist, vle, link); zle = ISC_LIST_HEAD(vle->zonelist); while (zle != NULL) { ISC_LIST_UNLINK(vle->zonelist, zle, link); dns_zone_detach(&zle->zone); isc_mem_put(dctx->mctx, zle, sizeof *zle); zle = ISC_LIST_HEAD(vle->zonelist); } dns_view_detach(&vle->view); isc_mem_put(dctx->mctx, vle, sizeof *vle); vle = ISC_LIST_HEAD(dctx->viewlist); } if (dctx->version != NULL) dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE); if (dctx->db != NULL) dns_db_detach(&dctx->db); if (dctx->cache != NULL) dns_db_detach(&dctx->cache); if (dctx->task != NULL) isc_task_detach(&dctx->task); if (dctx->fp != NULL) (void)isc_stdio_close(dctx->fp); if (dctx->mdctx != NULL) dns_dumpctx_detach(&dctx->mdctx); isc_mem_put(dctx->mctx, dctx, sizeof *dctx); } static void dumpdone(void *arg, isc_result_t result) { struct dumpcontext *dctx = arg; char buf[1024+32]; const dns_master_style_t *style; if (result != ISC_R_SUCCESS) goto cleanup; if (dctx->mdctx != NULL) dns_dumpctx_detach(&dctx->mdctx); if (dctx->view == NULL) { dctx->view = ISC_LIST_HEAD(dctx->viewlist); if (dctx->view == NULL) goto done; INSIST(dctx->zone == NULL); } else goto resume; nextview: fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name); resume: if (dctx->dumpcache && dns_view_iscacheshared(dctx->view->view)) { fprintf(dctx->fp, ";\n; Cache of view '%s' is shared as '%s'\n", dctx->view->view->name, dns_cache_getname(dctx->view->view->cache)); } else if (dctx->zone == NULL && dctx->cache == NULL && dctx->dumpcache) { style = &dns_master_style_cache; /* start cache dump */ if (dctx->view->view->cachedb != NULL) dns_db_attach(dctx->view->view->cachedb, &dctx->cache); if (dctx->cache != NULL) { fprintf(dctx->fp, ";\n; Cache dump of view '%s' (cache %s)\n;\n", dctx->view->view->name, dns_cache_getname(dctx->view->view->cache)); result = dns_master_dumptostreaminc(dctx->mctx, dctx->cache, NULL, style, dctx->fp, dctx->task, dumpdone, dctx, &dctx->mdctx); if (result == DNS_R_CONTINUE) return; if (result == ISC_R_NOTIMPLEMENTED) fprintf(dctx->fp, "; %s\n", dns_result_totext(result)); else if (result != ISC_R_SUCCESS) goto cleanup; } } if (dctx->cache != NULL) { dns_adb_dump(dctx->view->view->adb, dctx->fp); dns_resolver_printbadcache(dctx->view->view->resolver, dctx->fp); dns_db_detach(&dctx->cache); } if (dctx->dumpzones) { style = &dns_master_style_full; nextzone: if (dctx->version != NULL) dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE); if (dctx->db != NULL) dns_db_detach(&dctx->db); if (dctx->zone == NULL) dctx->zone = ISC_LIST_HEAD(dctx->view->zonelist); else dctx->zone = ISC_LIST_NEXT(dctx->zone, link); if (dctx->zone != NULL) { /* start zone dump */ dns_zone_name(dctx->zone->zone, buf, sizeof(buf)); fprintf(dctx->fp, ";\n; Zone dump of '%s'\n;\n", buf); result = dns_zone_getdb(dctx->zone->zone, &dctx->db); if (result != ISC_R_SUCCESS) { fprintf(dctx->fp, "; %s\n", dns_result_totext(result)); goto nextzone; } dns_db_currentversion(dctx->db, &dctx->version); result = dns_master_dumptostreaminc(dctx->mctx, dctx->db, dctx->version, style, dctx->fp, dctx->task, dumpdone, dctx, &dctx->mdctx); if (result == DNS_R_CONTINUE) return; if (result == ISC_R_NOTIMPLEMENTED) { fprintf(dctx->fp, "; %s\n", dns_result_totext(result)); result = ISC_R_SUCCESS; POST(result); goto nextzone; } if (result != ISC_R_SUCCESS) goto cleanup; } } if (dctx->view != NULL) dctx->view = ISC_LIST_NEXT(dctx->view, link); if (dctx->view != NULL) goto nextview; done: fprintf(dctx->fp, "; Dump complete\n"); result = isc_stdio_flush(dctx->fp); if (result == ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dumpdb complete"); cleanup: if (result != ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "dumpdb failed: %s", dns_result_totext(result)); dumpcontext_destroy(dctx); } isc_result_t ns_server_dumpdb(ns_server_t *server, char *args) { struct dumpcontext *dctx = NULL; dns_view_t *view; isc_result_t result; char *ptr; const char *sep; /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); dctx = isc_mem_get(server->mctx, sizeof(*dctx)); if (dctx == NULL) return (ISC_R_NOMEMORY); dctx->mctx = server->mctx; dctx->dumpcache = ISC_TRUE; dctx->dumpzones = ISC_FALSE; dctx->fp = NULL; ISC_LIST_INIT(dctx->viewlist); dctx->view = NULL; dctx->zone = NULL; dctx->cache = NULL; dctx->mdctx = NULL; dctx->db = NULL; dctx->cache = NULL; dctx->task = NULL; dctx->version = NULL; isc_task_attach(server->task, &dctx->task); CHECKMF(isc_stdio_open(server->dumpfile, "w", &dctx->fp), "could not open dump file", server->dumpfile); sep = (args == NULL) ? "" : ": "; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dumpdb started%s%s", sep, (args != NULL) ? args : ""); ptr = next_token(&args, " \t"); if (ptr != NULL && strcmp(ptr, "-all") == 0) { dctx->dumpzones = ISC_TRUE; dctx->dumpcache = ISC_TRUE; ptr = next_token(&args, " \t"); } else if (ptr != NULL && strcmp(ptr, "-cache") == 0) { dctx->dumpzones = ISC_FALSE; dctx->dumpcache = ISC_TRUE; ptr = next_token(&args, " \t"); } else if (ptr != NULL && strcmp(ptr, "-zones") == 0) { dctx->dumpzones = ISC_TRUE; dctx->dumpcache = ISC_FALSE; ptr = next_token(&args, " \t"); } nextview: for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (ptr != NULL && strcmp(view->name, ptr) != 0) continue; CHECK(add_view_tolist(dctx, view)); } if (ptr != NULL) { ptr = next_token(&args, " \t"); if (ptr != NULL) goto nextview; } dumpdone(dctx, ISC_R_SUCCESS); return (ISC_R_SUCCESS); cleanup: if (dctx != NULL) dumpcontext_destroy(dctx); return (result); } isc_result_t ns_server_dumpsecroots(ns_server_t *server, char *args) { dns_view_t *view; dns_keytable_t *secroots = NULL; isc_result_t result; char *ptr; FILE *fp = NULL; isc_time_t now; char tbuf[64]; /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); ptr = next_token(&args, " \t"); CHECKMF(isc_stdio_open(server->secrootsfile, "w", &fp), "could not open secroots dump file", server->secrootsfile); TIME_NOW(&now); isc_time_formattimestamp(&now, tbuf, sizeof(tbuf)); fprintf(fp, "%s\n", tbuf); do { for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (ptr != NULL && strcmp(view->name, ptr) != 0) continue; if (secroots != NULL) dns_keytable_detach(&secroots); result = dns_view_getsecroots(view, &secroots); if (result == ISC_R_NOTFOUND) { result = ISC_R_SUCCESS; continue; } fprintf(fp, "\n Start view %s\n\n", view->name); result = dns_keytable_dump(secroots, fp); if (result != ISC_R_SUCCESS) fprintf(fp, " dumpsecroots failed: %s\n", isc_result_totext(result)); } if (ptr != NULL) ptr = next_token(&args, " \t"); } while (ptr != NULL); cleanup: if (secroots != NULL) dns_keytable_detach(&secroots); if (fp != NULL) (void)isc_stdio_close(fp); if (result == ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dumpsecroots complete"); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "dumpsecroots failed: %s", dns_result_totext(result)); return (result); } isc_result_t ns_server_dumprecursing(ns_server_t *server) { FILE *fp = NULL; isc_result_t result; CHECKMF(isc_stdio_open(server->recfile, "w", &fp), "could not open dump file", server->recfile); fprintf(fp,";\n; Recursing Queries\n;\n"); ns_interfacemgr_dumprecursing(fp, server->interfacemgr); fprintf(fp, "; Dump complete\n"); cleanup: if (fp != NULL) result = isc_stdio_close(fp); if (result == ISC_R_SUCCESS) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dumprecursing complete"); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "dumprecursing failed: %s", dns_result_totext(result)); return (result); } isc_result_t ns_server_setdebuglevel(ns_server_t *server, char *args) { char *ptr; char *levelstr; char *endp; long newlevel; UNUSED(server); /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); /* Look for the new level name. */ levelstr = next_token(&args, " \t"); if (levelstr == NULL) { if (ns_g_debuglevel < 99) ns_g_debuglevel++; } else { newlevel = strtol(levelstr, &endp, 10); if (*endp != '\0' || newlevel < 0 || newlevel > 99) return (ISC_R_RANGE); ns_g_debuglevel = (unsigned int)newlevel; } isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "debug level is now %d", ns_g_debuglevel); return (ISC_R_SUCCESS); } isc_result_t ns_server_validation(ns_server_t *server, char *args) { char *ptr, *viewname; dns_view_t *view; isc_boolean_t changed = ISC_FALSE; isc_result_t result; isc_boolean_t enable; /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); /* Find out what we are to do. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") || !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true")) enable = ISC_TRUE; else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") || !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false")) enable = ISC_FALSE; else return (DNS_R_SYNTAX); /* Look for the view name. */ viewname = next_token(&args, " \t"); result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (viewname != NULL && strcasecmp(viewname, view->name) != 0) continue; result = dns_view_flushcache(view); if (result != ISC_R_SUCCESS) goto out; view->enablevalidation = enable; changed = ISC_TRUE; } if (changed) result = ISC_R_SUCCESS; else result = ISC_R_FAILURE; out: isc_task_endexclusive(server->task); return (result); } isc_result_t ns_server_flushcache(ns_server_t *server, char *args) { char *ptr, *viewname; dns_view_t *view; isc_boolean_t flushed; isc_boolean_t found; isc_result_t result; ns_cache_t *nsc; /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); /* Look for the view name. */ viewname = next_token(&args, " \t"); result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); flushed = ISC_TRUE; found = ISC_FALSE; /* * Flushing a cache is tricky when caches are shared by multiple views. * We first identify which caches should be flushed in the local cache * list, flush these caches, and then update other views that refer to * the flushed cache DB. */ if (viewname != NULL) { /* * Mark caches that need to be flushed. This is an O(#view^2) * operation in the very worst case, but should be normally * much more lightweight because only a few (most typically just * one) views will match. */ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (strcasecmp(viewname, view->name) != 0) continue; found = ISC_TRUE; for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL; nsc = ISC_LIST_NEXT(nsc, link)) { if (nsc->cache == view->cache) break; } INSIST(nsc != NULL); nsc->needflush = ISC_TRUE; } } else found = ISC_TRUE; /* Perform flush */ for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL; nsc = ISC_LIST_NEXT(nsc, link)) { if (viewname != NULL && !nsc->needflush) continue; nsc->needflush = ISC_TRUE; result = dns_view_flushcache2(nsc->primaryview, ISC_FALSE); if (result != ISC_R_SUCCESS) { flushed = ISC_FALSE; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "flushing cache in view '%s' failed: %s", nsc->primaryview->name, isc_result_totext(result)); } } /* * Fix up views that share a flushed cache: let the views update the * cache DB they're referring to. This could also be an expensive * operation, but should typically be marginal: the inner loop is only * necessary for views that share a cache, and if there are many such * views the number of shared cache should normally be small. * A worst case is that we have n views and n/2 caches, each shared by * two views. Then this will be a O(n^2/4) operation. */ for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (!dns_view_iscacheshared(view)) continue; for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL; nsc = ISC_LIST_NEXT(nsc, link)) { if (!nsc->needflush || nsc->cache != view->cache) continue; result = dns_view_flushcache2(view, ISC_TRUE); if (result != ISC_R_SUCCESS) { flushed = ISC_FALSE; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "fixing cache in view '%s' " "failed: %s", view->name, isc_result_totext(result)); } } } /* Cleanup the cache list. */ for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL; nsc = ISC_LIST_NEXT(nsc, link)) { nsc->needflush = ISC_FALSE; } if (flushed && found) { if (viewname != NULL) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "flushing cache in view '%s' succeeded", viewname); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "flushing caches in all views succeeded"); result = ISC_R_SUCCESS; } else { if (!found) { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "flushing cache in view '%s' failed: " "view not found", viewname); result = ISC_R_NOTFOUND; } else result = ISC_R_FAILURE; } isc_task_endexclusive(server->task); return (result); } isc_result_t ns_server_flushnode(ns_server_t *server, char *args, isc_boolean_t tree) { char *ptr, *target, *viewname; dns_view_t *view; isc_boolean_t flushed; isc_boolean_t found; isc_result_t result; isc_buffer_t b; dns_fixedname_t fixed; dns_name_t *name; /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); /* Find the domain name to flush. */ target = next_token(&args, " \t"); if (target == NULL) return (ISC_R_UNEXPECTEDEND); isc_buffer_constinit(&b, target, strlen(target)); isc_buffer_add(&b, strlen(target)); dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) return (result); /* Look for the view name. */ viewname = next_token(&args, " \t"); result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); flushed = ISC_TRUE; found = ISC_FALSE; for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (viewname != NULL && strcasecmp(viewname, view->name) != 0) continue; found = ISC_TRUE; /* * It's a little inefficient to try flushing name for all views * if some of the views share a single cache. But since the * operation is lightweight we prefer simplicity here. */ result = dns_view_flushnode(view, name, tree); if (result != ISC_R_SUCCESS) { flushed = ISC_FALSE; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "flushing %s '%s' in cache view '%s' " "failed: %s", tree ? "tree" : "name", target, view->name, isc_result_totext(result)); } } if (flushed && found) { if (viewname != NULL) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "flushing %s '%s' in cache view '%s' " "succeeded", tree ? "tree" : "name", target, viewname); else isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "flushing %s '%s' in all cache views " "succeeded", tree ? "tree" : "name", target); result = ISC_R_SUCCESS; } else { if (!found) isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_ERROR, "flushing %s '%s' in cache view '%s' " "failed: view not found", tree ? "tree" : "name", target, viewname); result = ISC_R_FAILURE; } isc_task_endexclusive(server->task); return (result); } isc_result_t ns_server_status(ns_server_t *server, isc_buffer_t *text) { int zonecount, xferrunning, xferdeferred, soaqueries; unsigned int n; const char *ob = "", *cb = "", *alt = ""; if (ns_g_server->version_set) { ob = " ("; cb = ")"; if (ns_g_server->version == NULL) alt = "version.bind/txt/ch disabled"; else alt = ns_g_server->version; } zonecount = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_ANY); xferrunning = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_XFERRUNNING); xferdeferred = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_XFERDEFERRED); soaqueries = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_SOAQUERY); n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "version: %s%s%s%s \n" #ifdef ISC_PLATFORM_USETHREADS "CPUs found: %u\n" "worker threads: %u\n" "UDP listeners per interface: %u\n" #endif "number of zones: %u\n" "debug level: %d\n" "xfers running: %u\n" "xfers deferred: %u\n" "soa queries in progress: %u\n" "query logging is %s\n" "recursive clients: %d/%d/%d\n" "tcp clients: %d/%d\n" "server is up and running", ns_g_version, ob, alt, cb, ns_g_srcid, #ifdef ISC_PLATFORM_USETHREADS ns_g_cpus_detected, ns_g_cpus, ns_g_udpdisp, #endif zonecount, ns_g_debuglevel, xferrunning, xferdeferred, soaqueries, server->log_queries ? "ON" : "OFF", server->recursionquota.used, server->recursionquota.soft, server->recursionquota.max, server->tcpquota.used, server->tcpquota.max); if (n >= isc_buffer_availablelength(text)) return (ISC_R_NOSPACE); isc_buffer_add(text, n); return (ISC_R_SUCCESS); } static isc_result_t delete_keynames(dns_tsig_keyring_t *ring, char *target, unsigned int *foundkeys) { char namestr[DNS_NAME_FORMATSIZE]; isc_result_t result; dns_rbtnodechain_t chain; dns_name_t foundname; dns_fixedname_t fixedorigin; dns_name_t *origin; dns_rbtnode_t *node; dns_tsigkey_t *tkey; dns_name_init(&foundname, NULL); dns_fixedname_init(&fixedorigin); origin = dns_fixedname_name(&fixedorigin); again: dns_rbtnodechain_init(&chain, ring->mctx); result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, origin); if (result == ISC_R_NOTFOUND) { dns_rbtnodechain_invalidate(&chain); return (ISC_R_SUCCESS); } if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { dns_rbtnodechain_invalidate(&chain); return (result); } for (;;) { node = NULL; dns_rbtnodechain_current(&chain, &foundname, origin, &node); tkey = node->data; if (tkey != NULL) { if (!tkey->generated) goto nextkey; dns_name_format(&tkey->name, namestr, sizeof(namestr)); if (strcmp(namestr, target) == 0) { (*foundkeys)++; dns_rbtnodechain_invalidate(&chain); (void)dns_rbt_deletename(ring->keys, &tkey->name, ISC_FALSE); goto again; } } nextkey: result = dns_rbtnodechain_next(&chain, &foundname, origin); if (result == ISC_R_NOMORE) break; if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { dns_rbtnodechain_invalidate(&chain); return (result); } } return (ISC_R_SUCCESS); } isc_result_t ns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text) { isc_result_t result; unsigned int n; dns_view_t *view; unsigned int foundkeys = 0; char *target; char *viewname; (void)next_token(&command, " \t"); /* skip command name */ target = next_token(&command, " \t"); if (target == NULL) return (ISC_R_UNEXPECTEDEND); viewname = next_token(&command, " \t"); result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { if (viewname == NULL || strcmp(view->name, viewname) == 0) { RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_write); result = delete_keynames(view->dynamickeys, target, &foundkeys); RWUNLOCK(&view->dynamickeys->lock, isc_rwlocktype_write); if (result != ISC_R_SUCCESS) { isc_task_endexclusive(server->task); return (result); } } } isc_task_endexclusive(server->task); n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "%d tsig keys deleted.\n", foundkeys); if (n >= isc_buffer_availablelength(text)) return (ISC_R_NOSPACE); isc_buffer_add(text, n); return (ISC_R_SUCCESS); } static isc_result_t list_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t *text, unsigned int *foundkeys) { char namestr[DNS_NAME_FORMATSIZE]; char creatorstr[DNS_NAME_FORMATSIZE]; isc_result_t result; dns_rbtnodechain_t chain; dns_name_t foundname; dns_fixedname_t fixedorigin; dns_name_t *origin; dns_rbtnode_t *node; dns_tsigkey_t *tkey; unsigned int n; const char *viewname; if (view != NULL) viewname = view->name; else viewname = "(global)"; dns_name_init(&foundname, NULL); dns_fixedname_init(&fixedorigin); origin = dns_fixedname_name(&fixedorigin); dns_rbtnodechain_init(&chain, ring->mctx); result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, origin); if (result == ISC_R_NOTFOUND) { dns_rbtnodechain_invalidate(&chain); return (ISC_R_SUCCESS); } if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { dns_rbtnodechain_invalidate(&chain); return (result); } for (;;) { node = NULL; dns_rbtnodechain_current(&chain, &foundname, origin, &node); tkey = node->data; if (tkey != NULL) { (*foundkeys)++; dns_name_format(&tkey->name, namestr, sizeof(namestr)); if (tkey->generated) { dns_name_format(tkey->creator, creatorstr, sizeof(creatorstr)); n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "view \"%s\"; type \"dynamic\"; key \"%s\"; creator \"%s\";\n", viewname, namestr, creatorstr); } else { n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "view \"%s\"; type \"static\"; key \"%s\";\n", viewname, namestr); } if (n >= isc_buffer_availablelength(text)) { dns_rbtnodechain_invalidate(&chain); return (ISC_R_NOSPACE); } isc_buffer_add(text, n); } result = dns_rbtnodechain_next(&chain, &foundname, origin); if (result == ISC_R_NOMORE) break; if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { dns_rbtnodechain_invalidate(&chain); return (result); } } return (ISC_R_SUCCESS); } isc_result_t ns_server_tsiglist(ns_server_t *server, isc_buffer_t *text) { isc_result_t result; unsigned int n; dns_view_t *view; unsigned int foundkeys = 0; result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { RWLOCK(&view->statickeys->lock, isc_rwlocktype_read); result = list_keynames(view, view->statickeys, text, &foundkeys); RWUNLOCK(&view->statickeys->lock, isc_rwlocktype_read); if (result != ISC_R_SUCCESS) { isc_task_endexclusive(server->task); return (result); } RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_read); result = list_keynames(view, view->dynamickeys, text, &foundkeys); RWUNLOCK(&view->dynamickeys->lock, isc_rwlocktype_read); if (result != ISC_R_SUCCESS) { isc_task_endexclusive(server->task); return (result); } } isc_task_endexclusive(server->task); if (foundkeys == 0) { n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "no tsig keys found.\n"); if (n >= isc_buffer_availablelength(text)) return (ISC_R_NOSPACE); isc_buffer_add(text, n); } return (ISC_R_SUCCESS); } /* * Act on a "sign" or "loadkeys" command from the command channel. */ isc_result_t ns_server_rekey(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result; dns_zone_t *zone = NULL; dns_zonetype_t type; isc_uint16_t keyopts; isc_boolean_t fullsign = ISC_FALSE; if (strncasecmp(args, NS_COMMAND_SIGN, strlen(NS_COMMAND_SIGN)) == 0) fullsign = ISC_TRUE; result = zone_from_args(server, args, NULL, &zone, NULL, text, ISC_TRUE); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) return (ISC_R_UNEXPECTEDEND); /* XXX: or do all zones? */ type = dns_zone_gettype(zone); if (type != dns_zone_master) { dns_zone_detach(&zone); return (DNS_R_NOTMASTER); } keyopts = dns_zone_getkeyopts(zone); /* "rndc loadkeys" requires "auto-dnssec maintain". */ if ((keyopts & DNS_ZONEKEY_ALLOW) == 0) result = ISC_R_NOPERM; else if ((keyopts & DNS_ZONEKEY_MAINTAIN) == 0 && !fullsign) result = ISC_R_NOPERM; else dns_zone_rekey(zone, fullsign); dns_zone_detach(&zone); return (result); } /* * Act on a "sync" command from the command channel. */ static isc_result_t synczone(dns_zone_t *zone, void *uap) { isc_boolean_t cleanup = *(isc_boolean_t *)uap; isc_result_t result; dns_zone_t *raw = NULL; char *journal; dns_zone_getraw(zone, &raw); if (raw != NULL) { synczone(raw, uap); dns_zone_detach(&raw); } result = dns_zone_flush(zone); if (result != ISC_R_SUCCESS) cleanup = ISC_FALSE; if (cleanup) { journal = dns_zone_getjournal(zone); if (journal != NULL) (void)isc_file_remove(journal); } return (result); } isc_result_t ns_server_sync(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result, tresult; dns_view_t *view; dns_zone_t *zone = NULL; char classstr[DNS_RDATACLASS_FORMATSIZE]; char zonename[DNS_NAME_FORMATSIZE]; const char *vname, *sep, *msg = NULL, *arg; isc_boolean_t cleanup = ISC_FALSE; (void) next_token(&args, " \t"); arg = next_token(&args, " \t"); if (arg != NULL && (strcmp(arg, "-clean") == 0 || strcmp(arg, "-clear") == 0)) { cleanup = ISC_TRUE; arg = next_token(&args, " \t"); } result = zone_from_args(server, args, arg, &zone, NULL, text, ISC_FALSE); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) { result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); tresult = ISC_R_SUCCESS; for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { result = dns_zt_apply(view->zonetable, ISC_FALSE, synczone, &cleanup); if (result != ISC_R_SUCCESS && tresult == ISC_R_SUCCESS) tresult = result; } isc_task_endexclusive(server->task); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "dumping all zones%s: %s", cleanup ? ", removing journal files" : "", isc_result_totext(result)); return (tresult); } result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); result = synczone(zone, &cleanup); isc_task_endexclusive(server->task); if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text)) isc_buffer_putmem(text, (const unsigned char *)msg, strlen(msg) + 1); view = dns_zone_getview(zone); if (strcmp(view->name, "_default") == 0 || strcmp(view->name, "_bind") == 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)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "sync: dumping zone '%s/%s'%s%s%s: %s", zonename, classstr, sep, vname, cleanup ? ", removing journal file" : "", isc_result_totext(result)); dns_zone_detach(&zone); return (result); } /* * Act on a "freeze" or "thaw" command from the command channel. */ isc_result_t ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args, isc_buffer_t *text) { isc_result_t result, tresult; dns_zone_t *zone = NULL, *raw = NULL; dns_zonetype_t type; char classstr[DNS_RDATACLASS_FORMATSIZE]; char zonename[DNS_NAME_FORMATSIZE]; dns_view_t *view; const char *vname, *sep; isc_boolean_t frozen; const char *msg = NULL; result = zone_from_args(server, args, NULL, &zone, NULL, text, ISC_TRUE); if (result != ISC_R_SUCCESS) return (result); if (zone == NULL) { result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); tresult = ISC_R_SUCCESS; for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { result = dns_view_freezezones(view, freeze); if (result != ISC_R_SUCCESS && tresult == ISC_R_SUCCESS) tresult = result; } isc_task_endexclusive(server->task); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "%s all zones: %s", freeze ? "freezing" : "thawing", isc_result_totext(tresult)); return (tresult); } dns_zone_getraw(zone, &raw); if (raw != NULL) { dns_zone_detach(&zone); dns_zone_attach(raw, &zone); dns_zone_detach(&raw); } type = dns_zone_gettype(zone); if (type != dns_zone_master) { dns_zone_detach(&zone); return (DNS_R_NOTMASTER); } if (freeze && !dns_zone_isdynamic(zone, ISC_TRUE)) { dns_zone_detach(&zone); return (DNS_R_NOTDYNAMIC); } result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); frozen = dns_zone_getupdatedisabled(zone); if (freeze) { if (frozen) { msg = "WARNING: The zone was already frozen.\n" "Someone else may be editing it or " "it may still be re-loading."; result = DNS_R_FROZEN; } if (result == ISC_R_SUCCESS) { result = dns_zone_flush(zone); if (result != ISC_R_SUCCESS) msg = "Flushing the zone updates to " "disk failed."; } if (result == ISC_R_SUCCESS) dns_zone_setupdatedisabled(zone, freeze); } else { if (frozen) { result = dns_zone_loadandthaw(zone); switch (result) { case ISC_R_SUCCESS: case DNS_R_UPTODATE: msg = "The zone reload and thaw was " "successful."; result = ISC_R_SUCCESS; break; case DNS_R_CONTINUE: msg = "A zone reload and thaw was started.\n" "Check the logs to see the result."; result = ISC_R_SUCCESS; break; } } } isc_task_endexclusive(server->task); if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text)) isc_buffer_putmem(text, (const unsigned char *)msg, strlen(msg) + 1); view = dns_zone_getview(zone); if (strcmp(view->name, "_default") == 0 || strcmp(view->name, "_bind") == 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)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "%s zone '%s/%s'%s%s: %s", freeze ? "freezing" : "thawing", zonename, classstr, sep, vname, isc_result_totext(result)); dns_zone_detach(&zone); return (result); } #ifdef HAVE_LIBSCF /* * This function adds a message for rndc to echo if named * is managed by smf and is also running chroot. */ isc_result_t ns_smf_add_message(isc_buffer_t *text) { unsigned int n; n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "use svcadm(1M) to manage named"); if (n >= isc_buffer_availablelength(text)) return (ISC_R_NOSPACE); isc_buffer_add(text, n); return (ISC_R_SUCCESS); } #endif /* HAVE_LIBSCF */ /* * Emit a comment at the top of the nzf file containing the viewname * Expects the fp to already be open for writing */ #define HEADER1 "# New zone file for view: " #define HEADER2 "\n# This file contains configuration for zones added by\n" \ "# the 'rndc addzone' command. DO NOT EDIT BY HAND.\n" isc_result_t add_comment(FILE *fp, const char *viewname) { isc_result_t result; CHECK(isc_stdio_write(HEADER1, sizeof(HEADER1) - 1, 1, fp, NULL)); CHECK(isc_stdio_write(viewname, strlen(viewname), 1, fp, NULL)); CHECK(isc_stdio_write(HEADER2, sizeof(HEADER2) - 1, 1, fp, NULL)); cleanup: return (result); } /* * Act on an "addzone" command from the command channel. */ isc_result_t ns_server_add_zone(ns_server_t *server, char *args) { isc_result_t result; isc_buffer_t argbuf; size_t arglen; cfg_parser_t *parser = NULL; cfg_obj_t *config = NULL; const cfg_obj_t *vconfig = NULL; const cfg_obj_t *views = NULL; const cfg_obj_t *parms = NULL; const cfg_obj_t *obj = NULL; const cfg_listelt_t *element; const char *zonename; const char *classname = NULL; const char *argp; const char *viewname = NULL; dns_rdataclass_t rdclass; dns_view_t *view = 0; isc_buffer_t buf; dns_fixedname_t fname; dns_name_t *dnsname; dns_zone_t *zone = NULL; FILE *fp = NULL; struct cfg_context *cfg = NULL; char namebuf[DNS_NAME_FORMATSIZE]; off_t offset; /* Try to parse the argument string */ arglen = strlen(args); isc_buffer_init(&argbuf, args, (unsigned int)arglen); isc_buffer_add(&argbuf, strlen(args)); CHECK(cfg_parser_create(server->mctx, ns_g_lctx, &parser)); CHECK(cfg_parse_buffer(parser, &argbuf, &cfg_type_addzoneconf, &config)); CHECK(cfg_map_get(config, "addzone", &parms)); zonename = cfg_obj_asstring(cfg_tuple_get(parms, "name")); isc_buffer_constinit(&buf, zonename, strlen(zonename)); isc_buffer_add(&buf, strlen(zonename)); dns_fixedname_init(&fname); dnsname = dns_fixedname_name(&fname); CHECK(dns_name_fromtext(dnsname, &buf, dns_rootname, ISC_FALSE, NULL)); /* Make sense of optional class argument */ obj = cfg_tuple_get(parms, "class"); CHECK(ns_config_getclass(obj, dns_rdataclass_in, &rdclass)); if (rdclass != dns_rdataclass_in && obj) classname = cfg_obj_asstring(obj); /* Make sense of optional view argument */ obj = cfg_tuple_get(parms, "view"); if (obj && cfg_obj_isstring(obj)) viewname = cfg_obj_asstring(obj); if (viewname == NULL || *viewname == '\0') viewname = "_default"; CHECK(dns_viewlist_find(&server->viewlist, viewname, rdclass, &view)); /* Are we accepting new zones? */ if (view->new_zone_file == NULL) { result = ISC_R_NOPERM; goto cleanup; } cfg = (struct cfg_context *) view->new_zone_config; if (cfg == NULL) { result = ISC_R_FAILURE; goto cleanup; } /* Zone shouldn't already exist */ result = dns_zt_find(view->zonetable, dnsname, 0, NULL, &zone); if (result == ISC_R_SUCCESS) { result = ISC_R_EXISTS; goto cleanup; } else if (result == DNS_R_PARTIALMATCH) { /* Create our sub-zone anyway */ dns_zone_detach(&zone); zone = NULL; } else if (result != ISC_R_NOTFOUND) goto cleanup; /* Find the view statement */ cfg_map_get(cfg->config, "view", &views); for (element = cfg_list_first(views); element != NULL; element = cfg_list_next(element)) { const char *vname; vconfig = cfg_listelt_value(element); vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name")); if (vname && !strcasecmp(vname, viewname)) break; vconfig = NULL; } /* Open save file for write configuration */ CHECK(isc_stdio_open(view->new_zone_file, "a", &fp)); CHECK(isc_stdio_tell(fp, &offset)); if (offset == 0) CHECK(add_comment(fp, view->name)); /* Mark view unfrozen so that zone can be added */ result = isc_task_beginexclusive(server->task); RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_view_thaw(view); result = configure_zone(cfg->config, parms, vconfig, server->mctx, view, cfg->actx, ISC_FALSE); dns_view_freeze(view); isc_task_endexclusive(server->task); if (result != ISC_R_SUCCESS) goto cleanup; /* Is it there yet? */ CHECK(dns_zt_find(view->zonetable, dnsname, 0, NULL, &zone)); /* * Load the zone from the master file. If this fails, we'll * need to undo the configuration we've done already. */ result = dns_zone_loadnew(zone); if (result != ISC_R_SUCCESS) { dns_db_t *dbp = NULL; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "addzone failed; reverting."); /* If the zone loaded partially, unload it */ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) { dns_db_detach(&dbp); dns_zone_unload(zone); } /* Remove the zone from the zone table */ dns_zt_unmount(view->zonetable, zone); goto cleanup; } /* Flag the zone as having been added at runtime */ dns_zone_setadded(zone, ISC_TRUE); /* Emit the zone name, quoted and escaped */ isc_buffer_init(&buf, namebuf, sizeof(namebuf)); CHECK(dns_name_totext(dnsname, ISC_TRUE, &buf)); isc_buffer_putuint8(&buf, 0); CHECK(isc_stdio_write("zone \"", 6, 1, fp, NULL)); CHECK(isc_stdio_write(namebuf, strlen(namebuf), 1, fp, NULL)); CHECK(isc_stdio_write("\" ", 2, 1, fp, NULL)); /* Classname, if not default */ if (classname != NULL && *classname != '\0') { CHECK(isc_stdio_write(classname, strlen(classname), 1, fp, NULL)); CHECK(isc_stdio_write(" ", 1, 1, fp, NULL)); } /* Find beginning of option block from args */ for (argp = args; *argp; argp++, arglen--) { if (*argp == '{') { /* Assume matching '}' */ /* Add that to our file */ CHECK(isc_stdio_write(argp, arglen, 1, fp, NULL)); /* Make sure we end with a LF */ if (argp[arglen-1] != '\n') { CHECK(isc_stdio_write("\n", 1, 1, fp, NULL)); } break; } } CHECK(isc_stdio_close(fp)); fp = NULL; isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "zone %s added to view %s via addzone", zonename, viewname); result = ISC_R_SUCCESS; cleanup: if (fp != NULL) isc_stdio_close(fp); if (parser != NULL) { if (config != NULL) cfg_obj_destroy(parser, &config); cfg_parser_destroy(&parser); } if (zone != NULL) dns_zone_detach(&zone); if (view != NULL) dns_view_detach(&view); return (result); } /* * Act on a "delzone" command from the command channel. */ isc_result_t ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result; dns_zone_t *zone = NULL; dns_view_t *view = NULL; dns_db_t *dbp = NULL; const char *filename = NULL; char *tmpname = NULL; char buf[1024]; const char *zonename = NULL; size_t znamelen = 0; FILE *ifp = NULL, *ofp = NULL; isc_boolean_t inheader = ISC_TRUE; /* Parse parameters */ CHECK(zone_from_args(server, args, NULL, &zone, &zonename, text, ISC_TRUE)); if (zone == NULL) { result = ISC_R_UNEXPECTEDEND; goto cleanup; } /* * Was this zone originally added at runtime? * If not, we can't delete it now. */ if (!dns_zone_getadded(zone)) { result = ISC_R_NOPERM; goto cleanup; } INSIST(zonename != NULL); znamelen = strlen(zonename); /* Dig out configuration for this zone */ view = dns_zone_getview(zone); filename = view->new_zone_file; if (filename == NULL) { /* No adding zones in this view */ result = ISC_R_FAILURE; goto cleanup; } /* Rewrite zone list */ result = isc_stdio_open(filename, "r", &ifp); if (ifp != NULL && result == ISC_R_SUCCESS) { char *found = NULL, *p = NULL; size_t n; /* Create a temporary file */ CHECK(isc_string_printf(buf, 1023, "%s.%ld", filename, (long)getpid())); if (!(tmpname = isc_mem_strdup(server->mctx, buf))) { result = ISC_R_NOMEMORY; goto cleanup; } CHECK(isc_stdio_open(tmpname, "w", &ofp)); CHECK(add_comment(ofp, view->name)); /* Look for the entry for that zone */ while (fgets(buf, 1024, ifp)) { /* Skip initial comment, if any */ if (inheader && *buf == '#') continue; if (*buf != '#') inheader = ISC_FALSE; /* * Any other lines not starting with zone, copy * them out and continue. */ if (strncasecmp(buf, "zone", 4) != 0) { fputs(buf, ofp); continue; } p = buf+4; /* This is a zone; find its name. */ while (*p && ((*p == '"') || isspace((unsigned char)*p))) p++; /* * If it's not the zone we're looking for, copy * it out and continue */ if (strncasecmp(p, zonename, znamelen) != 0) { fputs(buf, ofp); continue; } /* * But if it is the zone we want, skip over it * so it will be omitted from the new file */ p += znamelen; if (isspace((unsigned char)*p) || *p == '"' || *p == '{') { /* This must be the entry */ found = p; break; } /* Copy the rest of the buffer out and continue */ fputs(buf, ofp); } /* Skip over an option block (matching # of braces) */ if (found) { int obrace = 0, cbrace = 0; for (;;) { while (*p) { if (*p == '{') obrace++; if (*p == '}') cbrace++; p++; } if (obrace && (obrace == cbrace)) break; if (!fgets(buf, 1024, ifp)) break; p = buf; } /* Just spool the remainder of the file out */ result = isc_stdio_read(buf, 1, 1024, ifp, &n); while (n > 0U) { if (result == ISC_R_EOF) result = ISC_R_SUCCESS; CHECK(result); isc_stdio_write(buf, 1, n, ofp, NULL); result = isc_stdio_read(buf, 1, 1024, ifp, &n); } /* Move temporary into place */ CHECK(isc_file_rename(tmpname, view->new_zone_file)); } else { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_WARNING, "deleted zone %s was missing from " "new zone file", zonename); goto cleanup; } } /* Stop answering for this zone */ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) { dns_db_detach(&dbp); dns_zone_unload(zone); } CHECK(dns_zt_unmount(view->zonetable, zone)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER, ISC_LOG_INFO, "zone %s removed via delzone", zonename); result = ISC_R_SUCCESS; cleanup: if (ifp != NULL) isc_stdio_close(ifp); if (ofp != NULL) { isc_stdio_close(ofp); isc_file_remove(tmpname); } if (tmpname != NULL) isc_mem_free(server->mctx, tmpname); if (zone != NULL) dns_zone_detach(&zone); return (result); } static void newzone_cfgctx_destroy(void **cfgp) { struct cfg_context *cfg; REQUIRE(cfgp != NULL && *cfgp != NULL); cfg = *cfgp; if (cfg->actx != NULL) cfg_aclconfctx_detach(&cfg->actx); if (cfg->parser != NULL) { if (cfg->config != NULL) cfg_obj_destroy(cfg->parser, &cfg->config); cfg_parser_destroy(&cfg->parser); } if (cfg->nzparser != NULL) { if (cfg->nzconfig != NULL) cfg_obj_destroy(cfg->nzparser, &cfg->nzconfig); cfg_parser_destroy(&cfg->nzparser); } isc_mem_putanddetach(&cfg->mctx, cfg, sizeof(*cfg)); *cfgp = NULL; } isc_result_t ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) { isc_result_t result = ISC_R_SUCCESS; dns_zone_t *zone = NULL; dns_name_t *origin; dns_db_t *db = NULL; dns_dbnode_t *node = NULL; dns_dbversion_t *version = NULL; dns_rdatatype_t privatetype; dns_rdataset_t privset; isc_boolean_t first = ISC_TRUE; isc_boolean_t list = ISC_FALSE, clear = ISC_FALSE; isc_boolean_t chain = ISC_FALSE; char keystr[DNS_SECALG_FORMATSIZE + 7]; unsigned short hash = 0, flags = 0, iter = 0, saltlen = 0; unsigned char salt[255]; const char *ptr; size_t n; dns_rdataset_init(&privset); /* Skip the command name. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); /* Find out what we are to do. */ ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); if (strcasecmp(ptr, "-list") == 0) list = ISC_TRUE; else if ((strcasecmp(ptr, "-clear") == 0) || (strcasecmp(ptr, "-clean") == 0)) { clear = ISC_TRUE; ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); memmove(keystr, ptr, sizeof(keystr)); } else if (strcasecmp(ptr, "-nsec3param") == 0) { const char *hashstr, *flagstr, *iterstr; char nbuf[512]; chain = ISC_TRUE; hashstr = next_token(&args, " \t"); if (hashstr == NULL) return (ISC_R_UNEXPECTEDEND); if (strcasecmp(hashstr, "none") == 0) hash = 0; else { flagstr = next_token(&args, " \t"); iterstr = next_token(&args, " \t"); if (flagstr == NULL || iterstr == NULL) return (ISC_R_UNEXPECTEDEND); n = snprintf(nbuf, sizeof(nbuf), "%s %s %s", hashstr, flagstr, iterstr); if (n == sizeof(nbuf)) return (ISC_R_NOSPACE); n = sscanf(nbuf, "%hu %hu %hu", &hash, &flags, &iter); if (n != 3U) return (ISC_R_BADNUMBER); if (hash > 0xffU || flags > 0xffU) return (ISC_R_RANGE); ptr = next_token(&args, " \t"); if (ptr == NULL) return (ISC_R_UNEXPECTEDEND); if (strcmp(ptr, "-") != 0) { isc_buffer_t buf; isc_buffer_init(&buf, salt, sizeof(salt)); CHECK(isc_hex_decodestring(ptr, &buf)); saltlen = isc_buffer_usedlength(&buf); } } } else CHECK(DNS_R_SYNTAX); CHECK(zone_from_args(server, args, NULL, &zone, NULL, text, ISC_FALSE)); if (zone == NULL) CHECK(ISC_R_UNEXPECTEDEND); if (clear) { CHECK(dns_zone_keydone(zone, keystr)); isc_buffer_putstr(text, "request queued"); isc_buffer_putuint8(text, 0); } else if (chain) { CHECK(dns_zone_setnsec3param(zone, (isc_uint8_t)hash, (isc_uint8_t)flags, iter, (isc_uint8_t)saltlen, salt, ISC_TRUE)); isc_buffer_putstr(text, "request queued"); isc_buffer_putuint8(text, 0); } else if (list) { privatetype = dns_zone_getprivatetype(zone); origin = dns_zone_getorigin(zone); CHECK(dns_zone_getdb(zone, &db)); CHECK(dns_db_findnode(db, origin, ISC_FALSE, &node)); dns_db_currentversion(db, &version); result = dns_db_findrdataset(db, node, version, privatetype, dns_rdatatype_none, 0, &privset, NULL); if (result == ISC_R_NOTFOUND) { isc_buffer_putstr(text, "No signing records found"); isc_buffer_putuint8(text, 0); result = ISC_R_SUCCESS; goto cleanup; } for (result = dns_rdataset_first(&privset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&privset)) { dns_rdata_t priv = DNS_RDATA_INIT; char output[BUFSIZ]; isc_buffer_t buf; dns_rdataset_current(&privset, &priv); isc_buffer_init(&buf, output, sizeof(output)); CHECK(dns_private_totext(&priv, &buf)); if (!first) isc_buffer_putstr(text, "\n"); first = ISC_FALSE; n = snprintf((char *)isc_buffer_used(text), isc_buffer_availablelength(text), "%s", output); if (n >= isc_buffer_availablelength(text)) CHECK(ISC_R_NOSPACE); isc_buffer_add(text, (unsigned int)n); } if (!first && isc_buffer_availablelength(text) > 0) isc_buffer_putuint8(text, 0); if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; } cleanup: if (dns_rdataset_isassociated(&privset)) dns_rdataset_disassociate(&privset); if (node != NULL) dns_db_detachnode(db, &node); if (version != NULL) dns_db_closeversion(db, &version, ISC_FALSE); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); return (result); } static isc_result_t putstr(isc_buffer_t *b, const char *str) { size_t l = strlen(str); /* * Use >= to leave space for NUL termination. */ if (l >= isc_buffer_availablelength(b)) return (ISC_R_NOSPACE); isc_buffer_putmem(b, (const unsigned char *)str, l); return (ISC_R_SUCCESS); } Index: stable/9/contrib/bind9/lib/dns/adb.c =================================================================== --- stable/9/contrib/bind9/lib/dns/adb.c (revision 275668) +++ stable/9/contrib/bind9/lib/dns/adb.c (revision 275669) @@ -1,4148 +1,4174 @@ /* * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: adb.c,v 1.264 2011/12/05 17:10:51 each Exp $ */ /*! \file * * \note * In finds, if task == NULL, no events will be generated, and no events * have been sent. If task != NULL but taskaction == NULL, an event has been * posted but not yet freed. If neither are NULL, no event was posted. * */ #include #include #include #include #include #include #include /* Required for HP/UX (and others?) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #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; isc_boolean_t *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; isc_boolean_t *entry_sd; /*%< shutting down */ unsigned int *entry_refcnt; isc_event_t cevent; isc_boolean_t cevent_sent; isc_boolean_t shutting_down; isc_eventlist_t whenshutdown; isc_event_t growentries; isc_boolean_t growentries_sent; isc_event_t grownames; isc_boolean_t grownames_sent; }; /* * 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 flags; unsigned int srtt; isc_sockaddr_t sockaddr; isc_stdtime_t expires; /*%< * A nonzero 'expires' field indicates that the entry should * persist until that time. This allows entries found * using dns_adb_findaddrinfo() to persist for a limited time * even though they are not necessarily associated with a * name. */ ISC_LIST(dns_adblameinfo_t) lameinfo; ISC_LINK(dns_adbentry_t) plink; }; /* * Internal functions (and prototypes). */ static inline dns_adbname_t *new_adbname(dns_adb_t *, dns_name_t *); static inline void free_adbname(dns_adb_t *, dns_adbname_t **); static inline dns_adbnamehook_t *new_adbnamehook(dns_adb_t *, dns_adbentry_t *); static inline void free_adbnamehook(dns_adb_t *, dns_adbnamehook_t **); static inline dns_adblameinfo_t *new_adblameinfo(dns_adb_t *, dns_name_t *, dns_rdatatype_t); static inline void free_adblameinfo(dns_adb_t *, dns_adblameinfo_t **); static inline dns_adbentry_t *new_adbentry(dns_adb_t *); static inline void free_adbentry(dns_adb_t *, dns_adbentry_t **); static inline dns_adbfind_t *new_adbfind(dns_adb_t *); static inline isc_boolean_t free_adbfind(dns_adb_t *, dns_adbfind_t **); static inline dns_adbaddrinfo_t *new_adbaddrinfo(dns_adb_t *, dns_adbentry_t *, in_port_t); static inline dns_adbfetch_t *new_adbfetch(dns_adb_t *); static inline void free_adbfetch(dns_adb_t *, dns_adbfetch_t **); static inline dns_adbname_t *find_name_and_lock(dns_adb_t *, dns_name_t *, unsigned int, int *); static inline dns_adbentry_t *find_entry_and_lock(dns_adb_t *, isc_sockaddr_t *, int *, isc_stdtime_t); static void dump_adb(dns_adb_t *, FILE *, isc_boolean_t debug, isc_stdtime_t); static void print_dns_name(FILE *, dns_name_t *); static void print_namehook_list(FILE *, const char *legend, dns_adbnamehooklist_t *list, isc_boolean_t debug, isc_stdtime_t now); static void print_find_list(FILE *, dns_adbname_t *); static void print_fetch_list(FILE *, dns_adbname_t *); static inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *); static inline void inc_adb_irefcnt(dns_adb_t *); static inline void inc_adb_erefcnt(dns_adb_t *); static inline void inc_entry_refcnt(dns_adb_t *, dns_adbentry_t *, isc_boolean_t); static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, isc_boolean_t, dns_adbentry_t *, isc_boolean_t); static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *); static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *); static void clean_target(dns_adb_t *, dns_name_t *); static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, - unsigned int); + isc_uint32_t, unsigned int); static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t); static isc_boolean_t 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 *, isc_boolean_t, - dns_rdatatype_t); + unsigned int, dns_rdatatype_t); static inline void check_exit(dns_adb_t *); static void destroy(dns_adb_t *); static isc_boolean_t shutdown_names(dns_adb_t *); static isc_boolean_t shutdown_entries(dns_adb_t *); static inline void link_name(dns_adb_t *, int, dns_adbname_t *); static inline isc_boolean_t unlink_name(dns_adb_t *, dns_adbname_t *); static inline void link_entry(dns_adb_t *, int, dns_adbentry_t *); static inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *); static isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t); static void water(void *, int); static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t); /* * MUST NOT overlap DNS_ADBFIND_* flags! */ #define FIND_EVENT_SENT 0x40000000 #define FIND_EVENT_FREED 0x80000000 #define FIND_EVENTSENT(h) (((h)->flags & FIND_EVENT_SENT) != 0) #define FIND_EVENTFREED(h) (((h)->flags & FIND_EVENT_FREED) != 0) #define NAME_NEEDS_POKE 0x80000000 #define NAME_IS_DEAD 0x40000000 #define NAME_HINT_OK DNS_ADBFIND_HINTOK #define NAME_GLUE_OK DNS_ADBFIND_GLUEOK #define NAME_STARTATZONE DNS_ADBFIND_STARTATZONE #define NAME_DEAD(n) (((n)->flags & NAME_IS_DEAD) != 0) #define NAME_NEEDSPOKE(n) (((n)->flags & NAME_NEEDS_POKE) != 0) #define NAME_GLUEOK(n) (((n)->flags & NAME_GLUE_OK) != 0) #define NAME_HINTOK(n) (((n)->flags & NAME_HINT_OK) != 0) /* * Private flag(s) for entries. * MUST NOT overlap FCTX_ADDRINFO_xxx and DNS_FETCHOPT_NOEDNS0. */ #define ENTRY_IS_DEAD 0x80000000 /* * 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); } 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; isc_boolean_t *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] = ISC_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, ISC_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, ISC_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; /* * Only on success do we set adb->growentries_sent to ISC_FALSE. * This will prevent us being continuously being called on error. */ adb->growentries_sent = ISC_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; isc_boolean_t *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] = ISC_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, ISC_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, ISC_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; /* * Only on success do we set adb->grownames_sent to ISC_FALSE. * This will prevent us being continuously being called on error. */ adb->grownames_sent = ISC_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; isc_boolean_t new_addresses_added; dns_rdatatype_t rdtype; unsigned int findoptions; dns_adbnamehooklist_t *hookhead; INSIST(DNS_ADBNAME_VALID(adbname)); adb = adbname->adb; INSIST(DNS_ADB_VALID(adb)); rdtype = rdataset->type; INSIST((rdtype == dns_rdatatype_a) || (rdtype == dns_rdatatype_aaaa)); if (rdtype == dns_rdatatype_a) findoptions = DNS_ADBFIND_INET; else findoptions = DNS_ADBFIND_INET6; addr_bucket = DNS_ADB_INVALIDBUCKET; new_addresses_added = ISC_FALSE; nh = NULL; result = dns_rdataset_first(rdataset); while (result == ISC_R_SUCCESS) { dns_rdata_reset(&rdata); dns_rdataset_current(rdataset, &rdata); if (rdtype == dns_rdatatype_a) { INSIST(rdata.length == 4); 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; nh->entry = entry; link_entry(adb, addr_bucket, entry); } else { for (anh = ISC_LIST_HEAD(*hookhead); anh != NULL; anh = ISC_LIST_NEXT(anh, plink)) if (anh->entry == foundentry) break; if (anh == NULL) { foundentry->refcnt++; nh->entry = foundentry; } else free_adbnamehook(adb, &nh); } new_addresses_added = ISC_TRUE; if (nh != NULL) ISC_LIST_APPEND(*hookhead, nh, plink); nh = NULL; result = dns_rdataset_next(rdataset); } fail: if (nh != NULL) free_adbnamehook(adb, &nh); if (addr_bucket != DNS_ADB_INVALIDBUCKET) UNLOCK(&adb->entrylocks[addr_bucket]); if (rdataset->trust == dns_trust_glue || rdataset->trust == dns_trust_additional) rdataset->ttl = ADB_CACHE_MINIMUM; else 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, now + rdataset->ttl); } else { DP(NCACHE_LEVEL, "expire_v6 set to MIN(%u,%u) import_rdataset", adbname->expire_v6, now + rdataset->ttl); adbname->expire_v6 = ISC_MIN(adbname->expire_v6, now + rdataset->ttl); } if (new_addresses_added) { /* * Lie a little here. This is more or less so code that cares * can find out if any new information was added or not. */ return (ISC_R_SUCCESS); } return (result); } /* * Requires the name's bucket be locked. */ static isc_boolean_t kill_name(dns_adbname_t **n, isc_eventtype_t ev) { dns_adbname_t *name; isc_boolean_t result = ISC_FALSE; isc_boolean_t 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); + clean_finds_at_name(name, ev, 0, DNS_ADBFIND_ADDRESSMASK); result4 = clean_namehooks(adb, &name->v4); result6 = clean_namehooks(adb, &name->v6); clean_target(adb, &name->target); result = ISC_TF(result4 || result6); /* * If fetches are running, cancel them. If none are running, we can * just kill the name here. */ if (!NAME_FETCH(name)) { INSIST(result == ISC_FALSE); result = unlink_name(adb, name); free_adbname(adb, &name); if (result) result = dec_adb_irefcnt(adb); } else { 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 isc_boolean_t check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) { dns_adb_t *adb; isc_boolean_t result4 = ISC_FALSE; isc_boolean_t result6 = ISC_FALSE; INSIST(DNS_ADBNAME_VALID(name)); adb = name->adb; INSIST(DNS_ADB_VALID(adb)); /* * Check to see if we need to remove the v4 addresses */ if (!NAME_FETCH_V4(name) && EXPIRE_OK(name->expire_v4, now)) { if (NAME_HAS_V4(name)) { DP(DEF_LEVEL, "expiring v4 for name %p", name); result4 = clean_namehooks(adb, &name->v4); 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 (ISC_TF(result4 || result6)); } /* * Requires the name's bucket be locked. */ static inline void link_name(dns_adb_t *adb, int bucket, dns_adbname_t *name) { INSIST(name->lock_bucket == DNS_ADB_INVALIDBUCKET); ISC_LIST_PREPEND(adb->names[bucket], name, plink); name->lock_bucket = bucket; adb->name_refcnt[bucket]++; } /* * Requires the name's bucket be locked. */ static inline isc_boolean_t unlink_name(dns_adb_t *adb, dns_adbname_t *name) { int bucket; isc_boolean_t result = ISC_FALSE; bucket = name->lock_bucket; INSIST(bucket != DNS_ADB_INVALIDBUCKET); 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 = ISC_TRUE; return (result); } /* * Requires the entry's bucket be locked. */ static inline void link_entry(dns_adb_t *adb, int bucket, dns_adbentry_t *entry) { 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 isc_boolean_t unlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) { int bucket; isc_boolean_t result = ISC_FALSE; bucket = entry->lock_bucket; INSIST(bucket != DNS_ADB_INVALIDBUCKET); 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 = ISC_TRUE; return (result); } static inline void violate_locking_hierarchy(isc_mutex_t *have, isc_mutex_t *want) { if (isc_mutex_trylock(want) != ISC_R_SUCCESS) { UNLOCK(have); LOCK(want); LOCK(have); } } /* * The ADB _MUST_ be locked before calling. Also, exit conditions must be * checked after calling this function. */ static isc_boolean_t shutdown_names(dns_adb_t *adb) { unsigned int bucket; isc_boolean_t result = ISC_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] = ISC_TRUE; name = ISC_LIST_HEAD(adb->names[bucket]); if (name == NULL) { /* * This bucket has no names. We must decrement the * irefcnt ourselves, since it will not be * automatically triggered by a name being unlinked. */ INSIST(result == ISC_FALSE); result = dec_adb_irefcnt(adb); } else { /* * Run through the list. For each name, clean up finds * found there, and cancel any fetches running. When * all the fetches are canceled, the name will destroy * itself. */ while (name != NULL) { next_name = ISC_LIST_NEXT(name, plink); INSIST(result == ISC_FALSE); result = kill_name(&name, DNS_EVENT_ADBSHUTDOWN); name = next_name; } } UNLOCK(&adb->namelocks[bucket]); } return (result); } /* * The ADB _MUST_ be locked before calling. Also, exit conditions must be * checked after calling this function. */ static isc_boolean_t shutdown_entries(dns_adb_t *adb) { unsigned int bucket; isc_boolean_t result = ISC_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] = ISC_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 isc_boolean_t clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) { dns_adbentry_t *entry; dns_adbnamehook_t *namehook; int addr_bucket; isc_boolean_t result = ISC_FALSE; isc_boolean_t 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]); } result = dec_entry_refcnt(adb, overmem, entry, ISC_FALSE); } /* * Free the namehook */ namehook->entry = NULL; ISC_LIST_UNLINK(*namehooks, namehook, plink); free_adbnamehook(adb, &namehook); namehook = ISC_LIST_HEAD(*namehooks); } if (addr_bucket != DNS_ADB_INVALIDBUCKET) UNLOCK(&adb->entrylocks[addr_bucket]); return (result); } static void clean_target(dns_adb_t *adb, dns_name_t *target) { if (dns_name_countlabels(target) > 0) { dns_name_free(target, adb->mctx); dns_name_init(target, NULL); } } static isc_result_t set_target(dns_adb_t *adb, dns_name_t *name, dns_name_t *fname, dns_rdataset_t *rdataset, dns_name_t *target) { isc_result_t result; dns_namereln_t namereln; unsigned int nlabels; int order; dns_rdata_t rdata = DNS_RDATA_INIT; dns_fixedname_t fixed1, fixed2; dns_name_t *prefix, *new_target; REQUIRE(dns_name_countlabels(target) == 0); if (rdataset->type == dns_rdatatype_cname) { dns_rdata_cname_t cname; /* * Copy the CNAME's target into the target name. */ result = dns_rdataset_first(rdataset); if (result != ISC_R_SUCCESS) return (result); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &cname, NULL); if (result != ISC_R_SUCCESS) return (result); result = dns_name_dup(&cname.cname, adb->mctx, target); dns_rdata_freestruct(&cname); if (result != ISC_R_SUCCESS) return (result); } else { dns_rdata_dname_t dname; INSIST(rdataset->type == dns_rdatatype_dname); namereln = dns_name_fullcompare(name, fname, &order, &nlabels); INSIST(namereln == dns_namereln_subdomain); /* * Get the target name of the DNAME. */ result = dns_rdataset_first(rdataset); if (result != ISC_R_SUCCESS) return (result); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &dname, NULL); if (result != ISC_R_SUCCESS) return (result); /* * Construct the new target name. */ dns_fixedname_init(&fixed1); prefix = dns_fixedname_name(&fixed1); dns_fixedname_init(&fixed2); new_target = dns_fixedname_name(&fixed2); dns_name_split(name, nlabels, prefix, NULL); result = dns_name_concatenate(prefix, &dname.dname, new_target, NULL); dns_rdata_freestruct(&dname); if (result != ISC_R_SUCCESS) return (result); result = dns_name_dup(new_target, adb->mctx, target); if (result != ISC_R_SUCCESS) return (result); } return (ISC_R_SUCCESS); } /* * Assumes nothing is locked, since this is called by the client. */ static void event_free(isc_event_t *event) { dns_adbfind_t *find; INSIST(event != NULL); find = event->ev_destroy_arg; INSIST(DNS_ADBFIND_VALID(find)); LOCK(&find->lock); find->flags |= FIND_EVENT_FREED; event->ev_destroy_arg = NULL; UNLOCK(&find->lock); } /* * Assumes the name bucket is locked. */ static void clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype, - unsigned int addrs) + isc_uint32_t qtotal, unsigned int addrs) { isc_event_t *ev; isc_task_t *task; dns_adbfind_t *find; dns_adbfind_t *next_find; isc_boolean_t process; unsigned int wanted, notify; DP(ENTER_LEVEL, "ENTER clean_finds_at_name, name %p, evtype %08x, addrs %08x", name, evtype, addrs); find = ISC_LIST_HEAD(name->finds); while (find != NULL) { LOCK(&find->lock); next_find = ISC_LIST_NEXT(find, plink); process = ISC_FALSE; wanted = find->flags & DNS_ADBFIND_ADDRESSMASK; notify = wanted & addrs; switch (evtype) { case DNS_EVENT_ADBMOREADDRESSES: DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBMOREADDRESSES"); if ((notify) != 0) { find->flags &= ~addrs; process = ISC_TRUE; } break; case DNS_EVENT_ADBNOMOREADDRESSES: DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBNOMOREADDRESSES"); find->flags &= ~addrs; wanted = find->flags & DNS_ADBFIND_ADDRESSMASK; if (wanted == 0) process = ISC_TRUE; break; default: find->flags &= ~addrs; process = ISC_TRUE; } if (process) { DP(DEF_LEVEL, "cfan: processing find %p", find); /* * Unlink the find from the name, letting the caller * call dns_adb_destroyfind() on it to clean it up * later. */ ISC_LIST_UNLINK(name->finds, find, plink); find->adbname = NULL; find->name_bucket = DNS_ADB_INVALIDBUCKET; INSIST(!FIND_EVENTSENT(find)); ev = &find->event; task = ev->ev_sender; ev->ev_sender = find; find->result_v4 = find_err_map[name->fetch_err]; find->result_v6 = find_err_map[name->fetch6_err]; + find->qtotal += qtotal; ev->ev_type = evtype; ev->ev_destroy = event_free; ev->ev_destroy_arg = find; DP(DEF_LEVEL, "sending event %p to task %p for find %p", ev, task, find); isc_task_sendanddetach(&task, (isc_event_t **)&ev); } else { DP(DEF_LEVEL, "cfan: skipping find %p", find); } UNLOCK(&find->lock); find = next_find; } DP(ENTER_LEVEL, "EXIT clean_finds_at_name, name %p", name); } static inline void check_exit(dns_adb_t *adb) { isc_event_t *event; /* * The caller must be holding the adb lock. */ if (adb->shutting_down) { /* * If there aren't any external references either, we're * done. Send the control event to initiate shutdown. */ INSIST(!adb->cevent_sent); /* Sanity check. */ event = &adb->cevent; isc_task_send(adb->task, &event); adb->cevent_sent = ISC_TRUE; } } static inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *adb) { isc_event_t *event; isc_task_t *etask; isc_boolean_t result = ISC_FALSE; LOCK(&adb->reflock); INSIST(adb->irefcnt > 0); adb->irefcnt--; if (adb->irefcnt == 0) { event = ISC_LIST_HEAD(adb->whenshutdown); while (event != NULL) { ISC_LIST_UNLINK(adb->whenshutdown, event, ev_link); etask = event->ev_sender; event->ev_sender = adb; isc_task_sendanddetach(&etask, &event); event = ISC_LIST_HEAD(adb->whenshutdown); } } if (adb->irefcnt == 0 && adb->erefcnt == 0) result = ISC_TRUE; UNLOCK(&adb->reflock); return (result); } static inline void inc_adb_irefcnt(dns_adb_t *adb) { LOCK(&adb->reflock); adb->irefcnt++; UNLOCK(&adb->reflock); } static inline void inc_adb_erefcnt(dns_adb_t *adb) { LOCK(&adb->reflock); adb->erefcnt++; UNLOCK(&adb->reflock); } static inline void inc_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) { int bucket; bucket = entry->lock_bucket; if (lock) LOCK(&adb->entrylocks[bucket]); entry->refcnt++; if (lock) UNLOCK(&adb->entrylocks[bucket]); } static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *adb, isc_boolean_t overmem, dns_adbentry_t *entry, isc_boolean_t lock) { int bucket; isc_boolean_t destroy_entry; isc_boolean_t result = ISC_FALSE; bucket = entry->lock_bucket; if (lock) LOCK(&adb->entrylocks[bucket]); INSIST(entry->refcnt > 0); entry->refcnt--; destroy_entry = ISC_FALSE; if (entry->refcnt == 0 && (adb->entry_sd[bucket] || entry->expires == 0 || overmem || (entry->flags & ENTRY_IS_DEAD) != 0)) { destroy_entry = ISC_TRUE; result = unlink_entry(adb, entry); } if (lock) UNLOCK(&adb->entrylocks[bucket]); if (!destroy_entry) return (result); entry->lock_bucket = DNS_ADB_INVALIDBUCKET; free_adbentry(adb, &entry); if (result) result = dec_adb_irefcnt(adb); return (result); } static inline dns_adbname_t * new_adbname(dns_adb_t *adb, dns_name_t *dnsname) { dns_adbname_t *name; name = isc_mempool_get(adb->nmp); if (name == NULL) return (NULL); dns_name_init(&name->name, NULL); if (dns_name_dup(dnsname, adb->mctx, &name->name) != ISC_R_SUCCESS) { isc_mempool_put(adb->nmp, name); return (NULL); } dns_name_init(&name->target, NULL); name->magic = DNS_ADBNAME_MAGIC; name->adb = adb; name->partial_result = 0; name->flags = 0; name->expire_v4 = INT_MAX; name->expire_v6 = INT_MAX; name->expire_target = INT_MAX; name->chains = 0; name->lock_bucket = DNS_ADB_INVALIDBUCKET; ISC_LIST_INIT(name->v4); ISC_LIST_INIT(name->v6); name->fetch_a = NULL; name->fetch_aaaa = NULL; name->fetch_err = FIND_ERR_UNEXPECTED; name->fetch6_err = FIND_ERR_UNEXPECTED; ISC_LIST_INIT(name->finds); ISC_LINK_INIT(name, plink); LOCK(&adb->namescntlock); adb->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 = ISC_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--; 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; isc_uint32_t r; e = isc_mempool_get(adb->emp); if (e == NULL) return (NULL); e->magic = DNS_ADBENTRY_MAGIC; e->lock_bucket = DNS_ADB_INVALIDBUCKET; e->refcnt = 0; e->flags = 0; isc_random_get(&r); e->srtt = (r & 0x1f) + 1; e->expires = 0; ISC_LIST_INIT(e->lameinfo); ISC_LINK_INIT(e, plink); LOCK(&adb->entriescntlock); adb->entriescnt++; if (!adb->growentries_sent && adb->growentries_sent && adb->entriescnt > (adb->nentries * 8)) { isc_event_t *event = &adb->growentries; inc_adb_irefcnt(adb); isc_task_send(adb->task, &event); adb->growentries_sent = ISC_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; 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--; 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; + h->qtotal = 0; 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 isc_boolean_t free_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) { dns_adbfind_t *find; INSIST(findp != NULL && DNS_ADBFIND_VALID(*findp)); find = *findp; *findp = NULL; INSIST(!FIND_HAS_ADDRS(find)); INSIST(!ISC_LINK_LINKED(find, publink)); INSIST(!ISC_LINK_LINKED(find, plink)); INSIST(find->name_bucket == DNS_ADB_INVALIDBUCKET); INSIST(find->adbname == NULL); find->magic = 0; DESTROYLOCK(&find->lock); isc_mempool_put(adb->ahmp, find); return (dec_adb_irefcnt(adb)); } /* * Copy bits from the entry into the newly allocated addrinfo. The entry * must be locked, and the reference count must be bumped up by one * if this function returns a valid pointer. */ static inline dns_adbaddrinfo_t * new_adbaddrinfo(dns_adb_t *adb, dns_adbentry_t *entry, in_port_t port) { dns_adbaddrinfo_t *ai; ai = isc_mempool_get(adb->aimp); if (ai == NULL) return (NULL); ai->magic = DNS_ADBADDRINFO_MAGIC; ai->sockaddr = entry->sockaddr; isc_sockaddr_setport(&ai->sockaddr, port); ai->srtt = entry->srtt; ai->flags = entry->flags; ai->entry = entry; ISC_LINK_INIT(ai, publink); return (ai); } static inline void free_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) { dns_adbaddrinfo_t *ai; INSIST(ainfo != NULL && DNS_ADBADDRINFO_VALID(*ainfo)); ai = *ainfo; *ainfo = NULL; INSIST(ai->entry == NULL); INSIST(!ISC_LINK_LINKED(ai, publink)); ai->magic = 0; isc_mempool_put(adb->aimp, ai); } /* * Search for the name. NOTE: The bucket is kept locked on both * success and failure, so it must always be unlocked by the caller! * * On the first call to this function, *bucketp must be set to * DNS_ADB_INVALIDBUCKET. */ static inline dns_adbname_t * find_name_and_lock(dns_adb_t *adb, dns_name_t *name, unsigned int options, int *bucketp) { dns_adbname_t *adbname; int bucket; bucket = dns_name_fullhash(name, ISC_FALSE) % 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, ISC_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 && 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 isc_boolean_t entry_is_lame(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *qname, dns_rdatatype_t qtype, isc_stdtime_t now) { dns_adblameinfo_t *li, *next_li; isc_boolean_t is_bad; is_bad = ISC_FALSE; li = ISC_LIST_HEAD(entry->lameinfo); if (li == NULL) return (ISC_FALSE); while (li != NULL) { next_li = ISC_LIST_NEXT(li, plink); /* * Has the entry expired? */ if (li->lame_timer < now) { ISC_LIST_UNLINK(entry->lameinfo, li, plink); free_adblameinfo(adb, &li); } /* * Order tests from least to most expensive. * * We do not break out of the main loop here as * we use the loop for house keeping. */ if (li != NULL && !is_bad && li->qtype == qtype && dns_name_equal(qname, &li->qname)) is_bad = ISC_TRUE; li = next_li; } return (is_bad); } static void copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname, dns_rdatatype_t qtype, dns_adbname_t *name, isc_stdtime_t now) { dns_adbnamehook_t *namehook; dns_adbaddrinfo_t *addrinfo; dns_adbentry_t *entry; int bucket; bucket = DNS_ADB_INVALIDBUCKET; if (find->options & DNS_ADBFIND_INET) { namehook = ISC_LIST_HEAD(name->v4); while (namehook != NULL) { entry = namehook->entry; bucket = entry->lock_bucket; INSIST(bucket != DNS_ADB_INVALIDBUCKET); LOCK(&adb->entrylocks[bucket]); if (!FIND_RETURNLAME(find) && entry_is_lame(adb, entry, qname, qtype, now)) { find->options |= DNS_ADBFIND_LAMEPRUNED; goto nextv4; } addrinfo = new_adbaddrinfo(adb, entry, find->port); if (addrinfo == NULL) { find->partial_result |= DNS_ADBFIND_INET; goto out; } /* * Found a valid entry. Add it to the find's list. */ inc_entry_refcnt(adb, entry, ISC_FALSE); ISC_LIST_APPEND(find->list, addrinfo, publink); addrinfo = NULL; nextv4: UNLOCK(&adb->entrylocks[bucket]); bucket = DNS_ADB_INVALIDBUCKET; namehook = ISC_LIST_NEXT(namehook, plink); } } if (find->options & DNS_ADBFIND_INET6) { namehook = ISC_LIST_HEAD(name->v6); while (namehook != NULL) { entry = namehook->entry; bucket = entry->lock_bucket; INSIST(bucket != DNS_ADB_INVALIDBUCKET); LOCK(&adb->entrylocks[bucket]); if (!FIND_RETURNLAME(find) && entry_is_lame(adb, entry, qname, qtype, now)) { find->options |= DNS_ADBFIND_LAMEPRUNED; goto nextv6; } addrinfo = new_adbaddrinfo(adb, entry, find->port); if (addrinfo == NULL) { find->partial_result |= DNS_ADBFIND_INET6; goto out; } /* * Found a valid entry. Add it to the find's list. */ inc_entry_refcnt(adb, entry, ISC_FALSE); ISC_LIST_APPEND(find->list, addrinfo, publink); addrinfo = NULL; nextv6: UNLOCK(&adb->entrylocks[bucket]); bucket = DNS_ADB_INVALIDBUCKET; namehook = ISC_LIST_NEXT(namehook, plink); } } out: if (bucket != DNS_ADB_INVALIDBUCKET) UNLOCK(&adb->entrylocks[bucket]); } static void shutdown_task(isc_task_t *task, isc_event_t *ev) { dns_adb_t *adb; UNUSED(task); adb = ev->ev_arg; INSIST(DNS_ADB_VALID(adb)); isc_event_free(&ev); /* * Wait for lock around check_exit() call to be released. */ LOCK(&adb->lock); UNLOCK(&adb->lock); destroy(adb); } /* * Name bucket must be locked; adb may be locked; no other locks held. */ static isc_boolean_t check_expire_name(dns_adbname_t **namep, isc_stdtime_t now) { dns_adbname_t *name; isc_boolean_t result = ISC_FALSE; INSIST(namep != NULL && DNS_ADBNAME_VALID(*namep)); name = *namep; if (NAME_HAS_V4(name) || NAME_HAS_V6(name)) return (result); if (NAME_FETCH(name)) return (result); if (!EXPIRE_OK(name->expire_v4, now)) return (result); if (!EXPIRE_OK(name->expire_v6, now)) return (result); if (!EXPIRE_OK(name->expire_target, now)) return (result); /* * The name is empty. Delete it. */ result = kill_name(&name, DNS_EVENT_ADBEXPIRED); *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; isc_boolean_t 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) == ISC_FALSE); victims++; } next: if (!overmem) break; } } /* * Entry bucket must be locked; adb may be locked; no other locks held. */ static isc_boolean_t check_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now) { dns_adbentry_t *entry; isc_boolean_t result = ISC_FALSE; INSIST(entryp != NULL && DNS_ADBENTRY_VALID(*entryp)); entry = *entryp; if (entry->refcnt != 0) return (result); if (entry->expires == 0 || entry->expires > now) return (result); /* * The entry is not in use. Delete it. */ DP(DEF_LEVEL, "killing entry %p", entry); INSIST(ISC_LINK_LINKED(entry, plink)); result = unlink_entry(adb, entry); free_adbentry(adb, &entry); if (result) dec_adb_irefcnt(adb); *entryp = NULL; return (result); } /* * ADB must be locked, and no other locks held. */ static isc_boolean_t cleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) { dns_adbname_t *name; dns_adbname_t *next_name; isc_boolean_t result = ISC_FALSE; DP(CLEAN_LEVEL, "cleaning name bucket %d", bucket); LOCK(&adb->namelocks[bucket]); if (adb->name_sd[bucket]) { UNLOCK(&adb->namelocks[bucket]); return (result); } name = ISC_LIST_HEAD(adb->names[bucket]); while (name != NULL) { next_name = ISC_LIST_NEXT(name, plink); INSIST(result == ISC_FALSE); result = check_expire_namehooks(name, now); if (!result) result = check_expire_name(&name, now); name = next_name; } UNLOCK(&adb->namelocks[bucket]); return (result); } /* * ADB must be locked, and no other locks held. */ static isc_boolean_t cleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) { dns_adbentry_t *entry, *next_entry; isc_boolean_t result = ISC_FALSE; DP(CLEAN_LEVEL, "cleaning entry bucket %d", bucket); LOCK(&adb->entrylocks[bucket]); entry = ISC_LIST_HEAD(adb->entries[bucket]); while (entry != NULL) { next_entry = ISC_LIST_NEXT(entry, plink); INSIST(result == ISC_FALSE); result = check_expire_entry(adb, &entry, now); entry = next_entry; } UNLOCK(&adb->entrylocks[bucket]); return (result); } static void destroy(dns_adb_t *adb) { adb->magic = 0; isc_task_detach(&adb->task); 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, DNS_EVENT_ADBCONTROL, shutdown_task, adb, adb, NULL, NULL); adb->cevent_sent = ISC_FALSE; adb->shutting_down = ISC_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 = ISC_FALSE; 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 = ISC_FALSE; result = isc_taskmgr_excltask(adb->taskmgr, &adb->excl); if (result != ISC_R_SUCCESS) { DP(ISC_LOG_INFO, "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] = ISC_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] = ISC_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); /* * 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: isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t)); return (result); } void dns_adb_attach(dns_adb_t *adb, dns_adb_t **adbx) { REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(adbx != NULL && *adbx == NULL); inc_adb_erefcnt(adb); *adbx = adb; } void dns_adb_detach(dns_adb_t **adbx) { dns_adb_t *adb; isc_boolean_t need_exit_check; REQUIRE(adbx != NULL && DNS_ADB_VALID(*adbx)); adb = *adbx; *adbx = NULL; INSIST(adb->erefcnt > 0); LOCK(&adb->reflock); adb->erefcnt--; need_exit_check = ISC_TF(adb->erefcnt == 0 && adb->irefcnt == 0); UNLOCK(&adb->reflock); if (need_exit_check) { LOCK(&adb->lock); INSIST(adb->shutting_down); check_exit(adb); UNLOCK(&adb->lock); } } void dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) { isc_task_t *clone; isc_event_t *event; isc_boolean_t zeroirefcnt = ISC_FALSE; /* * Send '*eventp' to 'task' when 'adb' has shutdown. */ REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(eventp != NULL); event = *eventp; *eventp = NULL; LOCK(&adb->lock); LOCK(&adb->reflock); zeroirefcnt = ISC_TF(adb->irefcnt == 0); if (adb->shutting_down && zeroirefcnt && isc_mempool_getallocated(adb->ahmp) == 0) { /* * We're already shutdown. Send the event. */ event->ev_sender = adb; isc_task_send(task, &event); } else { clone = NULL; isc_task_attach(task, &clone); event->ev_sender = clone; ISC_LIST_APPEND(adb->whenshutdown, event, ev_link); } UNLOCK(&adb->reflock); UNLOCK(&adb->lock); } void dns_adb_shutdown(dns_adb_t *adb) { isc_boolean_t need_check_exit; /* * Shutdown 'adb'. */ LOCK(&adb->lock); if (!adb->shutting_down) { adb->shutting_down = ISC_TRUE; isc_mem_setwater(adb->mctx, water, adb, 0, 0); need_check_exit = shutdown_names(adb); if (!need_check_exit) need_check_exit = shutdown_entries(adb); if (need_check_exit) check_exit(adb); } UNLOCK(&adb->lock); } isc_result_t dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, void *arg, dns_name_t *name, dns_name_t *qname, dns_rdatatype_t qtype, unsigned int options, isc_stdtime_t now, dns_name_t *target, in_port_t port, dns_adbfind_t **findp) { + return (dns_adb_createfind2(adb, task, action, arg, name, + qname, qtype, options, now, + target, port, 0, 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, + dns_adbfind_t **findp) +{ dns_adbfind_t *find; dns_adbname_t *adbname; int bucket; isc_boolean_t want_event, start_at_zone, alias, have_address; isc_result_t result; unsigned int wanted_addresses; unsigned int wanted_fetches; unsigned int query_pending; REQUIRE(DNS_ADB_VALID(adb)); if (task != NULL) { REQUIRE(action != NULL); } REQUIRE(name != NULL); REQUIRE(qname != NULL); REQUIRE(findp != NULL && *findp == NULL); REQUIRE(target == NULL || dns_name_hasbuffer(target)); REQUIRE((options & DNS_ADBFIND_ADDRESSMASK) != 0); result = ISC_R_UNEXPECTED; POST(result); wanted_addresses = (options & DNS_ADBFIND_ADDRESSMASK); wanted_fetches = 0; query_pending = 0; want_event = ISC_FALSE; start_at_zone = ISC_FALSE; alias = ISC_FALSE; if (now == 0) isc_stdtime_get(&now); /* * XXXMLG Move this comment somewhere else! * * Look up the name in our internal database. * * Possibilities: Note that these are not always exclusive. * * No name found. In this case, allocate a new name header and * an initial namehook or two. If any of these allocations * fail, clean up and return ISC_R_NOMEMORY. * * Name found, valid addresses present. Allocate one addrinfo * structure for each found and append it to the linked list * of addresses for this header. * * Name found, queries pending. In this case, if a task was * passed in, allocate a job id, attach it to the name's job * list and remember to tell the caller that there will be * more info coming later. */ find = new_adbfind(adb); if (find == NULL) return (ISC_R_NOMEMORY); find->port = port; /* * Remember what types of addresses we are interested in. */ find->options = options; find->flags |= wanted_addresses; if (FIND_WANTEVENT(find)) { REQUIRE(task != NULL); } /* * Try to see if we know anything about this name at all. */ bucket = DNS_ADB_INVALIDBUCKET; adbname = find_name_and_lock(adb, name, find->options, &bucket); 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) == ISC_FALSE); result = ISC_R_SHUTTINGDOWN; goto out; } /* * Nothing found. Allocate a new adbname structure for this name. */ if (adbname == NULL) { /* * See if there is any stale name at the end of list, and purge * it if so. */ check_stale_name(adb, bucket, now); adbname = new_adbname(adb, name); if (adbname == NULL) { RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE); result = ISC_R_NOMEMORY; goto out; } link_name(adb, bucket, adbname); if (FIND_HINTOK(find)) adbname->flags |= NAME_HINT_OK; if (FIND_GLUEOK(find)) adbname->flags |= NAME_GLUE_OK; if (FIND_STARTATZONE(find)) adbname->flags |= NAME_STARTATZONE; } else { /* Move this name forward in the LRU list */ ISC_LIST_UNLINK(adb->names[bucket], adbname, plink); ISC_LIST_PREPEND(adb->names[bucket], adbname, plink); } adbname->last_used = now; /* * Expire old entries, etc. */ RUNTIME_CHECK(check_expire_namehooks(adbname, now) == ISC_FALSE); /* * Do we know that the name is an alias? */ if (!EXPIRE_OK(adbname->expire_target, now)) { /* * Yes, it is. */ DP(DEF_LEVEL, "dns_adb_createfind: name %p is an alias (cached)", adbname); alias = ISC_TRUE; goto post_copy; } /* * Try to populate the name from the database and/or * start fetches. First try looking for an A record * in the database. */ if (!NAME_HAS_V4(adbname) && EXPIRE_OK(adbname->expire_v4, now) && WANT_INET(wanted_addresses)) { result = dbfind_name(adbname, now, dns_rdatatype_a); if (result == ISC_R_SUCCESS) { DP(DEF_LEVEL, "dns_adb_createfind: found A for name %p in db", adbname); goto v6; } /* * Did we get a CNAME or DNAME? */ if (result == DNS_R_ALIAS) { DP(DEF_LEVEL, "dns_adb_createfind: name %p is an alias", adbname); alias = ISC_TRUE; goto post_copy; } /* * If the name doesn't exist at all, don't bother with * v6 queries; they won't work. * * If the name does exist but we didn't get our data, go * ahead and try AAAA. * * If the result is neither of these, try a fetch for A. */ if (NXDOMAIN_RESULT(result)) goto fetch; else if (NXRRSET_RESULT(result)) goto v6; if (!NAME_FETCH_V4(adbname)) wanted_fetches |= DNS_ADBFIND_INET; } v6: if (!NAME_HAS_V6(adbname) && EXPIRE_OK(adbname->expire_v6, now) && WANT_INET6(wanted_addresses)) { result = dbfind_name(adbname, now, dns_rdatatype_aaaa); if (result == ISC_R_SUCCESS) { DP(DEF_LEVEL, "dns_adb_createfind: found AAAA for name %p", adbname); goto fetch; } /* * Did we get a CNAME or DNAME? */ if (result == DNS_R_ALIAS) { DP(DEF_LEVEL, "dns_adb_createfind: name %p is an alias", adbname); alias = ISC_TRUE; goto post_copy; } /* * Listen to negative cache hints, and don't start * another query. */ if (NCACHE_RESULT(result) || AUTH_NX(result)) goto fetch; if (!NAME_FETCH_V6(adbname)) wanted_fetches |= DNS_ADBFIND_INET6; } fetch: if ((WANT_INET(wanted_addresses) && NAME_HAS_V4(adbname)) || (WANT_INET6(wanted_addresses) && NAME_HAS_V6(adbname))) have_address = ISC_TRUE; else have_address = ISC_FALSE; if (wanted_fetches != 0 && ! (FIND_AVOIDFETCHES(find) && have_address)) { /* * We're missing at least one address family. Either the * caller hasn't instructed us to avoid fetches, or we don't * know anything about any of the address families that would * be acceptable so we have to launch fetches. */ if (FIND_STARTATZONE(find)) start_at_zone = ISC_TRUE; /* * Start V4. */ if (WANT_INET(wanted_fetches) && - fetch_name(adbname, start_at_zone, + fetch_name(adbname, start_at_zone, depth, dns_rdatatype_a) == ISC_R_SUCCESS) { DP(DEF_LEVEL, "dns_adb_createfind: started A fetch for name %p", adbname); } /* * Start V6. */ if (WANT_INET6(wanted_fetches) && - fetch_name(adbname, start_at_zone, + fetch_name(adbname, start_at_zone, depth, dns_rdatatype_aaaa) == ISC_R_SUCCESS) { DP(DEF_LEVEL, "dns_adb_createfind: " "started AAAA fetch for name %p", adbname); } } /* * Run through the name and copy out the bits we are * interested in. */ copy_namehook_lists(adb, find, qname, qtype, adbname, now); post_copy: if (NAME_FETCH_V4(adbname)) query_pending |= DNS_ADBFIND_INET; if (NAME_FETCH_V6(adbname)) query_pending |= DNS_ADBFIND_INET6; /* * Attach to the name's query list if there are queries * already running, and we have been asked to. */ want_event = ISC_TRUE; if (!FIND_WANTEVENT(find)) want_event = ISC_FALSE; if (FIND_WANTEMPTYEVENT(find) && FIND_HAS_ADDRS(find)) want_event = ISC_FALSE; if ((wanted_addresses & query_pending) == 0) want_event = ISC_FALSE; if (alias) want_event = ISC_FALSE; if (want_event) { find->adbname = adbname; find->name_bucket = bucket; ISC_LIST_APPEND(adbname->finds, find, plink); find->query_pending = (query_pending & wanted_addresses); find->flags &= ~DNS_ADBFIND_ADDRESSMASK; find->flags |= (find->query_pending & DNS_ADBFIND_ADDRESSMASK); DP(DEF_LEVEL, "createfind: attaching find %p to adbname %p", find, adbname); } else { /* * Remove the flag so the caller knows there will never * be an event, and set internal flags to fake that * the event was sent and freed, so dns_adb_destroyfind() will * do the right thing. */ find->query_pending = (query_pending & wanted_addresses); find->options &= ~DNS_ADBFIND_WANTEVENT; find->flags |= (FIND_EVENT_SENT | FIND_EVENT_FREED); find->flags &= ~DNS_ADBFIND_ADDRESSMASK; } find->partial_result |= (adbname->partial_result & wanted_addresses); if (alias) { if (target != NULL) { result = dns_name_copy(&adbname->target, target, NULL); if (result != ISC_R_SUCCESS) goto out; } result = DNS_R_ALIAS; } else result = ISC_R_SUCCESS; /* * Copy out error flags from the name structure into the find. */ find->result_v4 = find_err_map[adbname->fetch_err]; find->result_v6 = find_err_map[adbname->fetch6_err]; out: if (find != NULL) { *findp = find; if (want_event) { isc_task_t *taskp; INSIST((find->flags & DNS_ADBFIND_ADDRESSMASK) != 0); taskp = NULL; isc_task_attach(task, &taskp); find->event.ev_sender = taskp; find->event.ev_action = action; find->event.ev_arg = arg; } } UNLOCK(&adb->namelocks[bucket]); return (result); } void dns_adb_destroyfind(dns_adbfind_t **findp) { dns_adbfind_t *find; dns_adbentry_t *entry; dns_adbaddrinfo_t *ai; int bucket; dns_adb_t *adb; isc_boolean_t 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, ISC_TRUE) == ISC_FALSE); free_adbaddrinfo(adb, &ai); ai = ISC_LIST_HEAD(find->list); } /* * WARNING: The find is freed with the adb locked. This is done * to avoid a race condition where we free the find, some other * thread tests to see if it should be destroyed, detects it should * be, destroys it, and then we try to lock it for our check, but the * lock is destroyed. */ LOCK(&adb->lock); if (free_adbfind(adb, &find)) check_exit(adb); UNLOCK(&adb->lock); } void dns_adb_cancelfind(dns_adbfind_t *find) { isc_event_t *ev; isc_task_t *task; dns_adb_t *adb; int bucket; int unlock_bucket; LOCK(&find->lock); DP(DEF_LEVEL, "dns_adb_cancelfind on find %p", find); adb = find->adb; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(!FIND_EVENTFREED(find)); REQUIRE(FIND_WANTEVENT(find)); bucket = find->name_bucket; if (bucket == DNS_ADB_INVALIDBUCKET) goto cleanup; /* * We need to get the adbname's lock to unlink the find. */ unlock_bucket = bucket; violate_locking_hierarchy(&find->lock, &adb->namelocks[unlock_bucket]); bucket = find->name_bucket; if (bucket != DNS_ADB_INVALIDBUCKET) { ISC_LIST_UNLINK(find->adbname->finds, find, plink); find->adbname = NULL; find->name_bucket = DNS_ADB_INVALIDBUCKET; } UNLOCK(&adb->namelocks[unlock_bucket]); bucket = DNS_ADB_INVALIDBUCKET; 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) == ISC_FALSE); for (i = 0; i < adb->nentries; i++) RUNTIME_CHECK(cleanup_entries(adb, i, now) == ISC_FALSE); dump_adb(adb, f, ISC_FALSE, now); UNLOCK(&adb->lock); } static void dump_ttl(FILE *f, const char *legend, isc_stdtime_t value, isc_stdtime_t now) { if (value == INT_MAX) return; fprintf(f, " [%s TTL %d]", legend, value - now); } static void dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) { unsigned int i; dns_adbname_t *name; dns_adbentry_t *entry; fprintf(f, ";\n; Address database dump\n;\n"); if (debug) fprintf(f, "; addr %p, erefcnt %u, irefcnt %u, finds out %u\n", adb, adb->erefcnt, adb->irefcnt, isc_mempool_getallocated(adb->nhmp)); for (i = 0; i < 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 %d\n", i); for (; name != NULL; name = ISC_LIST_NEXT(name, plink)) { if (debug) fprintf(f, "; name %p (flags %08x)\n", name, name->flags); fprintf(f, "; "); print_dns_name(f, &name->name); if (dns_name_countlabels(&name->target) > 0) { fprintf(f, " alias "); print_dns_name(f, &name->target); } dump_ttl(f, "v4", name->expire_v4, now); dump_ttl(f, "v6", name->expire_v6, now); dump_ttl(f, "target", name->expire_target, now); fprintf(f, " [v4 %s] [v6 %s]", errnames[name->fetch_err], errnames[name->fetch6_err]); fprintf(f, "\n"); print_namehook_list(f, "v4", &name->v4, debug, now); print_namehook_list(f, "v6", &name->v6, debug, now); if (debug) print_fetch_list(f, name); if (debug) print_find_list(f, name); } } fprintf(f, ";\n; Unassociated entries\n;\n"); for (i = 0; i < adb->nentries; i++) { entry = ISC_LIST_HEAD(adb->entries[i]); while (entry != NULL) { if (entry->refcnt == 0) dump_entry(f, entry, debug, now); entry = ISC_LIST_NEXT(entry, plink); } } /* * Unlock everything */ for (i = 0; i < 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_adbentry_t *entry, isc_boolean_t debug, isc_stdtime_t now) { char addrbuf[ISC_NETADDR_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; isc_netaddr_t netaddr; dns_adblameinfo_t *li; isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr); isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf)); if (debug) fprintf(f, ";\t%p: refcnt %u\n", entry, entry->refcnt); fprintf(f, ";\t%s [srtt %u] [flags %08x]", addrbuf, entry->srtt, entry->flags); if (entry->expires != 0) fprintf(f, " [ttl %d]", entry->expires - now); fprintf(f, "\n"); for (li = ISC_LIST_HEAD(entry->lameinfo); li != NULL; li = ISC_LIST_NEXT(li, plink)) { fprintf(f, ";\t\t"); print_dns_name(f, &li->qname); dns_rdatatype_format(li->qtype, typebuf, sizeof(typebuf)); fprintf(f, " %s [lame TTL %d]\n", typebuf, li->lame_timer - now); } } void dns_adb_dumpfind(dns_adbfind_t *find, FILE *f) { char tmp[512]; const char *tmpp; dns_adbaddrinfo_t *ai; isc_sockaddr_t *sa; /* * Not used currently, in the API Just In Case we * want to dump out the name and/or entries too. */ LOCK(&find->lock); fprintf(f, ";Find %p\n", find); fprintf(f, ";\tqpending %08x partial %08x options %08x flags %08x\n", find->query_pending, find->partial_result, find->options, find->flags); fprintf(f, ";\tname_bucket %d, name %p, event sender %p\n", find->name_bucket, find->adbname, find->event.ev_sender); ai = ISC_LIST_HEAD(find->list); if (ai != NULL) fprintf(f, "\tAddresses:\n"); while (ai != NULL) { sa = &ai->sockaddr; switch (sa->type.sa.sa_family) { case AF_INET: tmpp = inet_ntop(AF_INET, &sa->type.sin.sin_addr, tmp, sizeof(tmp)); break; case AF_INET6: tmpp = inet_ntop(AF_INET6, &sa->type.sin6.sin6_addr, tmp, sizeof(tmp)); break; default: tmpp = "UnkFamily"; } if (tmpp == NULL) tmpp = "BadAddress"; fprintf(f, "\t\tentry %p, flags %08x" " srtt %u addr %s\n", ai->entry, ai->flags, ai->srtt, tmpp); ai = ISC_LIST_NEXT(ai, publink); } UNLOCK(&find->lock); } static void print_dns_name(FILE *f, dns_name_t *name) { char buf[DNS_NAME_FORMATSIZE]; INSIST(f != NULL); dns_name_format(name, buf, sizeof(buf)); fprintf(f, "%s", buf); } static void print_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list, isc_boolean_t debug, isc_stdtime_t now) { dns_adbnamehook_t *nh; for (nh = ISC_LIST_HEAD(*list); nh != NULL; nh = ISC_LIST_NEXT(nh, plink)) { if (debug) fprintf(f, ";\tHook(%s) %p\n", legend, nh); dump_entry(f, nh->entry, debug, now); } } static inline void print_fetch(FILE *f, dns_adbfetch_t *ft, const char *type) { fprintf(f, "\t\tFetch(%s): %p -> { 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); dns_fixedname_init(&foundname); fname = dns_fixedname_name(&foundname); dns_rdataset_init(&rdataset); if (rdtype == dns_rdatatype_a) adbname->fetch_err = FIND_ERR_UNEXPECTED; else adbname->fetch6_err = FIND_ERR_UNEXPECTED; /* * 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, ISC_TF(NAME_HINTOK(adbname)), (adbname->flags & NAME_STARTATZONE) != 0 ? ISC_TRUE : ISC_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; isc_boolean_t want_check_exit = ISC_FALSE; + isc_uint32_t qtotal = 0; 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)); + qtotal = dev->qtotal; + 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 + 300); name->fetch_err = FIND_ERR_FAILURE; inc_stats(adb, dns_resstatscounter_gluefetchv4fail); } else { name->expire_v6 = ISC_MIN(name->expire_v6, now + 300); name->fetch6_err = FIND_ERR_FAILURE; inc_stats(adb, dns_resstatscounter_gluefetchv6fail); } goto out; } /* * We got something potentially useful. */ result = import_rdataset(name, &fetch->rdataset, now); check_result: if (result == ISC_R_SUCCESS) { ev_status = DNS_EVENT_ADBMOREADDRESSES; if (address_type == DNS_ADBFIND_INET) name->fetch_err = FIND_ERR_SUCCESS; else name->fetch6_err = FIND_ERR_SUCCESS; } out: free_adbfetch(adb, &fetch); isc_event_free(&ev); - clean_finds_at_name(name, ev_status, address_type); + clean_finds_at_name(name, ev_status, qtotal, address_type); UNLOCK(&adb->namelocks[bucket]); } static isc_result_t -fetch_name(dns_adbname_t *adbname, - isc_boolean_t start_at_zone, - dns_rdatatype_t type) +fetch_name(dns_adbname_t *adbname, isc_boolean_t start_at_zone, + unsigned int depth, dns_rdatatype_t type) { isc_result_t result; dns_adbfetch_t *fetch = NULL; dns_adb_t *adb; dns_fixedname_t fixed; dns_name_t *name; dns_rdataset_t rdataset; dns_rdataset_t *nameservers; unsigned int options; INSIST(DNS_ADBNAME_VALID(adbname)); adb = adbname->adb; INSIST(DNS_ADB_VALID(adb)); INSIST((type == dns_rdatatype_a && !NAME_FETCH_V4(adbname)) || (type == dns_rdatatype_aaaa && !NAME_FETCH_V6(adbname))); adbname->fetch_err = FIND_ERR_NOTFOUND; name = NULL; nameservers = NULL; dns_rdataset_init(&rdataset); options = DNS_FETCHOPT_NOVALIDATE; if (start_at_zone) { DP(ENTER_LEVEL, "fetch_name: starting at zone for name %p", adbname); dns_fixedname_init(&fixed); name = dns_fixedname_name(&fixed); result = dns_view_findzonecut2(adb->view, &adbname->name, name, 0, 0, ISC_TRUE, ISC_FALSE, &rdataset, NULL); if (result != ISC_R_SUCCESS && result != DNS_R_HINT) goto cleanup; nameservers = &rdataset; options |= DNS_FETCHOPT_UNSHARED; } fetch = new_adbfetch(adb); if (fetch == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } + fetch->depth = depth; - result = dns_resolver_createfetch(adb->view->resolver, &adbname->name, - type, name, nameservers, NULL, - options, adb->task, fetch_callback, - adbname, &fetch->rdataset, NULL, - &fetch->fetch); + result = dns_resolver_createfetch3(adb->view->resolver, &adbname->name, + type, name, nameservers, NULL, + NULL, 0, options, depth, adb->task, + fetch_callback, adbname, + &fetch->rdataset, NULL, + &fetch->fetch); if (result != ISC_R_SUCCESS) goto cleanup; if (type == dns_rdatatype_a) { adbname->fetch_a = fetch; inc_stats(adb, dns_resstatscounter_gluefetchv4); } else { adbname->fetch_aaaa = fetch; inc_stats(adb, dns_resstatscounter_gluefetchv6); } fetch = NULL; /* Keep us from cleaning this up below. */ cleanup: if (fetch != NULL) free_adbfetch(adb, &fetch); if (dns_rdataset_isassociated(&rdataset)) dns_rdataset_disassociate(&rdataset); return (result); } /* * XXXMLG Needs to take a find argument and an address info, no zone or adb, * since these can be extracted from the find itself. */ isc_result_t dns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr, dns_name_t *qname, dns_rdatatype_t qtype, isc_stdtime_t expire_time) { dns_adblameinfo_t *li; int bucket; isc_result_t result = ISC_R_SUCCESS; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); REQUIRE(qname != NULL); bucket = addr->entry->lock_bucket; LOCK(&adb->entrylocks[bucket]); li = ISC_LIST_HEAD(addr->entry->lameinfo); while (li != NULL && (li->qtype != qtype || !dns_name_equal(qname, &li->qname))) li = ISC_LIST_NEXT(li, plink); if (li != NULL) { if (expire_time > li->lame_timer) li->lame_timer = expire_time; goto unlock; } li = new_adblameinfo(adb, qname, qtype); if (li == NULL) { result = ISC_R_NOMEMORY; goto unlock; } li->lame_timer = expire_time; ISC_LIST_PREPEND(addr->entry->lameinfo, li, plink); unlock: UNLOCK(&adb->entrylocks[bucket]); return (result); } void dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int rtt, unsigned int factor) { int bucket; unsigned int new_srtt; isc_stdtime_t now; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); REQUIRE(factor <= 10); bucket = addr->entry->lock_bucket; LOCK(&adb->entrylocks[bucket]); if (factor == DNS_ADB_RTTADJAGE) new_srtt = addr->entry->srtt * 98 / 100; else new_srtt = (addr->entry->srtt / 10 * factor) + (rtt / 10 * (10 - factor)); addr->entry->srtt = new_srtt; addr->srtt = new_srtt; if (addr->entry->expires == 0) { isc_stdtime_get(&now); addr->entry->expires = now + ADB_ENTRY_WINDOW; } UNLOCK(&adb->entrylocks[bucket]); } void dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int bits, unsigned int mask) { int bucket; isc_stdtime_t now; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); bucket = addr->entry->lock_bucket; LOCK(&adb->entrylocks[bucket]); addr->entry->flags = (addr->entry->flags & ~mask) | (bits & mask); 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]); } 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, ISC_FALSE); *addrp = addr; } unlock: UNLOCK(&adb->entrylocks[bucket]); return (result); } void dns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp) { dns_adbaddrinfo_t *addr; dns_adbentry_t *entry; int bucket; isc_stdtime_t now; isc_boolean_t want_check_exit = ISC_FALSE; isc_boolean_t 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, ISC_FALSE); UNLOCK(&adb->entrylocks[bucket]); addr->entry = NULL; free_adbaddrinfo(adb, &addr); if (want_check_exit) { LOCK(&adb->lock); check_exit(adb); UNLOCK(&adb->lock); } } void dns_adb_flush(dns_adb_t *adb) { unsigned int i; INSIST(DNS_ADB_VALID(adb)); LOCK(&adb->lock); /* * Call our cleanup routines. */ for (i = 0; i < adb->nnames; i++) RUNTIME_CHECK(cleanup_names(adb, i, INT_MAX) == ISC_FALSE); for (i = 0; i < adb->nentries; i++) RUNTIME_CHECK(cleanup_entries(adb, i, INT_MAX) == ISC_FALSE); #ifdef DUMP_ADB_AFTER_CLEANING dump_adb(adb, stdout, ISC_TRUE, INT_MAX); #endif UNLOCK(&adb->lock); } void dns_adb_flushname(dns_adb_t *adb, dns_name_t *name) { dns_adbname_t *adbname; dns_adbname_t *nextname; int bucket; INSIST(DNS_ADB_VALID(adb)); LOCK(&adb->lock); bucket = dns_name_hash(name, ISC_FALSE) % 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) == ISC_FALSE); } adbname = nextname; } UNLOCK(&adb->namelocks[bucket]); 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; isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER); REQUIRE(DNS_ADB_VALID(adb)); DP(ISC_LOG_DEBUG(1), "adb reached %s water mark", overmem ? "high" : "low"); } 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); } Index: stable/9/contrib/bind9/lib/dns/include/dns/adb.h =================================================================== --- stable/9/contrib/bind9/lib/dns/include/dns/adb.h (revision 275668) +++ stable/9/contrib/bind9/lib/dns/include/dns/adb.h (revision 275669) @@ -1,634 +1,642 @@ /* * Copyright (C) 2004-2008, 2011, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: adb.h,v 1.88 2011/12/05 17:10:51 each Exp $ */ #ifndef DNS_ADB_H #define DNS_ADB_H 1 /***** ***** Module Info *****/ /*! \file dns/adb.h *\brief * DNS Address Database * * This module implements an address database (ADB) for mapping a name * to an isc_sockaddr_t. It also provides statistical information on * how good that address might be. * * A client will pass in a dns_name_t, and the ADB will walk through * the rdataset looking up addresses associated with the name. If it * is found on the internal lists, a structure is filled in with the * address information and stats for found addresses. * * If the name cannot be found on the internal lists, a new entry will * be created for a name if all the information needed can be found * in the zone table or cache. This new address will then be returned. * * If a request must be made to remote servers to satisfy a name lookup, * this module will start fetches to try to complete these addresses. When * at least one more completes, an event is sent to the caller. If none of * them resolve before the fetch times out, an event indicating this is * sent instead. * * Records are stored internally until a timer expires. The timer is the * smaller of the TTL or signature validity period. * * Lameness is stored per 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 #include #include #include #include #include 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 */ + isc_uint32_t qtotal; + /* Private */ isc_mutex_t lock; /* locks all below */ in_port_t port; int name_bucket; unsigned int flags; dns_adbname_t *adbname; dns_adb_t *adb; isc_event_t event; ISC_LINK(dns_adbfind_t) plink; }; /* * _INET: * _INET6: * return addresses of that type. * * _EMPTYEVENT: * Only schedule an event if no addresses are known. * Must set _WANTEVENT for this to be meaningful. * * _WANTEVENT: * An event is desired. Check this bit in the returned find to see * if one will actually be generated. * * _AVOIDFETCHES: * If set, fetches will not be generated unless no addresses are * available in any of the address families requested. * * _STARTATZONE: * Fetches will start using the closest zone data or use the root servers. * This is useful for reestablishing glue that has expired. * * _GLUEOK: * _HINTOK: * Glue or hints are ok. These are used when matching names already * in the adb, and when dns databases are searched. * * _RETURNLAME: * Return lame servers in a find, so that all addresses are returned. * * _LAMEPRUNED: * At least one address was omitted from the list because it was lame. * This bit will NEVER be set if _RETURNLAME is set in the createfind(). */ /*% Return addresses of type INET. */ #define DNS_ADBFIND_INET 0x00000001 /*% Return addresses of type INET6. */ #define DNS_ADBFIND_INET6 0x00000002 #define DNS_ADBFIND_ADDRESSMASK 0x00000003 /*% * Only schedule an event if no addresses are known. * Must set _WANTEVENT for this to be meaningful. */ #define DNS_ADBFIND_EMPTYEVENT 0x00000004 /*% * An event is desired. Check this bit in the returned find to see * if one will actually be generated. */ #define DNS_ADBFIND_WANTEVENT 0x00000008 /*% * If set, fetches will not be generated unless no addresses are * available in any of the address families requested. */ #define DNS_ADBFIND_AVOIDFETCHES 0x00000010 /*% * Fetches will start using the closest zone data or use the root servers. * This is useful for reestablishing glue that has expired. */ #define DNS_ADBFIND_STARTATZONE 0x00000020 /*% * Glue or hints are ok. These are used when matching names already * in the adb, and when dns databases are searched. */ #define DNS_ADBFIND_GLUEOK 0x00000040 /*% * Glue or hints are ok. These are used when matching names already * in the adb, and when dns databases are searched. */ #define DNS_ADBFIND_HINTOK 0x00000080 /*% * Return lame servers in a find, so that all addresses are returned. */ #define DNS_ADBFIND_RETURNLAME 0x00000100 /*% * Only schedule an event if no addresses are known. * Must set _WANTEVENT for this to be meaningful. */ #define DNS_ADBFIND_LAMEPRUNED 0x00000200 /*% * The answers to queries come back as a list of these. */ struct dns_adbaddrinfo { unsigned int magic; /*%< private */ isc_sockaddr_t sockaddr; /*%< [rw] */ unsigned int srtt; /*%< [rw] microseconds */ unsigned int flags; /*%< [rw] */ dns_adbentry_t *entry; /*%< private */ ISC_LINK(dns_adbaddrinfo_t) publink; }; /*!< * The event sent to the caller task is just a plain old isc_event_t. It * contains no data other than a simple status, passed in the "type" field * to indicate that another address resolved, or all partially resolved * addresses have failed to resolve. * * "sender" is the dns_adbfind_t used to issue this query. * * This is simply a standard event, with the "type" set to: * *\li #DNS_EVENT_ADBMOREADDRESSES -- another address resolved. *\li #DNS_EVENT_ADBNOMOREADDRESSES -- all pending addresses failed, * were canceled, or otherwise will * not be usable. *\li #DNS_EVENT_ADBCANCELED -- The request was canceled by a * 3rd party. *\li #DNS_EVENT_ADBNAMEDELETED -- The name was deleted, so this request * was canceled. * * In each of these cases, the addresses returned by the initial call * to dns_adb_createfind() can still be used until they are no longer needed. */ /**** **** FUNCTIONS ****/ isc_result_t dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *tmgr, isc_taskmgr_t *taskmgr, dns_adb_t **newadb); /*%< * Create a new ADB. * * Notes: * *\li Generally, applications should not create an ADB directly, but * should instead call dns_view_createresolver(). * * Requires: * *\li 'mem' must be a valid memory context. * *\li 'view' be a pointer to a valid view. * *\li 'tmgr' be a pointer to a valid timer manager. * *\li 'taskmgr' be a pointer to a valid task manager. * *\li 'newadb' != NULL && '*newadb' == NULL. * * Returns: * *\li #ISC_R_SUCCESS after happiness. *\li #ISC_R_NOMEMORY after resource allocation failure. */ void dns_adb_attach(dns_adb_t *adb, dns_adb_t **adbp); /*% * Attach to an 'adb' to 'adbp'. * * Requires: *\li 'adb' to be a valid dns_adb_t, created via dns_adb_create(). *\li 'adbp' to be a valid pointer to a *dns_adb_t which is initialized * to NULL. */ void dns_adb_detach(dns_adb_t **adb); /*% * Delete the ADB. Sets *ADB to NULL. Cancels any outstanding requests. * * Requires: * *\li 'adb' be non-NULL and '*adb' be a valid dns_adb_t, created via * dns_adb_create(). */ void dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp); /*% * Send '*eventp' to 'task' when 'adb' has shutdown. * * Requires: * *\li '*adb' is a valid dns_adb_t. * *\li eventp != NULL && *eventp is a valid event. * * Ensures: * *\li *eventp == NULL * *\li The event's sender field is set to the value of adb when the event * is sent. */ void dns_adb_shutdown(dns_adb_t *adb); /*%< * Shutdown 'adb'. * * Requires: * * \li '*adb' is a valid dns_adb_t. */ isc_result_t dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, void *arg, dns_name_t *name, dns_name_t *qname, dns_rdatatype_t qtype, unsigned int options, isc_stdtime_t now, dns_name_t *target, in_port_t port, dns_adbfind_t **find); +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, 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 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 . expire_time should * be set to the time when the entry should expire. That is, if it is to * expire 10 minutes in the future, it should set it to (now + 10 * 60). * * Requires: * *\li adb be valid. * *\li addr be valid. * *\li qname be the qname used in the dns_adb_createfind() call. * * Returns: * *\li #ISC_R_SUCCESS -- all is well. *\li #ISC_R_NOMEMORY -- could not mark address as lame. */ /* * A reasonable default for RTT adjustments */ #define DNS_ADB_RTTADJDEFAULT 7 /*%< default scale */ #define DNS_ADB_RTTADJREPLACE 0 /*%< replace with our rtt */ #define DNS_ADB_RTTADJAGE 10 /*%< age this rtt */ void dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int rtt, unsigned int factor); /*%< * Mix the round trip time into the existing smoothed rtt. * The formula used * (where srtt is the existing rtt value, and rtt and factor are arguments to * this function): * *\code * new_srtt = (old_srtt / 10 * factor) + (rtt / 10 * (10 - factor)); *\endcode * * XXXRTH Do we want to publish the formula? What if we want to change how * this works later on? Recommend/require that the units are * microseconds? * * Requires: * *\li adb be valid. * *\li addr be valid. * *\li 0 <= factor <= 10 * * Note: * *\li The srtt in addr will be updated to reflect the new global * srtt value. This may include changes made by others. */ void dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int bits, unsigned int mask); /*% * Change Flags. * * Set the flags as given by: * *\li newflags = (oldflags & ~mask) | (bits & mask); * * Requires: * *\li adb be valid. * *\li addr be valid. */ isc_result_t dns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa, dns_adbaddrinfo_t **addrp, isc_stdtime_t now); /*%< * Return a dns_adbaddrinfo_t that is associated with address 'sa'. * * Requires: * *\li adb is valid. * *\li sa is valid. * *\li addrp != NULL && *addrp == NULL * * Returns: *\li #ISC_R_SUCCESS *\li #ISC_R_NOMEMORY *\li #ISC_R_SHUTTINGDOWN */ void dns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp); /*%< * Free a dns_adbaddrinfo_t allocated by dns_adb_findaddrinfo(). * * Requires: * *\li adb is valid. * *\li *addrp is a valid dns_adbaddrinfo_t *. */ void dns_adb_flush(dns_adb_t *adb); /*%< * Flushes all cached data from the adb. * * Requires: *\li adb is valid. */ void dns_adb_setadbsize(dns_adb_t *adb, 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. */ ISC_LANG_ENDDECLS #endif /* DNS_ADB_H */ Index: stable/9/contrib/bind9/lib/dns/include/dns/resolver.h =================================================================== --- stable/9/contrib/bind9/lib/dns/include/dns/resolver.h (revision 275668) +++ stable/9/contrib/bind9/lib/dns/include/dns/resolver.h (revision 275669) @@ -1,580 +1,605 @@ /* * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: resolver.h,v 1.72 2011/12/05 17:10:51 each Exp $ */ #ifndef DNS_RESOLVER_H #define DNS_RESOLVER_H 1 /***** ***** Module Info *****/ /*! \file dns/resolver.h * * \brief * This is the BIND 9 resolver, the module responsible for resolving DNS * requests by iteratively querying authoritative servers and following * referrals. This is a "full resolver", not to be confused with * the stub resolvers most people associate with the word "resolver". * The full resolver is part of the caching name server or resolver * daemon the stub resolver talks to. * * MP: *\li The module ensures appropriate synchronization of data structures it * creates and manipulates. * * Reliability: *\li No anticipated impact. * * Resources: *\li TBS * * Security: *\li No anticipated impact. * * Standards: *\li RFCs: 1034, 1035, 2181, TBS *\li Drafts: TBS */ #include #include #include #include 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; + isc_uint32_t qtotal; } dns_fetchevent_t; /* * Options that modify how a 'fetch' is done. */ #define DNS_FETCHOPT_TCP 0x01 /*%< Use TCP. */ #define DNS_FETCHOPT_UNSHARED 0x02 /*%< See below. */ #define DNS_FETCHOPT_RECURSIVE 0x04 /*%< Set RD? */ #define DNS_FETCHOPT_NOEDNS0 0x08 /*%< Do not use EDNS. */ #define DNS_FETCHOPT_FORWARDONLY 0x10 /*%< Only use forwarders. */ #define DNS_FETCHOPT_NOVALIDATE 0x20 /*%< Disable validation. */ #define DNS_FETCHOPT_EDNS512 0x40 /*%< Advertise a 512 byte UDP buffer. */ #define DNS_FETCHOPT_WANTNSID 0x80 /*%< Request NSID */ #define DNS_FETCHOPT_EDNSVERSIONSET 0x00800000 #define DNS_FETCHOPT_EDNSVERSIONMASK 0xff000000 #define DNS_FETCHOPT_EDNSVERSIONSHIFT 24 /* * 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, isc_uint16_t id, unsigned int options, isc_task_t *task, isc_taskaction_t action, void *arg, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, dns_fetch_t **fetchp); +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, isc_uint16_t id, + unsigned int options, unsigned int depth, + 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, isc_boolean_t 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); isc_uint32_t dns_resolver_getlamettl(dns_resolver_t *resolver); /*%< * Get the resolver's lame-ttl. zero => no lame processing. * * Requires: *\li 'resolver' to be valid. */ void dns_resolver_setlamettl(dns_resolver_t *resolver, isc_uint32_t lame_ttl); /*%< * Set the resolver's lame-ttl. zero => no lame processing. * * Requires: *\li 'resolver' to be valid. */ unsigned int dns_resolver_nrunning(dns_resolver_t *resolver); /*%< * Return the number of currently running resolutions in this * resolver. This is may be less than the number of outstanding * fetches due to multiple identical fetches, or more than the * number of of outstanding fetches due to the fact that resolution * can continue even though a fetch has been canceled. */ isc_result_t dns_resolver_addalternate(dns_resolver_t *resolver, isc_sockaddr_t *alt, dns_name_t *name, in_port_t port); /*%< * Add alternate addresses to be tried in the event that the nameservers * for a zone are not available in the address families supported by the * operating system. * * Require: * \li only one of 'name' or 'alt' to be valid. */ void dns_resolver_setudpsize(dns_resolver_t *resolver, isc_uint16_t udpsize); /*%< * Set the EDNS UDP buffer size advertised by the server. */ isc_uint16_t dns_resolver_getudpsize(dns_resolver_t *resolver); /*%< * Get the current EDNS UDP buffer size. */ void dns_resolver_reset_algorithms(dns_resolver_t *resolver); /*%< * Clear the disabled DNSSEC algorithms. */ isc_result_t dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name, unsigned int alg); /*%< * Mark the give DNSSEC algorithm as disabled and below 'name'. * Valid algorithms are less than 256. * * Returns: *\li #ISC_R_SUCCESS *\li #ISC_R_RANGE *\li #ISC_R_NOMEMORY */ isc_boolean_t dns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name, unsigned int alg); /*%< * Check if the given algorithm is supported by this resolver. * This checks if the algorithm has been disabled via * dns_resolver_disable_algorithm() then the underlying * crypto libraries if not specifically disabled. */ isc_boolean_t dns_resolver_digest_supported(dns_resolver_t *resolver, unsigned int digest_type); /*%< * Is this digest type supported. */ void dns_resolver_resetmustbesecure(dns_resolver_t *resolver); isc_result_t dns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name, isc_boolean_t value); isc_boolean_t dns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name); void dns_resolver_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, isc_uint32_t min, isc_uint32_t max); void dns_resolver_getclientsperquery(dns_resolver_t *resolver, isc_uint32_t *cur, isc_uint32_t *min, isc_uint32_t *max); isc_boolean_t dns_resolver_getzeronosoattl(dns_resolver_t *resolver); void dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state); unsigned int dns_resolver_getoptions(dns_resolver_t *resolver); 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 that will expire at 'expire'. * * Requires: * \li resolver to be valid. * \li name to be valid. */ isc_boolean_t 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 * . * * 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_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_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. */ ISC_LANG_ENDDECLS #endif /* DNS_RESOLVER_H */ Index: stable/9/contrib/bind9/lib/dns/resolver.c =================================================================== --- stable/9/contrib/bind9/lib/dns/resolver.c (revision 275668) +++ stable/9/contrib/bind9/lib/dns/resolver.c (revision 275669) @@ -1,9051 +1,9115 @@ /* * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id$ */ /*! \file */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DNS_RESOLVER_TRACE #ifdef DNS_RESOLVER_TRACE #define RTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "res %p: %s", res, (m)) #define RRTRACE(r, m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "res %p: %s", (r), (m)) #define FCTXTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "fctx %p(%s): %s", fctx, fctx->info, (m)) #define FCTXTRACE2(m1, m2) \ isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "fctx %p(%s): %s %s", \ fctx, fctx->info, (m1), (m2)) #define FTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "fetch %p (fctx %p(%s)): %s", \ fetch, fetch->private, \ fetch->private->info, (m)) #define QTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "resquery %p (fctx %p(%s)): %s", \ query, query->fctx, \ query->fctx->info, (m)) #else #define RTRACE(m) #define RRTRACE(r, m) #define FCTXTRACE(m) #define FCTXTRACE2(m1, m2) #define FTRACE(m) #define QTRACE(m) #endif #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 #ifndef MAXIMUM_QUERY_TIMEOUT #define MAXIMUM_QUERY_TIMEOUT 30 /* The maximum time in seconds for the whole query to live. */ #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 50 +#endif + /*% * Maximum EDNS0 input packet size. */ #define RECV_BUFFER_SIZE 4096 /* XXXRTH Constant. */ #define EDNSOPTS 2 /*% * This defines the maximum number of timeouts we will permit before we * disable EDNS0 on the query. */ #define MAX_EDNS0_TIMEOUTS 3 typedef struct fetchctx fetchctx_t; typedef struct query { /* Locked by task event serialization. */ unsigned int magic; fetchctx_t * fctx; isc_mem_t * mctx; dns_dispatchmgr_t * dispatchmgr; dns_dispatch_t * dispatch; isc_boolean_t exclusivesocket; dns_adbaddrinfo_t * addrinfo; isc_socket_t * tcpsocket; isc_time_t start; dns_messageid_t id; dns_dispentry_t * dispentry; ISC_LINK(struct query) link; isc_buffer_t buffer; isc_buffer_t *tsig; dns_tsigkey_t *tsigkey; isc_socketevent_t sendevent; unsigned int options; unsigned int attributes; unsigned int sends; unsigned int connects; unsigned char data[512]; } resquery_t; #define QUERY_MAGIC ISC_MAGIC('Q', '!', '!', '!') #define VALID_QUERY(query) ISC_MAGIC_VALID(query, QUERY_MAGIC) #define RESQUERY_ATTR_CANCELED 0x02 #define RESQUERY_CONNECTING(q) ((q)->connects > 0) #define RESQUERY_CANCELED(q) (((q)->attributes & \ RESQUERY_ATTR_CANCELED) != 0) #define RESQUERY_SENDING(q) ((q)->sends > 0) typedef enum { fetchstate_init = 0, /*%< Start event has not run yet. */ fetchstate_active, fetchstate_done /*%< FETCHDONE events posted. */ } fetchstate; 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; char * info; isc_mem_t * mctx; /*% Locked by appropriate bucket lock. */ fetchstate state; isc_boolean_t want_shutdown; isc_boolean_t cloned; isc_boolean_t spilled; unsigned int references; isc_event_t control_event; ISC_LINK(struct fetchctx) link; ISC_LIST(dns_fetchevent_t) events; /*% Locked by task event serialization. */ dns_name_t domain; dns_rdataset_t nameservers; unsigned int attributes; isc_timer_t * timer; isc_time_t expires; isc_interval_t interval; dns_message_t * qmessage; dns_message_t * rmessage; ISC_LIST(resquery_t) queries; dns_adbfindlist_t finds; dns_adbfind_t * find; dns_adbfindlist_t altfinds; dns_adbfind_t * altfind; dns_adbaddrinfolist_t forwaddrs; dns_adbaddrinfolist_t altaddrs; isc_sockaddrlist_t forwarders; dns_fwdpolicy_t fwdpolicy; isc_sockaddrlist_t bad; isc_sockaddrlist_t edns; isc_sockaddrlist_t edns512; isc_sockaddrlist_t bad_edns; dns_validator_t *validator; ISC_LIST(dns_validator_t) validators; dns_db_t * cache; dns_adb_t * adb; isc_boolean_t ns_ttl_ok; isc_uint32_t ns_ttl; /*% * The number of events we're waiting for. */ unsigned int pending; /*% * The number of times we've "restarted" the current * nameserver set. This acts as a failsafe to prevent * us from pounding constantly on a particular set of * servers that, for whatever reason, are not giving * us useful responses, but are responding in such a * way that they are not marked "bad". */ unsigned int restarts; /*% * The number of timeouts that have occurred since we * last successfully received a response packet. This * is used for EDNS0 black hole detection. */ unsigned int timeouts; /*% * Look aside state for DS lookups. */ dns_name_t nsname; dns_fetch_t * nsfetch; dns_rdataset_t nsrrset; /*% * Number of queries that reference this context. */ unsigned int nqueries; /*% * The reason to print when logging a successful * response to a query. */ const char * reason; /*% * Random numbers to use for mixing up server addresses. */ isc_uint32_t rand_buf; isc_uint32_t rand_bits; /*% * Fetch-local statistics for detailed logging. */ isc_result_t result; /*%< fetch result */ isc_result_t vresult; /*%< validation result */ int exitline; isc_time_t start; isc_uint64_t duration; isc_boolean_t logged; unsigned int querysent; + unsigned int totalqueries; unsigned int referrals; unsigned int lamecount; unsigned int neterr; unsigned int badresp; unsigned int adberr; unsigned int findfail; unsigned int valfail; isc_boolean_t 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; fetchctx_t * private; }; #define DNS_FETCH_MAGIC ISC_MAGIC('F', 't', 'c', 'h') #define DNS_FETCH_VALID(fetch) ISC_MAGIC_VALID(fetch, DNS_FETCH_MAGIC) typedef struct fctxbucket { isc_task_t * task; isc_mutex_t lock; ISC_LIST(fetchctx_t) fctxs; isc_boolean_t exiting; isc_mem_t * mctx; } fctxbucket_t; typedef struct alternate { isc_boolean_t isaddress; union { isc_sockaddr_t addr; struct { dns_name_t name; in_port_t port; } _n; } _u; ISC_LINK(struct alternate) link; } alternate_t; typedef struct dns_badcache dns_badcache_t; struct dns_badcache { dns_badcache_t * next; dns_rdatatype_t type; isc_time_t expire; unsigned int hashval; dns_name_t name; }; #define DNS_BADCACHE_SIZE 1021 #define DNS_BADCACHE_TTL(fctx) \ (((fctx)->res->lame_ttl > 30 ) ? (fctx)->res->lame_ttl : 30) struct dns_resolver { /* Unlocked. */ unsigned int magic; isc_mem_t * mctx; isc_mutex_t lock; isc_mutex_t nlock; isc_mutex_t primelock; dns_rdataclass_t rdclass; isc_socketmgr_t * socketmgr; isc_timermgr_t * timermgr; isc_taskmgr_t * taskmgr; dns_view_t * view; isc_boolean_t frozen; unsigned int options; dns_dispatchmgr_t * dispatchmgr; dns_dispatchset_t * dispatches4; isc_boolean_t exclusivev4; dns_dispatchset_t * dispatches6; isc_boolean_t exclusivev6; unsigned int nbuckets; fctxbucket_t * buckets; isc_uint32_t lame_ttl; ISC_LIST(alternate_t) alternates; isc_uint16_t udpsize; #if USE_ALGLOCK isc_rwlock_t alglock; #endif dns_rbt_t * algorithms; #if USE_MBSLOCK isc_rwlock_t mbslock; #endif dns_rbt_t * mustbesecure; unsigned int spillatmax; unsigned int spillatmin; isc_timer_t * spillattimer; isc_boolean_t zero_no_soa_ttl; unsigned int query_timeout; + unsigned int maxdepth; /* Locked by lock. */ unsigned int references; isc_boolean_t exiting; isc_eventlist_t whenshutdown; unsigned int activebuckets; isc_boolean_t priming; unsigned int spillat; /* clients-per-query */ /* Bad cache. */ dns_badcache_t ** badcache; unsigned int badcount; unsigned int badhash; unsigned int badsweep; /* Locked by primelock. */ dns_fetch_t * primefetch; /* Locked by nlock. */ unsigned int nfctx; }; #define RES_MAGIC ISC_MAGIC('R', 'e', 's', '!') #define VALID_RESOLVER(res) ISC_MAGIC_VALID(res, RES_MAGIC) /*% * Private addrinfo flags. These must not conflict with DNS_FETCHOPT_NOEDNS0, * which we also use as an addrinfo flag. */ #define FCTX_ADDRINFO_MARK 0x0001 #define FCTX_ADDRINFO_FORWARDER 0x1000 #define FCTX_ADDRINFO_TRIED 0x2000 #define UNMARKED(a) (((a)->flags & FCTX_ADDRINFO_MARK) \ == 0) #define ISFORWARDER(a) (((a)->flags & \ FCTX_ADDRINFO_FORWARDER) != 0) #define TRIED(a) (((a)->flags & \ FCTX_ADDRINFO_TRIED) != 0) #define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0) #define NEGATIVE(r) (((r)->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) static void destroy(dns_resolver_t *res); static void empty_bucket(dns_resolver_t *res); static isc_result_t resquery_send(resquery_t *query); static void resquery_response(isc_task_t *task, isc_event_t *event); static void resquery_connected(isc_task_t *task, isc_event_t *event); static void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache); static void fctx_destroy(fetchctx_t *fctx); static isc_boolean_t 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, isc_boolean_t optout, isc_boolean_t secure, dns_rdataset_t *ardataset, isc_result_t *eresultp); static void validated(isc_task_t *task, isc_event_t *event); static isc_boolean_t maybe_destroy(fetchctx_t *fctx, isc_boolean_t 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); /*% * 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 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)) INSIST((valoptions & DNS_VALIDATOR_DEFER) != 0); result = dns_validator_create(fctx->res->view, name, type, rdataset, sigrdataset, fctx->rmessage, valoptions, task, validated, valarg, &validator); if (result == ISC_R_SUCCESS) { inc_stats(fctx->res, dns_resstatscounter_val); if ((valoptions & DNS_VALIDATOR_DEFER) == 0) { INSIST(fctx->validator == NULL); fctx->validator = validator; } ISC_LIST_APPEND(fctx->validators, validator, link); } else isc_mem_put(fctx->mctx, valarg, sizeof(*valarg)); return (result); } static isc_boolean_t 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 (ISC_TRUE); dns_rdata_reset(&rdata); } return (ISC_FALSE); } static isc_boolean_t fix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) { dns_name_t *name; dns_name_t *domain = &fctx->domain; dns_rdataset_t *rdataset; dns_rdatatype_t type; isc_result_t result; isc_boolean_t keep_auth = ISC_FALSE; if (message->rcode == dns_rcode_nxdomain) return (ISC_FALSE); /* * 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 (ISC_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 (ISC_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 (ISC_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 (ISC_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 = ISC_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 (ISC_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 (ISC_FALSE); } else if (dns_name_issubdomain(name, domain)) { /* Referral or answer from child zone. */ return (ISC_FALSE); } } result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); } munge: message->rcode = dns_rcode_nxdomain; message->counts[DNS_SECTION_ANSWER] = 0; if (!keep_auth) message->counts[DNS_SECTION_AUTHORITY] = 0; message->counts[DNS_SECTION_ADDITIONAL] = 0; return (ISC_TRUE); } static inline isc_result_t fctx_starttimer(fetchctx_t *fctx) { /* * Start the lifetime timer for fctx. * * This is also used for stopping the idle timer; in that * case we must purge events already posted to ensure that * no further idle events are delivered. */ return (isc_timer_reset(fctx->timer, isc_timertype_once, &fctx->expires, NULL, ISC_TRUE)); } static inline void fctx_stoptimer(fetchctx_t *fctx) { isc_result_t result; /* * We don't return a result if resetting the timer to inactive fails * since there's nothing to be done about it. Resetting to inactive * should never fail anyway, since the code as currently written * cannot fail in that case. */ result = isc_timer_reset(fctx->timer, isc_timertype_inactive, NULL, NULL, ISC_TRUE); if (result != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_timer_reset(): %s", isc_result_totext(result)); } } static inline isc_result_t fctx_startidletimer(fetchctx_t *fctx, isc_interval_t *interval) { /* * Start the idle timer for fctx. The lifetime timer continues * to be in effect. */ return (isc_timer_reset(fctx->timer, isc_timertype_once, &fctx->expires, interval, ISC_FALSE)); } /* * Stopping the idle timer is equivalent to calling fctx_starttimer(), but * we use fctx_stopidletimer for readability in the code below. */ #define fctx_stopidletimer fctx_starttimer static inline void resquery_destroy(resquery_t **queryp) { resquery_t *query; REQUIRE(queryp != NULL); query = *queryp; REQUIRE(!ISC_LINK_LINKED(query, link)); INSIST(query->tcpsocket == NULL); query->fctx->nqueries--; if (SHUTTINGDOWN(query->fctx)) { dns_resolver_t *res = query->fctx->res; if (maybe_destroy(query->fctx, ISC_FALSE)) empty_bucket(res); } query->magic = 0; isc_mem_put(query->mctx, query, sizeof(*query)); *queryp = NULL; } static void fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp, isc_time_t *finish, isc_boolean_t no_response) { fetchctx_t *fctx; resquery_t *query; unsigned int rtt, rttms; unsigned int factor; dns_adbfind_t *find; dns_adbaddrinfo_t *addrinfo; isc_socket_t *socket; query = *queryp; fctx = query->fctx; FCTXTRACE("cancelquery"); REQUIRE(!RESQUERY_CANCELED(query)); query->attributes |= RESQUERY_ATTR_CANCELED; /* * Should we update the RTT? */ if (finish != NULL || no_response) { if (finish != NULL) { /* * We have both the start and finish times for this * packet, so we can compute a real RTT. */ rtt = (unsigned int)isc_time_microdiff(finish, &query->start); factor = DNS_ADB_RTTADJDEFAULT; 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 { /* * We don't have an RTT for this query. Maybe the * packet was lost, or maybe this server is very * slow. We don't know. Increase the RTT. */ INSIST(no_response); rtt = query->addrinfo->srtt + 200000; if (rtt > 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); } /* Remember that the server has been tried. */ if (!TRIED(query->addrinfo)) { dns_adb_changeflags(fctx->adb, query->addrinfo, FCTX_ADDRINFO_TRIED, FCTX_ADDRINFO_TRIED); } /* * Age RTTs of servers not tried. */ factor = DNS_ADB_RTTADJAGE; if (finish != NULL) for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) if (UNMARKED(addrinfo)) dns_adb_adjustsrtt(fctx->adb, addrinfo, 0, factor); if (finish != NULL && TRIEDFIND(fctx)) for (find = ISC_LIST_HEAD(fctx->finds); find != NULL; find = ISC_LIST_NEXT(find, publink)) for (addrinfo = ISC_LIST_HEAD(find->list); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) if (UNMARKED(addrinfo)) dns_adb_adjustsrtt(fctx->adb, addrinfo, 0, factor); if (finish != NULL && TRIEDALT(fctx)) { for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) if (UNMARKED(addrinfo)) dns_adb_adjustsrtt(fctx->adb, addrinfo, 0, factor); for (find = ISC_LIST_HEAD(fctx->altfinds); find != NULL; find = ISC_LIST_NEXT(find, publink)) for (addrinfo = ISC_LIST_HEAD(find->list); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) if (UNMARKED(addrinfo)) dns_adb_adjustsrtt(fctx->adb, addrinfo, 0, factor); } /* * Check for any outstanding socket events. If they exist, cancel * them and let the event handlers finish the cleanup. The resolver * only needs to worry about managing the connect and send events; * the dispatcher manages the recv events. */ if (RESQUERY_CONNECTING(query)) { /* * Cancel the connect. */ if (query->tcpsocket != NULL) { isc_socket_cancel(query->tcpsocket, NULL, ISC_SOCKCANCEL_CONNECT); } else if (query->dispentry != NULL) { INSIST(query->exclusivesocket); socket = dns_dispatch_getentrysocket(query->dispentry); if (socket != NULL) isc_socket_cancel(socket, NULL, ISC_SOCKCANCEL_CONNECT); } } else if (RESQUERY_SENDING(query)) { /* * Cancel the pending send. */ if (query->exclusivesocket && query->dispentry != NULL) socket = dns_dispatch_getentrysocket(query->dispentry); else socket = dns_dispatch_getsocket(query->dispatch); if (socket != NULL) isc_socket_cancel(socket, NULL, ISC_SOCKCANCEL_SEND); } if (query->dispentry != NULL) dns_dispatch_removeresponse(&query->dispentry, deventp); ISC_LIST_UNLINK(fctx->queries, query, link); if (query->tsig != NULL) isc_buffer_free(&query->tsig); if (query->tsigkey != NULL) dns_tsigkey_detach(&query->tsigkey); if (query->dispatch != NULL) dns_dispatch_detach(&query->dispatch); if (! (RESQUERY_CONNECTING(query) || RESQUERY_SENDING(query))) /* * It's safe to destroy the query now. */ resquery_destroy(&query); } static void fctx_cancelqueries(fetchctx_t *fctx, isc_boolean_t no_response) { resquery_t *query, *next_query; FCTXTRACE("cancelqueries"); for (query = ISC_LIST_HEAD(fctx->queries); query != NULL; query = next_query) { next_query = ISC_LIST_NEXT(query, link); fctx_cancelquery(&query, NULL, NULL, no_response); } } static void fctx_cleanupfinds(fetchctx_t *fctx) { dns_adbfind_t *find, *next_find; REQUIRE(ISC_LIST_EMPTY(fctx->queries)); for (find = ISC_LIST_HEAD(fctx->finds); find != NULL; find = next_find) { next_find = ISC_LIST_NEXT(find, publink); ISC_LIST_UNLINK(fctx->finds, find, publink); dns_adb_destroyfind(&find); } fctx->find = NULL; } static void fctx_cleanupaltfinds(fetchctx_t *fctx) { dns_adbfind_t *find, *next_find; REQUIRE(ISC_LIST_EMPTY(fctx->queries)); for (find = ISC_LIST_HEAD(fctx->altfinds); find != NULL; find = next_find) { next_find = ISC_LIST_NEXT(find, publink); ISC_LIST_UNLINK(fctx->altfinds, find, publink); dns_adb_destroyfind(&find); } fctx->altfind = NULL; } static void fctx_cleanupforwaddrs(fetchctx_t *fctx) { dns_adbaddrinfo_t *addr, *next_addr; REQUIRE(ISC_LIST_EMPTY(fctx->queries)); for (addr = ISC_LIST_HEAD(fctx->forwaddrs); addr != NULL; addr = next_addr) { next_addr = ISC_LIST_NEXT(addr, publink); ISC_LIST_UNLINK(fctx->forwaddrs, addr, publink); dns_adb_freeaddrinfo(fctx->adb, &addr); } } static void fctx_cleanupaltaddrs(fetchctx_t *fctx) { dns_adbaddrinfo_t *addr, *next_addr; REQUIRE(ISC_LIST_EMPTY(fctx->queries)); for (addr = ISC_LIST_HEAD(fctx->altaddrs); addr != NULL; addr = next_addr) { next_addr = ISC_LIST_NEXT(addr, publink); ISC_LIST_UNLINK(fctx->altaddrs, addr, publink); dns_adb_freeaddrinfo(fctx->adb, &addr); } } static inline void fctx_stopeverything(fetchctx_t *fctx, isc_boolean_t no_response) { FCTXTRACE("stopeverything"); fctx_cancelqueries(fctx, no_response); fctx_cleanupfinds(fctx); fctx_cleanupaltfinds(fctx); fctx_cleanupforwaddrs(fctx); fctx_cleanupaltaddrs(fctx); fctx_stoptimer(fctx); } static inline void fctx_sendevents(fetchctx_t *fctx, isc_result_t result, int line) { dns_fetchevent_t *event, *next_event; isc_task_t *task; unsigned int count = 0; isc_interval_t i; isc_boolean_t logit = ISC_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(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); } + event->qtotal = fctx->totalqueries; isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event)); count++; } if ((fctx->attributes & FCTX_ATTR_HAVEANSWER) != 0 && fctx->spilled && (count < fctx->res->spillatmax || fctx->res->spillatmax == 0)) { LOCK(&fctx->res->lock); if (count == fctx->res->spillat && !fctx->res->exiting) { old_spillat = fctx->res->spillat; fctx->res->spillat += 5; if (fctx->res->spillat > fctx->res->spillatmax && fctx->res->spillatmax != 0) fctx->res->spillat = fctx->res->spillatmax; new_spillat = fctx->res->spillat; if (new_spillat != old_spillat) { logit = ISC_TRUE; } isc_interval_set(&i, 20 * 60, 0); result = isc_timer_reset(fctx->res->spillattimer, isc_timertype_ticker, NULL, &i, ISC_TRUE); RUNTIME_CHECK(result == ISC_R_SUCCESS); } UNLOCK(&fctx->res->lock); if (logit) isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, "clients-per-query increased to %u", new_spillat); } } static inline void log_edns(fetchctx_t *fctx) { char domainbuf[DNS_NAME_FORMATSIZE]; if (fctx->reason == NULL) return; /* * 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; isc_boolean_t no_response; REQUIRE(line >= 0); FCTXTRACE("done"); res = fctx->res; if (result == ISC_R_SUCCESS) { /*% * Log any deferred EDNS timeout messages. */ log_edns(fctx); no_response = ISC_TRUE; } else no_response = ISC_FALSE; fctx->reason = NULL; fctx_stopeverything(fctx, no_response); LOCK(&res->buckets[fctx->bucketnum].lock); fctx->state = fetchstate_done; fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; fctx_sendevents(fctx, result, 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; isc_boolean_t retry = ISC_FALSE; isc_result_t result; fetchctx_t *fctx; fctx = query->fctx; if (RESQUERY_CANCELED(query)) { if (query->sends == 0 && query->connects == 0) { /* * This query was canceled while the * isc_socket_sendto/connect() was in progress. */ if (query->tcpsocket != NULL) isc_socket_detach(&query->tcpsocket); resquery_destroy(&query); } } else { switch (sevent->result) { case ISC_R_SUCCESS: break; case ISC_R_HOSTUNREACH: case ISC_R_NETUNREACH: case ISC_R_NOPERM: case ISC_R_ADDRNOTAVAIL: case ISC_R_CONNREFUSED: /* * No route to remote. */ add_bad(fctx, query->addrinfo, sevent->result, badns_unreachable); fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); retry = ISC_TRUE; break; default: fctx_cancelquery(&query, NULL, NULL, ISC_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, ISC_TRUE, ISC_FALSE); } } static void resquery_udpconnected(isc_task_t *task, isc_event_t *event) { resquery_t *query = event->ev_arg; REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT); QTRACE("udpconnected"); UNUSED(task); INSIST(RESQUERY_CONNECTING(query)); query->connects--; process_sendevent(query, event); } static void resquery_senddone(isc_task_t *task, isc_event_t *event) { resquery_t *query = event->ev_arg; REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE); QTRACE("senddone"); /* * XXXRTH * * Currently we don't wait for the senddone event before retrying * a query. This means that if we get really behind, we may end * up doing extra work! */ UNUSED(task); INSIST(RESQUERY_SENDING(query)); query->sends--; process_sendevent(query, event); } static inline isc_result_t fctx_addopt(dns_message_t *message, unsigned int version, isc_uint16_t udpsize, 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; isc_boolean_t have_addr = ISC_FALSE; unsigned int srtt; 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; /* * Note that the caller MUST guarantee that 'addrinfo' will remain * valid until this query is canceled. */ query->addrinfo = addrinfo; TIME_NOW(&query->start); /* * If this is a TCP query, then we need to make a socket and * a dispatch for it here. Otherwise we use the resolver's * shared dispatch. */ query->dispatchmgr = res->dispatchmgr; query->dispatch = NULL; query->exclusivesocket = ISC_FALSE; query->tcpsocket = NULL; if (res->view->peers != NULL) { dns_peer_t *peer = NULL; isc_netaddr_t dstip; isc_netaddr_fromsockaddr(&dstip, &addrinfo->sockaddr); result = dns_peerlist_peerbyaddr(res->view->peers, &dstip, &peer); if (result == ISC_R_SUCCESS) { result = dns_peer_getquerysource(peer, &addr); if (result == ISC_R_SUCCESS) have_addr = ISC_TRUE; } } if ((query->options & DNS_FETCHOPT_TCP) != 0) { int pf; pf = isc_sockaddr_pf(&addrinfo->sockaddr); if (!have_addr) { switch (pf) { case PF_INET: result = dns_dispatch_getlocaladdress( res->dispatches4->dispatches[0], &addr); break; case PF_INET6: result = dns_dispatch_getlocaladdress( res->dispatches6->dispatches[0], &addr); break; default: result = ISC_R_NOTIMPLEMENTED; break; } if (result != ISC_R_SUCCESS) goto cleanup_query; } isc_sockaddr_setport(&addr, 0); result = isc_socket_create(res->socketmgr, pf, isc_sockettype_tcp, &query->tcpsocket); if (result != ISC_R_SUCCESS) goto cleanup_query; #ifndef BROKEN_TCP_BIND_BEFORE_CONNECT result = isc_socket_bind(query->tcpsocket, &addr, 0); if (result != ISC_R_SUCCESS) goto cleanup_socket; #endif /* * A dispatch will be created once the connect succeeds. */ } else { if (have_addr) { unsigned int attrs, attrmask; attrs = DNS_DISPATCHATTR_UDP; switch (isc_sockaddr_pf(&addr)) { case AF_INET: attrs |= DNS_DISPATCHATTR_IPV4; break; case AF_INET6: attrs |= DNS_DISPATCHATTR_IPV6; break; default: result = ISC_R_NOTIMPLEMENTED; goto cleanup_query; } attrmask = DNS_DISPATCHATTR_UDP; attrmask |= DNS_DISPATCHATTR_TCP; attrmask |= DNS_DISPATCHATTR_IPV4; attrmask |= DNS_DISPATCHATTR_IPV6; result = dns_dispatch_getudp(res->dispatchmgr, res->socketmgr, res->taskmgr, &addr, 4096, 1000, 32768, 16411, 16433, attrs, attrmask, &query->dispatch); if (result != ISC_R_SUCCESS) goto cleanup_query; } else { switch (isc_sockaddr_pf(&addrinfo->sockaddr)) { case PF_INET: dns_dispatch_attach( dns_resolver_dispatchv4(res), &query->dispatch); query->exclusivesocket = res->exclusivev4; break; case PF_INET6: dns_dispatch_attach( dns_resolver_dispatchv6(res), &query->dispatch); query->exclusivesocket = res->exclusivev6; break; default: result = ISC_R_NOTIMPLEMENTED; goto cleanup_query; } } /* * We should always have a valid dispatcher here. If we * don't support a protocol family, then its dispatcher * will be NULL, but we shouldn't be finding addresses for * protocol types we don't support, so the dispatcher * we found should never be NULL. */ INSIST(query->dispatch != NULL); } query->dispentry = NULL; query->fctx = fctx; query->tsig = NULL; query->tsigkey = NULL; ISC_LINK_INIT(query, link); query->magic = QUERY_MAGIC; if ((query->options & DNS_FETCHOPT_TCP) != 0) { /* * Connect to the remote server. * * XXXRTH Should we attach to the socket? */ result = isc_socket_connect(query->tcpsocket, &addrinfo->sockaddr, task, resquery_connected, query); if (result != ISC_R_SUCCESS) goto cleanup_socket; query->connects++; QTRACE("connecting via TCP"); } else { result = resquery_send(query); if (result != ISC_R_SUCCESS) goto cleanup_dispatch; } + fctx->querysent++; + fctx->totalqueries++; 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 isc_boolean_t 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 (ISC_TRUE); } return (ISC_FALSE); } static void add_bad_edns(fetchctx_t *fctx, isc_sockaddr_t *address) { isc_sockaddr_t *sa; 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 isc_boolean_t triededns(fetchctx_t *fctx, isc_sockaddr_t *address) { isc_sockaddr_t *sa; for (sa = ISC_LIST_HEAD(fctx->edns); sa != NULL; sa = ISC_LIST_NEXT(sa, link)) { if (isc_sockaddr_equal(sa, address)) return (ISC_TRUE); } return (ISC_FALSE); } static void add_triededns(fetchctx_t *fctx, isc_sockaddr_t *address) { isc_sockaddr_t *sa; if (triededns(fctx, address)) return; sa = isc_mem_get(fctx->mctx, sizeof(*sa)); if (sa == NULL) return; *sa = *address; ISC_LIST_INITANDAPPEND(fctx->edns, sa, link); } static isc_boolean_t triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) { isc_sockaddr_t *sa; for (sa = ISC_LIST_HEAD(fctx->edns512); sa != NULL; sa = ISC_LIST_NEXT(sa, link)) { if (isc_sockaddr_equal(sa, address)) return (ISC_TRUE); } return (ISC_FALSE); } static void add_triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) { isc_sockaddr_t *sa; if (triededns512(fctx, address)) return; sa = isc_mem_get(fctx->mctx, sizeof(*sa)); if (sa == NULL) return; *sa = *address; ISC_LIST_INITANDAPPEND(fctx->edns512, sa, link); } static isc_result_t resquery_send(resquery_t *query) { fetchctx_t *fctx; isc_result_t result; dns_name_t *qname = NULL; dns_rdataset_t *qrdataset = NULL; isc_region_t r; dns_resolver_t *res; isc_task_t *task; isc_socket_t *socket; isc_buffer_t tcpbuffer; isc_sockaddr_t *address; isc_buffer_t *buffer; isc_netaddr_t ipaddr; dns_tsigkey_t *tsigkey = NULL; dns_peer_t *peer = NULL; isc_boolean_t useedns; dns_compress_t cctx; isc_boolean_t cleanup_cctx = ISC_FALSE; isc_boolean_t secure_domain; isc_boolean_t connecting = ISC_FALSE; dns_ednsopt_t ednsopts[EDNSOPTS]; unsigned ednsopt = 0; fctx = query->fctx; QTRACE("send"); res = fctx->res; task = res->buckets[fctx->bucketnum].task; address = NULL; if ((query->options & DNS_FETCHOPT_TCP) != 0) { /* * Reserve space for the TCP message length. */ isc_buffer_init(&tcpbuffer, query->data, sizeof(query->data)); isc_buffer_init(&query->buffer, query->data + 2, sizeof(query->data) - 2); buffer = &tcpbuffer; } else { isc_buffer_init(&query->buffer, query->data, sizeof(query->data)); buffer = &query->buffer; } result = dns_message_gettempname(fctx->qmessage, &qname); if (result != ISC_R_SUCCESS) goto cleanup_temps; result = dns_message_gettemprdataset(fctx->qmessage, &qrdataset); if (result != ISC_R_SUCCESS) goto cleanup_temps; /* * Get a query id from the dispatch. */ result = dns_dispatch_addresponse2(query->dispatch, &query->addrinfo->sockaddr, task, resquery_response, query, &query->id, &query->dispentry, res->socketmgr); if (result != ISC_R_SUCCESS) goto cleanup_temps; fctx->qmessage->opcode = dns_opcode_query; /* * Set up question. */ dns_name_init(qname, NULL); dns_name_clone(&fctx->name, qname); dns_rdataset_init(qrdataset); dns_rdataset_makequestion(qrdataset, res->rdclass, fctx->type); ISC_LIST_APPEND(qname->list, qrdataset, link); dns_message_addname(fctx->qmessage, qname, DNS_SECTION_QUESTION); qname = NULL; qrdataset = NULL; /* * Set RD if the client has requested that we do a recursive query, * or if we're sending to a forwarder. */ if ((query->options & DNS_FETCHOPT_RECURSIVE) != 0 || ISFORWARDER(query->addrinfo)) fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD; /* * Set CD if the client says don't validate or the question is * under a secure entry point. */ if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) { fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; } else if (res->view->enablevalidation) { result = dns_view_issecuredomain(res->view, &fctx->name, &secure_domain); if (result != ISC_R_SUCCESS) secure_domain = ISC_FALSE; if (res->view->dlv != NULL) secure_domain = ISC_TRUE; if (secure_domain) fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; } /* * We don't have to set opcode because it defaults to query. */ fctx->qmessage->id = query->id; /* * Convert the question to wire format. */ result = dns_compress_init(&cctx, -1, fctx->res->mctx); if (result != ISC_R_SUCCESS) goto cleanup_message; cleanup_cctx = ISC_TRUE; result = dns_message_renderbegin(fctx->qmessage, &cctx, &query->buffer); if (result != ISC_R_SUCCESS) goto cleanup_message; result = dns_message_rendersection(fctx->qmessage, DNS_SECTION_QUESTION, 0); if (result != ISC_R_SUCCESS) goto cleanup_message; peer = NULL; isc_netaddr_fromsockaddr(&ipaddr, &query->addrinfo->sockaddr); (void) dns_peerlist_peerbyaddr(fctx->res->view->peers, &ipaddr, &peer); /* * The ADB does not know about servers with "edns no". Check this, * and then inform the ADB for future use. */ if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0 && peer != NULL && dns_peer_getsupportedns(peer, &useedns) == ISC_R_SUCCESS && !useedns) { query->options |= DNS_FETCHOPT_NOEDNS0; dns_adb_changeflags(fctx->adb, query->addrinfo, DNS_FETCHOPT_NOEDNS0, DNS_FETCHOPT_NOEDNS0); } /* Sync NOEDNS0 flag in addrinfo->flags and options now. */ if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) != 0) query->options |= DNS_FETCHOPT_NOEDNS0; /* * Handle timeouts by reducing the UDP response size to 512 bytes * then if that doesn't work disabling EDNS (includes DO) and CD. * * These timeout can be due to: * * broken nameservers that don't respond to EDNS queries. * * broken/misconfigured firewalls and NAT implementations * that don't handle IP fragmentation. * * broken/misconfigured firewalls that don't handle responses * greater than 512 bytes. * * broken/misconfigured firewalls that don't handle EDNS, DO * or CD. * * packet loss / link outage. */ if (fctx->timeout) { if ((triededns512(fctx, &query->addrinfo->sockaddr) || fctx->timeouts >= (MAX_EDNS0_TIMEOUTS * 2)) && (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { query->options |= DNS_FETCHOPT_NOEDNS0; fctx->reason = "disabling EDNS"; } else if ((triededns(fctx, &query->addrinfo->sockaddr) || fctx->timeouts >= MAX_EDNS0_TIMEOUTS) && (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { query->options |= DNS_FETCHOPT_EDNS512; fctx->reason = "reducing the advertised EDNS UDP " "packet size to 512 octets"; } fctx->timeout = ISC_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 = 0; /* Default version. */ unsigned int flags; isc_uint16_t udpsize = res->udpsize; isc_boolean_t reqnsid = res->view->requestnsid; flags = query->addrinfo->flags; if ((flags & DNS_FETCHOPT_EDNSVERSIONSET) != 0) { version = flags & DNS_FETCHOPT_EDNSVERSIONMASK; version >>= DNS_FETCHOPT_EDNSVERSIONSHIFT; } if ((query->options & DNS_FETCHOPT_EDNS512) != 0) udpsize = 512; else if (peer != NULL) (void)dns_peer_getudpsize(peer, &udpsize); /* request NSID for current view or peer? */ if (peer != NULL) (void) dns_peer_getrequestnsid(peer, &reqnsid); if (reqnsid) { INSIST(ednsopt < EDNSOPTS); ednsopts[ednsopt].code = DNS_OPT_NSID; ednsopts[ednsopt].length = 0; ednsopts[ednsopt].value = NULL; ednsopt++; } 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; } } else { /* * We know this server doesn't like EDNS0, so we * won't use it. Set the NOEDNS0 bit since we're * not using EDNS0. */ query->options |= DNS_FETCHOPT_NOEDNS0; } } /* * If we need EDNS0 to do this query and aren't using it, we lose. */ if (NEEDEDNS0(fctx) && (query->options & DNS_FETCHOPT_NOEDNS0) != 0) { result = DNS_R_SERVFAIL; goto cleanup_message; } if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) add_triededns(fctx, &query->addrinfo->sockaddr); if ((query->options & DNS_FETCHOPT_EDNS512) != 0) add_triededns512(fctx, &query->addrinfo->sockaddr); /* * Clear CD if EDNS is not in use. */ if ((query->options & DNS_FETCHOPT_NOEDNS0) != 0) fctx->qmessage->flags &= ~DNS_MESSAGEFLAG_CD; /* * Add TSIG record tailored to the current recipient. */ result = dns_view_getpeertsig(fctx->res->view, &ipaddr, &tsigkey); if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) goto cleanup_message; if (tsigkey != NULL) { result = dns_message_settsigkey(fctx->qmessage, tsigkey); dns_tsigkey_detach(&tsigkey); if (result != ISC_R_SUCCESS) goto cleanup_message; } result = dns_message_rendersection(fctx->qmessage, DNS_SECTION_ADDITIONAL, 0); if (result != ISC_R_SUCCESS) goto cleanup_message; result = dns_message_renderend(fctx->qmessage); if (result != ISC_R_SUCCESS) goto cleanup_message; dns_compress_invalidate(&cctx); cleanup_cctx = ISC_FALSE; if (dns_message_gettsigkey(fctx->qmessage) != NULL) { dns_tsigkey_attach(dns_message_gettsigkey(fctx->qmessage), &query->tsigkey); result = dns_message_getquerytsig(fctx->qmessage, fctx->res->mctx, &query->tsig); if (result != ISC_R_SUCCESS) goto cleanup_message; } /* * If using TCP, write the length of the message at the beginning * of the buffer. */ if ((query->options & DNS_FETCHOPT_TCP) != 0) { isc_buffer_usedregion(&query->buffer, &r); isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t)r.length); isc_buffer_add(&tcpbuffer, r.length); } /* * We're now done with the query message. */ dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); if (query->exclusivesocket) socket = dns_dispatch_getentrysocket(query->dispentry); else socket = dns_dispatch_getsocket(query->dispatch); /* * Send the query! */ if ((query->options & DNS_FETCHOPT_TCP) == 0) { address = &query->addrinfo->sockaddr; if (query->exclusivesocket) { result = isc_socket_connect(socket, address, task, resquery_udpconnected, query); if (result != ISC_R_SUCCESS) goto cleanup_message; connecting = ISC_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. */ ISC_EVENT_INIT(&query->sendevent, sizeof(query->sendevent), 0, NULL, ISC_SOCKEVENT_SENDDONE, resquery_senddone, query, NULL, NULL, NULL); result = isc_socket_sendto2(socket, &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"); return (ISC_R_SUCCESS); cleanup_message: if (cleanup_cctx) dns_compress_invalidate(&cctx); dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); /* * Stop the dispatcher from listening. */ dns_dispatch_removeresponse(&query->dispentry, NULL); cleanup_temps: if (qname != NULL) dns_message_puttempname(fctx->qmessage, &qname); if (qrdataset != NULL) dns_message_puttemprdataset(fctx->qmessage, &qrdataset); return (result); } static void resquery_connected(isc_task_t *task, isc_event_t *event) { isc_socketevent_t *sevent = (isc_socketevent_t *)event; resquery_t *query = event->ev_arg; isc_boolean_t retry = ISC_FALSE; isc_interval_t interval; isc_result_t result; unsigned int attrs; fetchctx_t *fctx; REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT); REQUIRE(VALID_QUERY(query)); QTRACE("connected"); UNUSED(task); /* * XXXRTH * * Currently we don't wait for the connect event before retrying * a query. This means that if we get really behind, we may end * up doing extra work! */ query->connects--; fctx = query->fctx; if (RESQUERY_CANCELED(query)) { /* * This query was canceled while the connect() was in * progress. */ isc_socket_detach(&query->tcpsocket); resquery_destroy(&query); } else { switch (sevent->result) { case ISC_R_SUCCESS: /* * Extend the idle timer for TCP. 20 seconds * should be long enough for a TCP connection to be * established, a single DNS request to be sent, * and the response received. */ isc_interval_set(&interval, 20, 0); result = fctx_startidletimer(query->fctx, &interval); if (result != ISC_R_SUCCESS) { fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); fctx_done(fctx, result, __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) { fctx_cancelquery(&query, NULL, NULL, ISC_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: /* * No route to remote. */ isc_socket_detach(&query->tcpsocket); fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); retry = ISC_TRUE; break; default: isc_socket_detach(&query->tcpsocket); fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); break; } } isc_event_free(&event); if (retry) { /* * Behave as if the idle timer has expired. For TCP * connections this may not actually reflect the latest timer. */ fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; result = fctx_stopidletimer(fctx); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); else fctx_try(fctx, ISC_TRUE, ISC_FALSE); } } static void fctx_finddone(isc_task_t *task, isc_event_t *event) { fetchctx_t *fctx; dns_adbfind_t *find; dns_resolver_t *res; isc_boolean_t want_try = ISC_FALSE; isc_boolean_t want_done = ISC_FALSE; isc_boolean_t bucket_empty = ISC_FALSE; unsigned int bucketnum; isc_boolean_t destroy = ISC_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)); fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; - if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) + if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) { want_try = ISC_TRUE; - else { + fctx->totalqueries += find->qtotal; + } 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. */ want_done = ISC_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); destroy = ISC_TRUE; } } UNLOCK(&res->buckets[bucketnum].lock); isc_event_free(&event); dns_adb_destroyfind(&find); if (want_try) fctx_try(fctx, ISC_TRUE, ISC_FALSE); else if (want_done) fctx_done(fctx, ISC_R_FAILURE, __LINE__); else if (destroy) { fctx_destroy(fctx); if (bucket_empty) empty_bucket(res); } } static inline isc_boolean_t bad_server(fetchctx_t *fctx, isc_sockaddr_t *address) { isc_sockaddr_t *sa; for (sa = ISC_LIST_HEAD(fctx->bad); sa != NULL; sa = ISC_LIST_NEXT(sa, link)) { if (isc_sockaddr_equal(sa, address)) return (ISC_TRUE); } return (ISC_FALSE); } static inline isc_boolean_t mark_bad(fetchctx_t *fctx) { dns_adbfind_t *curr; dns_adbaddrinfo_t *addrinfo; isc_boolean_t all_bad = ISC_TRUE; /* * Mark all known bad servers, so we don't try to talk to them * again. */ /* * Mark any bad nameservers. */ for (curr = ISC_LIST_HEAD(fctx->finds); curr != NULL; curr = ISC_LIST_NEXT(curr, publink)) { for (addrinfo = ISC_LIST_HEAD(curr->list); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (bad_server(fctx, &addrinfo->sockaddr)) addrinfo->flags |= FCTX_ADDRINFO_MARK; else all_bad = ISC_FALSE; } } /* * Mark any bad forwarders. */ for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (bad_server(fctx, &addrinfo->sockaddr)) addrinfo->flags |= FCTX_ADDRINFO_MARK; else all_bad = ISC_FALSE; } /* * Mark any bad alternates. */ for (curr = ISC_LIST_HEAD(fctx->altfinds); curr != NULL; curr = ISC_LIST_NEXT(curr, publink)) { for (addrinfo = ISC_LIST_HEAD(curr->list); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (bad_server(fctx, &addrinfo->sockaddr)) addrinfo->flags |= FCTX_ADDRINFO_MARK; else all_bad = ISC_FALSE; } } for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (bad_server(fctx, &addrinfo->sockaddr)) addrinfo->flags |= FCTX_ADDRINFO_MARK; else all_bad = ISC_FALSE; } return (all_bad); } static void add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason, 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; 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, "error (%s%s%s) resolving '%s/%s/%s': %s", dns_result_totext(reason), spc, code, namebuf, typebuf, classbuf, addrbuf); } /* * Sort addrinfo list by RTT. */ static void sort_adbfind(dns_adbfind_t *find) { dns_adbaddrinfo_t *best, *curr; dns_adbaddrinfolist_t sorted; /* Lame N^2 bubble sort. */ ISC_LIST_INIT(sorted); while (!ISC_LIST_EMPTY(find->list)) { best = ISC_LIST_HEAD(find->list); curr = ISC_LIST_NEXT(best, publink); while (curr != NULL) { if (curr->srtt < best->srtt) best = curr; curr = ISC_LIST_NEXT(curr, publink); } ISC_LIST_UNLINK(find->list, best, publink); ISC_LIST_APPEND(sorted, best, publink); } find->list = sorted; } /* * Sort a list of finds by server RTT. */ static void sort_finds(dns_adbfindlist_t *findlist) { dns_adbfind_t *best, *curr; dns_adbfindlist_t sorted; dns_adbaddrinfo_t *addrinfo, *bestaddrinfo; /* Sort each find's addrinfo list by SRTT. */ for (curr = ISC_LIST_HEAD(*findlist); curr != NULL; curr = ISC_LIST_NEXT(curr, publink)) sort_adbfind(curr); /* Lame N^2 bubble sort. */ ISC_LIST_INIT(sorted); while (!ISC_LIST_EMPTY(*findlist)) { best = ISC_LIST_HEAD(*findlist); bestaddrinfo = ISC_LIST_HEAD(best->list); INSIST(bestaddrinfo != NULL); curr = ISC_LIST_NEXT(best, publink); while (curr != NULL) { addrinfo = ISC_LIST_HEAD(curr->list); INSIST(addrinfo != NULL); if (addrinfo->srtt < bestaddrinfo->srtt) { best = curr; bestaddrinfo = addrinfo; } curr = ISC_LIST_NEXT(curr, publink); } ISC_LIST_UNLINK(*findlist, best, publink); ISC_LIST_APPEND(sorted, best, publink); } *findlist = sorted; } static void findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port, unsigned int options, unsigned int flags, isc_stdtime_t now, isc_boolean_t *need_alternate) { dns_adbaddrinfo_t *ai; dns_adbfind_t *find; dns_resolver_t *res; isc_boolean_t unshared; isc_result_t result; res = fctx->res; unshared = ISC_TF((fctx->options & DNS_FETCHOPT_UNSHARED) != 0); /* * If this name is a subdomain of the query domain, tell * the ADB to start looking using zone/hint data. This keeps us * from getting stuck if the nameserver is beneath the zone cut * and we don't know its address (e.g. because the A record has * expired). */ if (dns_name_issubdomain(name, &fctx->domain)) options |= DNS_ADBFIND_STARTATZONE; options |= DNS_ADBFIND_GLUEOK; options |= DNS_ADBFIND_HINTOK; /* * See what we know about this address. */ find = NULL; - result = dns_adb_createfind(fctx->adb, - res->buckets[fctx->bucketnum].task, - fctx_finddone, fctx, name, - &fctx->name, fctx->type, - options, now, NULL, - res->view->dstport, &find); + 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, &find); if (result != ISC_R_SUCCESS) { if (result == DNS_R_ALIAS) { /* * XXXRTH Follow the CNAME/DNAME chain? */ dns_adb_destroyfind(&find); fctx->adberr++; } } 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 = ISC_TRUE; } 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 = ISC_TRUE; dns_adb_destroyfind(&find); } } } static isc_boolean_t 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 (ISC_TF(namereln == dns_namereln_subdomain)); } static isc_result_t fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) { dns_rdata_t rdata = DNS_RDATA_INIT; isc_result_t result; dns_resolver_t *res; isc_stdtime_t now; unsigned int stdoptions = 0; isc_sockaddr_t *sa; dns_adbaddrinfo_t *ai; isc_boolean_t all_bad; dns_rdata_ns_t ns; isc_boolean_t need_alternate = ISC_FALSE; FCTXTRACE("getaddresses"); /* * Don't pound on remote servers. (Failsafe!) */ fctx->restarts++; if (fctx->restarts > 10) { FCTXTRACE("too many restarts"); return (DNS_R_SERVFAIL); } res = fctx->res; + if (fctx->depth > res->maxdepth) { + FCTXTRACE("too much NS indirection"); + 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). */ sa = ISC_LIST_HEAD(fctx->forwarders); if (sa == NULL) { dns_forwarders_t *forwarders = NULL; dns_name_t *name = &fctx->name; dns_name_t suffix; unsigned int labels; 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; } dns_fixedname_init(&fixed); domain = dns_fixedname_name(&fixed); result = dns_fwdtable_find2(fctx->res->view->fwdtable, name, domain, &forwarders); if (result == ISC_R_SUCCESS) { sa = ISC_LIST_HEAD(forwarders->addrs); fctx->fwdpolicy = forwarders->fwdpolicy; if (fctx->fwdpolicy == dns_fwdpolicy_only && isstrictsubdomain(domain, &fctx->domain)) { 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); } } } while (sa != NULL) { if ((isc_sockaddr_pf(sa) == AF_INET && fctx->res->dispatches4 == NULL) || (isc_sockaddr_pf(sa) == AF_INET6 && fctx->res->dispatches6 == NULL)) { sa = ISC_LIST_NEXT(sa, link); continue; } ai = NULL; result = dns_adb_findaddrinfo(fctx->adb, sa, &ai, 0); /* XXXMLG */ if (result == ISC_R_SUCCESS) { dns_adbaddrinfo_t *cur; ai->flags |= FCTX_ADDRINFO_FORWARDER; cur = ISC_LIST_HEAD(fctx->forwaddrs); while (cur != NULL && cur->srtt < ai->srtt) cur = ISC_LIST_NEXT(cur, publink); if (cur != NULL) ISC_LIST_INSERTBEFORE(fctx->forwaddrs, cur, ai, publink); else ISC_LIST_APPEND(fctx->forwaddrs, ai, publink); } sa = ISC_LIST_NEXT(sa, link); } /* * If the forwarding policy is "only", we don't need the addresses * of the nameservers. */ if (fctx->fwdpolicy == dns_fwdpolicy_only) goto out; /* * Normal nameservers. */ stdoptions = DNS_ADBFIND_WANTEVENT | DNS_ADBFIND_EMPTYEVENT; if (fctx->restarts == 1) { /* * To avoid sending out a flood of queries likely to * result in NXRRSET, we suppress fetches for address * families we don't have the first time through, * provided that we have addresses in some family we * can use. * * We don't want to set this option all the time, since * if fctx->restarts > 1, we've clearly been having trouble * with the addresses we had, so getting more could help. */ stdoptions |= DNS_ADBFIND_AVOIDFETCHES; } if (res->dispatches4 != NULL) stdoptions |= DNS_ADBFIND_INET; if (res->dispatches6 != NULL) stdoptions |= DNS_ADBFIND_INET6; isc_stdtime_get(&now); INSIST(ISC_LIST_EMPTY(fctx->finds)); INSIST(ISC_LIST_EMPTY(fctx->altfinds)); for (result = dns_rdataset_first(&fctx->nameservers); result == ISC_R_SUCCESS; result = dns_rdataset_next(&fctx->nameservers)) { dns_rdataset_current(&fctx->nameservers, &rdata); /* * Extract the name from the NS record. */ result = dns_rdata_tostruct(&rdata, &ns, NULL); if (result != ISC_R_SUCCESS) continue; findname(fctx, &ns.name, 0, stdoptions, 0, now, &need_alternate); dns_rdata_reset(&rdata); dns_rdata_freestruct(&ns); } if (result != ISC_R_NOMORE) return (result); /* * Do we need to use 6 to 4? */ if (need_alternate) { int family; alternate_t *a; family = (res->dispatches6 != NULL) ? AF_INET6 : AF_INET; for (a = ISC_LIST_HEAD(fctx->res->alternates); a != NULL; a = ISC_LIST_NEXT(a, link)) { if (!a->isaddress) { findname(fctx, &a->_u._n.name, a->_u._n.port, stdoptions, FCTX_ADDRINFO_FORWARDER, now, NULL); continue; } if (isc_sockaddr_pf(&a->_u.addr) != family) continue; ai = NULL; result = dns_adb_findaddrinfo(fctx->adb, &a->_u.addr, &ai, 0); if (result == ISC_R_SUCCESS) { dns_adbaddrinfo_t *cur; ai->flags |= FCTX_ADDRINFO_FORWARDER; cur = ISC_LIST_HEAD(fctx->altaddrs); while (cur != NULL && cur->srtt < ai->srtt) cur = ISC_LIST_NEXT(cur, publink); if (cur != NULL) ISC_LIST_INSERTBEFORE(fctx->altaddrs, cur, ai, publink); else ISC_LIST_APPEND(fctx->altaddrs, ai, publink); } } } out: /* * Mark all known bad servers. */ all_bad = mark_bad(fctx); /* * How are we doing? */ if (all_bad) { /* * We've got no addresses. */ if (fctx->pending > 0) { /* * We're fetching the addresses, but don't have any * yet. Tell the caller to wait for an answer. */ result = DNS_R_WAIT; } else { 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_BADCACHE_TTL(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(fctx->res, &fctx->name, fctx->type, &expire); result = ISC_R_FAILURE; } } else { /* * We've found some addresses. We might still be looking * for more addresses. */ sort_finds(&fctx->finds); sort_finds(&fctx->altfinds); result = ISC_R_SUCCESS; } return (result); } static inline void possibly_mark(fetchctx_t *fctx, dns_adbaddrinfo_t *addr) { isc_netaddr_t na; char buf[ISC_NETADDR_FORMATSIZE]; isc_sockaddr_t *sa; isc_boolean_t aborted = ISC_FALSE; isc_boolean_t bogus; dns_acl_t *blackhole; isc_netaddr_t ipaddr; dns_peer_t *peer = NULL; dns_resolver_t *res; const char *msg = NULL; sa = &addr->sockaddr; res = fctx->res; isc_netaddr_fromsockaddr(&ipaddr, sa); blackhole = dns_dispatchmgr_getblackhole(res->dispatchmgr); (void) dns_peerlist_peerbyaddr(res->view->peers, &ipaddr, &peer); if (blackhole != NULL) { int match; if (dns_acl_match(&ipaddr, NULL, blackhole, &res->view->aclenv, &match, NULL) == ISC_R_SUCCESS && match > 0) aborted = ISC_TRUE; } if (peer != NULL && dns_peer_getbogus(peer, &bogus) == ISC_R_SUCCESS && bogus) aborted = ISC_TRUE; if (aborted) { addr->flags |= FCTX_ADDRINFO_MARK; msg = "ignoring blackholed / bogus server: "; } else if (isc_sockaddr_ismulticast(sa)) { addr->flags |= FCTX_ADDRINFO_MARK; msg = "ignoring multicast address: "; } else if (isc_sockaddr_isexperimental(sa)) { addr->flags |= FCTX_ADDRINFO_MARK; msg = "ignoring experimental address: "; } else if (sa->type.sa.sa_family != AF_INET6) { return; } else if (IN6_IS_ADDR_V4MAPPED(&sa->type.sin6.sin6_addr)) { addr->flags |= FCTX_ADDRINFO_MARK; msg = "ignoring IPv6 mapped IPV4 address: "; } else if (IN6_IS_ADDR_V4COMPAT(&sa->type.sin6.sin6_addr)) { addr->flags |= FCTX_ADDRINFO_MARK; msg = "ignoring IPv6 compatibility IPV4 address: "; } else return; if (!isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3))) return; isc_netaddr_fromsockaddr(&na, sa); isc_netaddr_format(&na, buf, sizeof(buf)); FCTXTRACE2(msg, buf); } static inline dns_adbaddrinfo_t * fctx_nextaddress(fetchctx_t *fctx) { dns_adbfind_t *find, *start; dns_adbaddrinfo_t *addrinfo; dns_adbaddrinfo_t *faddrinfo; /* * Return the next untried address, if any. */ /* * Find the first unmarked forwarder (if any). */ for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (!UNMARKED(addrinfo)) continue; possibly_mark(fctx, addrinfo); if (UNMARKED(addrinfo)) { addrinfo->flags |= FCTX_ADDRINFO_MARK; fctx->find = NULL; return (addrinfo); } } /* * No forwarders. Move to the next find. */ fctx->attributes |= FCTX_ATTR_TRIEDFIND; find = fctx->find; if (find == NULL) find = ISC_LIST_HEAD(fctx->finds); else { find = ISC_LIST_NEXT(find, publink); if (find == NULL) find = ISC_LIST_HEAD(fctx->finds); } /* * Find the first unmarked addrinfo. */ addrinfo = NULL; if (find != NULL) { start = find; do { for (addrinfo = ISC_LIST_HEAD(find->list); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (!UNMARKED(addrinfo)) continue; possibly_mark(fctx, addrinfo); if (UNMARKED(addrinfo)) { addrinfo->flags |= FCTX_ADDRINFO_MARK; break; } } if (addrinfo != NULL) break; find = ISC_LIST_NEXT(find, publink); if (find == NULL) find = ISC_LIST_HEAD(fctx->finds); } while (find != start); } fctx->find = find; if (addrinfo != NULL) return (addrinfo); /* * No nameservers left. Try alternates. */ fctx->attributes |= FCTX_ATTR_TRIEDALT; find = fctx->altfind; if (find == NULL) find = ISC_LIST_HEAD(fctx->altfinds); else { find = ISC_LIST_NEXT(find, publink); if (find == NULL) find = ISC_LIST_HEAD(fctx->altfinds); } /* * Find the first unmarked addrinfo. */ addrinfo = NULL; if (find != NULL) { start = find; do { for (addrinfo = ISC_LIST_HEAD(find->list); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (!UNMARKED(addrinfo)) continue; possibly_mark(fctx, addrinfo); if (UNMARKED(addrinfo)) { addrinfo->flags |= FCTX_ADDRINFO_MARK; break; } } if (addrinfo != NULL) break; find = ISC_LIST_NEXT(find, publink); if (find == NULL) find = ISC_LIST_HEAD(fctx->altfinds); } while (find != start); } faddrinfo = addrinfo; /* * See if we have a better alternate server by address. */ for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); addrinfo != NULL; addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { if (!UNMARKED(addrinfo)) continue; possibly_mark(fctx, addrinfo); if (UNMARKED(addrinfo) && (faddrinfo == NULL || addrinfo->srtt < faddrinfo->srtt)) { if (faddrinfo != NULL) faddrinfo->flags &= ~FCTX_ADDRINFO_MARK; addrinfo->flags |= FCTX_ADDRINFO_MARK; break; } } if (addrinfo == NULL) { addrinfo = faddrinfo; fctx->altfind = find; } return (addrinfo); } static void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) { isc_result_t result; dns_adbaddrinfo_t *addrinfo; FCTXTRACE("try"); REQUIRE(!ADDRWAIT(fctx)); + if (fctx->totalqueries > DEFAULT_MAX_QUERIES) + fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); + addrinfo = fctx_nextaddress(fctx); if (addrinfo == NULL) { /* * We have no more addresses. Start over. */ fctx_cancelqueries(fctx, ISC_TRUE); fctx_cleanupfinds(fctx); fctx_cleanupaltfinds(fctx); fctx_cleanupforwaddrs(fctx); fctx_cleanupaltaddrs(fctx); result = fctx_getaddresses(fctx, 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 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; } } result = fctx_query(fctx, addrinfo, fctx->options); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); else if (retrying) inc_stats(fctx->res, dns_resstatscounter_retry); } static isc_boolean_t 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); if (res->buckets[bucketnum].exiting && ISC_LIST_EMPTY(res->buckets[bucketnum].fctxs)) return (ISC_TRUE); return (ISC_FALSE); } static void fctx_destroy(fetchctx_t *fctx) { isc_sockaddr_t *sa, *next_sa; 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 (sa = ISC_LIST_HEAD(fctx->edns); sa != NULL; sa = next_sa) { next_sa = ISC_LIST_NEXT(sa, link); ISC_LIST_UNLINK(fctx->edns, sa, link); isc_mem_put(fctx->mctx, sa, sizeof(*sa)); } for (sa = ISC_LIST_HEAD(fctx->edns512); sa != NULL; sa = next_sa) { next_sa = ISC_LIST_NEXT(sa, link); ISC_LIST_UNLINK(fctx->edns512, sa, link); isc_mem_put(fctx->mctx, sa, sizeof(*sa)); } 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_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 = ISC_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) { fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); } fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; /* * Our timer has triggered. Reestablish the fctx lifetime * timer. */ result = fctx_starttimer(fctx); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); else /* * Keep trying. */ fctx_try(fctx, ISC_TRUE, ISC_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 = ISC_TRUE; /* * Unless we're still initializing (in which case the * control event is still outstanding), we need to post * the control event to tell the fetch we want it to * exit. */ if (fctx->state != fetchstate_init) { cevent = &fctx->control_event; isc_task_send(fctx->res->buckets[fctx->bucketnum].task, &cevent); } } static void fctx_doshutdown(isc_task_t *task, isc_event_t *event) { fetchctx_t *fctx = event->ev_arg; isc_boolean_t bucket_empty = ISC_FALSE; dns_resolver_t *res; unsigned int bucketnum; dns_validator_t *validator; isc_boolean_t destroy = ISC_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 that is still running on behalf of this * fetch. To avoid deadlock with the ADB, we must do this * before we lock the bucket lock. */ fctx_stopeverything(fctx, ISC_FALSE); LOCK(&res->buckets[bucketnum].lock); fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN; INSIST(fctx->state == fetchstate_active || fctx->state == fetchstate_done); INSIST(fctx->want_shutdown); if (fctx->state != fetchstate_done) { fctx->state = fetchstate_done; fctx_sendevents(fctx, ISC_R_CANCELED, __LINE__); } if (fctx->references == 0 && fctx->pending == 0 && fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) { bucket_empty = fctx_unlink(fctx); destroy = ISC_TRUE; } UNLOCK(&res->buckets[bucketnum].lock); if (destroy) { 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; isc_boolean_t done = ISC_FALSE, bucket_empty = ISC_FALSE; dns_resolver_t *res; unsigned int bucketnum; isc_boolean_t destroy = ISC_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); destroy = ISC_TRUE; } done = ISC_TRUE; } else { /* * Normal fctx startup. */ fctx->state = fetchstate_active; + fctx->totalqueries = 0; /* * 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(!destroy); /* * 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, ISC_FALSE, ISC_FALSE); } else if (destroy) { 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 *clone; dns_fetchevent_t *event; FCTXTRACE("join"); /* * We store the task we're going to send this event to in the * sender field. We'll make the fetch the sender when we actually * send the event. */ clone = NULL; isc_task_attach(task, &clone); event = (dns_fetchevent_t *) isc_event_allocate(fctx->res->mctx, clone, DNS_EVENT_FETCHDONE, action, arg, sizeof(*event)); if (event == NULL) { isc_task_detach(&clone); return (ISC_R_NOMEMORY); } event->result = DNS_R_SERVFAIL; event->qtype = fctx->type; event->db = NULL; event->node = NULL; event->rdataset = rdataset; event->sigrdataset = sigrdataset; event->fetch = fetch; event->client = client; event->id = id; + event->qtotal = 0; 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, fetchctx_t **fctxp) + unsigned int options, unsigned int bucketnum, unsigned int depth, + fetchctx_t **fctxp) { fetchctx_t *fctx; isc_result_t result; isc_result_t iresult; isc_interval_t interval; dns_fixedname_t fixed; unsigned int findoptions = 0; char buf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; dns_name_t suffix; 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); dns_name_format(name, buf, sizeof(buf)); dns_rdatatype_format(type, typebuf, sizeof(typebuf)); strcat(buf, "/"); /* checked */ strcat(buf, typebuf); /* checked */ fctx->info = isc_mem_strdup(mctx, buf); if (fctx->info == NULL) { result = ISC_R_NOMEMORY; goto cleanup_fetch; } FCTXTRACE("create"); dns_name_init(&fctx->name, NULL); result = dns_name_dup(name, mctx, &fctx->name); if (result != ISC_R_SUCCESS) goto cleanup_info; dns_name_init(&fctx->domain, NULL); dns_rdataset_init(&fctx->nameservers); fctx->type = type; fctx->options = options; /* * Note! We do not attach to the task. We are relying on the * resolver to ensure that this task doesn't go away while we are * using it. */ fctx->res = res; fctx->references = 0; fctx->bucketnum = bucketnum; fctx->state = fetchstate_init; fctx->want_shutdown = ISC_FALSE; fctx->cloned = ISC_FALSE; + 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->totalqueries = 0; fctx->referrals = 0; TIME_NOW(&fctx->start); fctx->timeouts = 0; fctx->lamecount = 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 = ISC_FALSE; fctx->attributes = 0; fctx->spilled = ISC_FALSE; fctx->nqueries = 0; fctx->reason = NULL; fctx->rand_buf = 0; fctx->rand_bits = 0; fctx->timeout = ISC_FALSE; fctx->addrinfo = NULL; fctx->client = NULL; fctx->ns_ttl = 0; fctx->ns_ttl_ok = ISC_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; /* * 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); fwdname = &suffix; } dns_fixedname_init(&fixed); domain = dns_fixedname_name(&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, fwdname, domain, 0, findoptions, ISC_TRUE, &fctx->nameservers, NULL); if (result != ISC_R_SUCCESS) goto cleanup_name; result = dns_name_dup(domain, mctx, &fctx->domain); if (result != ISC_R_SUCCESS) { dns_rdataset_disassociate(&fctx->nameservers); goto cleanup_name; } fctx->ns_ttl = fctx->nameservers.ttl; fctx->ns_ttl_ok = ISC_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 = ISC_TRUE; } 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_domain; 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); *fctxp = fctx; return (ISC_R_SUCCESS); cleanup_rmessage: dns_message_destroy(&fctx->rmessage); cleanup_qmessage: dns_message_destroy(&fctx->qmessage); cleanup_domain: if (dns_name_countlabels(&fctx->domain) > 0) dns_name_free(&fctx->domain, mctx); 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_fetch: isc_mem_put(mctx, fctx, sizeof(*fctx)); return (result); } /* * Handle Responses */ static inline isc_boolean_t is_lame(fetchctx_t *fctx) { dns_message_t *message = fctx->rmessage; dns_name_t *name; dns_rdataset_t *rdataset; isc_result_t result; if (message->rcode != dns_rcode_noerror && message->rcode != dns_rcode_nxdomain) return (ISC_FALSE); if (message->counts[DNS_SECTION_ANSWER] != 0) return (ISC_FALSE); if (message->counts[DNS_SECTION_AUTHORITY] == 0) return (ISC_FALSE); result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); while (result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { dns_namereln_t namereln; int order; unsigned int labels; if (rdataset->type != dns_rdatatype_ns) continue; namereln = dns_name_fullcompare(name, &fctx->domain, &order, &labels); if (namereln == dns_namereln_equal && (message->flags & DNS_MESSAGEFLAG_AA) != 0) return (ISC_FALSE); if (namereln == dns_namereln_subdomain) return (ISC_FALSE); return (ISC_TRUE); } result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); } return (ISC_FALSE); } static inline void log_lame(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo) { char namebuf[DNS_NAME_FORMATSIZE]; char domainbuf[DNS_NAME_FORMATSIZE]; char addrbuf[ISC_SOCKADDR_FORMATSIZE]; dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); isc_sockaddr_format(&addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS, DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, "lame server resolving '%s' (in '%s'?): %s", namebuf, domainbuf, addrbuf); } static inline 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 inline isc_result_t same_question(fetchctx_t *fctx) { isc_result_t result; dns_message_t *message = fctx->rmessage; dns_name_t *name; dns_rdataset_t *rdataset; /* * Caller must be holding the fctx lock. */ /* * XXXRTH Currently we support only one question. */ if (message->counts[DNS_SECTION_QUESTION] != 1) { 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 class[DNS_RDATACLASS_FORMATSIZE]; char type[DNS_RDATATYPE_FORMATSIZE]; dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdataclass_format(rdataset->rdclass, class, sizeof(class)); dns_rdatatype_format(rdataset->type, type, sizeof(type)); log_formerr(fctx, "question section mismatch: got %s/%s/%s", namebuf, class, type); return (DNS_R_FORMERR); } return (ISC_R_SUCCESS); } static void clone_results(fetchctx_t *fctx) { dns_fetchevent_t *event, *hevent; isc_result_t result; dns_name_t *name, *hname; FCTXTRACE("clone_results"); /* * Set up any other events to have the same data as the first * event. * * Caller must be holding the appropriate lock. */ fctx->cloned = ISC_TRUE; hevent = ISC_LIST_HEAD(fctx->events); if (hevent == NULL) return; hname = dns_fixedname_name(&hevent->foundname); for (event = ISC_LIST_NEXT(hevent, ev_link); event != NULL; event = ISC_LIST_NEXT(event, ev_link)) { name = dns_fixedname_name(&event->foundname); result = dns_name_copy(hname, name, NULL); if (result != ISC_R_SUCCESS) event->result = result; else event->result = hevent->result; dns_db_attach(hevent->db, &event->db); dns_db_attachnode(hevent->db, hevent->node, &event->node); INSIST(hevent->rdataset != NULL); INSIST(event->rdataset != NULL); if (dns_rdataset_isassociated(hevent->rdataset)) dns_rdataset_clone(hevent->rdataset, event->rdataset); INSIST(! (hevent->sigrdataset == NULL && event->sigrdataset != NULL)); if (hevent->sigrdataset != NULL && dns_rdataset_isassociated(hevent->sigrdataset) && event->sigrdataset != NULL) dns_rdataset_clone(hevent->sigrdataset, event->sigrdataset); } } #define CACHE(r) (((r)->attributes & DNS_RDATASETATTR_CACHE) != 0) #define ANSWER(r) (((r)->attributes & DNS_RDATASETATTR_ANSWER) != 0) #define ANSWERSIG(r) (((r)->attributes & DNS_RDATASETATTR_ANSWERSIG) != 0) #define EXTERNAL(r) (((r)->attributes & DNS_RDATASETATTR_EXTERNAL) != 0) #define CHAINING(r) (((r)->attributes & DNS_RDATASETATTR_CHAINING) != 0) #define CHASE(r) (((r)->attributes & DNS_RDATASETATTR_CHASE) != 0) #define CHECKNAMES(r) (((r)->attributes & DNS_RDATASETATTR_CHECKNAMES) != 0) /* * Destroy '*fctx' if it is ready to be destroyed (i.e., if it has * no references and is no longer waiting for any events). * * Requires: * '*fctx' is shutting down. * * Returns: * true if the resolver is exiting and this is the last fctx in the bucket. */ static isc_boolean_t maybe_destroy(fetchctx_t *fctx, isc_boolean_t locked) { unsigned int bucketnum; isc_boolean_t bucket_empty = ISC_FALSE; dns_resolver_t *res = fctx->res; dns_validator_t *validator, *next_validator; isc_boolean_t destroy = ISC_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); destroy = ISC_TRUE; } unlock: if (!locked) UNLOCK(&res->buckets[bucketnum].lock); if (destroy) 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; isc_boolean_t chaining; isc_boolean_t negative; isc_boolean_t sentresponse; isc_result_t eresult = ISC_R_SUCCESS; isc_result_t result = ISC_R_SUCCESS; isc_stdtime_t now; isc_uint32_t ttl; 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"); LOCK(&res->buckets[fctx->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 = ISC_TF(vevent->rdataset == NULL); sentresponse = ISC_TF((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0); /* * If shutting down, ignore the results. Check to see if we're * done waiting for validator completions and ADB pending events; if * so, destroy the fctx. */ if (SHUTTINGDOWN(fctx) && !sentresponse) { isc_uint32_t bucketnum = fctx->bucketnum; isc_boolean_t bucket_empty; bucket_empty = maybe_destroy(fctx, ISC_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 = ISC_TRUE; } else chaining = ISC_FALSE; /* * Either we're not shutting down, or we are shutting down but want * to cache the result anyway (if this was a validation started by * a query with cd set) */ hevent = ISC_LIST_HEAD(fctx->events); if (hevent != NULL) { if (!negative && !chaining && (fctx->type == dns_rdatatype_any || fctx->type == dns_rdatatype_rrsig || fctx->type == dns_rdatatype_sig)) { /* * Don't bind rdatasets; the caller * will iterate the node. */ } else { ardataset = hevent->rdataset; asigrdataset = hevent->sigrdataset; } } if (vevent->result != ISC_R_SUCCESS) { FCTXTRACE("validation failed"); inc_stats(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, ISC_TRUE, &node); if (result == ISC_R_SUCCESS) (void)dns_db_deleterdataset(fctx->cache, node, NULL, vevent->type, 0); if (result == ISC_R_SUCCESS && vevent->sigrdataset != NULL) (void)dns_db_deleterdataset(fctx->cache, node, NULL, dns_rdatatype_rrsig, vevent->type); if (result == ISC_R_SUCCESS) dns_db_detachnode(fctx->cache, &node); } 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, ISC_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[fctx->bucketnum].lock); INSIST(fctx->validator == NULL); fctx->validator = ISC_LIST_HEAD(fctx->validators); if (fctx->validator != NULL) dns_validator_send(fctx->validator); else if (sentresponse) fctx_done(fctx, result, __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_BADCACHE_TTL(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, ISC_TRUE, ISC_TRUE); /* Locks bucket. */ return; } if (negative) { dns_rdatatype_t covers; FCTXTRACE("nonexistence validation OK"); inc_stats(res, dns_resstatscounter_valnegsuccess); if (fctx->rmessage->rcode == dns_rcode_nxdomain) covers = dns_rdatatype_any; else covers = fctx->type; result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE, &node); if (result != ISC_R_SUCCESS) goto noanswer_response; /* * If we are asking for a SOA record set the cache time * to zero to facilitate locating the containing zone of * a 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, ISC_TRUE, &node); if (result != ISC_R_SUCCESS) goto noanswer_response; result = dns_db_addrdataset(fctx->cache, node, NULL, now, vevent->rdataset, 0, ardataset); if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) goto noanswer_response; if (ardataset != NULL && 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, 0, asigrdataset); if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) goto noanswer_response; } if (sentresponse) { isc_boolean_t bucket_empty = ISC_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, ISC_TRUE); UNLOCK(&res->buckets[fctx->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[fctx->bucketnum].lock); dns_validator_send(ISC_LIST_HEAD(fctx->validators)); goto cleanup_event; } answer_response: /* * Cache any NS/NSEC records that happened to be validated. */ result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY); while (result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY, &name); for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if ((rdataset->type != dns_rdatatype_ns && rdataset->type != dns_rdatatype_nsec) || rdataset->trust != dns_trust_secure) continue; for (sigrdataset = ISC_LIST_HEAD(name->list); sigrdataset != NULL; sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { if (sigrdataset->type != dns_rdatatype_rrsig || sigrdataset->covers != rdataset->type) continue; break; } if (sigrdataset == NULL || sigrdataset->trust != dns_trust_secure) continue; result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &nsnode); if (result != ISC_R_SUCCESS) continue; result = dns_db_addrdataset(fctx->cache, nsnode, NULL, now, rdataset, 0, NULL); if (result == ISC_R_SUCCESS) result = dns_db_addrdataset(fctx->cache, nsnode, NULL, now, sigrdataset, 0, NULL); dns_db_detachnode(fctx->cache, &nsnode); 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[fctx->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); dns_fixedname_init(&fzonename); zonename = dns_fixedname_name(&fzonename); dns_fixedname_init(&fclosest); closest = dns_fixedname_name(&fclosest); dns_fixedname_init(&fnearest); nearest = dns_fixedname_name(&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) { isc_boolean_t data = ISC_FALSE, exists = ISC_FALSE; isc_boolean_t optout = ISC_FALSE, unknown = ISC_FALSE; isc_boolean_t setclosest = ISC_FALSE; isc_boolean_t setnearest = ISC_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, *sigrdataset; dns_rdataset_t *addedrdataset, *ardataset, *asigrdataset; dns_rdataset_t *valrdataset = NULL, *valsigrdataset = NULL; dns_dbnode_t *node, **anodep; dns_db_t **adbp; dns_name_t *aname; dns_resolver_t *res; isc_boolean_t need_validation, secure_domain, have_answer; isc_result_t result, eresult; dns_fetchevent_t *event; unsigned int options; isc_task_t *task; isc_boolean_t fail; unsigned int valoptions = 0; /* * The appropriate bucket lock must be held. */ res = fctx->res; need_validation = ISC_FALSE; POST(need_validation); secure_domain = ISC_FALSE; have_answer = ISC_FALSE; eresult = ISC_R_SUCCESS; task = res->buckets[fctx->bucketnum].task; /* * Is DNSSEC validation required for this name? */ if (res->view->enablevalidation) { result = dns_view_issecuredomain(res->view, name, &secure_domain); if (result != ISC_R_SUCCESS) return (result); if (!secure_domain && res->view->dlv != NULL) { valoptions = DNS_VALIDATOR_DLV; secure_domain = ISC_TRUE; } } if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) need_validation = ISC_FALSE; else need_validation = secure_domain; adbp = NULL; aname = NULL; anodep = NULL; ardataset = NULL; asigrdataset = NULL; event = NULL; if ((name->attributes & DNS_NAMEATTR_ANSWER) != 0 && !need_validation) { have_answer = ISC_TRUE; event = ISC_LIST_HEAD(fctx->events); if (event != NULL) { adbp = &event->db; aname = dns_fixedname_name(&event->foundname); result = dns_name_copy(name, aname, NULL); if (result != ISC_R_SUCCESS) return (result); anodep = &event->node; /* * If this is an ANY, SIG or RRSIG query, we're not * going to return any rdatasets, unless we encountered * a CNAME or DNAME as "the answer". In this case, * we're going to return DNS_R_CNAME or DNS_R_DNAME * and we must set up the rdatasets. */ if ((fctx->type != dns_rdatatype_any && fctx->type != dns_rdatatype_rrsig && fctx->type != dns_rdatatype_sig) || (name->attributes & DNS_NAMEATTR_CHAINING) != 0) { ardataset = event->rdataset; asigrdataset = event->sigrdataset; } } } /* * Find or create the cache node. */ node = NULL; result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node); if (result != ISC_R_SUCCESS) return (result); /* * Cache or validate each cacheable rdataset. */ fail = ISC_TF((fctx->res->options & DNS_RESOLVER_CHECKNAMESFAIL) != 0); for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if (!CACHE(rdataset)) continue; if (CHECKNAMES(rdataset)) { char namebuf[DNS_NAME_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; char classbuf[DNS_RDATATYPE_FORMATSIZE]; dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf)); dns_rdataclass_format(rdataset->rdclass, classbuf, sizeof(classbuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, "check-names %s %s/%s/%s", fail ? "failure" : "warning", namebuf, typebuf, classbuf); if (fail) { if (ANSWER(rdataset)) { dns_db_detachnode(fctx->cache, &node); return (DNS_R_BADNAME); } continue; } } /* * Enforce the configure maximum cache TTL. */ if (rdataset->ttl > res->view->maxcachettl) rdataset->ttl = res->view->maxcachettl; /* * 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) { if (!ANSWER(rdataset) && need_validation) { /* * Ignore non-answer rdatasets that * are missing signatures. */ continue; } } /* * Normalize the rdataset and sigrdataset TTLs. */ if (sigrdataset != NULL) { rdataset->ttl = ISC_MIN(rdataset->ttl, sigrdataset->ttl); sigrdataset->ttl = rdataset->ttl; } /* * Cache this rdataset/sigrdataset pair as * pending data. Track whether it was additional * or not. */ 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)) { 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) { tresult = dns_rdataset_addnoqname( rdataset, noqname); RUNTIME_CHECK(tresult == ISC_R_SUCCESS); } } addedrdataset = ardataset; result = dns_db_addrdataset(fctx->cache, node, NULL, now, rdataset, 0, addedrdataset); if (result == DNS_R_UNCHANGED) { result = ISC_R_SUCCESS; if (!need_validation && ardataset != NULL && 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, 0, addedrdataset); if (result == DNS_R_UNCHANGED) result = ISC_R_SUCCESS; if (result != ISC_R_SUCCESS) break; } else if (!ANSWER(rdataset)) continue; } if (ANSWER(rdataset) && need_validation) { if (fctx->type != dns_rdatatype_any && fctx->type != dns_rdatatype_rrsig && fctx->type != dns_rdatatype_sig) { /* * This is The Answer. We will * validate it, but first we cache * the rest of the response - it may * contain useful keys. */ INSIST(valrdataset == NULL && valsigrdataset == NULL); valrdataset = rdataset; valsigrdataset = sigrdataset; } else { /* * This is one of (potentially) * multiple answers to an ANY * or SIG query. To keep things * simple, we just start the * validator right away rather * than caching first and * having to remember which * rdatasets needed validation. */ result = valcreate(fctx, addrinfo, name, rdataset->type, rdataset, sigrdataset, valoptions, task); /* * Defer any further validations. * This prevents multiple validators * from manipulating fctx->rmessage * simultaneously. */ valoptions |= DNS_VALIDATOR_DEFER; } } else if (CHAINING(rdataset)) { if (rdataset->type == dns_rdatatype_cname) eresult = DNS_R_CNAME; else { INSIST(rdataset->type == dns_rdatatype_dname); eresult = DNS_R_DNAME; } } } else if (!EXTERNAL(rdataset)) { /* * It's OK to cache this rdataset now. */ if (ANSWER(rdataset)) addedrdataset = ardataset; else if (ANSWERSIG(rdataset)) addedrdataset = asigrdataset; else addedrdataset = NULL; if (CHAINING(rdataset)) { if (rdataset->type == dns_rdatatype_cname) eresult = DNS_R_CNAME; else { INSIST(rdataset->type == dns_rdatatype_dname); eresult = DNS_R_DNAME; } } if (rdataset->trust == dns_trust_glue && (rdataset->type == dns_rdatatype_ns || (rdataset->type == dns_rdatatype_rrsig && rdataset->covers == dns_rdatatype_ns))) { /* * If the trust level is 'dns_trust_glue' * then we are adding data from a referral * we got while executing the search algorithm. * New referral data always takes precedence * over the existing cache contents. */ options = DNS_DBADD_FORCE; } else options = 0; 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) { tresult = dns_rdataset_addnoqname( rdataset, noqname); RUNTIME_CHECK(tresult == ISC_R_SUCCESS); } } /* * 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) result = valcreate(fctx, addrinfo, name, fctx->type, valrdataset, valsigrdataset, valoptions, task); if (result == ISC_R_SUCCESS && have_answer) { fctx->attributes |= FCTX_ATTR_HAVEANSWER; if (event != NULL) { /* * Negative results must be indicated in event->result. */ if (dns_rdataset_isassociated(event->rdataset) && NEGATIVE(event->rdataset)) { INSIST(eresult == DNS_R_NCACHENXDOMAIN || eresult == DNS_R_NCACHENXRRSET); } event->result = eresult; dns_db_attach(fctx->cache, adbp); dns_db_transfernode(fctx->cache, &node, anodep); clone_results(fctx); } } if (node != NULL) dns_db_detachnode(fctx->cache, &node); return (result); } static inline isc_result_t cache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now) { isc_result_t result; dns_section_t section; dns_name_t *name; FCTXTRACE("cache_message"); fctx->attributes &= ~FCTX_ATTR_WANTCACHE; LOCK(&fctx->res->buckets[fctx->bucketnum].lock); for (section = DNS_SECTION_ANSWER; section <= DNS_SECTION_ADDITIONAL; section++) { result = dns_message_firstname(fctx->rmessage, section); while (result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(fctx->rmessage, section, &name); if ((name->attributes & DNS_NAMEATTR_CACHE) != 0) { result = cache_name(fctx, name, addrinfo, now); if (result != ISC_R_SUCCESS) break; } result = dns_message_nextname(fctx->rmessage, section); } if (result != ISC_R_NOMORE) break; } if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); return (result); } /* * Do what dns_ncache_addoptout() does, and then compute an appropriate eresult. */ static isc_result_t ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl, isc_boolean_t optout, isc_boolean_t 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; isc_boolean_t need_validation, secure_domain; dns_name_t *aname; dns_fetchevent_t *event; isc_uint32_t ttl; unsigned int valoptions = 0; FCTXTRACE("ncache_message"); fctx->attributes &= ~FCTX_ATTR_WANTNCACHE; res = fctx->res; need_validation = ISC_FALSE; POST(need_validation); secure_domain = ISC_FALSE; eresult = ISC_R_SUCCESS; name = &fctx->name; node = NULL; /* * XXXMPA remove when we follow cnames and adjust the setting * of FCTX_ATTR_WANTNCACHE in noanswer_response(). */ INSIST(fctx->rmessage->counts[DNS_SECTION_ANSWER] == 0); /* * Is DNSSEC validation required for this name? */ if (fctx->res->view->enablevalidation) { result = dns_view_issecuredomain(res->view, name, &secure_domain); if (result != ISC_R_SUCCESS) return (result); if (!secure_domain && res->view->dlv != NULL) { valoptions = DNS_VALIDATOR_DLV; secure_domain = ISC_TRUE; } } if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) need_validation = ISC_FALSE; else need_validation = secure_domain; if (secure_domain) { /* * Mark all rdatasets as pending. */ dns_rdataset_t *trdataset; dns_name_t *tname; result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY); while (result == ISC_R_SUCCESS) { tname = NULL; dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY, &tname); for (trdataset = ISC_LIST_HEAD(tname->list); trdataset != NULL; trdataset = ISC_LIST_NEXT(trdataset, link)) trdataset->trust = dns_trust_pending_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, ISC_TRUE, &node); if (result != ISC_R_SUCCESS) goto unlock; /* * If we are asking for a SOA record set the cache time * to zero to facilitate locating the containing zone of * a 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, ISC_FALSE, ISC_FALSE, ardataset, &eresult); if (result != ISC_R_SUCCESS) goto unlock; if (!HAVE_ANSWER(fctx)) { fctx->attributes |= FCTX_ATTR_HAVEANSWER; if (event != NULL) { event->result = eresult; dns_db_attach(fctx->cache, adbp); dns_db_transfernode(fctx->cache, &node, anodep); clone_results(fctx); } } unlock: UNLOCK(&res->buckets[fctx->bucketnum].lock); if (node != NULL) dns_db_detachnode(fctx->cache, &node); return (result); } static inline void mark_related(dns_name_t *name, dns_rdataset_t *rdataset, isc_boolean_t external, isc_boolean_t gluing) { name->attributes |= DNS_NAMEATTR_CACHE; if (gluing) { rdataset->trust = dns_trust_glue; /* * Glue with 0 TTL causes problems. We force the TTL to * 1 second to prevent this. */ if (rdataset->ttl == 0) rdataset->ttl = 1; } else rdataset->trust = dns_trust_additional; /* * Avoid infinite loops by only marking new rdatasets. */ if (!CACHE(rdataset)) { name->attributes |= DNS_NAMEATTR_CHASE; rdataset->attributes |= DNS_RDATASETATTR_CHASE; } rdataset->attributes |= DNS_RDATASETATTR_CACHE; if (external) rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL; } static isc_result_t check_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; dns_rdataset_t *rdataset; isc_boolean_t external; dns_rdatatype_t rtype; isc_boolean_t 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 if (GLUING(fctx)) gluing = ISC_TRUE; else gluing = ISC_FALSE; name = NULL; rdataset = NULL; result = dns_message_findname(fctx->rmessage, section, addname, dns_rdatatype_any, 0, &name, NULL); if (result == ISC_R_SUCCESS) { external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); if (type == dns_rdatatype_a) { for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if (rdataset->type == dns_rdatatype_rrsig) rtype = rdataset->covers; else rtype = rdataset->type; if (rtype == dns_rdatatype_a || rtype == dns_rdatatype_aaaa) mark_related(name, rdataset, external, gluing); } } else { result = dns_message_findtype(name, type, 0, &rdataset); if (result == ISC_R_SUCCESS) { mark_related(name, rdataset, external, gluing); /* * Do we have its SIG too? */ rdataset = NULL; result = dns_message_findtype(name, dns_rdatatype_rrsig, type, &rdataset); if (result == ISC_R_SUCCESS) mark_related(name, rdataset, external, gluing); } } } return (ISC_R_SUCCESS); } static 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) { isc_boolean_t rescan; dns_section_t section = DNS_SECTION_ADDITIONAL; isc_result_t result; again: rescan = ISC_FALSE; for (result = dns_message_firstname(fctx->rmessage, section); result == ISC_R_SUCCESS; result = dns_message_nextname(fctx->rmessage, section)) { dns_name_t *name = NULL; dns_rdataset_t *rdataset; dns_message_currentname(fctx->rmessage, DNS_SECTION_ADDITIONAL, &name); if ((name->attributes & DNS_NAMEATTR_CHASE) == 0) continue; name->attributes &= ~DNS_NAMEATTR_CHASE; for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if (CHASE(rdataset)) { rdataset->attributes &= ~DNS_RDATASETATTR_CHASE; (void)dns_rdataset_additionaldata(rdataset, check_related, fctx); rescan = ISC_TRUE; } } } if (rescan) goto again; } static inline isc_result_t cname_target(dns_rdataset_t *rdataset, dns_name_t *tname) { isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_cname_t cname; result = dns_rdataset_first(rdataset); if (result != ISC_R_SUCCESS) return (result); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &cname, NULL); if (result != ISC_R_SUCCESS) return (result); dns_name_init(tname, NULL); dns_name_clone(&cname.cname, tname); dns_rdata_freestruct(&cname); return (ISC_R_SUCCESS); } static inline isc_result_t dname_target(fetchctx_t *fctx, dns_rdataset_t *rdataset, dns_name_t *qname, dns_name_t *oname, dns_fixedname_t *fixeddname) { isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; unsigned int nlabels; int order; dns_namereln_t namereln; dns_rdata_dname_t dname; dns_fixedname_t prefix; /* * Get the target name of the DNAME. */ result = dns_rdataset_first(rdataset); if (result != ISC_R_SUCCESS) return (result); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &dname, NULL); if (result != ISC_R_SUCCESS) return (result); /* * Get the prefix of qname. */ namereln = dns_name_fullcompare(qname, oname, &order, &nlabels); if (namereln != dns_namereln_subdomain) { char qbuf[DNS_NAME_FORMATSIZE]; char obuf[DNS_NAME_FORMATSIZE]; dns_rdata_freestruct(&dname); dns_name_format(qname, qbuf, sizeof(qbuf)); dns_name_format(oname, obuf, sizeof(obuf)); log_formerr(fctx, "unrelated DNAME in answer: " "%s is not in %s", qbuf, obuf); return (DNS_R_FORMERR); } dns_fixedname_init(&prefix); dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL); dns_fixedname_init(fixeddname); result = dns_name_concatenate(dns_fixedname_name(&prefix), &dname.dname, dns_fixedname_name(fixeddname), NULL); dns_rdata_freestruct(&dname); return (result); } static isc_boolean_t 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 (ISC_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 (ISC_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 (ISC_FALSE); } } return (ISC_TRUE); } static isc_boolean_t is_answertarget_allowed(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, dns_name_t *tname, dns_name_t *domain) { 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]; /* By default, we allow any target name. */ if (view->denyanswernames == NULL) return (ISC_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, name, NULL, &node, NULL, 0, NULL, NULL); if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) return (ISC_TRUE); } /* * If the target name is a subdomain of the search domain, allow it. */ if (dns_name_issubdomain(tname, domain)) return (ISC_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(name, qnamebuf, sizeof(qnamebuf)); dns_name_format(tname, tnamebuf, sizeof(tnamebuf)); dns_rdatatype_format(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 (ISC_FALSE); } return (ISC_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; isc_boolean_t 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 = ISC_TRUE; else aa = ISC_FALSE; qname = &fctx->name; } else { /* * We're being invoked by answer_response() after it has * followed a CNAME/DNAME chain. */ qname = oqname; aa = ISC_FALSE; /* * If the current qname is not a subdomain of the query * domain, there's no point in looking at the authority * section without doing DNSSEC validation. * * Until we do that validation, we'll just return success * in this case. */ if (!dns_name_issubdomain(qname, &fctx->domain)) return (ISC_R_SUCCESS); } /* * We have to figure out if this is a negative response, or a * referral. */ /* * Sometimes we can tell if its a negative response by looking at * the message header. */ negative_response = ISC_FALSE; if (message->rcode == dns_rcode_nxdomain || (message->counts[DNS_SECTION_ANSWER] == 0 && message->counts[DNS_SECTION_AUTHORITY] == 0)) negative_response = ISC_TRUE; /* * Process the authority section. */ ns_name = NULL; ns_rdataset = NULL; soa_name = NULL; ds_name = NULL; 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(fctx->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, qbuf, nbuf); return (DNS_R_FORMERR); } if (type == dns_rdatatype_ns) { /* * NS or RRSIG NS. * * Only one set of NS RRs is allowed. */ if (rdataset->type == dns_rdatatype_ns) { if (ns_name != NULL && name != ns_name) { 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; } } } 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 = ISC_TRUE; result = dns_message_firstname(message, section); while (result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(message, section, &name); if (dns_name_issubdomain(name, &fctx->domain)) { for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { type = rdataset->type; if (type == dns_rdatatype_rrsig) type = rdataset->covers; if (type == dns_rdatatype_nsec || type == dns_rdatatype_nsec3) { /* * NSEC or RRSIG NSEC. */ if (negative_response) { name->attributes |= DNS_NAMEATTR_NCACHE; rdataset->attributes |= DNS_RDATASETATTR_NCACHE; } else if (type == dns_rdatatype_nsec) { name->attributes |= DNS_NAMEATTR_CACHE; rdataset->attributes |= DNS_RDATASETATTR_CACHE; } if (aa) rdataset->trust = dns_trust_authauthority; else 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); 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); fctx->attributes |= FCTX_ATTR_WANTCACHE; fctx->ns_ttl_ok = ISC_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 isc_result_t answer_response(fetchctx_t *fctx) { isc_result_t result; dns_message_t *message; dns_name_t *name, *qname, tname, *ns_name; dns_rdataset_t *rdataset, *ns_rdataset; isc_boolean_t done, external, chaining, aa, found, want_chaining; isc_boolean_t have_answer, found_cname, found_type, wanted_chaining; unsigned int aflag; dns_rdatatype_t type; dns_fixedname_t dname, fqname; dns_view_t *view; FCTXTRACE("answer_response"); message = fctx->rmessage; /* * Examine the answer section, marking those rdatasets which are * part of the answer and should be cached. */ done = ISC_FALSE; found_cname = ISC_FALSE; found_type = ISC_FALSE; chaining = ISC_FALSE; have_answer = ISC_FALSE; want_chaining = ISC_FALSE; POST(want_chaining); if ((message->flags & DNS_MESSAGEFLAG_AA) != 0) aa = ISC_TRUE; else aa = ISC_FALSE; qname = &fctx->name; type = fctx->type; view = fctx->res->view; result = dns_message_firstname(message, DNS_SECTION_ANSWER); while (!done && result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(message, DNS_SECTION_ANSWER, &name); external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); if (dns_name_equal(name, qname)) { wanted_chaining = ISC_FALSE; for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { found = ISC_FALSE; want_chaining = ISC_FALSE; aflag = 0; if (rdataset->type == dns_rdatatype_nsec3) { /* * NSEC3 records are not allowed to * appear in the answer section. */ log_formerr(fctx, "NSEC3 in answer"); return (DNS_R_FORMERR); } /* * Apply filters, if given, on answers to reject * a malicious attempt of rebinding. */ if ((rdataset->type == dns_rdatatype_a || rdataset->type == dns_rdatatype_aaaa) && !is_answeraddress_allowed(view, name, rdataset)) { return (DNS_R_SERVFAIL); } if (rdataset->type == type && !found_cname) { /* * We've found an ordinary answer. */ found = ISC_TRUE; found_type = ISC_TRUE; done = ISC_TRUE; aflag = DNS_RDATASETATTR_ANSWER; } else if (type == dns_rdatatype_any) { /* * We've found an answer matching * an ANY query. There may be * more. */ found = ISC_TRUE; aflag = DNS_RDATASETATTR_ANSWER; } else if (rdataset->type == dns_rdatatype_rrsig && rdataset->covers == type && !found_cname) { /* * We've found a signature that * covers the type we're looking for. */ found = ISC_TRUE; found_type = ISC_TRUE; aflag = DNS_RDATASETATTR_ANSWERSIG; } else if (rdataset->type == dns_rdatatype_cname && !found_type) { /* * We're looking for something else, * but we found a CNAME. * * Getting a CNAME response for some * query types is an error, see * RFC 4035, Section 2.5. */ if (type == dns_rdatatype_rrsig || type == dns_rdatatype_key || type == dns_rdatatype_nsec) { char buf[DNS_RDATATYPE_FORMATSIZE]; dns_rdatatype_format(fctx->type, buf, sizeof(buf)); log_formerr(fctx, "CNAME response " "for %s RR", buf); return (DNS_R_FORMERR); } found = ISC_TRUE; found_cname = ISC_TRUE; want_chaining = ISC_TRUE; aflag = DNS_RDATASETATTR_ANSWER; result = cname_target(rdataset, &tname); if (result != ISC_R_SUCCESS) return (result); /* Apply filters on the target name. */ if (!is_answertarget_allowed(view, name, rdataset->type, &tname, &fctx->domain)) { return (DNS_R_SERVFAIL); } } else if (rdataset->type == dns_rdatatype_rrsig && rdataset->covers == dns_rdatatype_cname && !found_type) { /* * We're looking for something else, * but we found a SIG CNAME. */ found = ISC_TRUE; found_cname = ISC_TRUE; aflag = DNS_RDATASETATTR_ANSWERSIG; } if (found) { /* * We've found an answer to our * question. */ name->attributes |= DNS_NAMEATTR_CACHE; rdataset->attributes |= DNS_RDATASETATTR_CACHE; rdataset->trust = dns_trust_answer; if (!chaining) { /* * This data is "the" answer * to our question only if * we're not chaining (i.e. * if we haven't followed * a CNAME or DNAME). */ INSIST(!external); if (aflag == DNS_RDATASETATTR_ANSWER) have_answer = ISC_TRUE; name->attributes |= DNS_NAMEATTR_ANSWER; rdataset->attributes |= aflag; if (aa) rdataset->trust = dns_trust_authanswer; } else if (external) { /* * This data is outside of * our query domain, and * may not be cached. */ rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL; } /* * Mark any additional data related * to this rdataset. */ (void)dns_rdataset_additionaldata( rdataset, check_related, fctx); /* * CNAME chaining. */ if (want_chaining) { wanted_chaining = ISC_TRUE; name->attributes |= DNS_NAMEATTR_CHAINING; rdataset->attributes |= DNS_RDATASETATTR_CHAINING; qname = &tname; } } /* * We could add an "else" clause here and * log that we're ignoring this rdataset. */ } /* * If wanted_chaining is true, we've done * some chaining as the result of processing * this node, and thus we need to set * chaining to true. * * We don't set chaining inside of the * rdataset loop because doing that would * cause us to ignore the signatures of * CNAMEs. */ if (wanted_chaining) chaining = ISC_TRUE; } else { /* * Look for a DNAME (or its SIG). Anything else is * ignored. */ wanted_chaining = ISC_FALSE; for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { isc_boolean_t found_dname = ISC_FALSE; dns_name_t *dname_name; found = ISC_FALSE; aflag = 0; if (rdataset->type == dns_rdatatype_dname) { /* * We're looking for something else, * but we found a DNAME. * * If we're not chaining, then the * DNAME should not be external. */ if (!chaining && external) { log_formerr(fctx, "external DNAME"); return (DNS_R_FORMERR); } found = ISC_TRUE; want_chaining = ISC_TRUE; POST(want_chaining); aflag = DNS_RDATASETATTR_ANSWER; result = dname_target(fctx, rdataset, qname, name, &dname); if (result == ISC_R_NOSPACE) { /* * We can't construct the * DNAME target. Do not * try to continue. */ want_chaining = ISC_FALSE; POST(want_chaining); } else if (result != ISC_R_SUCCESS) return (result); else found_dname = ISC_TRUE; dname_name = dns_fixedname_name(&dname); if (!is_answertarget_allowed(view, qname, rdataset->type, dname_name, &fctx->domain)) { return (DNS_R_SERVFAIL); } } else if (rdataset->type == dns_rdatatype_rrsig && rdataset->covers == dns_rdatatype_dname) { /* * We've found a signature that * covers the DNAME. */ found = ISC_TRUE; aflag = DNS_RDATASETATTR_ANSWERSIG; } if (found) { /* * We've found an answer to our * question. */ name->attributes |= DNS_NAMEATTR_CACHE; rdataset->attributes |= DNS_RDATASETATTR_CACHE; rdataset->trust = dns_trust_answer; if (!chaining) { /* * This data is "the" answer * to our question only if * we're not chaining. */ INSIST(!external); if (aflag == DNS_RDATASETATTR_ANSWER) have_answer = ISC_TRUE; name->attributes |= DNS_NAMEATTR_ANSWER; rdataset->attributes |= aflag; if (aa) rdataset->trust = dns_trust_authanswer; } else if (external) { rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL; } /* * DNAME chaining. */ if (found_dname) { /* * Copy the dname into the * qname fixed name. * * Although we check for * failure of the copy * operation, in practice it * should never fail since * we already know that the * result fits in a fixedname. */ dns_fixedname_init(&fqname); result = dns_name_copy( dns_fixedname_name(&dname), dns_fixedname_name(&fqname), NULL); if (result != ISC_R_SUCCESS) return (result); wanted_chaining = ISC_TRUE; name->attributes |= DNS_NAMEATTR_CHAINING; rdataset->attributes |= DNS_RDATASETATTR_CHAINING; qname = dns_fixedname_name( &fqname); } } } if (wanted_chaining) chaining = ISC_TRUE; } result = dns_message_nextname(message, DNS_SECTION_ANSWER); } if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; if (result != ISC_R_SUCCESS) return (result); /* * We should have found an answer. */ if (!have_answer) { 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) { /* * Yes. This may be a negative reply, so hand off * authority section processing to the noanswer code. * If it isn't a noanswer response, no harm will be * done. */ return (noanswer_response(fctx, qname, 0)); } /* * 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. */ done = ISC_FALSE; ns_name = NULL; ns_rdataset = NULL; result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); while (!done && result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); if (!external) { /* * We expect to find NS or SIG NS rdatasets, and * nothing else. */ for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if (rdataset->type == dns_rdatatype_ns || (rdataset->type == dns_rdatatype_rrsig && rdataset->covers == dns_rdatatype_ns)) { name->attributes |= DNS_NAMEATTR_CACHE; rdataset->attributes |= DNS_RDATASETATTR_CACHE; if (aa && !chaining) rdataset->trust = dns_trust_authauthority; else rdataset->trust = dns_trust_additional; 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 = ISC_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 isc_boolean_t fctx_decreference(fetchctx_t *fctx) { isc_boolean_t bucket_empty = ISC_FALSE; 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; isc_boolean_t bucket_empty; isc_boolean_t locked = ISC_FALSE; unsigned int bucketnum; dns_rdataset_t nameservers; dns_fixedname_t fixed; dns_name_t *domain; REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); fevent = (dns_fetchevent_t *)event; fctx = event->ev_arg; REQUIRE(VALID_FCTX(fctx)); res = fctx->res; UNUSED(task); FCTXTRACE("resume_dslookup"); if (fevent->node != NULL) dns_db_detachnode(fevent->db, &fevent->node); if (fevent->db != NULL) dns_db_detach(&fevent->db); dns_rdataset_init(&nameservers); bucketnum = fctx->bucketnum; if (fevent->result == ISC_R_CANCELED) { dns_resolver_destroyfetch(&fctx->nsfetch); fctx_done(fctx, ISC_R_CANCELED, __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 = ISC_TRUE; log_ns_ttl(fctx, "resume_dslookup"); 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; } /* * Try again. */ fctx_try(fctx, ISC_TRUE, ISC_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)) { 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); FCTXTRACE("continuing to look for parent's NS records"); result = dns_resolver_createfetch(fctx->res, &fctx->nsname, dns_rdatatype_ns, domain, nsrdataset, NULL, 0, task, resume_dslookup, fctx, &fctx->nsrrset, NULL, &fctx->nsfetch); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); else { LOCK(&res->buckets[bucketnum].lock); locked = ISC_TRUE; fctx->references++; } } cleanup: if (dns_rdataset_isassociated(&nameservers)) dns_rdataset_disassociate(&nameservers); if (dns_rdataset_isassociated(fevent->rdataset)) dns_rdataset_disassociate(fevent->rdataset); INSIST(fevent->sigrdataset == NULL); isc_event_free(&event); if (!locked) LOCK(&res->buckets[bucketnum].lock); 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, ISC_FALSE) || !dns_rdata_checknames(&rdata, name, NULL)) { rdataset->attributes |= DNS_RDATASETATTR_CHECKNAMES; } dns_rdata_reset(&rdata); } } } } static void checknames(dns_message_t *message) { checknamessection(message, DNS_SECTION_ANSWER); checknamessection(message, DNS_SECTION_AUTHORITY); checknamessection(message, DNS_SECTION_ADDITIONAL); } /* * Log server NSID at log level 'level' */ static 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]; isc_uint16_t buflen, i; unsigned char *p, *buf, *nsid; /* Allocate buffer for storing hex version of the NSID */ buflen = (isc_uint16_t)nsid_len * 2 + 1; buf = isc_mem_get(mctx, buflen); if (buf == NULL) return; /* Convert to hex */ p = buf; nsid = isc_buffer_current(opt); for (i = 0; i < nsid_len; i++) { *p++ = hex[(nsid[0] >> 4) & 0xf]; *p++ = hex[nsid[0] & 0xf]; nsid++; } *p = '\0'; isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, level, "received NSID '%s' from %s", buf, addrbuf); /* Clean up */ isc_mem_put(mctx, buf, buflen); return; } static void log_packet(dns_message_t *message, int level, isc_mem_t *mctx) { isc_buffer_t buffer; char *buf = NULL; int len = 1024; isc_result_t result; if (! isc_log_wouldlog(dns_lctx, level)) return; /* * Note that these are multiline debug messages. We want a newline * to appear in the log after each message. */ do { buf = isc_mem_get(mctx, len); if (buf == NULL) break; isc_buffer_init(&buffer, buf, len); result = dns_message_totext(message, &dns_master_style_debug, 0, &buffer); if (result == ISC_R_NOSPACE) { isc_mem_put(mctx, buf, len); len += 1024; } else if (result == ISC_R_SUCCESS) isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, level, "received packet:\n%.*s", (int)isc_buffer_usedlength(&buffer), buf); } while (result == ISC_R_NOSPACE); if (buf != NULL) isc_mem_put(mctx, buf, len); } static isc_boolean_t 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 ? ISC_TRUE : ISC_FALSE); } static isc_boolean_t 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 (ISC_TRUE); } return (ISC_FALSE); } static void process_opt(resquery_t *query, dns_rdataset_t *opt) { dns_rdata_t rdata; isc_buffer_t optbuf; isc_result_t result; isc_uint16_t optcode; isc_uint16_t optlen; 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 (query->options & DNS_FETCHOPT_WANTNSID) log_nsid(&optbuf, optlen, query, ISC_LOG_INFO, query->fctx->res->mctx); isc_buffer_forward(&optbuf, optlen); 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; isc_boolean_t keep_trying, get_nameservers, resend; isc_boolean_t truncated; dns_message_t *message; dns_rdataset_t *opt; fetchctx_t *fctx; dns_name_t *fname; dns_fixedname_t foundname; isc_stdtime_t now; isc_time_t tnow, *finish; dns_adbaddrinfo_t *addrinfo; unsigned int options; unsigned int findoptions; isc_result_t broken_server; badnstype_t broken_type = badns_response; isc_boolean_t no_response; REQUIRE(VALID_QUERY(query)); fctx = query->fctx; options = query->options; REQUIRE(VALID_FCTX(fctx)); REQUIRE(event->ev_type == DNS_EVENT_DISPATCH); QTRACE("response"); if (isc_sockaddr_pf(&query->addrinfo->sockaddr) == PF_INET) inc_stats(fctx->res, dns_resstatscounter_responsev4); else inc_stats(fctx->res, dns_resstatscounter_responsev6); (void)isc_timer_touch(fctx->timer); keep_trying = ISC_FALSE; broken_server = ISC_R_SUCCESS; get_nameservers = ISC_FALSE; resend = ISC_FALSE; truncated = ISC_FALSE; finish = NULL; no_response = ISC_FALSE; if (fctx->res->exiting) { result = ISC_R_SHUTTINGDOWN; goto done; } fctx->timeouts = 0; fctx->timeout = ISC_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 = ISC_TRUE; add_bad_edns(fctx, &query->addrinfo->sockaddr); } else { /* * There's no hope for this query. */ keep_trying = ISC_TRUE; /* * If this is a network error on an exclusive query * socket, mark the server as bad so that we won't try * it for this fetch again. 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 = ISC_TRUE; } } goto done; } message = fctx->rmessage; if (query->tsig != NULL) { result = dns_message_setquerytsig(message, query->tsig); if (result != ISC_R_SUCCESS) goto done; } if (query->tsigkey) { result = dns_message_settsigkey(message, query->tsigkey); if (result != ISC_R_SUCCESS) goto done; } result = dns_message_parse(message, &devent->buffer, 0); if (result != ISC_R_SUCCESS) { switch (result) { case ISC_R_UNEXPECTEDEND: if (!message->question_ok || (message->flags & DNS_MESSAGEFLAG_TC) == 0 || (options & DNS_FETCHOPT_TCP) != 0) { /* * Either the message ended prematurely, * and/or wasn't marked as being truncated, * and/or this is a response to a query we * sent over TCP. In all of these cases, * something is wrong with the remote * server and we don't want to retry using * TCP. */ if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) { /* * The problem might be that they * don't understand EDNS0. Turn it * off and try again. */ options |= DNS_FETCHOPT_NOEDNS0; resend = ISC_TRUE; add_bad_edns(fctx, &query->addrinfo->sockaddr); inc_stats(fctx->res, dns_resstatscounter_edns0fail); } else { broken_server = result; keep_trying = ISC_TRUE; } goto done; } /* * We defer retrying via TCP for a bit so we can * check out this message further. */ truncated = ISC_TRUE; break; case DNS_R_FORMERR: if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) { /* * The problem might be that they * don't understand EDNS0. Turn it * off and try again. */ options |= DNS_FETCHOPT_NOEDNS0; resend = ISC_TRUE; add_bad_edns(fctx, &query->addrinfo->sockaddr); inc_stats(fctx->res, dns_resstatscounter_edns0fail); } else { broken_server = DNS_R_UNEXPECTEDRCODE; keep_trying = ISC_TRUE; } goto done; default: /* * Something bad has happened. */ goto done; } } /* * Log the incoming packet. */ log_packet(message, ISC_LOG_DEBUG(10), fctx->res->mctx); /* * Process receive opt record. */ opt = dns_message_getopt(message); if (opt != NULL) process_opt(query, opt); /* * If the message is signed, check the signature. If not, this * returns success anyway. */ result = dns_message_checksig(message, fctx->res->view); if (result != ISC_R_SUCCESS) goto done; /* * The dispatcher should ensure we only get responses with QR set. */ INSIST((message->flags & DNS_MESSAGEFLAG_QR) != 0); /* * INSIST() that the message comes from the place we sent it to, * since the dispatch code should ensure this. * * INSIST() that the message id is correct (this should also be * ensured by the dispatch code). */ /* * 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 && (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)) { char addrbuf[ISC_SOCKADDR_FORMATSIZE]; isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); dns_adb_changeflags(fctx->adb, query->addrinfo, DNS_FETCHOPT_NOEDNS0, DNS_FETCHOPT_NOEDNS0); } /* * Deal with truncated responses by retrying using TCP. */ if ((message->flags & DNS_MESSAGEFLAG_TC) != 0) truncated = ISC_TRUE; if (truncated) { inc_stats(fctx->res, dns_resstatscounter_truncated); if ((options & DNS_FETCHOPT_TCP) != 0) { broken_server = DNS_R_TRUNCATEDTCP; keep_trying = ISC_TRUE; } else { options |= DNS_FETCHOPT_TCP; resend = ISC_TRUE; } goto done; } /* * Is it a query response? */ if (message->opcode != dns_opcode_query) { /* XXXRTH Log */ broken_server = DNS_R_UNEXPECTEDOPCODE; keep_trying = ISC_TRUE; goto done; } /* * Update statistics about erroneous responses. */ if (message->rcode != dns_rcode_noerror) { switch (message->rcode) { case dns_rcode_nxdomain: inc_stats(fctx->res, dns_resstatscounter_nxdomain); break; case dns_rcode_servfail: inc_stats(fctx->res, dns_resstatscounter_servfail); break; case dns_rcode_formerr: inc_stats(fctx->res, dns_resstatscounter_formerr); break; default: inc_stats(fctx->res, dns_resstatscounter_othererror); break; } } /* * Is the remote server broken, or does it dislike us? */ if (message->rcode != dns_rcode_noerror && message->rcode != dns_rcode_nxdomain) { if (((message->rcode == dns_rcode_formerr || message->rcode == dns_rcode_notimp) || (message->rcode == dns_rcode_servfail && dns_message_getopt(message) == NULL)) && (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { /* * It's very likely they don't like EDNS0. * If the response code is SERVFAIL, also check if the * response contains an OPT RR and don't cache the * failure since it can be returned for various other * reasons. * * XXXRTH We should check if the question * we're asking requires EDNS0, and * if so, we should bail out. */ options |= DNS_FETCHOPT_NOEDNS0; resend = ISC_TRUE; /* * Remember that they may not like EDNS0. */ add_bad_edns(fctx, &query->addrinfo->sockaddr); inc_stats(fctx->res, dns_resstatscounter_edns0fail); } else if (message->rcode == dns_rcode_formerr) { if (ISFORWARDER(query->addrinfo)) { /* * This forwarder doesn't understand us, * but other forwarders might. Keep trying. */ broken_server = DNS_R_REMOTEFORMERR; keep_trying = ISC_TRUE; } else { /* * The server doesn't understand us. Since * all servers for a zone need similar * capabilities, we assume that we will get * FORMERR from all servers, and thus we * cannot make any more progress with this * fetch. */ log_formerr(fctx, "server sent FORMERR"); result = DNS_R_FORMERR; } } else if (message->rcode == dns_rcode_yxdomain) { /* * DNAME mapping failed because the new name * was too long. There's no chance of success * for this fetch. */ result = DNS_R_YXDOMAIN; } else if (message->rcode == dns_rcode_badvers) { unsigned int flags, mask; unsigned int version; resend = ISC_TRUE; INSIST(opt != NULL); version = (opt->ttl >> 16) & 0xff; flags = (version << DNS_FETCHOPT_EDNSVERSIONSHIFT) | DNS_FETCHOPT_EDNSVERSIONSET; mask = DNS_FETCHOPT_EDNSVERSIONMASK | DNS_FETCHOPT_EDNSVERSIONSET; switch (version) { case 0: dns_adb_changeflags(fctx->adb, query->addrinfo, flags, mask); break; default: broken_server = DNS_R_BADVERS; keep_trying = ISC_TRUE; break; } } else { /* * XXXRTH log. */ broken_server = DNS_R_UNEXPECTEDRCODE; INSIST(broken_server != ISC_R_SUCCESS); keep_trying = ISC_TRUE; } goto done; } /* * Is the question the same as the one we asked? */ result = same_question(fctx); if (result != ISC_R_SUCCESS) { /* XXXRTH Log */ if (result == DNS_R_FORMERR) keep_trying = ISC_TRUE; goto done; } /* * Is the server lame? */ if (fctx->res->lame_ttl != 0 && !ISFORWARDER(query->addrinfo) && is_lame(fctx)) { inc_stats(fctx->res, dns_resstatscounter_lame); log_lame(fctx, query->addrinfo); result = dns_adb_marklame(fctx->adb, query->addrinfo, &fctx->name, fctx->type, now + fctx->res->lame_ttl); if (result != ISC_R_SUCCESS) isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR, "could not mark server as lame: %s", isc_result_totext(result)); broken_server = DNS_R_LAME; keep_trying = ISC_TRUE; goto done; } /* * Enforce delegations only zones like NET and COM. */ if (!ISFORWARDER(query->addrinfo) && dns_view_isdelegationonly(fctx->res->view, &fctx->domain) && !dns_name_equal(&fctx->domain, &fctx->name) && fix_mustbedelegationornxdomain(message, fctx)) { char namebuf[DNS_NAME_FORMATSIZE]; char domainbuf[DNS_NAME_FORMATSIZE]; char addrbuf[ISC_SOCKADDR_FORMATSIZE]; char classbuf[64]; char typebuf[64]; dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf)); dns_rdataclass_format(fctx->res->rdclass, classbuf, sizeof(classbuf)); isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DELEGATION_ONLY, DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, "enforced delegation-only for '%s' (%s/%s/%s) " "from %s", domainbuf, namebuf, typebuf, classbuf, addrbuf); } if ((fctx->res->options & DNS_RESOLVER_CHECKNAMES) != 0) checknames(message); /* * Clear cache bits. */ fctx->attributes &= ~(FCTX_ATTR_WANTNCACHE | FCTX_ATTR_WANTCACHE); /* * Did we get any answers? */ if (message->counts[DNS_SECTION_ANSWER] > 0 && (message->rcode == dns_rcode_noerror || message->rcode == dns_rcode_nxdomain)) { /* * [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); 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); } else if (fctx->type != dns_rdatatype_ns && !betterreferral(fctx)) { /* * Lame response !!!. */ result = answer_response(fctx); } 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); } 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 != 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 = ISC_TRUE; goto done; } goto force_referral; } if (result != ISC_R_SUCCESS) { if (result == DNS_R_FORMERR) keep_trying = ISC_TRUE; goto done; } } else if (message->counts[DNS_SECTION_AUTHORITY] > 0 || message->rcode == dns_rcode_noerror || message->rcode == dns_rcode_nxdomain) { /* * NXDOMAIN, NXRDATASET, or referral. */ result = noanswer_response(fctx, NULL, 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 = ISC_TRUE; keep_trying = ISC_TRUE; /* * We have a new set of name servers, and it * has not experienced any restarts yet. */ fctx->restarts = 0; /* * Update local statistics counters collected for each * new zone. */ fctx->referrals++; fctx->querysent = 0; fctx->lamecount = 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 = ISC_TRUE; goto done; } } else { /* * The server is insane. */ /* XXXRTH Log */ broken_server = DNS_R_UNEXPECTEDRCODE; keep_trying = ISC_TRUE; goto done; } /* * Follow additional section data chains. */ chase_additional(fctx); /* * Cache the cacheable parts of the message. This may also cause * work to be queued to the DNSSEC validator. */ if (WANTCACHE(fctx)) { result = cache_message(fctx, query->addrinfo, now); if (result != ISC_R_SUCCESS) goto done; } /* * Ncache the negatively cacheable parts of the message. This may * also cause work to be queued to the DNSSEC validator. */ if (WANTNCACHE(fctx)) { dns_rdatatype_t covers; if (message->rcode == dns_rcode_nxdomain) covers = dns_rdatatype_any; else covers = fctx->type; /* * Cache any negative cache entries in the message. */ result = ncache_message(fctx, query->addrinfo, covers, now); } done: /* * Remember the query's addrinfo, in case we need to mark the * server as broken. */ addrinfo = query->addrinfo; /* * Cancel the query. * * XXXRTH Don't cancel the query if waiting for validation? */ fctx_cancelquery(&query, &devent, finish, no_response); 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; dns_fixedname_init(&foundname); fname = dns_fixedname_name(&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(fctx->res->view, name, fname, now, findoptions, ISC_TRUE, &fctx->nameservers, NULL); if (result != ISC_R_SUCCESS) { FCTXTRACE("couldn't find a zonecut"); fctx_done(fctx, DNS_R_SERVFAIL, __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; } 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; } fctx->ns_ttl = fctx->nameservers.ttl; fctx->ns_ttl_ok = ISC_TRUE; fctx_cancelqueries(fctx, ISC_TRUE); fctx_cleanupfinds(fctx); fctx_cleanupaltfinds(fctx); fctx_cleanupforwaddrs(fctx); fctx_cleanupaltaddrs(fctx); } /* * Try again. */ fctx_try(fctx, !get_nameservers, ISC_FALSE); } else if (resend) { /* * Resend (probably with changed options). */ FCTXTRACE("resend"); inc_stats(fctx->res, dns_resstatscounter_retry); result = fctx_query(fctx, addrinfo, options); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __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, ISC_TRUE); /* * We must not retransmit while the validator is working; * it has references to the current rmessage. */ result = fctx_stopidletimer(fctx); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); } else if (result == DNS_R_CHASEDSSERVERS) { unsigned int n; add_bad(fctx, addrinfo, result, broken_type); fctx_cancelqueries(fctx, ISC_TRUE); fctx_cleanupfinds(fctx); fctx_cleanupforwaddrs(fctx); n = dns_name_countlabels(&fctx->name); dns_name_getlabelsequence(&fctx->name, 1, n - 1, &fctx->nsname); FCTXTRACE("suspending DS lookup to find parent's NS records"); result = dns_resolver_createfetch(fctx->res, &fctx->nsname, dns_rdatatype_ns, NULL, NULL, NULL, 0, task, resume_dslookup, fctx, &fctx->nsrrset, NULL, &fctx->nsfetch); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); else { LOCK(&fctx->res->buckets[fctx->bucketnum].lock); fctx->references++; UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); result = fctx_stopidletimer(fctx); if (result != ISC_R_SUCCESS) fctx_done(fctx, result, __LINE__); } } else { /* * We're done. */ fctx_done(fctx, result, __LINE__); } } /*** *** Resolver Methods ***/ static void destroy_badcache(dns_resolver_t *res) { dns_badcache_t *bad, *next; unsigned int i; if (res->badcache != NULL) { for (i = 0; i < res->badhash; i++) for (bad = res->badcache[i]; bad != NULL; bad = next) { next = bad->next; isc_mem_put(res->mctx, bad, sizeof(*bad) + bad->name.length); res->badcount--; } isc_mem_put(res->mctx, res->badcache, sizeof(*res->badcache) * res->badhash); res->badcache = NULL; res->badhash = 0; INSIST(res->badcount == 0); } } static void destroy(dns_resolver_t *res) { unsigned int i; alternate_t *a; REQUIRE(res->references == 0); REQUIRE(!res->priming); REQUIRE(res->primefetch == NULL); RTRACE("destroy"); INSIST(res->nfctx == 0); DESTROYLOCK(&res->primelock); DESTROYLOCK(&res->nlock); DESTROYLOCK(&res->lock); for (i = 0; i < res->nbuckets; i++) { INSIST(ISC_LIST_EMPTY(res->buckets[i].fctxs)); isc_task_shutdown(res->buckets[i].task); isc_task_detach(&res->buckets[i].task); DESTROYLOCK(&res->buckets[i].lock); isc_mem_detach(&res->buckets[i].mctx); } isc_mem_put(res->mctx, res->buckets, res->nbuckets * sizeof(fctxbucket_t)); if (res->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); destroy_badcache(res); dns_resolver_resetmustbesecure(res); #if USE_ALGLOCK isc_rwlock_destroy(&res->alglock); #endif #if USE_MBSLOCK isc_rwlock_destroy(&res->mbslock); #endif isc_timer_detach(&res->spillattimer); res->magic = 0; isc_mem_put(res->mctx, res, sizeof(*res)); } static void send_shutdown_events(dns_resolver_t *res) { isc_event_t *event, *next_event; isc_task_t *etask; /* * Caller must be holding the resolver lock. */ for (event = ISC_LIST_HEAD(res->whenshutdown); event != NULL; event = next_event) { next_event = ISC_LIST_NEXT(event, ev_link); ISC_LIST_UNLINK(res->whenshutdown, event, ev_link); etask = event->ev_sender; event->ev_sender = res; isc_task_sendanddetach(&etask, &event); } } static void empty_bucket(dns_resolver_t *res) { RTRACE("empty_bucket"); LOCK(&res->lock); INSIST(res->activebuckets > 0); res->activebuckets--; if (res->activebuckets == 0) send_shutdown_events(res); UNLOCK(&res->lock); } static void spillattimer_countdown(isc_task_t *task, isc_event_t *event) { dns_resolver_t *res = event->ev_arg; isc_result_t result; unsigned int count; isc_boolean_t logit = ISC_FALSE; REQUIRE(VALID_RESOLVER(res)); UNUSED(task); LOCK(&res->lock); INSIST(!res->exiting); if (res->spillat > res->spillatmin) { res->spillat--; logit = ISC_TRUE; } if (res->spillat <= res->spillatmin) { result = isc_timer_reset(res->spillattimer, isc_timertype_inactive, NULL, NULL, ISC_TRUE); RUNTIME_CHECK(result == ISC_R_SUCCESS); } count = res->spillat; UNLOCK(&res->lock); if (logit) isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, "clients-per-query decreased to %u", count); isc_event_free(&event); } isc_result_t dns_resolver_create(dns_view_t *view, isc_taskmgr_t *taskmgr, unsigned int ntasks, 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; 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->badcache = NULL; res->badcount = 0; res->badhash = 0; res->badsweep = 0; res->mustbesecure = NULL; res->spillatmin = res->spillat = 10; res->spillatmax = 100; res->spillattimer = NULL; res->zero_no_soa_ttl = ISC_FALSE; res->query_timeout = DEFAULT_QUERY_TIMEOUT; + res->maxdepth = DEFAULT_RECURSION_DEPTH; res->nbuckets = ntasks; res->activebuckets = ntasks; res->buckets = isc_mem_get(view->mctx, ntasks * sizeof(fctxbucket_t)); if (res->buckets == NULL) { result = ISC_R_NOMEMORY; goto cleanup_res; } for (i = 0; i < ntasks; i++) { result = isc_mutex_init(&res->buckets[i].lock); if (result != ISC_R_SUCCESS) goto cleanup_buckets; res->buckets[i].task = NULL; result = isc_task_create(taskmgr, 0, &res->buckets[i].task); if (result != ISC_R_SUCCESS) { DESTROYLOCK(&res->buckets[i].lock); goto cleanup_buckets; } res->buckets[i].mctx = NULL; snprintf(name, sizeof(name), "res%u", i); #ifdef ISC_PLATFORM_USETHREADS /* * Use a separate memory context for each bucket to reduce * contention among multiple threads. Do this only when * enabling threads because it will be require more memory. */ result = isc_mem_create(0, 0, &res->buckets[i].mctx); if (result != ISC_R_SUCCESS) { isc_task_detach(&res->buckets[i].task); DESTROYLOCK(&res->buckets[i].lock); goto cleanup_buckets; } isc_mem_setname(res->buckets[i].mctx, name, NULL); #else isc_mem_attach(view->mctx, &res->buckets[i].mctx); #endif isc_task_setname(res->buckets[i].task, name, res); ISC_LIST_INIT(res->buckets[i].fctxs); res->buckets[i].exiting = ISC_FALSE; buckets_created++; } res->dispatches4 = NULL; if (dispatchv4 != NULL) { dns_dispatchset_create(view->mctx, socketmgr, taskmgr, dispatchv4, &res->dispatches4, ndisp); dispattr = dns_dispatch_getattributes(dispatchv4); res->exclusivev4 = ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0); } res->dispatches6 = NULL; if (dispatchv6 != NULL) { dns_dispatchset_create(view->mctx, socketmgr, taskmgr, dispatchv6, &res->dispatches6, ndisp); dispattr = dns_dispatch_getattributes(dispatchv6); res->exclusivev6 = ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0); } res->references = 1; res->exiting = ISC_FALSE; res->frozen = ISC_FALSE; ISC_LIST_INIT(res->whenshutdown); res->priming = ISC_FALSE; res->primefetch = NULL; res->nfctx = 0; result = isc_mutex_init(&res->lock); if (result != ISC_R_SUCCESS) goto cleanup_dispatches; result = isc_mutex_init(&res->nlock); if (result != ISC_R_SUCCESS) goto cleanup_lock; result = isc_mutex_init(&res->primelock); if (result != ISC_R_SUCCESS) goto cleanup_nlock; task = NULL; result = isc_task_create(taskmgr, 0, &task); if (result != ISC_R_SUCCESS) goto cleanup_primelock; result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL, task, spillattimer_countdown, res, &res->spillattimer); isc_task_detach(&task); if (result != ISC_R_SUCCESS) goto cleanup_primelock; #if USE_ALGLOCK result = isc_rwlock_init(&res->alglock, 0, 0); if (result != ISC_R_SUCCESS) goto cleanup_spillattimer; #endif #if USE_MBSLOCK result = isc_rwlock_init(&res->mbslock, 0, 0); if (result != ISC_R_SUCCESS) goto cleanup_alglock; #endif res->magic = RES_MAGIC; *resp = res; return (ISC_R_SUCCESS); #if USE_MBSLOCK cleanup_alglock: #if USE_ALGLOCK isc_rwlock_destroy(&res->alglock); #endif #endif #if USE_ALGLOCK || USE_MBSLOCK cleanup_spillattimer: isc_timer_detach(&res->spillattimer); #endif cleanup_primelock: DESTROYLOCK(&res->primelock); cleanup_nlock: DESTROYLOCK(&res->nlock); cleanup_lock: DESTROYLOCK(&res->lock); cleanup_dispatches: if (res->dispatches6 != NULL) dns_dispatchset_destroy(&res->dispatches6); if (res->dispatches4 != NULL) dns_dispatchset_destroy(&res->dispatches4); 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); } #ifdef BIND9 static void prime_done(isc_task_t *task, isc_event_t *event) { dns_resolver_t *res; dns_fetchevent_t *fevent; dns_fetch_t *fetch; dns_db_t *db = NULL; REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); fevent = (dns_fetchevent_t *)event; res = event->ev_arg; REQUIRE(VALID_RESOLVER(res)); UNUSED(task); LOCK(&res->lock); INSIST(res->priming); res->priming = ISC_FALSE; LOCK(&res->primelock); fetch = res->primefetch; res->primefetch = NULL; UNLOCK(&res->primelock); UNLOCK(&res->lock); if (fevent->result == ISC_R_SUCCESS && res->view->cache != NULL && res->view->hints != NULL) { dns_cache_attachdb(res->view->cache, &db); dns_root_checkhints(res->view, res->view->hints, db); dns_db_detach(&db); } if (fevent->node != NULL) dns_db_detachnode(fevent->db, &fevent->node); if (fevent->db != NULL) dns_db_detach(&fevent->db); if (dns_rdataset_isassociated(fevent->rdataset)) dns_rdataset_disassociate(fevent->rdataset); INSIST(fevent->sigrdataset == NULL); isc_mem_put(res->mctx, fevent->rdataset, sizeof(*fevent->rdataset)); isc_event_free(&event); dns_resolver_destroyfetch(&fetch); } void dns_resolver_prime(dns_resolver_t *res) { isc_boolean_t want_priming = ISC_FALSE; dns_rdataset_t *rdataset; isc_result_t result; REQUIRE(VALID_RESOLVER(res)); REQUIRE(res->frozen); RTRACE("dns_resolver_prime"); LOCK(&res->lock); if (!res->exiting && !res->priming) { INSIST(res->primefetch == NULL); res->priming = ISC_TRUE; want_priming = ISC_TRUE; } UNLOCK(&res->lock); if (want_priming) { /* * To avoid any possible recursive locking problems, we * start the priming fetch like any other fetch, and holding * no resolver locks. No one else will try to start it * because we're the ones who set res->priming to true. * Any other callers of dns_resolver_prime() while we're * running will see that res->priming is already true and * do nothing. */ RTRACE("priming"); rdataset = isc_mem_get(res->mctx, sizeof(*rdataset)); if (rdataset == NULL) { LOCK(&res->lock); INSIST(res->priming); INSIST(res->primefetch == NULL); res->priming = ISC_FALSE; UNLOCK(&res->lock); return; } dns_rdataset_init(rdataset); LOCK(&res->primelock); result = dns_resolver_createfetch(res, dns_rootname, dns_rdatatype_ns, NULL, NULL, NULL, 0, res->buckets[0].task, prime_done, res, rdataset, NULL, &res->primefetch); UNLOCK(&res->primelock); if (result != ISC_R_SUCCESS) { LOCK(&res->lock); INSIST(res->priming); res->priming = ISC_FALSE; UNLOCK(&res->lock); } } } #endif /* BIND9 */ void dns_resolver_freeze(dns_resolver_t *res) { /* * Freeze resolver. */ REQUIRE(VALID_RESOLVER(res)); res->frozen = ISC_TRUE; } void dns_resolver_attach(dns_resolver_t *source, dns_resolver_t **targetp) { REQUIRE(VALID_RESOLVER(source)); REQUIRE(targetp != NULL && *targetp == NULL); RRTRACE(source, "attach"); LOCK(&source->lock); REQUIRE(!source->exiting); INSIST(source->references > 0); source->references++; INSIST(source->references != 0); UNLOCK(&source->lock); *targetp = source; } void dns_resolver_whenshutdown(dns_resolver_t *res, isc_task_t *task, isc_event_t **eventp) { isc_task_t *clone; isc_event_t *event; REQUIRE(VALID_RESOLVER(res)); REQUIRE(eventp != NULL); event = *eventp; *eventp = NULL; LOCK(&res->lock); if (res->exiting && res->activebuckets == 0) { /* * We're already shutdown. Send the event. */ event->ev_sender = res; isc_task_send(task, &event); } else { clone = NULL; isc_task_attach(task, &clone); event->ev_sender = clone; ISC_LIST_APPEND(res->whenshutdown, event, ev_link); } UNLOCK(&res->lock); } void dns_resolver_shutdown(dns_resolver_t *res) { unsigned int i; fetchctx_t *fctx; isc_result_t result; REQUIRE(VALID_RESOLVER(res)); RTRACE("shutdown"); LOCK(&res->lock); if (!res->exiting) { RTRACE("exiting"); res->exiting = ISC_TRUE; for (i = 0; i < res->nbuckets; i++) { LOCK(&res->buckets[i].lock); for (fctx = ISC_LIST_HEAD(res->buckets[i].fctxs); fctx != NULL; fctx = ISC_LIST_NEXT(fctx, link)) fctx_shutdown(fctx); if (res->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 = ISC_TRUE; if (ISC_LIST_EMPTY(res->buckets[i].fctxs)) { INSIST(res->activebuckets > 0); res->activebuckets--; } UNLOCK(&res->buckets[i].lock); } if (res->activebuckets == 0) send_shutdown_events(res); result = isc_timer_reset(res->spillattimer, isc_timertype_inactive, NULL, NULL, ISC_TRUE); RUNTIME_CHECK(result == ISC_R_SUCCESS); } UNLOCK(&res->lock); } void dns_resolver_detach(dns_resolver_t **resp) { dns_resolver_t *res; isc_boolean_t need_destroy = ISC_FALSE; REQUIRE(resp != NULL); res = *resp; REQUIRE(VALID_RESOLVER(res)); RTRACE("detach"); LOCK(&res->lock); INSIST(res->references > 0); res->references--; if (res->references == 0) { INSIST(res->exiting && res->activebuckets == 0); need_destroy = ISC_TRUE; } UNLOCK(&res->lock); if (need_destroy) destroy(res); *resp = NULL; } static inline isc_boolean_t fctx_match(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type, unsigned int options) { /* * Don't match fetch contexts that are shutting down. */ if (fctx->cloned || fctx->state == fetchstate_done || ISC_LIST_EMPTY(fctx->events)) return (ISC_FALSE); if (fctx->type != type || fctx->options != options) return (ISC_FALSE); return (dns_name_equal(&fctx->name, name)); } static inline void log_fetch(dns_name_t *name, dns_rdatatype_t type) { char namebuf[DNS_NAME_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; int level = ISC_LOG_DEBUG(1); if (! isc_log_wouldlog(dns_lctx, level)) return; dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdatatype_format(type, typebuf, sizeof(typebuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, level, "createfetch: %s %s", namebuf, typebuf); } isc_result_t dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, dns_name_t *domain, dns_rdataset_t *nameservers, dns_forwarders_t *forwarders, unsigned int options, isc_task_t *task, isc_taskaction_t action, void *arg, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, dns_fetch_t **fetchp) { - return (dns_resolver_createfetch2(res, name, type, domain, + return (dns_resolver_createfetch3(res, name, type, domain, nameservers, forwarders, NULL, 0, - options, task, action, arg, + options, 0, 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, 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_task_t *task, + isc_taskaction_t action, void *arg, + dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset, + dns_fetch_t **fetchp) +{ dns_fetch_t *fetch; fetchctx_t *fctx = NULL; isc_result_t result = ISC_R_SUCCESS; unsigned int bucketnum; isc_boolean_t new_fctx = ISC_FALSE; isc_event_t *event; unsigned int count = 0; unsigned int spillat; unsigned int spillatmin; isc_boolean_t destroy = ISC_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); bucketnum = dns_name_fullhash(name, ISC_FALSE) % res->nbuckets; LOCK(&res->lock); spillat = res->spillat; spillatmin = res->spillatmin; UNLOCK(&res->lock); LOCK(&res->buckets[bucketnum].lock); if (res->buckets[bucketnum].exiting) { result = ISC_R_SHUTTINGDOWN; goto unlock; } if ((options & DNS_FETCHOPT_UNSHARED) == 0) { for (fctx = ISC_LIST_HEAD(res->buckets[bucketnum].fctxs); fctx != NULL; fctx = ISC_LIST_NEXT(fctx, link)) { if (fctx_match(fctx, name, type, options)) break; } } /* * Is this a duplicate? */ if (fctx != NULL && client != NULL) { dns_fetchevent_t *fevent; for (fevent = ISC_LIST_HEAD(fctx->events); fevent != NULL; fevent = ISC_LIST_NEXT(fevent, ev_link)) { if (fevent->client != NULL && fevent->id == id && isc_sockaddr_equal(fevent->client, client)) { result = DNS_R_DUPLICATE; goto unlock; } count++; } } if (count >= spillatmin && spillatmin != 0) { INSIST(fctx != NULL); if (count >= spillat) fctx->spilled = ISC_TRUE; if (fctx->spilled) { result = DNS_R_DROP; goto unlock; } } if (fctx == NULL) { result = fctx_create(res, name, type, domain, nameservers, - options, bucketnum, &fctx); + options, bucketnum, depth, &fctx); if (result != ISC_R_SUCCESS) goto unlock; new_fctx = ISC_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); destroy = ISC_TRUE; } } unlock: UNLOCK(&res->buckets[bucketnum].lock); if (destroy) fctx_destroy(fctx); if (result == ISC_R_SUCCESS) { FTRACE("created"); *fetchp = fetch; } else isc_mem_put(res->mctx, fetch, sizeof(*fetch)); return (result); } void dns_resolver_cancelfetch(dns_fetch_t *fetch) { fetchctx_t *fctx; dns_resolver_t *res; dns_fetchevent_t *event, *next_event; isc_task_t *etask; REQUIRE(DNS_FETCH_VALID(fetch)); fctx = fetch->private; REQUIRE(VALID_FCTX(fctx)); res = fctx->res; FTRACE("cancelfetch"); LOCK(&res->buckets[fctx->bucketnum].lock); /* * Find the completion event for this fetch (as opposed * to those for other fetches that have joined the same * fctx) and send it with result = ISC_R_CANCELED. */ event = NULL; if (fctx->state != fetchstate_done) { for (event = ISC_LIST_HEAD(fctx->events); event != NULL; event = next_event) { next_event = ISC_LIST_NEXT(event, ev_link); if (event->fetch == fetch) { ISC_LIST_UNLINK(fctx->events, event, ev_link); break; } } } if (event != NULL) { etask = event->ev_sender; event->ev_sender = fctx; event->result = ISC_R_CANCELED; isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event)); } /* * The fctx continues running even if no fetches remain; * the answer is still cached. */ UNLOCK(&res->buckets[fctx->bucketnum].lock); } void dns_resolver_destroyfetch(dns_fetch_t **fetchp) { dns_fetch_t *fetch; dns_resolver_t *res; dns_fetchevent_t *event, *next_event; fetchctx_t *fctx; unsigned int bucketnum; isc_boolean_t bucket_empty; 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_put(res->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, isc_boolean_t 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 " "%" ISC_PRINT_QUADFORMAT "u." "%06" ISC_PRINT_QUADFORMAT "u: %s/%s " "[domain:%s,referral:%u,restart:%u,qrysent:%u," "timeout:%u,lame:%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->neterr, fctx->badresp, fctx->adberr, fctx->findfail, fctx->valfail); fctx->logged = ISC_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); } isc_uint32_t dns_resolver_getlamettl(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); return (resolver->lame_ttl); } void dns_resolver_setlamettl(dns_resolver_t *resolver, isc_uint32_t lame_ttl) { REQUIRE(VALID_RESOLVER(resolver)); resolver->lame_ttl = lame_ttl; } unsigned int dns_resolver_nrunning(dns_resolver_t *resolver) { unsigned int n; LOCK(&resolver->nlock); n = resolver->nfctx; UNLOCK(&resolver->nlock); return (n); } isc_result_t dns_resolver_addalternate(dns_resolver_t *resolver, isc_sockaddr_t *alt, dns_name_t *name, in_port_t port) { alternate_t *a; isc_result_t result; REQUIRE(VALID_RESOLVER(resolver)); REQUIRE(!resolver->frozen); REQUIRE((alt == NULL) ^ (name == NULL)); a = isc_mem_get(resolver->mctx, sizeof(*a)); if (a == NULL) return (ISC_R_NOMEMORY); if (alt != NULL) { a->isaddress = ISC_TRUE; a->_u.addr = *alt; } else { a->isaddress = ISC_FALSE; a->_u._n.port = port; dns_name_init(&a->_u._n.name, NULL); result = dns_name_dup(name, resolver->mctx, &a->_u._n.name); if (result != ISC_R_SUCCESS) { isc_mem_put(resolver->mctx, a, sizeof(*a)); return (result); } } ISC_LINK_INIT(a, link); ISC_LIST_APPEND(resolver->alternates, a, link); return (ISC_R_SUCCESS); } void dns_resolver_setudpsize(dns_resolver_t *resolver, isc_uint16_t udpsize) { REQUIRE(VALID_RESOLVER(resolver)); resolver->udpsize = udpsize; } isc_uint16_t dns_resolver_getudpsize(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); return (resolver->udpsize); } void dns_resolver_flushbadcache(dns_resolver_t *resolver, dns_name_t *name) { unsigned int i; dns_badcache_t *bad, *prev, *next; REQUIRE(VALID_RESOLVER(resolver)); LOCK(&resolver->lock); if (resolver->badcache == NULL) goto unlock; if (name != NULL) { isc_time_t now; isc_result_t result; result = isc_time_now(&now); if (result != ISC_R_SUCCESS) isc_time_settoepoch(&now); i = dns_name_hash(name, ISC_FALSE) % resolver->badhash; prev = NULL; for (bad = resolver->badcache[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) resolver->badcache[i] = bad->next; else prev->next = bad->next; isc_mem_put(resolver->mctx, bad, sizeof(*bad) + bad->name.length); resolver->badcount--; } else prev = bad; } } else destroy_badcache(resolver); unlock: UNLOCK(&resolver->lock); } static void resizehash(dns_resolver_t *resolver, isc_time_t *now, isc_boolean_t grow) { unsigned int newsize; dns_badcache_t **new, *bad, *next; unsigned int i; if (grow) newsize = resolver->badhash * 2 + 1; else newsize = (resolver->badhash - 1) / 2; new = isc_mem_get(resolver->mctx, sizeof(*resolver->badcache) * newsize); if (new == NULL) return; memset(new, 0, sizeof(*resolver->badcache) * newsize); for (i = 0; i < resolver->badhash; i++) { for (bad = resolver->badcache[i]; bad != NULL; bad = next) { next = bad->next; if (isc_time_compare(&bad->expire, now) < 0) { isc_mem_put(resolver->mctx, bad, sizeof(*bad) + bad->name.length); resolver->badcount--; } else { bad->next = new[bad->hashval % newsize]; new[bad->hashval % newsize] = bad; } } } isc_mem_put(resolver->mctx, resolver->badcache, sizeof(*resolver->badcache) * resolver->badhash); resolver->badhash = newsize; resolver->badcache = new; } void dns_resolver_addbadcache(dns_resolver_t *resolver, dns_name_t *name, dns_rdatatype_t type, isc_time_t *expire) { isc_time_t now; isc_result_t result = ISC_R_SUCCESS; unsigned int i, hashval; dns_badcache_t *bad, *prev, *next; REQUIRE(VALID_RESOLVER(resolver)); LOCK(&resolver->lock); if (resolver->badcache == NULL) { resolver->badcache = isc_mem_get(resolver->mctx, sizeof(*resolver->badcache) * DNS_BADCACHE_SIZE); if (resolver->badcache == NULL) goto cleanup; resolver->badhash = DNS_BADCACHE_SIZE; memset(resolver->badcache, 0, sizeof(*resolver->badcache) * resolver->badhash); } result = isc_time_now(&now); if (result != ISC_R_SUCCESS) isc_time_settoepoch(&now); hashval = dns_name_hash(name, ISC_FALSE); i = hashval % resolver->badhash; prev = NULL; for (bad = resolver->badcache[i]; bad != NULL; bad = next) { next = bad->next; if (bad->type == type && dns_name_equal(name, &bad->name)) break; if (isc_time_compare(&bad->expire, &now) < 0) { if (prev == NULL) resolver->badcache[i] = bad->next; else prev->next = bad->next; isc_mem_put(resolver->mctx, bad, sizeof(*bad) + bad->name.length); resolver->badcount--; } else prev = bad; } if (bad == NULL) { isc_buffer_t buffer; bad = isc_mem_get(resolver->mctx, sizeof(*bad) + name->length); if (bad == NULL) goto cleanup; bad->type = type; bad->hashval = hashval; bad->expire = *expire; isc_buffer_init(&buffer, bad + 1, name->length); dns_name_init(&bad->name, NULL); dns_name_copy(name, &bad->name, &buffer); bad->next = resolver->badcache[i]; resolver->badcache[i] = bad; resolver->badcount++; if (resolver->badcount > resolver->badhash * 8) resizehash(resolver, &now, ISC_TRUE); if (resolver->badcount < resolver->badhash * 2 && resolver->badhash > DNS_BADCACHE_SIZE) resizehash(resolver, &now, ISC_FALSE); } else bad->expire = *expire; cleanup: UNLOCK(&resolver->lock); } isc_boolean_t dns_resolver_getbadcache(dns_resolver_t *resolver, dns_name_t *name, dns_rdatatype_t type, isc_time_t *now) { dns_badcache_t *bad, *prev, *next; isc_boolean_t answer = ISC_FALSE; unsigned int i; REQUIRE(VALID_RESOLVER(resolver)); LOCK(&resolver->lock); if (resolver->badcache == NULL) goto unlock; i = dns_name_hash(name, ISC_FALSE) % resolver->badhash; prev = NULL; for (bad = resolver->badcache[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 resolver->badcache[i] = bad->next; isc_mem_put(resolver->mctx, bad, sizeof(*bad) + bad->name.length); resolver->badcount--; continue; } if (bad->type == type && dns_name_equal(name, &bad->name)) { answer = ISC_TRUE; break; } prev = bad; } /* * Slow sweep to clean out stale records. */ i = resolver->badsweep++ % resolver->badhash; bad = resolver->badcache[i]; if (bad != NULL && isc_time_compare(&bad->expire, now) < 0) { resolver->badcache[i] = bad->next; isc_mem_put(resolver->mctx, bad, sizeof(*bad) + bad->name.length); resolver->badcount--; } unlock: UNLOCK(&resolver->lock); return (answer); } void dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp) { char namebuf[DNS_NAME_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; dns_badcache_t *bad, *next, *prev; isc_time_t now; unsigned int i; isc_uint64_t t; LOCK(&resolver->lock); fprintf(fp, ";\n; Bad cache\n;\n"); if (resolver->badcache == NULL) goto unlock; TIME_NOW(&now); for (i = 0; i < resolver->badhash; i++) { prev = NULL; for (bad = resolver->badcache[i]; bad != NULL; bad = next) { next = bad->next; if (isc_time_compare(&bad->expire, &now) < 0) { if (prev != NULL) prev->next = bad->next; else resolver->badcache[i] = bad->next; isc_mem_put(resolver->mctx, bad, sizeof(*bad) + bad->name.length); resolver->badcount--; 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 " "%" ISC_PLATFORM_QUADFORMAT "u]\n", namebuf, typebuf, t); } } unlock: UNLOCK(&resolver->lock); } static void free_algorithm(void *node, void *arg) { unsigned char *algorithms = node; isc_mem_t *mctx = arg; isc_mem_put(mctx, algorithms, *algorithms); } void dns_resolver_reset_algorithms(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); #if USE_ALGLOCK RWLOCK(&resolver->alglock, isc_rwlocktype_write); #endif if (resolver->algorithms != NULL) dns_rbt_destroy(&resolver->algorithms); #if USE_ALGLOCK RWUNLOCK(&resolver->alglock, isc_rwlocktype_write); #endif } isc_result_t dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name, unsigned int alg) { unsigned int len, mask; unsigned char *new; unsigned char *algorithms; isc_result_t result; dns_rbtnode_t *node = NULL; REQUIRE(VALID_RESOLVER(resolver)); if (alg > 255) return (ISC_R_RANGE); #if USE_ALGLOCK RWLOCK(&resolver->alglock, isc_rwlocktype_write); #endif if (resolver->algorithms == NULL) { result = dns_rbt_create(resolver->mctx, free_algorithm, resolver->mctx, &resolver->algorithms); if (result != ISC_R_SUCCESS) goto cleanup; } len = alg/8 + 2; mask = 1 << (alg%8); result = dns_rbt_addnode(resolver->algorithms, name, &node); if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) { algorithms = node->data; if (algorithms == NULL || len > *algorithms) { new = isc_mem_get(resolver->mctx, len); if (new == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } memset(new, 0, len); if (algorithms != NULL) memmove(new, algorithms, *algorithms); new[len-1] |= mask; *new = len; node->data = new; if (algorithms != NULL) isc_mem_put(resolver->mctx, algorithms, *algorithms); } else algorithms[len-1] |= mask; } result = ISC_R_SUCCESS; cleanup: #if USE_ALGLOCK RWUNLOCK(&resolver->alglock, isc_rwlocktype_write); #endif return (result); } isc_boolean_t dns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name, unsigned int alg) { unsigned int len, mask; unsigned char *algorithms; void *data = NULL; isc_result_t result; isc_boolean_t found = ISC_FALSE; REQUIRE(VALID_RESOLVER(resolver)); #if USE_ALGLOCK RWLOCK(&resolver->alglock, isc_rwlocktype_read); #endif if (resolver->algorithms == NULL) goto unlock; result = dns_rbt_findname(resolver->algorithms, name, 0, NULL, &data); if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { len = alg/8 + 2; mask = 1 << (alg%8); algorithms = data; if (len <= *algorithms && (algorithms[len-1] & mask) != 0) found = ISC_TRUE; } unlock: #if USE_ALGLOCK RWUNLOCK(&resolver->alglock, isc_rwlocktype_read); #endif if (found) return (ISC_FALSE); return (dst_algorithm_supported(alg)); } isc_boolean_t dns_resolver_digest_supported(dns_resolver_t *resolver, unsigned int digest) { UNUSED(resolver); return (dns_ds_digest_supported(digest)); } void dns_resolver_resetmustbesecure(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); #if USE_MBSLOCK RWLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif if (resolver->mustbesecure != NULL) dns_rbt_destroy(&resolver->mustbesecure); #if USE_MBSLOCK RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif } static isc_boolean_t yes = ISC_TRUE, no = ISC_FALSE; isc_result_t dns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name, isc_boolean_t value) { isc_result_t result; REQUIRE(VALID_RESOLVER(resolver)); #if USE_MBSLOCK RWLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif if (resolver->mustbesecure == NULL) { result = dns_rbt_create(resolver->mctx, NULL, NULL, &resolver->mustbesecure); if (result != ISC_R_SUCCESS) goto cleanup; } result = dns_rbt_addname(resolver->mustbesecure, name, value ? &yes : &no); cleanup: #if USE_MBSLOCK RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif return (result); } isc_boolean_t dns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name) { void *data = NULL; isc_boolean_t value = ISC_FALSE; isc_result_t result; REQUIRE(VALID_RESOLVER(resolver)); #if USE_MBSLOCK RWLOCK(&resolver->mbslock, isc_rwlocktype_read); #endif if (resolver->mustbesecure == NULL) goto unlock; result = dns_rbt_findname(resolver->mustbesecure, name, 0, NULL, &data); if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) value = *(isc_boolean_t*)data; unlock: #if USE_MBSLOCK RWUNLOCK(&resolver->mbslock, isc_rwlocktype_read); #endif return (value); } void dns_resolver_getclientsperquery(dns_resolver_t *resolver, isc_uint32_t *cur, isc_uint32_t *min, isc_uint32_t *max) { REQUIRE(VALID_RESOLVER(resolver)); LOCK(&resolver->lock); if (cur != NULL) *cur = resolver->spillat; if (min != NULL) *min = resolver->spillatmin; if (max != NULL) *max = resolver->spillatmax; UNLOCK(&resolver->lock); } void dns_resolver_setclientsperquery(dns_resolver_t *resolver, isc_uint32_t min, isc_uint32_t max) { REQUIRE(VALID_RESOLVER(resolver)); LOCK(&resolver->lock); resolver->spillatmin = resolver->spillat = min; resolver->spillatmax = max; UNLOCK(&resolver->lock); } isc_boolean_t dns_resolver_getzeronosoattl(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); return (resolver->zero_no_soa_ttl); } void dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state) { REQUIRE(VALID_RESOLVER(resolver)); resolver->zero_no_soa_ttl = state; } unsigned int dns_resolver_getoptions(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); return (resolver->options); } 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_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); } Index: stable/9/contrib/bind9/lib/isccfg/namedconf.c =================================================================== --- stable/9/contrib/bind9/lib/isccfg/namedconf.c (revision 275668) +++ stable/9/contrib/bind9/lib/isccfg/namedconf.c (revision 275669) @@ -1,2924 +1,2925 @@ /* * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2002, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id$ */ /*! \file */ #include #include #include #include #include #include #include #include #include #include #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base) /*% Check a return value. */ #define CHECK(op) \ do { result = (op); \ if (result != ISC_R_SUCCESS) goto cleanup; \ } while (0) /*% Clean up a configuration object if non-NULL. */ #define CLEANUP_OBJ(obj) \ do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0) /*% * Forward declarations of static functions. */ static isc_result_t parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype, const cfg_type_t *othertype, cfg_obj_t **ret); static void doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *type); static isc_result_t parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); static isc_result_t parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); static isc_result_t parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); static void print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj); static void doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type); static void print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj); static void doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type); static void doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type); static cfg_type_t cfg_type_acl; static cfg_type_t cfg_type_addrmatchelt; static cfg_type_t cfg_type_bracketed_aml; static cfg_type_t cfg_type_bracketed_namesockaddrkeylist; static cfg_type_t cfg_type_bracketed_sockaddrlist; static cfg_type_t cfg_type_bracketed_sockaddrnameportlist; static cfg_type_t cfg_type_controls; static cfg_type_t cfg_type_controls_sockaddr; static cfg_type_t cfg_type_destinationlist; static cfg_type_t cfg_type_dialuptype; static cfg_type_t cfg_type_ixfrdifftype; static cfg_type_t cfg_type_key; static cfg_type_t cfg_type_logfile; static cfg_type_t cfg_type_logging; static cfg_type_t cfg_type_logseverity; static cfg_type_t cfg_type_lwres; static cfg_type_t cfg_type_masterselement; static cfg_type_t cfg_type_nameportiplist; static cfg_type_t cfg_type_negated; static cfg_type_t cfg_type_notifytype; static cfg_type_t cfg_type_optional_allow; static cfg_type_t cfg_type_optional_class; static cfg_type_t cfg_type_optional_facility; static cfg_type_t cfg_type_optional_keyref; static cfg_type_t cfg_type_optional_port; static cfg_type_t cfg_type_options; static cfg_type_t cfg_type_portiplist; static cfg_type_t cfg_type_querysource4; static cfg_type_t cfg_type_querysource6; static cfg_type_t cfg_type_querysource; static cfg_type_t cfg_type_server; static cfg_type_t cfg_type_server_key_kludge; static cfg_type_t cfg_type_size; static cfg_type_t cfg_type_sizenodefault; static cfg_type_t cfg_type_sockaddr4wild; static cfg_type_t cfg_type_sockaddr6wild; static cfg_type_t cfg_type_statschannels; static cfg_type_t cfg_type_view; static cfg_type_t cfg_type_viewopts; static cfg_type_t cfg_type_zone; static cfg_type_t cfg_type_zoneopts; static cfg_type_t cfg_type_dynamically_loadable_zones; static cfg_type_t cfg_type_dynamically_loadable_zones_opts; static cfg_type_t cfg_type_v4_aaaa; /* * Clauses that can be found in a 'dynamically loadable zones' statement */ static cfg_clausedef_t dynamically_loadable_zones_clauses[] = { { "database", &cfg_type_astring, 0 }, { NULL, NULL, 0 } }; /* * A dynamically loadable zones statement. */ static cfg_tuplefielddef_t dynamically_loadable_zones_fields[] = { { "name", &cfg_type_astring, 0 }, { "options", &cfg_type_dynamically_loadable_zones_opts, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_dynamically_loadable_zones = { "dlz", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, dynamically_loadable_zones_fields }; /*% tkey-dhkey */ static cfg_tuplefielddef_t tkey_dhkey_fields[] = { { "name", &cfg_type_qstring, 0 }, { "keyid", &cfg_type_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_tkey_dhkey = { "tkey-dhkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, tkey_dhkey_fields }; /*% listen-on */ static cfg_tuplefielddef_t listenon_fields[] = { { "port", &cfg_type_optional_port, 0 }, { "acl", &cfg_type_bracketed_aml, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_listenon = { "listenon", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, listenon_fields }; /*% acl */ static cfg_tuplefielddef_t acl_fields[] = { { "name", &cfg_type_astring, 0 }, { "value", &cfg_type_bracketed_aml, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_acl = { "acl", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, acl_fields }; /*% masters */ static cfg_tuplefielddef_t masters_fields[] = { { "name", &cfg_type_astring, 0 }, { "port", &cfg_type_optional_port, 0 }, { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_masters = { "masters", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, masters_fields }; /*% * "sockaddrkeylist", a list of socket addresses with optional keys * and an optional default port, as used in the masters option. * E.g., * "port 1234 { mymasters; 10.0.0.1 key foo; 1::2 port 69; }" */ static cfg_tuplefielddef_t namesockaddrkey_fields[] = { { "masterselement", &cfg_type_masterselement, 0 }, { "key", &cfg_type_optional_keyref, 0 }, { NULL, NULL, 0 }, }; static cfg_type_t cfg_type_namesockaddrkey = { "namesockaddrkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, namesockaddrkey_fields }; static cfg_type_t cfg_type_bracketed_namesockaddrkeylist = { "bracketed_namesockaddrkeylist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_namesockaddrkey }; static cfg_tuplefielddef_t namesockaddrkeylist_fields[] = { { "port", &cfg_type_optional_port, 0 }, { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_namesockaddrkeylist = { "sockaddrkeylist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, namesockaddrkeylist_fields }; /*% * A list of socket addresses with an optional default port, as used * in the lwresd 'listen-on' option. E.g., "{ 10.0.0.1; 1::2 port 69; }" */ static cfg_tuplefielddef_t portiplist_fields[] = { { "port", &cfg_type_optional_port, 0 }, { "addresses", &cfg_type_bracketed_sockaddrlist, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_portiplist = { "portiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, portiplist_fields }; /*% * A public key, as in the "pubkey" statement. */ static cfg_tuplefielddef_t pubkey_fields[] = { { "flags", &cfg_type_uint32, 0 }, { "protocol", &cfg_type_uint32, 0 }, { "algorithm", &cfg_type_uint32, 0 }, { "key", &cfg_type_qstring, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_pubkey = { "pubkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, pubkey_fields }; /*% * A list of RR types, used in grant statements. * Note that the old parser allows quotes around the RR type names. */ static cfg_type_t cfg_type_rrtypelist = { "rrtypelist", cfg_parse_spacelist, cfg_print_spacelist, cfg_doc_terminal, &cfg_rep_list, &cfg_type_astring }; static const char *mode_enums[] = { "grant", "deny", NULL }; static cfg_type_t cfg_type_mode = { "mode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &mode_enums }; static isc_result_t parse_matchtype(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "zonesub") == 0) { pctx->flags |= CFG_PCTX_SKIP; } return (cfg_parse_enum(pctx, type, ret)); cleanup: return (result); } static isc_result_t parse_matchname(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; if ((pctx->flags & CFG_PCTX_SKIP) != 0) { pctx->flags &= ~CFG_PCTX_SKIP; CHECK(cfg_parse_void(pctx, NULL, &obj)); } else result = cfg_parse_astring(pctx, type, &obj); *ret = obj; cleanup: return (result); } static void doc_matchname(cfg_printer_t *pctx, const cfg_type_t *type) { cfg_print_chars(pctx, "[ ", 2); cfg_doc_obj(pctx, type->of); cfg_print_chars(pctx, " ]", 2); } static const char *matchtype_enums[] = { "name", "subdomain", "wildcard", "self", "selfsub", "selfwild", "krb5-self", "ms-self", "krb5-subdomain", "ms-subdomain", "tcp-self", "6to4-self", "zonesub", "external", NULL }; static cfg_type_t cfg_type_matchtype = { "matchtype", parse_matchtype, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &matchtype_enums }; static cfg_type_t cfg_type_matchname = { "optional_matchname", parse_matchname, cfg_print_ustring, &doc_matchname, &cfg_rep_tuple, &cfg_type_ustring }; /*% * A grant statement, used in the update policy. */ static cfg_tuplefielddef_t grant_fields[] = { { "mode", &cfg_type_mode, 0 }, { "identity", &cfg_type_astring, 0 }, /* domain name */ { "matchtype", &cfg_type_matchtype, 0 }, { "name", &cfg_type_matchname, 0 }, /* domain name */ { "types", &cfg_type_rrtypelist, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_grant = { "grant", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, grant_fields }; static cfg_type_t cfg_type_updatepolicy = { "update_policy", parse_updatepolicy, print_updatepolicy, doc_updatepolicy, &cfg_rep_list, &cfg_type_grant }; static isc_result_t parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_gettoken(pctx, 0)); if (pctx->token.type == isc_tokentype_special && pctx->token.value.as_char == '{') { cfg_ungettoken(pctx); return (cfg_parse_bracketed_list(pctx, type, ret)); } if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "local") == 0) { cfg_obj_t *obj = NULL; CHECK(cfg_create_obj(pctx, &cfg_type_ustring, &obj)); obj->value.string.length = strlen("local"); obj->value.string.base = isc_mem_get(pctx->mctx, obj->value.string.length + 1); if (obj->value.string.base == NULL) { isc_mem_put(pctx->mctx, obj, sizeof(*obj)); return (ISC_R_NOMEMORY); } memmove(obj->value.string.base, "local", 5); obj->value.string.base[5] = '\0'; *ret = obj; return (ISC_R_SUCCESS); } cfg_ungettoken(pctx); return (ISC_R_UNEXPECTEDTOKEN); cleanup: return (result); } static void print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj) { if (cfg_obj_isstring(obj)) cfg_print_ustring(pctx, obj); else cfg_print_bracketed_list(pctx, obj); } static void doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type) { cfg_print_cstr(pctx, "( local | { "); cfg_doc_obj(pctx, type->of); cfg_print_cstr(pctx, "; ... }"); } /*% * A view statement. */ static cfg_tuplefielddef_t view_fields[] = { { "name", &cfg_type_astring, 0 }, { "class", &cfg_type_optional_class, 0 }, { "options", &cfg_type_viewopts, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_view = { "view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, view_fields }; /*% * A zone statement. */ static cfg_tuplefielddef_t zone_fields[] = { { "name", &cfg_type_astring, 0 }, { "class", &cfg_type_optional_class, 0 }, { "options", &cfg_type_zoneopts, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_zone = { "zone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, zone_fields }; /*% * A "category" clause in the "logging" statement. */ static cfg_tuplefielddef_t category_fields[] = { { "name", &cfg_type_astring, 0 }, { "destinations", &cfg_type_destinationlist,0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_category = { "category", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, category_fields }; /*% * A dnssec key, as used in the "trusted-keys" statement. */ static cfg_tuplefielddef_t dnsseckey_fields[] = { { "name", &cfg_type_astring, 0 }, { "flags", &cfg_type_uint32, 0 }, { "protocol", &cfg_type_uint32, 0 }, { "algorithm", &cfg_type_uint32, 0 }, { "key", &cfg_type_qstring, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_dnsseckey = { "dnsseckey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, dnsseckey_fields }; /*% * A managed key initialization specifier, as used in the * "managed-keys" statement. */ static cfg_tuplefielddef_t managedkey_fields[] = { { "name", &cfg_type_astring, 0 }, { "init", &cfg_type_ustring, 0 }, /* must be literal "initial-key" */ { "flags", &cfg_type_uint32, 0 }, { "protocol", &cfg_type_uint32, 0 }, { "algorithm", &cfg_type_uint32, 0 }, { "key", &cfg_type_qstring, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_managedkey = { "managedkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, managedkey_fields }; static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring }; static cfg_type_t cfg_type_optional_wild_class = { "optional_wild_class", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_class_kw }; static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring }; static cfg_type_t cfg_type_optional_wild_type = { "optional_wild_type", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_type_kw }; static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring }; static cfg_type_t cfg_type_optional_wild_name = { "optional_wild_name", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_name_kw }; /*% * An rrset ordering element. */ static cfg_tuplefielddef_t rrsetorderingelement_fields[] = { { "class", &cfg_type_optional_wild_class, 0 }, { "type", &cfg_type_optional_wild_type, 0 }, { "name", &cfg_type_optional_wild_name, 0 }, { "order", &cfg_type_ustring, 0 }, /* must be literal "order" */ { "ordering", &cfg_type_ustring, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_rrsetorderingelement = { "rrsetorderingelement", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, rrsetorderingelement_fields }; /*% * A global or view "check-names" option. Note that the zone * "check-names" option has a different syntax. */ static const char *checktype_enums[] = { "master", "slave", "response", NULL }; static cfg_type_t cfg_type_checktype = { "checktype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &checktype_enums }; static const char *checkmode_enums[] = { "fail", "warn", "ignore", NULL }; static cfg_type_t cfg_type_checkmode = { "checkmode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &checkmode_enums }; static const char *warn_enums[] = { "warn", "ignore", NULL }; static cfg_type_t cfg_type_warn = { "warn", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &warn_enums }; static cfg_tuplefielddef_t checknames_fields[] = { { "type", &cfg_type_checktype, 0 }, { "mode", &cfg_type_checkmode, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_checknames = { "checknames", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, checknames_fields }; static cfg_type_t cfg_type_bracketed_sockaddrlist = { "bracketed_sockaddrlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_sockaddr }; static const char *autodnssec_enums[] = { "allow", "maintain", "off", NULL }; static cfg_type_t cfg_type_autodnssec = { "autodnssec", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &autodnssec_enums }; static const char *dnssecupdatemode_enums[] = { "maintain", "no-resign", NULL }; static cfg_type_t cfg_type_dnssecupdatemode = { "dnssecupdatemode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &dnssecupdatemode_enums }; static const char *updatemethods_enums[] = { "increment", "unixtime", NULL }; static cfg_type_t cfg_type_updatemethod = { "updatemethod", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &updatemethods_enums }; /* * zone-statistics: full, terse, or none. * * for backward compatibility, we also support boolean values. * yes represents "full", no represents "terse". in the future we * may change no to mean "none". */ static const char *zonestat_enums[] = { "full", "terse", "none", NULL }; static isc_result_t parse_zonestat(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); } static cfg_type_t cfg_type_zonestat = { "zonestat", parse_zonestat, cfg_print_ustring, doc_enum_or_other, &cfg_rep_string, zonestat_enums }; static cfg_type_t cfg_type_rrsetorder = { "rrsetorder", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_rrsetorderingelement }; static keyword_type_t port_kw = { "port", &cfg_type_uint32 }; static cfg_type_t cfg_type_optional_port = { "optional_port", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_uint32, &port_kw }; /*% A list of keys, as in the "key" clause of the controls statement. */ static cfg_type_t cfg_type_keylist = { "keylist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring }; /*% A list of dnssec keys, as in "trusted-keys" */ static cfg_type_t cfg_type_dnsseckeys = { "dnsseckeys", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_dnsseckey }; /*% * A list of managed key entries, as in "trusted-keys". Currently * (9.7.0) this has a format similar to dnssec keys, except the keyname * is followed by the keyword "initial-key". In future releases, this * keyword may take other values indicating different methods for the * key to be initialized. */ static cfg_type_t cfg_type_managedkeys = { "managedkeys", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_managedkey }; static const char *forwardtype_enums[] = { "first", "only", NULL }; static cfg_type_t cfg_type_forwardtype = { "forwardtype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &forwardtype_enums }; static const char *zonetype_enums[] = { "master", "slave", "stub", "static-stub", "hint", "forward", "delegation-only", "redirect", NULL }; static cfg_type_t cfg_type_zonetype = { "zonetype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &zonetype_enums }; static const char *loglevel_enums[] = { "critical", "error", "warning", "notice", "info", "dynamic", NULL }; static cfg_type_t cfg_type_loglevel = { "loglevel", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &loglevel_enums }; static const char *transferformat_enums[] = { "many-answers", "one-answer", NULL }; static cfg_type_t cfg_type_transferformat = { "transferformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &transferformat_enums }; /*% * The special keyword "none", as used in the pid-file option. */ static void print_none(cfg_printer_t *pctx, const cfg_obj_t *obj) { UNUSED(obj); cfg_print_cstr(pctx, "none"); } static cfg_type_t cfg_type_none = { "none", NULL, print_none, NULL, &cfg_rep_void, NULL }; /*% * A quoted string or the special keyword "none". Used in the pid-file option. */ static isc_result_t parse_qstringornone(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "none") == 0) return (cfg_create_obj(pctx, &cfg_type_none, ret)); cfg_ungettoken(pctx); return (cfg_parse_qstring(pctx, type, ret)); cleanup: return (result); } static void doc_qstringornone(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, "( | none )"); } static cfg_type_t cfg_type_qstringornone = { "qstringornone", parse_qstringornone, NULL, doc_qstringornone, NULL, NULL }; /*% * A boolean ("yes" or "no"), or the special keyword "auto". * Used in the dnssec-validation option. */ static void print_auto(cfg_printer_t *pctx, const cfg_obj_t *obj) { UNUSED(obj); cfg_print_cstr(pctx, "auto"); } static cfg_type_t cfg_type_auto = { "auto", NULL, print_auto, NULL, &cfg_rep_void, NULL }; static isc_result_t parse_boolorauto(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "auto") == 0) return (cfg_create_obj(pctx, &cfg_type_auto, ret)); cfg_ungettoken(pctx); return (cfg_parse_boolean(pctx, type, ret)); cleanup: return (result); } static void print_boolorauto(cfg_printer_t *pctx, const cfg_obj_t *obj) { if (obj->type->rep == &cfg_rep_void) cfg_print_chars(pctx, "auto", 4); else if (obj->value.boolean) cfg_print_chars(pctx, "yes", 3); else cfg_print_chars(pctx, "no", 2); } static void doc_boolorauto(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, "( yes | no | auto )"); } static cfg_type_t cfg_type_boolorauto = { "boolorauto", parse_boolorauto, print_boolorauto, doc_boolorauto, NULL, NULL }; /*% * keyword hostname */ static void print_hostname(cfg_printer_t *pctx, const cfg_obj_t *obj) { UNUSED(obj); cfg_print_cstr(pctx, "hostname"); } static cfg_type_t cfg_type_hostname = { "hostname", NULL, print_hostname, NULL, &cfg_rep_boolean, NULL }; /*% * "server-id" argument. */ static isc_result_t parse_serverid(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "none") == 0) return (cfg_create_obj(pctx, &cfg_type_none, ret)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "hostname") == 0) { return (cfg_create_obj(pctx, &cfg_type_hostname, ret)); } cfg_ungettoken(pctx); return (cfg_parse_qstring(pctx, type, ret)); cleanup: return (result); } static void doc_serverid(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, "( | none | hostname )"); } static cfg_type_t cfg_type_serverid = { "serverid", parse_serverid, NULL, doc_serverid, NULL, NULL }; /*% * Port list. */ static cfg_tuplefielddef_t porttuple_fields[] = { { "loport", &cfg_type_uint32, 0 }, { "hiport", &cfg_type_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_porttuple = { "porttuple", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, porttuple_fields }; static isc_result_t parse_port(cfg_parser_t *pctx, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_parse_uint32(pctx, NULL, ret)); if ((*ret)->value.uint32 > 0xffff) { cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid port"); cfg_obj_destroy(pctx, ret); result = ISC_R_RANGE; } cleanup: return (result); } static isc_result_t parse_portrange(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; UNUSED(type); CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER)); if (pctx->token.type == isc_tokentype_number) CHECK(parse_port(pctx, ret)); else { CHECK(cfg_gettoken(pctx, 0)); if (pctx->token.type != isc_tokentype_string || strcasecmp(TOKEN_STRING(pctx), "range") != 0) { cfg_parser_error(pctx, CFG_LOG_NEAR, "expected integer or 'range'"); return (ISC_R_UNEXPECTEDTOKEN); } CHECK(cfg_create_tuple(pctx, &cfg_type_porttuple, &obj)); CHECK(parse_port(pctx, &obj->value.tuple[0])); CHECK(parse_port(pctx, &obj->value.tuple[1])); if (obj->value.tuple[0]->value.uint32 > obj->value.tuple[1]->value.uint32) { cfg_parser_error(pctx, CFG_LOG_NOPREP, "low port '%u' must not be larger " "than high port", obj->value.tuple[0]->value.uint32); result = ISC_R_RANGE; goto cleanup; } *ret = obj; obj = NULL; } cleanup: if (obj != NULL) cfg_obj_destroy(pctx, &obj); return (result); } static cfg_type_t cfg_type_portrange = { "portrange", parse_portrange, NULL, cfg_doc_terminal, NULL, NULL }; static cfg_type_t cfg_type_bracketed_portlist = { "bracketed_sockaddrlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_portrange }; /*% * Clauses that can be found within the top level of the named.conf * file only. */ static cfg_clausedef_t namedconf_clauses[] = { { "options", &cfg_type_options, 0 }, { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI }, { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI }, { "masters", &cfg_type_masters, CFG_CLAUSEFLAG_MULTI }, { "logging", &cfg_type_logging, 0 }, { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI }, { "lwres", &cfg_type_lwres, CFG_CLAUSEFLAG_MULTI }, { "statistics-channels", &cfg_type_statschannels, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; /*% * Clauses that can occur at the top level or in the view * statement, but not in the options block. */ static cfg_clausedef_t namedconf_or_view_clauses[] = { { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI }, /* only 1 DLZ per view allowed */ { "dlz", &cfg_type_dynamically_loadable_zones, 0 }, { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI }, { "trusted-keys", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI }, { "managed-keys", &cfg_type_managedkeys, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; /*% * Clauses that can occur in the bind.keys file. */ static cfg_clausedef_t bindkeys_clauses[] = { { "trusted-keys", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI }, { "managed-keys", &cfg_type_managedkeys, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; /*% * Clauses that can be found within the 'options' statement. */ static cfg_clausedef_t options_clauses[] = { { "avoid-v4-udp-ports", &cfg_type_bracketed_portlist, 0 }, { "avoid-v6-udp-ports", &cfg_type_bracketed_portlist, 0 }, { "bindkeys-file", &cfg_type_qstring, 0 }, { "blackhole", &cfg_type_bracketed_aml, 0 }, { "coresize", &cfg_type_size, 0 }, { "datasize", &cfg_type_size, 0 }, { "session-keyfile", &cfg_type_qstringornone, 0 }, { "session-keyname", &cfg_type_astring, 0 }, { "session-keyalg", &cfg_type_astring, 0 }, { "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK }, { "dump-file", &cfg_type_qstring, 0 }, { "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "files", &cfg_type_size, 0 }, { "flush-zones-on-shutdown", &cfg_type_boolean, 0 }, { "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "heartbeat-interval", &cfg_type_uint32, 0 }, { "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP }, { "host-statistics-max", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP }, { "hostname", &cfg_type_qstringornone, 0 }, { "interface-interval", &cfg_type_uint32, 0 }, { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI }, { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI }, { "managed-keys-directory", &cfg_type_qstring, 0 }, { "match-mapped-addresses", &cfg_type_boolean, 0 }, { "max-rsa-exponent-size", &cfg_type_uint32, 0 }, { "memstatistics-file", &cfg_type_qstring, 0 }, { "memstatistics", &cfg_type_boolean, 0 }, { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, { "pid-file", &cfg_type_qstringornone, 0 }, { "port", &cfg_type_uint32, 0 }, { "querylog", &cfg_type_boolean, 0 }, { "recursing-file", &cfg_type_qstring, 0 }, { "random-device", &cfg_type_qstring, 0 }, { "recursive-clients", &cfg_type_uint32, 0 }, { "reserved-sockets", &cfg_type_uint32, 0 }, { "secroots-file", &cfg_type_qstring, 0 }, { "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE }, { "serial-query-rate", &cfg_type_uint32, 0 }, { "server-id", &cfg_type_serverid, 0 }, { "stacksize", &cfg_type_size, 0 }, { "statistics-file", &cfg_type_qstring, 0 }, { "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI }, { "tcp-clients", &cfg_type_uint32, 0 }, { "tcp-listen-queue", &cfg_type_uint32, 0 }, { "tkey-dhkey", &cfg_type_tkey_dhkey, 0 }, { "tkey-gssapi-credential", &cfg_type_qstring, 0 }, { "tkey-gssapi-keytab", &cfg_type_qstring, 0 }, { "tkey-domain", &cfg_type_qstring, 0 }, { "transfers-per-ns", &cfg_type_uint32, 0 }, { "transfers-in", &cfg_type_uint32, 0 }, { "transfers-out", &cfg_type_uint32, 0 }, { "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "use-ixfr", &cfg_type_boolean, 0 }, { "use-v4-udp-ports", &cfg_type_bracketed_portlist, 0 }, { "use-v6-udp-ports", &cfg_type_bracketed_portlist, 0 }, { "version", &cfg_type_qstringornone, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_namelist = { "namelist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_qstring }; static keyword_type_t exclude_kw = { "exclude", &cfg_type_namelist }; static cfg_type_t cfg_type_optional_exclude = { "optional_exclude", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_list, &exclude_kw }; static keyword_type_t exceptionnames_kw = { "except-from", &cfg_type_namelist }; static cfg_type_t cfg_type_optional_exceptionnames = { "optional_allow", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_list, &exceptionnames_kw }; static cfg_tuplefielddef_t denyaddresses_fields[] = { { "acl", &cfg_type_bracketed_aml, 0 }, { "except-from", &cfg_type_optional_exceptionnames, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_denyaddresses = { "denyaddresses", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, denyaddresses_fields }; static cfg_tuplefielddef_t denyaliases_fields[] = { { "name", &cfg_type_namelist, 0 }, { "except-from", &cfg_type_optional_exceptionnames, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_denyaliases = { "denyaliases", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, denyaliases_fields }; static cfg_type_t cfg_type_algorithmlist = { "algorithmlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring }; static cfg_tuplefielddef_t disablealgorithm_fields[] = { { "name", &cfg_type_astring, 0 }, { "algorithms", &cfg_type_algorithmlist, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_disablealgorithm = { "disablealgorithm", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, disablealgorithm_fields }; static cfg_tuplefielddef_t mustbesecure_fields[] = { { "name", &cfg_type_astring, 0 }, { "value", &cfg_type_boolean, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_mustbesecure = { "mustbesecure", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, mustbesecure_fields }; static const char *masterformat_enums[] = { "text", "raw", NULL }; static cfg_type_t cfg_type_masterformat = { "masterformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string, &masterformat_enums }; /*% * response-policy { * zone [ policy (given|disabled|passthru| * nxdomain|nodata|cname ) ] * [ recursive-only yes|no ] [ max-policy-ttl number ] ; * } [ recursive-only yes|no ] [ max-policy-ttl number ] ; * [ break-dnssec yes|no ] [ min-ns-dots number ] ; */ static void doc_rpz_policy(cfg_printer_t *pctx, const cfg_type_t *type) { const char * const *p; /* * This is cfg_doc_enum() without the trailing " )". */ cfg_print_chars(pctx, "( ", 2); for (p = type->of; *p != NULL; p++) { cfg_print_cstr(pctx, *p); if (p[1] != NULL) cfg_print_chars(pctx, " | ", 3); } } static void doc_rpz_cname(cfg_printer_t *pctx, const cfg_type_t *type) { cfg_doc_terminal(pctx, type); cfg_print_chars(pctx, " )", 2); } /* * Parse * given|disabled|passthru|nxdomain|nodata|cname */ static isc_result_t cfg_parse_rpz_policy(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj; const cfg_tuplefielddef_t *fields; CHECK(cfg_create_tuple(pctx, type, &obj)); fields = type->of; CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0])); /* * parse cname domain only after "policy cname" */ if (strcasecmp("cname", cfg_obj_asstring(obj->value.tuple[0])) != 0) { CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1])); } else { CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1])); } *ret = obj; return (ISC_R_SUCCESS); cleanup: CLEANUP_OBJ(obj); return (result); } /* * Parse a tuple consisting of any kind of required field followed * by 2 or more optional keyvalues that can be in any order. */ static isc_result_t cfg_parse_kv_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { const cfg_tuplefielddef_t *fields, *f; cfg_obj_t *obj; int fn; isc_result_t result; obj = NULL; CHECK(cfg_create_tuple(pctx, type, &obj)); /* * The zone first field is required and always first. */ fields = type->of; CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0])); for (;;) { CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type != isc_tokentype_string) break; for (fn = 1, f = &fields[1]; ; ++fn, ++f) { if (f->name == NULL) { cfg_parser_error(pctx, 0, "unexpected '%s'", TOKEN_STRING(pctx)); result = ISC_R_UNEXPECTEDTOKEN; goto cleanup; } if (obj->value.tuple[fn] == NULL && strcasecmp(f->name, TOKEN_STRING(pctx)) == 0) break; } CHECK(cfg_gettoken(pctx, 0)); CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[fn])); } for (fn = 1, f = &fields[1]; f->name != NULL; ++fn, ++f) { if (obj->value.tuple[fn] == NULL) CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[fn])); } *ret = obj; return (ISC_R_SUCCESS); cleanup: CLEANUP_OBJ(obj); return (result); } static void cfg_print_kv_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) { unsigned int i; const cfg_tuplefielddef_t *fields, *f; const cfg_obj_t *fieldobj; fields = obj->type->of; for (f = fields, i = 0; f->name != NULL; f++, i++) { fieldobj = obj->value.tuple[i]; if (fieldobj->type->print == cfg_print_void) continue; if (i != 0) { cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, f->name); cfg_print_chars(pctx, " ", 1); } cfg_print_obj(pctx, fieldobj); } } static void cfg_doc_kv_tuple(cfg_printer_t *pctx, const cfg_type_t *type) { const cfg_tuplefielddef_t *fields, *f; fields = type->of; for (f = fields; f->name != NULL; f++) { if (f != fields) { cfg_print_chars(pctx, " [ ", 3); cfg_print_cstr(pctx, f->name); if (f->type->doc != cfg_doc_void) cfg_print_chars(pctx, " ", 1); } cfg_doc_obj(pctx, f->type); if (f != fields) cfg_print_chars(pctx, " ]", 2); } } static keyword_type_t zone_kw = {"zone", &cfg_type_qstring}; static cfg_type_t cfg_type_rpz_zone = { "zone", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_string, &zone_kw }; static const char *rpz_policies[] = { "given", "disabled", "passthru", "no-op", "nxdomain", "nodata", "cname", NULL }; static cfg_type_t cfg_type_rpz_policy_name = { "policy name", cfg_parse_enum, cfg_print_ustring, doc_rpz_policy, &cfg_rep_string, &rpz_policies }; static cfg_type_t cfg_type_rpz_cname = { "quoted_string", cfg_parse_astring, NULL, doc_rpz_cname, &cfg_rep_string, NULL }; static cfg_tuplefielddef_t rpz_policy_fields[] = { { "policy name", &cfg_type_rpz_policy_name, 0 }, { "cname", &cfg_type_rpz_cname, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_rpz_policy = { "policy tuple", cfg_parse_rpz_policy, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, rpz_policy_fields }; static cfg_tuplefielddef_t rpz_zone_fields[] = { { "zone name", &cfg_type_rpz_zone, 0 }, { "policy", &cfg_type_rpz_policy, 0 }, { "recursive-only", &cfg_type_boolean, 0 }, { "max-policy-ttl", &cfg_type_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_rpz_tuple = { "rpz tuple", cfg_parse_kv_tuple, cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple, rpz_zone_fields }; static cfg_type_t cfg_type_rpz_list = { "zone list", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_rpz_tuple }; static cfg_tuplefielddef_t rpz_fields[] = { { "zone list", &cfg_type_rpz_list, 0 }, { "recursive-only", &cfg_type_boolean, 0 }, { "break-dnssec", &cfg_type_boolean, 0 }, { "max-policy-ttl", &cfg_type_uint32, 0 }, { "min-ns-dots", &cfg_type_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_rpz = { "rpz", cfg_parse_kv_tuple, cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple, rpz_fields }; #ifdef USE_RRL /* * rate-limit */ static cfg_clausedef_t rrl_clauses[] = { { "responses-per-second", &cfg_type_uint32, 0 }, { "referrals-per-second", &cfg_type_uint32, 0 }, { "nodata-per-second", &cfg_type_uint32, 0 }, { "nxdomains-per-second", &cfg_type_uint32, 0 }, { "errors-per-second", &cfg_type_uint32, 0 }, { "all-per-second", &cfg_type_uint32, 0 }, { "slip", &cfg_type_uint32, 0 }, { "window", &cfg_type_uint32, 0 }, { "log-only", &cfg_type_boolean, 0 }, { "qps-scale", &cfg_type_uint32, 0 }, { "ipv4-prefix-length", &cfg_type_uint32, 0 }, { "ipv6-prefix-length", &cfg_type_uint32, 0 }, { "exempt-clients", &cfg_type_bracketed_aml, 0 }, { "max-table-size", &cfg_type_uint32, 0 }, { "min-table-size", &cfg_type_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t *rrl_clausesets[] = { rrl_clauses, NULL }; static cfg_type_t cfg_type_rrl = { "rate-limit", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, rrl_clausesets }; #endif /* USE_RRL */ /*% * dnssec-lookaside */ static void print_lookaside(cfg_printer_t *pctx, const cfg_obj_t *obj) { const cfg_obj_t *domain = obj->value.tuple[0]; if (domain->value.string.length == 4 && strncmp(domain->value.string.base, "auto", 4) == 0) cfg_print_cstr(pctx, "auto"); else cfg_print_tuple(pctx, obj); } static void doc_lookaside(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, "( trust-anchor | auto | no )"); } static keyword_type_t trustanchor_kw = { "trust-anchor", &cfg_type_astring }; static cfg_type_t cfg_type_optional_trustanchor = { "optional_trustanchor", parse_optional_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_string, &trustanchor_kw }; static cfg_tuplefielddef_t lookaside_fields[] = { { "domain", &cfg_type_astring, 0 }, { "trust-anchor", &cfg_type_optional_trustanchor, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_lookaside = { "lookaside", cfg_parse_tuple, print_lookaside, doc_lookaside, &cfg_rep_tuple, lookaside_fields }; /* * DNS64. */ static cfg_clausedef_t dns64_clauses[] = { { "clients", &cfg_type_bracketed_aml, 0 }, { "mapped", &cfg_type_bracketed_aml, 0 }, { "exclude", &cfg_type_bracketed_aml, 0 }, { "suffix", &cfg_type_netaddr6, 0 }, { "recursive-only", &cfg_type_boolean, 0 }, { "break-dnssec", &cfg_type_boolean, 0 }, { NULL, NULL, 0 }, }; static cfg_clausedef_t * dns64_clausesets[] = { dns64_clauses, NULL }; static cfg_type_t cfg_type_dns64 = { "dns64", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, dns64_clausesets }; /*% * Clauses that can be found within the 'view' statement, * with defaults in the 'options' statement. */ static cfg_clausedef_t view_clauses[] = { { "acache-cleaning-interval", &cfg_type_uint32, 0 }, { "acache-enable", &cfg_type_boolean, 0 }, { "additional-from-auth", &cfg_type_boolean, 0 }, { "additional-from-cache", &cfg_type_boolean, 0 }, { "allow-new-zones", &cfg_type_boolean, 0 }, { "allow-query-cache", &cfg_type_bracketed_aml, 0 }, { "allow-query-cache-on", &cfg_type_bracketed_aml, 0 }, { "allow-recursion", &cfg_type_bracketed_aml, 0 }, { "allow-recursion-on", &cfg_type_bracketed_aml, 0 }, { "allow-v6-synthesis", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_OBSOLETE }, { "attach-cache", &cfg_type_astring, 0 }, { "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT }, { "cache-file", &cfg_type_qstring, 0 }, { "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI }, { "cleaning-interval", &cfg_type_uint32, 0 }, { "clients-per-query", &cfg_type_uint32, 0 }, { "deny-answer-addresses", &cfg_type_denyaddresses, 0 }, { "deny-answer-aliases", &cfg_type_denyaliases, 0 }, { "disable-algorithms", &cfg_type_disablealgorithm, CFG_CLAUSEFLAG_MULTI }, { "disable-empty-zone", &cfg_type_astring, CFG_CLAUSEFLAG_MULTI }, { "dns64", &cfg_type_dns64, CFG_CLAUSEFLAG_MULTI }, { "dns64-server", &cfg_type_astring, 0 }, { "dns64-contact", &cfg_type_astring, 0 }, { "dnssec-accept-expired", &cfg_type_boolean, 0 }, { "dnssec-enable", &cfg_type_boolean, 0 }, { "dnssec-lookaside", &cfg_type_lookaside, CFG_CLAUSEFLAG_MULTI }, { "dnssec-must-be-secure", &cfg_type_mustbesecure, CFG_CLAUSEFLAG_MULTI }, { "dnssec-validation", &cfg_type_boolorauto, 0 }, { "dual-stack-servers", &cfg_type_nameportiplist, 0 }, { "edns-udp-size", &cfg_type_uint32, 0 }, { "empty-contact", &cfg_type_astring, 0 }, { "empty-server", &cfg_type_astring, 0 }, { "empty-zones-enable", &cfg_type_boolean, 0 }, { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 }, { "lame-ttl", &cfg_type_uint32, 0 }, { "max-acache-size", &cfg_type_sizenodefault, 0 }, { "max-cache-size", &cfg_type_sizenodefault, 0 }, { "max-cache-ttl", &cfg_type_uint32, 0 }, { "max-clients-per-query", &cfg_type_uint32, 0 }, { "max-ncache-ttl", &cfg_type_uint32, 0 }, + { "max-recursion-depth", &cfg_type_uint32, 0 }, { "max-udp-size", &cfg_type_uint32, 0 }, { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP }, { "minimal-responses", &cfg_type_boolean, 0 }, { "preferred-glue", &cfg_type_astring, 0 }, { "provide-ixfr", &cfg_type_boolean, 0 }, /* * Note that the query-source option syntax is different * from the other -source options. */ { "query-source", &cfg_type_querysource4, 0 }, { "query-source-v6", &cfg_type_querysource6, 0 }, { "queryport-pool-ports", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE }, { "queryport-pool-updateinterval", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE }, { "recursion", &cfg_type_boolean, 0 }, { "request-nsid", &cfg_type_boolean, 0 }, { "resolver-query-timeout", &cfg_type_uint32, 0 }, { "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI }, { "root-delegation-only", &cfg_type_optional_exclude, 0 }, { "rrset-order", &cfg_type_rrsetorder, 0 }, { "sortlist", &cfg_type_bracketed_aml, 0 }, { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI }, { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP }, { "transfer-format", &cfg_type_transferformat, 0 }, { "use-queryport-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "zero-no-soa-ttl-cache", &cfg_type_boolean, 0 }, #ifdef ALLOW_FILTER_AAAA_ON_V4 { "filter-aaaa", &cfg_type_bracketed_aml, 0 }, { "filter-aaaa-on-v4", &cfg_type_v4_aaaa, 0 }, #else { "filter-aaaa", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTCONFIGURED }, { "filter-aaaa-on-v4", &cfg_type_v4_aaaa, CFG_CLAUSEFLAG_NOTCONFIGURED }, #endif { "response-policy", &cfg_type_rpz, 0 }, #ifdef USE_RRL { "rate-limit", &cfg_type_rrl, 0 }, #endif /* USE_RRL */ { NULL, NULL, 0 } }; /*% * Clauses that can be found within the 'view' statement only. */ static cfg_clausedef_t view_only_clauses[] = { { "match-clients", &cfg_type_bracketed_aml, 0 }, { "match-destinations", &cfg_type_bracketed_aml, 0 }, { "match-recursive-only", &cfg_type_boolean, 0 }, { NULL, NULL, 0 } }; /*% * Sig-validity-interval. */ static isc_result_t parse_optional_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; UNUSED(type); CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER)); if (pctx->token.type == isc_tokentype_number) { CHECK(cfg_parse_obj(pctx, &cfg_type_uint32, ret)); } else { CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret)); } cleanup: return (result); } static void doc_optional_uint32(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, "[ ]"); } static cfg_type_t cfg_type_optional_uint32 = { "optional_uint32", parse_optional_uint32, NULL, doc_optional_uint32, NULL, NULL }; static cfg_tuplefielddef_t validityinterval_fields[] = { { "validity", &cfg_type_uint32, 0 }, { "re-sign", &cfg_type_optional_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_validityinterval = { "validityinterval", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, validityinterval_fields }; /*% * Clauses that can be found in a 'zone' statement, * with defaults in the 'view' or 'options' statement. */ static cfg_clausedef_t zone_clauses[] = { { "allow-notify", &cfg_type_bracketed_aml, 0 }, { "allow-query", &cfg_type_bracketed_aml, 0 }, { "allow-query-on", &cfg_type_bracketed_aml, 0 }, { "allow-transfer", &cfg_type_bracketed_aml, 0 }, { "allow-update", &cfg_type_bracketed_aml, 0 }, { "allow-update-forwarding", &cfg_type_bracketed_aml, 0 }, { "also-notify", &cfg_type_namesockaddrkeylist, 0 }, { "alt-transfer-source", &cfg_type_sockaddr4wild, 0 }, { "alt-transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, { "auto-dnssec", &cfg_type_autodnssec, 0 }, { "check-dup-records", &cfg_type_checkmode, 0 }, { "check-integrity", &cfg_type_boolean, 0 }, { "check-mx", &cfg_type_checkmode, 0 }, { "check-mx-cname", &cfg_type_checkmode, 0 }, { "check-sibling", &cfg_type_boolean, 0 }, { "check-spf", &cfg_type_warn, 0 }, { "check-srv-cname", &cfg_type_checkmode, 0 }, { "check-wildcard", &cfg_type_boolean, 0 }, { "dialup", &cfg_type_dialuptype, 0 }, { "dnssec-dnskey-kskonly", &cfg_type_boolean, 0 }, { "dnssec-loadkeys-interval", &cfg_type_uint32, 0 }, { "dnssec-secure-to-insecure", &cfg_type_boolean, 0 }, { "dnssec-update-mode", &cfg_type_dnssecupdatemode, 0 }, { "forward", &cfg_type_forwardtype, 0 }, { "forwarders", &cfg_type_portiplist, 0 }, { "key-directory", &cfg_type_qstring, 0 }, { "maintain-ixfr-base", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "masterfile-format", &cfg_type_masterformat, 0 }, { "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE }, { "max-journal-size", &cfg_type_sizenodefault, 0 }, { "max-refresh-time", &cfg_type_uint32, 0 }, { "max-retry-time", &cfg_type_uint32, 0 }, { "max-transfer-idle-in", &cfg_type_uint32, 0 }, { "max-transfer-idle-out", &cfg_type_uint32, 0 }, { "max-transfer-time-in", &cfg_type_uint32, 0 }, { "max-transfer-time-out", &cfg_type_uint32, 0 }, { "min-refresh-time", &cfg_type_uint32, 0 }, { "min-retry-time", &cfg_type_uint32, 0 }, { "multi-master", &cfg_type_boolean, 0 }, { "notify", &cfg_type_notifytype, 0 }, { "notify-delay", &cfg_type_uint32, 0 }, { "notify-source", &cfg_type_sockaddr4wild, 0 }, { "notify-source-v6", &cfg_type_sockaddr6wild, 0 }, { "notify-to-soa", &cfg_type_boolean, 0 }, { "nsec3-test-zone", &cfg_type_boolean, CFG_CLAUSEFLAG_TESTONLY }, { "serial-update-method", &cfg_type_updatemethod, 0 }, { "request-ixfr", &cfg_type_boolean, 0 }, { "sig-signing-nodes", &cfg_type_uint32, 0 }, { "sig-signing-signatures", &cfg_type_uint32, 0 }, { "sig-signing-type", &cfg_type_uint32, 0 }, { "sig-validity-interval", &cfg_type_validityinterval, 0 }, { "inline-signing", &cfg_type_boolean, 0 }, { "transfer-source", &cfg_type_sockaddr4wild, 0 }, { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, { "try-tcp-refresh", &cfg_type_boolean, 0 }, { "update-check-ksk", &cfg_type_boolean, 0 }, { "use-alt-transfer-source", &cfg_type_boolean, 0 }, { "zero-no-soa-ttl", &cfg_type_boolean, 0 }, { "zone-statistics", &cfg_type_zonestat, 0 }, { NULL, NULL, 0 } }; /*% * Clauses that can be found in a 'zone' statement * only. */ static cfg_clausedef_t zone_only_clauses[] = { { "type", &cfg_type_zonetype, 0 }, { "file", &cfg_type_qstring, 0 }, { "journal", &cfg_type_qstring, 0 }, { "ixfr-base", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, { "ixfr-tmp-file", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, { "masters", &cfg_type_namesockaddrkeylist, 0 }, { "pubkey", &cfg_type_pubkey, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE }, { "update-policy", &cfg_type_updatepolicy, 0 }, { "database", &cfg_type_astring, 0 }, { "delegation-only", &cfg_type_boolean, 0 }, /* * Note that the format of the check-names option is different between * the zone options and the global/view options. Ugh. */ { "check-names", &cfg_type_checkmode, 0 }, { "ixfr-from-differences", &cfg_type_boolean, 0 }, { "server-addresses", &cfg_type_bracketed_sockaddrlist, 0 }, { "server-names", &cfg_type_namelist, 0 }, { NULL, NULL, 0 } }; /*% The top-level named.conf syntax. */ static cfg_clausedef_t * namedconf_clausesets[] = { namedconf_clauses, namedconf_or_view_clauses, NULL }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = { "namedconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, namedconf_clausesets }; /*% The bind.keys syntax (trusted-keys/managed-keys only). */ static cfg_clausedef_t * bindkeys_clausesets[] = { bindkeys_clauses, NULL }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_bindkeys = { "bindkeys", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, bindkeys_clausesets }; /*% The new-zone-file syntax (for zones added by 'rndc addzone') */ static cfg_clausedef_t newzones_clauses[] = { { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; static cfg_clausedef_t * newzones_clausesets[] = { newzones_clauses, NULL }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_newzones = { "newzones", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, newzones_clausesets }; /*% The "options" statement syntax. */ static cfg_clausedef_t * options_clausesets[] = { options_clauses, view_clauses, zone_clauses, NULL }; static cfg_type_t cfg_type_options = { "options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, options_clausesets }; /*% The "view" statement syntax. */ static cfg_clausedef_t * view_clausesets[] = { view_only_clauses, namedconf_or_view_clauses, view_clauses, zone_clauses, dynamically_loadable_zones_clauses, NULL }; static cfg_type_t cfg_type_viewopts = { "view", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, view_clausesets }; /*% The "zone" statement syntax. */ static cfg_clausedef_t * zone_clausesets[] = { zone_only_clauses, zone_clauses, NULL }; static cfg_type_t cfg_type_zoneopts = { "zoneopts", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, zone_clausesets }; /*% The "dynamically loadable zones" statement syntax. */ static cfg_clausedef_t * dynamically_loadable_zones_clausesets[] = { dynamically_loadable_zones_clauses, NULL }; static cfg_type_t cfg_type_dynamically_loadable_zones_opts = { "dynamically_loadable_zones_opts", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, dynamically_loadable_zones_clausesets }; /*% * Clauses that can be found within the 'key' statement. */ static cfg_clausedef_t key_clauses[] = { { "algorithm", &cfg_type_astring, 0 }, { "secret", &cfg_type_sstring, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * key_clausesets[] = { key_clauses, NULL }; static cfg_type_t cfg_type_key = { "key", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, key_clausesets }; /*% * Clauses that can be found in a 'server' statement. */ static cfg_clausedef_t server_clauses[] = { { "bogus", &cfg_type_boolean, 0 }, { "provide-ixfr", &cfg_type_boolean, 0 }, { "request-ixfr", &cfg_type_boolean, 0 }, { "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "transfers", &cfg_type_uint32, 0 }, { "transfer-format", &cfg_type_transferformat, 0 }, { "keys", &cfg_type_server_key_kludge, 0 }, { "edns", &cfg_type_boolean, 0 }, { "edns-udp-size", &cfg_type_uint32, 0 }, { "max-udp-size", &cfg_type_uint32, 0 }, { "notify-source", &cfg_type_sockaddr4wild, 0 }, { "notify-source-v6", &cfg_type_sockaddr6wild, 0 }, { "query-source", &cfg_type_querysource4, 0 }, { "query-source-v6", &cfg_type_querysource6, 0 }, { "transfer-source", &cfg_type_sockaddr4wild, 0 }, { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * server_clausesets[] = { server_clauses, NULL }; static cfg_type_t cfg_type_server = { "server", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, server_clausesets }; /*% * Clauses that can be found in a 'channel' clause in the * 'logging' statement. * * These have some additional constraints that need to be * checked after parsing: * - There must exactly one of file/syslog/null/stderr * */ static cfg_clausedef_t channel_clauses[] = { /* Destinations. We no longer require these to be first. */ { "file", &cfg_type_logfile, 0 }, { "syslog", &cfg_type_optional_facility, 0 }, { "null", &cfg_type_void, 0 }, { "stderr", &cfg_type_void, 0 }, /* Options. We now accept these for the null channel, too. */ { "severity", &cfg_type_logseverity, 0 }, { "print-time", &cfg_type_boolean, 0 }, { "print-severity", &cfg_type_boolean, 0 }, { "print-category", &cfg_type_boolean, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * channel_clausesets[] = { channel_clauses, NULL }; static cfg_type_t cfg_type_channel = { "channel", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, channel_clausesets }; /*% A list of log destination, used in the "category" clause. */ static cfg_type_t cfg_type_destinationlist = { "destinationlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring }; /*% * Clauses that can be found in a 'logging' statement. */ static cfg_clausedef_t logging_clauses[] = { { "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI }, { "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; static cfg_clausedef_t * logging_clausesets[] = { logging_clauses, NULL }; static cfg_type_t cfg_type_logging = { "logging", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, logging_clausesets }; /*% * For parsing an 'addzone' statement */ static cfg_tuplefielddef_t addzone_fields[] = { { "name", &cfg_type_astring, 0 }, { "class", &cfg_type_optional_class, 0 }, { "view", &cfg_type_optional_class, 0 }, { "options", &cfg_type_zoneopts, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_addzone = { "addzone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, addzone_fields }; static cfg_clausedef_t addzoneconf_clauses[] = { { "addzone", &cfg_type_addzone, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * addzoneconf_clausesets[] = { addzoneconf_clauses, NULL }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_addzoneconf = { "addzoneconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, addzoneconf_clausesets }; static isc_result_t parse_unitstring(char *str, isc_resourcevalue_t *valuep) { char *endp; unsigned int len; isc_uint64_t value; isc_uint64_t unit; value = isc_string_touint64(str, &endp, 10); if (*endp == 0) { *valuep = value; return (ISC_R_SUCCESS); } len = strlen(str); if (len < 2 || endp[1] != '\0') return (ISC_R_FAILURE); switch (str[len - 1]) { case 'k': case 'K': unit = 1024; break; case 'm': case 'M': unit = 1024 * 1024; break; case 'g': case 'G': unit = 1024 * 1024 * 1024; break; default: return (ISC_R_FAILURE); } if (value > ISC_UINT64_MAX / unit) return (ISC_R_FAILURE); *valuep = value * unit; return (ISC_R_SUCCESS); } static isc_result_t parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; isc_uint64_t val; UNUSED(type); CHECK(cfg_gettoken(pctx, 0)); if (pctx->token.type != isc_tokentype_string) { result = ISC_R_UNEXPECTEDTOKEN; goto cleanup; } CHECK(parse_unitstring(TOKEN_STRING(pctx), &val)); CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj)); obj->value.uint64 = val; *ret = obj; return (ISC_R_SUCCESS); cleanup: cfg_parser_error(pctx, CFG_LOG_NEAR, "expected integer and optional unit"); return (result); } /*% * A size value (number + optional unit). */ static cfg_type_t cfg_type_sizeval = { "sizeval", parse_sizeval, cfg_print_uint64, cfg_doc_terminal, &cfg_rep_uint64, NULL }; /*% * A size, "unlimited", or "default". */ static isc_result_t parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret)); } static const char *size_enums[] = { "unlimited", "default", NULL }; static cfg_type_t cfg_type_size = { "size", parse_size, cfg_print_ustring, cfg_doc_terminal, &cfg_rep_string, size_enums }; /*% * A size or "unlimited", but not "default". */ static const char *sizenodefault_enums[] = { "unlimited", NULL }; static cfg_type_t cfg_type_sizenodefault = { "size_no_default", parse_size, cfg_print_ustring, cfg_doc_terminal, &cfg_rep_string, sizenodefault_enums }; /*% * optional_keyvalue */ static isc_result_t parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, isc_boolean_t optional, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; const keyword_type_t *kw = type->of; CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), kw->name) == 0) { CHECK(cfg_gettoken(pctx, 0)); CHECK(kw->type->parse(pctx, kw->type, &obj)); obj->type = type; /* XXX kludge */ } else { if (optional) { CHECK(cfg_parse_void(pctx, NULL, &obj)); } else { cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '%s'", kw->name); result = ISC_R_UNEXPECTEDTOKEN; goto cleanup; } } *ret = obj; cleanup: return (result); } static isc_result_t parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype, const cfg_type_t *othertype, cfg_obj_t **ret) { isc_result_t result; CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string && cfg_is_enum(TOKEN_STRING(pctx), enumtype->of)) { CHECK(cfg_parse_enum(pctx, enumtype, ret)); } else { CHECK(cfg_parse_obj(pctx, othertype, ret)); } cleanup: return (result); } static void doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *type) { cfg_doc_terminal(pctx, type); #if 0 /* XXX */ cfg_print_chars(pctx, "( ", 2);... #endif } static isc_result_t parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_maybe_optional_keyvalue(pctx, type, ISC_FALSE, ret)); } static isc_result_t parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_maybe_optional_keyvalue(pctx, type, ISC_TRUE, ret)); } static void print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj) { const keyword_type_t *kw = obj->type->of; cfg_print_cstr(pctx, kw->name); cfg_print_chars(pctx, " ", 1); kw->type->print(pctx, obj); } static void doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) { const keyword_type_t *kw = type->of; cfg_print_cstr(pctx, kw->name); cfg_print_chars(pctx, " ", 1); cfg_doc_obj(pctx, kw->type); } static void doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) { const keyword_type_t *kw = type->of; cfg_print_chars(pctx, "[ ", 2); cfg_print_cstr(pctx, kw->name); cfg_print_chars(pctx, " ", 1); cfg_doc_obj(pctx, kw->type); cfg_print_chars(pctx, " ]", 2); } static const char *dialup_enums[] = { "notify", "notify-passive", "refresh", "passive", NULL }; static isc_result_t parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); } static cfg_type_t cfg_type_dialuptype = { "dialuptype", parse_dialup_type, cfg_print_ustring, doc_enum_or_other, &cfg_rep_string, dialup_enums }; static const char *notify_enums[] = { "explicit", "master-only", NULL }; static isc_result_t parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); } static cfg_type_t cfg_type_notifytype = { "notifytype", parse_notify_type, cfg_print_ustring, doc_enum_or_other, &cfg_rep_string, notify_enums, }; static const char *ixfrdiff_enums[] = { "master", "slave", NULL }; static isc_result_t parse_ixfrdiff_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); } static cfg_type_t cfg_type_ixfrdifftype = { "ixfrdiff", parse_ixfrdiff_type, cfg_print_ustring, doc_enum_or_other, &cfg_rep_string, ixfrdiff_enums, }; static const char *v4_aaaa_enums[] = { "break-dnssec", NULL }; static isc_result_t parse_v4_aaaa(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); } static cfg_type_t cfg_type_v4_aaaa = { "v4_aaaa", parse_v4_aaaa, cfg_print_ustring, doc_enum_or_other, &cfg_rep_string, v4_aaaa_enums, }; static keyword_type_t key_kw = { "key", &cfg_type_astring }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = { "keyref", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_string, &key_kw }; static cfg_type_t cfg_type_optional_keyref = { "optional_keyref", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &key_kw }; /*% * A "controls" statement is represented as a map with the multivalued * "inet" and "unix" clauses. */ static keyword_type_t controls_allow_kw = { "allow", &cfg_type_bracketed_aml }; static cfg_type_t cfg_type_controls_allow = { "controls_allow", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_list, &controls_allow_kw }; static keyword_type_t controls_keys_kw = { "keys", &cfg_type_keylist }; static cfg_type_t cfg_type_controls_keys = { "controls_keys", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_list, &controls_keys_kw }; static cfg_tuplefielddef_t inetcontrol_fields[] = { { "address", &cfg_type_controls_sockaddr, 0 }, { "allow", &cfg_type_controls_allow, 0 }, { "keys", &cfg_type_controls_keys, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_inetcontrol = { "inetcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, inetcontrol_fields }; static keyword_type_t controls_perm_kw = { "perm", &cfg_type_uint32 }; static cfg_type_t cfg_type_controls_perm = { "controls_perm", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_uint32, &controls_perm_kw }; static keyword_type_t controls_owner_kw = { "owner", &cfg_type_uint32 }; static cfg_type_t cfg_type_controls_owner = { "controls_owner", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_uint32, &controls_owner_kw }; static keyword_type_t controls_group_kw = { "group", &cfg_type_uint32 }; static cfg_type_t cfg_type_controls_group = { "controls_allow", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_uint32, &controls_group_kw }; static cfg_tuplefielddef_t unixcontrol_fields[] = { { "path", &cfg_type_qstring, 0 }, { "perm", &cfg_type_controls_perm, 0 }, { "owner", &cfg_type_controls_owner, 0 }, { "group", &cfg_type_controls_group, 0 }, { "keys", &cfg_type_controls_keys, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_unixcontrol = { "unixcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, unixcontrol_fields }; static cfg_clausedef_t controls_clauses[] = { { "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI }, { "unix", &cfg_type_unixcontrol, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; static cfg_clausedef_t * controls_clausesets[] = { controls_clauses, NULL }; static cfg_type_t cfg_type_controls = { "controls", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, &controls_clausesets }; /*% * A "statistics-channels" statement is represented as a map with the * multivalued "inet" clauses. */ static void doc_optional_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) { const keyword_type_t *kw = type->of; cfg_print_chars(pctx, "[ ", 2); cfg_print_cstr(pctx, kw->name); cfg_print_chars(pctx, " ", 1); cfg_doc_obj(pctx, kw->type); cfg_print_chars(pctx, " ]", 2); } static cfg_type_t cfg_type_optional_allow = { "optional_allow", parse_optional_keyvalue, print_keyvalue, doc_optional_bracketed_list, &cfg_rep_list, &controls_allow_kw }; static cfg_tuplefielddef_t statserver_fields[] = { { "address", &cfg_type_controls_sockaddr, 0 }, /* reuse controls def */ { "allow", &cfg_type_optional_allow, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_statschannel = { "statschannel", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, statserver_fields }; static cfg_clausedef_t statservers_clauses[] = { { "inet", &cfg_type_statschannel, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } }; static cfg_clausedef_t * statservers_clausesets[] = { statservers_clauses, NULL }; static cfg_type_t cfg_type_statschannels = { "statistics-channels", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, &statservers_clausesets }; /*% * An optional class, as used in view and zone statements. */ static isc_result_t parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; UNUSED(type); CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string) CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, ret)); else CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret)); cleanup: return (result); } static cfg_type_t cfg_type_optional_class = { "optional_class", parse_optional_class, NULL, cfg_doc_terminal, NULL, NULL }; static isc_result_t parse_querysource(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; isc_netaddr_t netaddr; in_port_t port; unsigned int have_address = 0; unsigned int have_port = 0; const unsigned int *flagp = type->of; if ((*flagp & CFG_ADDR_V4OK) != 0) isc_netaddr_any(&netaddr); else if ((*flagp & CFG_ADDR_V6OK) != 0) isc_netaddr_any6(&netaddr); else INSIST(0); port = 0; for (;;) { CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string) { if (strcasecmp(TOKEN_STRING(pctx), "address") == 0) { /* read "address" */ CHECK(cfg_gettoken(pctx, 0)); CHECK(cfg_parse_rawaddr(pctx, *flagp, &netaddr)); have_address++; } else if (strcasecmp(TOKEN_STRING(pctx), "port") == 0) { /* read "port" */ CHECK(cfg_gettoken(pctx, 0)); CHECK(cfg_parse_rawport(pctx, CFG_ADDR_WILDOK, &port)); have_port++; } else if (have_port == 0 && have_address == 0) { return (cfg_parse_sockaddr(pctx, type, ret)); } else { cfg_parser_error(pctx, CFG_LOG_NEAR, "expected 'address' or 'port'"); return (ISC_R_UNEXPECTEDTOKEN); } } else break; } if (have_address > 1 || have_port > 1 || have_address + have_port == 0) { cfg_parser_error(pctx, 0, "expected one address and/or port"); return (ISC_R_UNEXPECTEDTOKEN); } CHECK(cfg_create_obj(pctx, &cfg_type_querysource, &obj)); isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port); *ret = obj; return (ISC_R_SUCCESS); cleanup: cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source"); CLEANUP_OBJ(obj); return (result); } static void print_querysource(cfg_printer_t *pctx, const cfg_obj_t *obj) { isc_netaddr_t na; isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr); cfg_print_cstr(pctx, "address "); cfg_print_rawaddr(pctx, &na); cfg_print_cstr(pctx, " port "); cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr)); } static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK; static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK; static cfg_type_t cfg_type_querysource4 = { "querysource4", parse_querysource, NULL, cfg_doc_terminal, NULL, &sockaddr4wild_flags }; static cfg_type_t cfg_type_querysource6 = { "querysource6", parse_querysource, NULL, cfg_doc_terminal, NULL, &sockaddr6wild_flags }; static cfg_type_t cfg_type_querysource = { "querysource", NULL, print_querysource, NULL, &cfg_rep_sockaddr, NULL }; /*% addrmatchelt */ static isc_result_t parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; UNUSED(type); CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string || pctx->token.type == isc_tokentype_qstring) { if (pctx->token.type == isc_tokentype_string && (strcasecmp(TOKEN_STRING(pctx), "key") == 0)) { CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret)); } else { if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK | CFG_ADDR_V6OK)) { CHECK(cfg_parse_netprefix(pctx, NULL, ret)); } else { CHECK(cfg_parse_astring(pctx, NULL, ret)); } } } else if (pctx->token.type == isc_tokentype_special) { if (pctx->token.value.as_char == '{') { /* Nested match list. */ CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_aml, ret)); } else if (pctx->token.value.as_char == '!') { CHECK(cfg_gettoken(pctx, 0)); /* read "!" */ CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret)); } else { goto bad; } } else { bad: cfg_parser_error(pctx, CFG_LOG_NEAR, "expected IP match list element"); return (ISC_R_UNEXPECTEDTOKEN); } cleanup: return (result); } /*% * A negated address match list element (like "! 10.0.0.1"). * Somewhat sneakily, the caller is expected to parse the * "!", but not to print it. */ static cfg_tuplefielddef_t negated_fields[] = { { "value", &cfg_type_addrmatchelt, 0 }, { NULL, NULL, 0 } }; static void print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) { cfg_print_chars(pctx, "!", 1); cfg_print_tuple(pctx, obj); } static cfg_type_t cfg_type_negated = { "negated", cfg_parse_tuple, print_negated, NULL, &cfg_rep_tuple, &negated_fields }; /*% An address match list element */ static cfg_type_t cfg_type_addrmatchelt = { "address_match_element", parse_addrmatchelt, NULL, cfg_doc_terminal, NULL, NULL }; /*% A bracketed address match list */ static cfg_type_t cfg_type_bracketed_aml = { "bracketed_aml", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_addrmatchelt }; /*% * The socket address syntax in the "controls" statement is silly. * It allows both socket address families, but also allows "*", * whis is gratuitously interpreted as the IPv4 wildcard address. */ static unsigned int controls_sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK | CFG_ADDR_WILDOK; static cfg_type_t cfg_type_controls_sockaddr = { "controls_sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr, &cfg_rep_sockaddr, &controls_sockaddr_flags }; /*% * Handle the special kludge syntax of the "keys" clause in the "server" * statement, which takes a single key with or without braces and semicolon. */ static isc_result_t parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; isc_boolean_t braces = ISC_FALSE; UNUSED(type); /* Allow opening brace. */ CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_special && pctx->token.value.as_char == '{') { CHECK(cfg_gettoken(pctx, 0)); braces = ISC_TRUE; } CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret)); if (braces) { /* Skip semicolon if present. */ CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_special && pctx->token.value.as_char == ';') CHECK(cfg_gettoken(pctx, 0)); CHECK(cfg_parse_special(pctx, '}')); } cleanup: return (result); } static cfg_type_t cfg_type_server_key_kludge = { "server_key", parse_server_key_kludge, NULL, cfg_doc_terminal, NULL, NULL }; /*% * An optional logging facility. */ static isc_result_t parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; UNUSED(type); CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string || pctx->token.type == isc_tokentype_qstring) { CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret)); } else { CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret)); } cleanup: return (result); } static cfg_type_t cfg_type_optional_facility = { "optional_facility", parse_optional_facility, NULL, cfg_doc_terminal, NULL, NULL }; /*% * A log severity. Return as a string, except "debug N", * which is returned as a keyword object. */ static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 }; static cfg_type_t cfg_type_debuglevel = { "debuglevel", parse_keyvalue, print_keyvalue, doc_keyvalue, &cfg_rep_uint32, &debug_kw }; static isc_result_t parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; UNUSED(type); CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string && strcasecmp(TOKEN_STRING(pctx), "debug") == 0) { CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */ CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER)); if (pctx->token.type == isc_tokentype_number) { CHECK(cfg_parse_uint32(pctx, NULL, ret)); } else { /* * The debug level is optional and defaults to 1. * This makes little sense, but we support it for * compatibility with BIND 8. */ CHECK(cfg_create_obj(pctx, &cfg_type_uint32, ret)); (*ret)->value.uint32 = 1; } (*ret)->type = &cfg_type_debuglevel; /* XXX kludge */ } else { CHECK(cfg_parse_obj(pctx, &cfg_type_loglevel, ret)); } cleanup: return (result); } static cfg_type_t cfg_type_logseverity = { "log_severity", parse_logseverity, NULL, cfg_doc_terminal, NULL, NULL }; /*% * The "file" clause of the "channel" statement. * This is yet another special case. */ static const char *logversions_enums[] = { "unlimited", NULL }; static isc_result_t parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_uint32, ret)); } static cfg_type_t cfg_type_logversions = { "logversions", parse_logversions, cfg_print_ustring, cfg_doc_terminal, &cfg_rep_string, logversions_enums }; static cfg_tuplefielddef_t logfile_fields[] = { { "file", &cfg_type_qstring, 0 }, { "versions", &cfg_type_logversions, 0 }, { "size", &cfg_type_size, 0 }, { NULL, NULL, 0 } }; static isc_result_t parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; const cfg_tuplefielddef_t *fields = type->of; CHECK(cfg_create_tuple(pctx, type, &obj)); /* Parse the mandatory "file" field */ CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0])); /* Parse "versions" and "size" fields in any order. */ for (;;) { CHECK(cfg_peektoken(pctx, 0)); if (pctx->token.type == isc_tokentype_string) { CHECK(cfg_gettoken(pctx, 0)); if (strcasecmp(TOKEN_STRING(pctx), "versions") == 0 && obj->value.tuple[1] == NULL) { CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1])); } else if (strcasecmp(TOKEN_STRING(pctx), "size") == 0 && obj->value.tuple[2] == NULL) { CHECK(cfg_parse_obj(pctx, fields[2].type, &obj->value.tuple[2])); } else { break; } } else { break; } } /* Create void objects for missing optional values. */ if (obj->value.tuple[1] == NULL) CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1])); if (obj->value.tuple[2] == NULL) CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2])); *ret = obj; return (ISC_R_SUCCESS); cleanup: CLEANUP_OBJ(obj); return (result); } static void print_logfile(cfg_printer_t *pctx, const cfg_obj_t *obj) { cfg_print_obj(pctx, obj->value.tuple[0]); /* file */ if (obj->value.tuple[1]->type->print != cfg_print_void) { cfg_print_cstr(pctx, " versions "); cfg_print_obj(pctx, obj->value.tuple[1]); } if (obj->value.tuple[2]->type->print != cfg_print_void) { cfg_print_cstr(pctx, " size "); cfg_print_obj(pctx, obj->value.tuple[2]); } } static void doc_logfile(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ versions ( \"unlimited\" | ) ]"); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ size ]"); } static cfg_type_t cfg_type_logfile = { "log_file", parse_logfile, print_logfile, doc_logfile, &cfg_rep_tuple, logfile_fields }; /*% An IPv4 address with optional port, "*" accepted as wildcard. */ static cfg_type_t cfg_type_sockaddr4wild = { "sockaddr4wild", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr4wild_flags }; /*% An IPv6 address with optional port, "*" accepted as wildcard. */ static cfg_type_t cfg_type_sockaddr6wild = { "v6addrportwild", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr6wild_flags }; /*% * lwres */ static cfg_tuplefielddef_t lwres_view_fields[] = { { "name", &cfg_type_astring, 0 }, { "class", &cfg_type_optional_class, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_lwres_view = { "lwres_view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, lwres_view_fields }; static cfg_type_t cfg_type_lwres_searchlist = { "lwres_searchlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring }; static cfg_clausedef_t lwres_clauses[] = { { "listen-on", &cfg_type_portiplist, 0 }, { "view", &cfg_type_lwres_view, 0 }, { "search", &cfg_type_lwres_searchlist, 0 }, { "ndots", &cfg_type_uint32, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * lwres_clausesets[] = { lwres_clauses, NULL }; static cfg_type_t cfg_type_lwres = { "lwres", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, lwres_clausesets }; /*% * rndc */ static cfg_clausedef_t rndcconf_options_clauses[] = { { "default-key", &cfg_type_astring, 0 }, { "default-port", &cfg_type_uint32, 0 }, { "default-server", &cfg_type_astring, 0 }, { "default-source-address", &cfg_type_netaddr4wild, 0 }, { "default-source-address-v6", &cfg_type_netaddr6wild, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * rndcconf_options_clausesets[] = { rndcconf_options_clauses, NULL }; static cfg_type_t cfg_type_rndcconf_options = { "rndcconf_options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, rndcconf_options_clausesets }; static cfg_clausedef_t rndcconf_server_clauses[] = { { "key", &cfg_type_astring, 0 }, { "port", &cfg_type_uint32, 0 }, { "source-address", &cfg_type_netaddr4wild, 0 }, { "source-address-v6", &cfg_type_netaddr6wild, 0 }, { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * rndcconf_server_clausesets[] = { rndcconf_server_clauses, NULL }; static cfg_type_t cfg_type_rndcconf_server = { "rndcconf_server", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, rndcconf_server_clausesets }; static cfg_clausedef_t rndcconf_clauses[] = { { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, { "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI }, { "options", &cfg_type_rndcconf_options, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * rndcconf_clausesets[] = { rndcconf_clauses, NULL }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = { "rndcconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, rndcconf_clausesets }; static cfg_clausedef_t rndckey_clauses[] = { { "key", &cfg_type_key, 0 }, { NULL, NULL, 0 } }; static cfg_clausedef_t * rndckey_clausesets[] = { rndckey_clauses, NULL }; LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = { "rndckey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, rndckey_clausesets }; /* * session.key has exactly the same syntax as rndc.key, but it's defined * separately for clarity (and so we can extend it someday, if needed). */ LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sessionkey = { "sessionkey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody, &cfg_rep_map, rndckey_clausesets }; static cfg_tuplefielddef_t nameport_fields[] = { { "name", &cfg_type_astring, 0 }, { "port", &cfg_type_optional_port, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_nameport = { "nameport", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, nameport_fields }; static void doc_sockaddrnameport(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_chars(pctx, "( ", 2); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ port ]"); cfg_print_chars(pctx, " | ", 3); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ port ]"); cfg_print_chars(pctx, " | ", 3); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ port ]"); cfg_print_chars(pctx, " )", 2); } static isc_result_t parse_sockaddrnameport(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; UNUSED(type); CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string || pctx->token.type == isc_tokentype_qstring) { if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK)) CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret)); else { const cfg_tuplefielddef_t *fields = cfg_type_nameport.of; CHECK(cfg_create_tuple(pctx, &cfg_type_nameport, &obj)); CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0])); CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1])); *ret = obj; obj = NULL; } } else { cfg_parser_error(pctx, CFG_LOG_NEAR, "expected IP address or hostname"); return (ISC_R_UNEXPECTEDTOKEN); } cleanup: CLEANUP_OBJ(obj); return (result); } static cfg_type_t cfg_type_sockaddrnameport = { "sockaddrnameport_element", parse_sockaddrnameport, NULL, doc_sockaddrnameport, NULL, NULL }; static cfg_type_t cfg_type_bracketed_sockaddrnameportlist = { "bracketed_sockaddrnameportlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_sockaddrnameport }; /*% * A list of socket addresses or name with an optional default port, * as used in the dual-stack-servers option. E.g., * "port 1234 { dual-stack-servers.net; 10.0.0.1; 1::2 port 69; }" */ static cfg_tuplefielddef_t nameportiplist_fields[] = { { "port", &cfg_type_optional_port, 0 }, { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 }, { NULL, NULL, 0 } }; static cfg_type_t cfg_type_nameportiplist = { "nameportiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, nameportiplist_fields }; /*% * masters element. */ static void doc_masterselement(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(type); cfg_print_chars(pctx, "( ", 2); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " | ", 3); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ port ]"); cfg_print_chars(pctx, " | ", 3); cfg_print_cstr(pctx, ""); cfg_print_chars(pctx, " ", 1); cfg_print_cstr(pctx, "[ port ]"); cfg_print_chars(pctx, " )", 2); } static isc_result_t parse_masterselement(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; cfg_obj_t *obj = NULL; UNUSED(type); CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); if (pctx->token.type == isc_tokentype_string || pctx->token.type == isc_tokentype_qstring) { if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK)) CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret)); else CHECK(cfg_parse_astring(pctx, &cfg_type_astring, ret)); } else { cfg_parser_error(pctx, CFG_LOG_NEAR, "expected IP address or masters name"); return (ISC_R_UNEXPECTEDTOKEN); } cleanup: CLEANUP_OBJ(obj); return (result); } static cfg_type_t cfg_type_masterselement = { "masters_element", parse_masterselement, NULL, doc_masterselement, NULL, NULL }; Index: stable/9/contrib/file/elfclass.h =================================================================== --- stable/9/contrib/file/elfclass.h (revision 275668) +++ stable/9/contrib/file/elfclass.h (revision 275669) @@ -1,69 +1,77 @@ /* * Copyright (c) Christos Zoulas 2008. * 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 immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ if (nbytes <= sizeof(elfhdr)) return 0; u.l = 1; (void)memcpy(&elfhdr, buf, sizeof elfhdr); swap = (u.c[sizeof(int32_t) - 1] + 1) != elfhdr.e_ident[EI_DATA]; type = elf_getu16(swap, elfhdr.e_type); switch (type) { #ifdef ELFCORE case ET_CORE: + phnum = elf_getu16(swap, elfhdr.e_phnum); + if (phnum > MAX_PHNUM) + return toomany(ms, "program", phnum); flags |= FLAGS_IS_CORE; if (dophn_core(ms, clazz, swap, fd, - (off_t)elf_getu(swap, elfhdr.e_phoff), - elf_getu16(swap, elfhdr.e_phnum), + (off_t)elf_getu(swap, elfhdr.e_phoff), phnum, (size_t)elf_getu16(swap, elfhdr.e_phentsize), fsize, &flags) == -1) return -1; break; #endif case ET_EXEC: case ET_DYN: + phnum = elf_getu16(swap, elfhdr.e_phnum); + if (phnum > MAX_PHNUM) + return toomany(ms, "program", phnum); + shnum = elf_getu16(swap, elfhdr.e_shnum); + if (shnum > MAX_SHNUM) + return toomany(ms, "section", shnum); if (dophn_exec(ms, clazz, swap, fd, - (off_t)elf_getu(swap, elfhdr.e_phoff), - elf_getu16(swap, elfhdr.e_phnum), + (off_t)elf_getu(swap, elfhdr.e_phoff), phnum, (size_t)elf_getu16(swap, elfhdr.e_phentsize), - fsize, &flags, elf_getu16(swap, elfhdr.e_shnum)) - == -1) + fsize, &flags, shnum) == -1) return -1; /*FALLTHROUGH*/ case ET_REL: + shnum = elf_getu16(swap, elfhdr.e_shnum); + if (shnum > MAX_SHNUM) + return toomany(ms, "section", shnum); if (doshn(ms, clazz, swap, fd, - (off_t)elf_getu(swap, elfhdr.e_shoff), - elf_getu16(swap, elfhdr.e_shnum), + (off_t)elf_getu(swap, elfhdr.e_shoff), shnum, (size_t)elf_getu16(swap, elfhdr.e_shentsize), fsize, &flags, elf_getu16(swap, elfhdr.e_machine)) == -1) return -1; break; default: break; } return 1; Index: stable/9/contrib/file/readelf.c =================================================================== --- stable/9/contrib/file/readelf.c (revision 275668) +++ stable/9/contrib/file/readelf.c (revision 275669) @@ -1,1195 +1,1212 @@ /* * Copyright (c) Christos Zoulas 2003. * 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 immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "file.h" #ifndef lint FILE_RCSID("@(#)$File: readelf.c,v 1.90 2011/08/23 08:01:12 christos Exp $") #endif #ifdef BUILTIN_ELF #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "readelf.h" #include "magic.h" #ifdef ELFCORE private int dophn_core(struct magic_set *, int, int, int, off_t, int, size_t, off_t, int *); #endif private int dophn_exec(struct magic_set *, int, int, int, off_t, int, size_t, off_t, int *, int); private int doshn(struct magic_set *, int, int, int, off_t, int, size_t, off_t, int *, int); private size_t donote(struct magic_set *, void *, size_t, size_t, int, int, size_t, int *); #define ELF_ALIGN(a) ((((a) + align - 1) / align) * align) #define isquote(c) (strchr("'\"`", (c)) != NULL) private uint16_t getu16(int, uint16_t); private uint32_t getu32(int, uint32_t); private uint64_t getu64(int, uint64_t); +#define MAX_PHNUM 256 +#define MAX_SHNUM 1024 + +private int +toomany(struct magic_set *ms, const char *name, uint16_t num) +{ + if (file_printf(ms, ", too many %s header sections (%u)", name, num + ) == -1) + return -1; + return 0; +} + private uint16_t getu16(int swap, uint16_t value) { union { uint16_t ui; char c[2]; } retval, tmpval; if (swap) { tmpval.ui = value; retval.c[0] = tmpval.c[1]; retval.c[1] = tmpval.c[0]; return retval.ui; } else return value; } private uint32_t getu32(int swap, uint32_t value) { union { uint32_t ui; char c[4]; } retval, tmpval; if (swap) { tmpval.ui = value; retval.c[0] = tmpval.c[3]; retval.c[1] = tmpval.c[2]; retval.c[2] = tmpval.c[1]; retval.c[3] = tmpval.c[0]; return retval.ui; } else return value; } private uint64_t getu64(int swap, uint64_t value) { union { uint64_t ui; char c[8]; } retval, tmpval; if (swap) { tmpval.ui = value; retval.c[0] = tmpval.c[7]; retval.c[1] = tmpval.c[6]; retval.c[2] = tmpval.c[5]; retval.c[3] = tmpval.c[4]; retval.c[4] = tmpval.c[3]; retval.c[5] = tmpval.c[2]; retval.c[6] = tmpval.c[1]; retval.c[7] = tmpval.c[0]; return retval.ui; } else return value; } #define elf_getu16(swap, value) getu16(swap, value) #define elf_getu32(swap, value) getu32(swap, value) #ifdef USE_ARRAY_FOR_64BIT_TYPES # define elf_getu64(swap, array) \ ((swap ? ((uint64_t)elf_getu32(swap, array[0])) << 32 : elf_getu32(swap, array[0])) + \ (swap ? elf_getu32(swap, array[1]) : ((uint64_t)elf_getu32(swap, array[1]) << 32))) #else # define elf_getu64(swap, value) getu64(swap, value) #endif #define xsh_addr (clazz == ELFCLASS32 \ ? (void *) &sh32 \ : (void *) &sh64) #define xsh_sizeof (clazz == ELFCLASS32 \ ? sizeof sh32 \ : sizeof sh64) #define xsh_size (clazz == ELFCLASS32 \ ? elf_getu32(swap, sh32.sh_size) \ : elf_getu64(swap, sh64.sh_size)) #define xsh_offset (off_t)(clazz == ELFCLASS32 \ ? elf_getu32(swap, sh32.sh_offset) \ : elf_getu64(swap, sh64.sh_offset)) #define xsh_type (clazz == ELFCLASS32 \ ? elf_getu32(swap, sh32.sh_type) \ : elf_getu32(swap, sh64.sh_type)) #define xph_addr (clazz == ELFCLASS32 \ ? (void *) &ph32 \ : (void *) &ph64) #define xph_sizeof (clazz == ELFCLASS32 \ ? sizeof ph32 \ : sizeof ph64) #define xph_type (clazz == ELFCLASS32 \ ? elf_getu32(swap, ph32.p_type) \ : elf_getu32(swap, ph64.p_type)) #define xph_offset (off_t)(clazz == ELFCLASS32 \ ? elf_getu32(swap, ph32.p_offset) \ : elf_getu64(swap, ph64.p_offset)) #define xph_align (size_t)((clazz == ELFCLASS32 \ ? (off_t) (ph32.p_align ? \ elf_getu32(swap, ph32.p_align) : 4) \ : (off_t) (ph64.p_align ? \ elf_getu64(swap, ph64.p_align) : 4))) #define xph_filesz (size_t)((clazz == ELFCLASS32 \ ? elf_getu32(swap, ph32.p_filesz) \ : elf_getu64(swap, ph64.p_filesz))) #define xnh_addr (clazz == ELFCLASS32 \ ? (void *) &nh32 \ : (void *) &nh64) #define xph_memsz (size_t)((clazz == ELFCLASS32 \ ? elf_getu32(swap, ph32.p_memsz) \ : elf_getu64(swap, ph64.p_memsz))) #define xnh_sizeof (clazz == ELFCLASS32 \ ? sizeof nh32 \ : sizeof nh64) #define xnh_type (clazz == ELFCLASS32 \ ? elf_getu32(swap, nh32.n_type) \ : elf_getu32(swap, nh64.n_type)) #define xnh_namesz (clazz == ELFCLASS32 \ ? elf_getu32(swap, nh32.n_namesz) \ : elf_getu32(swap, nh64.n_namesz)) #define xnh_descsz (clazz == ELFCLASS32 \ ? elf_getu32(swap, nh32.n_descsz) \ : elf_getu32(swap, nh64.n_descsz)) #define prpsoffsets(i) (clazz == ELFCLASS32 \ ? prpsoffsets32[i] \ : prpsoffsets64[i]) #define xcap_addr (clazz == ELFCLASS32 \ ? (void *) &cap32 \ : (void *) &cap64) #define xcap_sizeof (clazz == ELFCLASS32 \ ? sizeof cap32 \ : sizeof cap64) #define xcap_tag (clazz == ELFCLASS32 \ ? elf_getu32(swap, cap32.c_tag) \ : elf_getu64(swap, cap64.c_tag)) #define xcap_val (clazz == ELFCLASS32 \ ? elf_getu32(swap, cap32.c_un.c_val) \ : elf_getu64(swap, cap64.c_un.c_val)) #ifdef ELFCORE /* * Try larger offsets first to avoid false matches * from earlier data that happen to look like strings. */ static const size_t prpsoffsets32[] = { #ifdef USE_NT_PSINFO 104, /* SunOS 5.x (command line) */ 88, /* SunOS 5.x (short name) */ #endif /* USE_NT_PSINFO */ 100, /* SunOS 5.x (command line) */ 84, /* SunOS 5.x (short name) */ 44, /* Linux (command line) */ 28, /* Linux 2.0.36 (short name) */ 8, /* FreeBSD */ }; static const size_t prpsoffsets64[] = { #ifdef USE_NT_PSINFO 152, /* SunOS 5.x (command line) */ 136, /* SunOS 5.x (short name) */ #endif /* USE_NT_PSINFO */ 136, /* SunOS 5.x, 64-bit (command line) */ 120, /* SunOS 5.x, 64-bit (short name) */ 56, /* Linux (command line) */ 40, /* Linux (tested on core from 2.4.x, short name) */ 16, /* FreeBSD, 64-bit */ }; #define NOFFSETS32 (sizeof prpsoffsets32 / sizeof prpsoffsets32[0]) #define NOFFSETS64 (sizeof prpsoffsets64 / sizeof prpsoffsets64[0]) #define NOFFSETS (clazz == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64) /* * Look through the program headers of an executable image, searching * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or * "FreeBSD"; if one is found, try looking in various places in its * contents for a 16-character string containing only printable * characters - if found, that string should be the name of the program * that dropped core. Note: right after that 16-character string is, * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and * Linux, a longer string (80 characters, in 5.x, probably other * SVR4-flavored systems, and Linux) containing the start of the * command line for that program. * * SunOS 5.x core files contain two PT_NOTE sections, with the types * NT_PRPSINFO (old) and NT_PSINFO (new). These structs contain the * same info about the command name and command line, so it probably * isn't worthwhile to look for NT_PSINFO, but the offsets are provided * above (see USE_NT_PSINFO), in case we ever decide to do so. The * NT_PRPSINFO and NT_PSINFO sections are always in order and adjacent; * the SunOS 5.x file command relies on this (and prefers the latter). * * The signal number probably appears in a section of type NT_PRSTATUS, * but that's also rather OS-dependent, in ways that are harder to * dissect with heuristics, so I'm not bothering with the signal number. * (I suppose the signal number could be of interest in situations where * you don't have the binary of the program that dropped core; if you * *do* have that binary, the debugger will probably tell you what * signal it was.) */ #define OS_STYLE_SVR4 0 #define OS_STYLE_FREEBSD 1 #define OS_STYLE_NETBSD 2 private const char os_style_names[][8] = { "SVR4", "FreeBSD", "NetBSD", }; #define FLAGS_DID_CORE 0x01 #define FLAGS_DID_NOTE 0x02 #define FLAGS_DID_BUILD_ID 0x04 #define FLAGS_DID_CORE_STYLE 0x08 #define FLAGS_IS_CORE 0x10 private int dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, size_t size, off_t fsize, int *flags) { Elf32_Phdr ph32; Elf64_Phdr ph64; size_t offset; unsigned char nbuf[BUFSIZ]; ssize_t bufsize; if (size != xph_sizeof) { if (file_printf(ms, ", corrupted program header size") == -1) return -1; return 0; } /* * Loop through all the program headers. */ for ( ; num; num--) { if (lseek(fd, off, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } if (read(fd, xph_addr, xph_sizeof) == -1) { file_badread(ms); return -1; } off += size; if (xph_offset > fsize) { /* Perhaps warn here */ continue; } if (xph_type != PT_NOTE) continue; /* * This is a PT_NOTE section; loop through all the notes * in the section. */ if (lseek(fd, xph_offset, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } bufsize = read(fd, nbuf, ((xph_filesz < sizeof(nbuf)) ? xph_filesz : sizeof(nbuf))); if (bufsize == -1) { file_badread(ms); return -1; } offset = 0; for (;;) { if (offset >= (size_t)bufsize) break; offset = donote(ms, nbuf, offset, (size_t)bufsize, clazz, swap, 4, flags); if (offset == 0) break; } } return 0; } #endif private size_t donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size, int clazz, int swap, size_t align, int *flags) { Elf32_Nhdr nh32; Elf64_Nhdr nh64; size_t noff, doff; #ifdef ELFCORE int os_style = -1; #endif uint32_t namesz, descsz; unsigned char *nbuf = CAST(unsigned char *, vbuf); (void)memcpy(xnh_addr, &nbuf[offset], xnh_sizeof); offset += xnh_sizeof; namesz = xnh_namesz; descsz = xnh_descsz; if ((namesz == 0) && (descsz == 0)) { /* * We're out of note headers. */ return (offset >= size) ? offset : size; } if (namesz & 0x80000000) { (void)file_printf(ms, ", bad note name size 0x%lx", (unsigned long)namesz); - return offset; + return 0; } if (descsz & 0x80000000) { (void)file_printf(ms, ", bad note description size 0x%lx", (unsigned long)descsz); - return offset; + return 0; } noff = offset; doff = ELF_ALIGN(offset + namesz); if (offset + namesz > size) { /* * We're past the end of the buffer. */ return doff; } offset = ELF_ALIGN(doff + descsz); if (doff + descsz > size) { /* * We're past the end of the buffer. */ return (offset >= size) ? offset : size; } if ((*flags & (FLAGS_DID_NOTE|FLAGS_DID_BUILD_ID)) == (FLAGS_DID_NOTE|FLAGS_DID_BUILD_ID)) goto core; if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 && xnh_type == NT_GNU_VERSION && descsz == 16) { uint32_t desc[4]; (void)memcpy(desc, &nbuf[doff], sizeof(desc)); if (file_printf(ms, ", for GNU/") == -1) return size; switch (elf_getu32(swap, desc[0])) { case GNU_OS_LINUX: if (file_printf(ms, "Linux") == -1) return size; break; case GNU_OS_HURD: if (file_printf(ms, "Hurd") == -1) return size; break; case GNU_OS_SOLARIS: if (file_printf(ms, "Solaris") == -1) return size; break; case GNU_OS_KFREEBSD: if (file_printf(ms, "kFreeBSD") == -1) return size; break; case GNU_OS_KNETBSD: if (file_printf(ms, "kNetBSD") == -1) return size; break; default: if (file_printf(ms, "") == -1) return size; } if (file_printf(ms, " %d.%d.%d", elf_getu32(swap, desc[1]), elf_getu32(swap, desc[2]), elf_getu32(swap, desc[3])) == -1) return size; *flags |= FLAGS_DID_NOTE; return size; } if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 && xnh_type == NT_GNU_BUILD_ID && (descsz == 16 || descsz == 20)) { uint32_t desc[5], i; if (file_printf(ms, ", BuildID[%s]=0x", descsz == 16 ? "md5/uuid" : "sha1") == -1) return size; (void)memcpy(desc, &nbuf[doff], descsz); for (i = 0; i < descsz >> 2; i++) if (file_printf(ms, "%.8x", desc[i]) == -1) return size; *flags |= FLAGS_DID_BUILD_ID; } if (namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0 && xnh_type == NT_NETBSD_VERSION && descsz == 4) { uint32_t desc; (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); desc = elf_getu32(swap, desc); if (file_printf(ms, ", for NetBSD") == -1) return size; /* * The version number used to be stuck as 199905, and was thus * basically content-free. Newer versions of NetBSD have fixed * this and now use the encoding of __NetBSD_Version__: * * MMmmrrpp00 * * M = major version * m = minor version * r = release ["",A-Z,Z[A-Z] but numeric] * p = patchlevel */ if (desc > 100000000U) { uint32_t ver_patch = (desc / 100) % 100; uint32_t ver_rel = (desc / 10000) % 100; uint32_t ver_min = (desc / 1000000) % 100; uint32_t ver_maj = desc / 100000000; if (file_printf(ms, " %u.%u", ver_maj, ver_min) == -1) return size; if (ver_rel == 0 && ver_patch != 0) { if (file_printf(ms, ".%u", ver_patch) == -1) return size; } else if (ver_rel != 0) { while (ver_rel > 26) { if (file_printf(ms, "Z") == -1) return size; ver_rel -= 26; } if (file_printf(ms, "%c", 'A' + ver_rel - 1) == -1) return size; } } *flags |= FLAGS_DID_NOTE; return size; } if (namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0 && xnh_type == NT_FREEBSD_VERSION && descsz == 4) { uint32_t desc; (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); desc = elf_getu32(swap, desc); if (file_printf(ms, ", for FreeBSD") == -1) return size; /* * Contents is __FreeBSD_version, whose relation to OS * versions is defined by a huge table in the Porter's * Handbook. This is the general scheme: * * Releases: * Mmp000 (before 4.10) * Mmi0p0 (before 5.0) * Mmm0p0 * * Development branches: * Mmpxxx (before 4.6) * Mmp1xx (before 4.10) * Mmi1xx (before 5.0) * M000xx (pre-M.0) * Mmm1xx * * M = major version * m = minor version * i = minor version increment (491000 -> 4.10) * p = patchlevel * x = revision * * The first release of FreeBSD to use ELF by default * was version 3.0. */ if (desc == 460002) { if (file_printf(ms, " 4.6.2") == -1) return size; } else if (desc < 460100) { if (file_printf(ms, " %d.%d", desc / 100000, desc / 10000 % 10) == -1) return size; if (desc / 1000 % 10 > 0) if (file_printf(ms, ".%d", desc / 1000 % 10) == -1) return size; if ((desc % 1000 > 0) || (desc % 100000 == 0)) if (file_printf(ms, " (%d)", desc) == -1) return size; } else if (desc < 500000) { if (file_printf(ms, " %d.%d", desc / 100000, desc / 10000 % 10 + desc / 1000 % 10) == -1) return size; if (desc / 100 % 10 > 0) { if (file_printf(ms, " (%d)", desc) == -1) return size; } else if (desc / 10 % 10 > 0) { if (file_printf(ms, ".%d", desc / 10 % 10) == -1) return size; } } else { if (file_printf(ms, " %d.%d", desc / 100000, desc / 1000 % 100) == -1) return size; if ((desc / 100 % 10 > 0) || (desc % 100000 / 100 == 0)) { if (file_printf(ms, " (%d)", desc) == -1) return size; } else if (desc / 10 % 10 > 0) { if (file_printf(ms, ".%d", desc / 10 % 10) == -1) return size; } } *flags |= FLAGS_DID_NOTE; return size; } if (namesz == 8 && strcmp((char *)&nbuf[noff], "OpenBSD") == 0 && xnh_type == NT_OPENBSD_VERSION && descsz == 4) { if (file_printf(ms, ", for OpenBSD") == -1) return size; /* Content of note is always 0 */ *flags |= FLAGS_DID_NOTE; return size; } if (namesz == 10 && strcmp((char *)&nbuf[noff], "DragonFly") == 0 && xnh_type == NT_DRAGONFLY_VERSION && descsz == 4) { uint32_t desc; if (file_printf(ms, ", for DragonFly") == -1) return size; (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); desc = elf_getu32(swap, desc); if (file_printf(ms, " %d.%d.%d", desc / 100000, desc / 10000 % 10, desc % 10000) == -1) return size; *flags |= FLAGS_DID_NOTE; return size; } core: /* * Sigh. The 2.0.36 kernel in Debian 2.1, at * least, doesn't correctly implement name * sections, in core dumps, as specified by * the "Program Linking" section of "UNIX(R) System * V Release 4 Programmer's Guide: ANSI C and * Programming Support Tools", because my copy * clearly says "The first 'namesz' bytes in 'name' * contain a *null-terminated* [emphasis mine] * character representation of the entry's owner * or originator", but the 2.0.36 kernel code * doesn't include the terminating null in the * name.... */ if ((namesz == 4 && strncmp((char *)&nbuf[noff], "CORE", 4) == 0) || (namesz == 5 && strcmp((char *)&nbuf[noff], "CORE") == 0)) { os_style = OS_STYLE_SVR4; } if ((namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0)) { os_style = OS_STYLE_FREEBSD; } if ((namesz >= 11 && strncmp((char *)&nbuf[noff], "NetBSD-CORE", 11) == 0)) { os_style = OS_STYLE_NETBSD; } #ifdef ELFCORE if ((*flags & FLAGS_DID_CORE) != 0) return size; if (os_style != -1 && (*flags & FLAGS_DID_CORE_STYLE) == 0) { if (file_printf(ms, ", %s-style", os_style_names[os_style]) == -1) return size; *flags |= FLAGS_DID_CORE_STYLE; } switch (os_style) { case OS_STYLE_NETBSD: if (xnh_type == NT_NETBSD_CORE_PROCINFO) { uint32_t signo; /* * Extract the program name. It is at * offset 0x7c, and is up to 32-bytes, * including the terminating NUL. */ if (file_printf(ms, ", from '%.31s'", &nbuf[doff + 0x7c]) == -1) return size; /* * Extract the signal number. It is at * offset 0x08. */ (void)memcpy(&signo, &nbuf[doff + 0x08], sizeof(signo)); if (file_printf(ms, " (signal %u)", elf_getu32(swap, signo)) == -1) return size; *flags |= FLAGS_DID_CORE; return size; } break; default: if (xnh_type == NT_PRPSINFO && *flags & FLAGS_IS_CORE) { size_t i, j; unsigned char c; /* * Extract the program name. We assume * it to be 16 characters (that's what it * is in SunOS 5.x and Linux). * * Unfortunately, it's at a different offset * in various OSes, so try multiple offsets. * If the characters aren't all printable, * reject it. */ for (i = 0; i < NOFFSETS; i++) { unsigned char *cname, *cp; size_t reloffset = prpsoffsets(i); size_t noffset = doff + reloffset; size_t k; for (j = 0; j < 16; j++, noffset++, reloffset++) { /* * Make sure we're not past * the end of the buffer; if * we are, just give up. */ if (noffset >= size) goto tryanother; /* * Make sure we're not past * the end of the contents; * if we are, this obviously * isn't the right offset. */ if (reloffset >= descsz) goto tryanother; c = nbuf[noffset]; if (c == '\0') { /* * A '\0' at the * beginning is * obviously wrong. * Any other '\0' * means we're done. */ if (j == 0) goto tryanother; else break; } else { /* * A nonprintable * character is also * wrong. */ if (!isprint(c) || isquote(c)) goto tryanother; } } /* * Well, that worked. */ /* * Try next offsets, in case this match is * in the middle of a string. */ for (k = i + 1 ; k < NOFFSETS ; k++) { size_t no; int adjust = 1; if (prpsoffsets(k) >= prpsoffsets(i)) continue; for (no = doff + prpsoffsets(k); no < doff + prpsoffsets(i); no++) adjust = adjust && isprint(nbuf[no]); if (adjust) i = k; } cname = (unsigned char *) &nbuf[doff + prpsoffsets(i)]; for (cp = cname; *cp && isprint(*cp); cp++) continue; /* * Linux apparently appends a space at the end * of the command line: remove it. */ while (cp > cname && isspace(cp[-1])) cp--; if (file_printf(ms, ", from '%.*s'", (int)(cp - cname), cname) == -1) return size; *flags |= FLAGS_DID_CORE; return size; tryanother: ; } } break; } #endif return offset; } /* SunOS 5.x hardware capability descriptions */ typedef struct cap_desc { uint64_t cd_mask; const char *cd_name; } cap_desc_t; static const cap_desc_t cap_desc_sparc[] = { { AV_SPARC_MUL32, "MUL32" }, { AV_SPARC_DIV32, "DIV32" }, { AV_SPARC_FSMULD, "FSMULD" }, { AV_SPARC_V8PLUS, "V8PLUS" }, { AV_SPARC_POPC, "POPC" }, { AV_SPARC_VIS, "VIS" }, { AV_SPARC_VIS2, "VIS2" }, { AV_SPARC_ASI_BLK_INIT, "ASI_BLK_INIT" }, { AV_SPARC_FMAF, "FMAF" }, { AV_SPARC_FJFMAU, "FJFMAU" }, { AV_SPARC_IMA, "IMA" }, { 0, NULL } }; static const cap_desc_t cap_desc_386[] = { { AV_386_FPU, "FPU" }, { AV_386_TSC, "TSC" }, { AV_386_CX8, "CX8" }, { AV_386_SEP, "SEP" }, { AV_386_AMD_SYSC, "AMD_SYSC" }, { AV_386_CMOV, "CMOV" }, { AV_386_MMX, "MMX" }, { AV_386_AMD_MMX, "AMD_MMX" }, { AV_386_AMD_3DNow, "AMD_3DNow" }, { AV_386_AMD_3DNowx, "AMD_3DNowx" }, { AV_386_FXSR, "FXSR" }, { AV_386_SSE, "SSE" }, { AV_386_SSE2, "SSE2" }, { AV_386_PAUSE, "PAUSE" }, { AV_386_SSE3, "SSE3" }, { AV_386_MON, "MON" }, { AV_386_CX16, "CX16" }, { AV_386_AHF, "AHF" }, { AV_386_TSCP, "TSCP" }, { AV_386_AMD_SSE4A, "AMD_SSE4A" }, { AV_386_POPCNT, "POPCNT" }, { AV_386_AMD_LZCNT, "AMD_LZCNT" }, { AV_386_SSSE3, "SSSE3" }, { AV_386_SSE4_1, "SSE4.1" }, { AV_386_SSE4_2, "SSE4.2" }, { 0, NULL } }; private int doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, size_t size, off_t fsize, int *flags, int mach) { Elf32_Shdr sh32; Elf64_Shdr sh64; int stripped = 1; + size_t nbadcap = 0; void *nbuf; off_t noff, coff; uint64_t cap_hw1 = 0; /* SunOS 5.x hardware capabilites */ uint64_t cap_sf1 = 0; /* SunOS 5.x software capabilites */ if (size != xsh_sizeof) { if (file_printf(ms, ", corrupted section header size") == -1) return -1; return 0; } for ( ; num; num--) { if (lseek(fd, off, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } if (read(fd, xsh_addr, xsh_sizeof) == -1) { file_badread(ms); return -1; } off += size; /* Things we can determine before we seek */ switch (xsh_type) { case SHT_SYMTAB: #if 0 case SHT_DYNSYM: #endif stripped = 0; break; default: if (xsh_offset > fsize) { /* Perhaps warn here */ continue; } break; } /* Things we can determine when we seek */ switch (xsh_type) { case SHT_NOTE: if ((nbuf = malloc((size_t)xsh_size)) == NULL) { file_error(ms, errno, "Cannot allocate memory" " for note"); return -1; } if ((noff = lseek(fd, (off_t)xsh_offset, SEEK_SET)) == (off_t)-1) { file_badread(ms); free(nbuf); return -1; } if (read(fd, nbuf, (size_t)xsh_size) != (ssize_t)xsh_size) { free(nbuf); file_badread(ms); return -1; } noff = 0; for (;;) { if (noff >= (off_t)xsh_size) break; noff = donote(ms, nbuf, (size_t)noff, (size_t)xsh_size, clazz, swap, 4, flags); if (noff == 0) break; } free(nbuf); break; case SHT_SUNW_cap: + if (nbadcap > 5) + break; if (lseek(fd, (off_t)xsh_offset, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } coff = 0; for (;;) { Elf32_Cap cap32; Elf64_Cap cap64; char cbuf[/*CONSTCOND*/ MAX(sizeof cap32, sizeof cap64)]; if ((coff += xcap_sizeof) > (off_t)xsh_size) break; if (read(fd, cbuf, (size_t)xcap_sizeof) != (ssize_t)xcap_sizeof) { file_badread(ms); return -1; } (void)memcpy(xcap_addr, cbuf, xcap_sizeof); switch (xcap_tag) { case CA_SUNW_NULL: break; case CA_SUNW_HW_1: cap_hw1 |= xcap_val; break; case CA_SUNW_SF_1: cap_sf1 |= xcap_val; break; default: if (file_printf(ms, ", with unknown capability " "0x%" INT64_T_FORMAT "x = 0x%" INT64_T_FORMAT "x", (unsigned long long)xcap_tag, (unsigned long long)xcap_val) == -1) return -1; + if (nbadcap++ > 2) + coff = xsh_size; break; } } break; default: break; } } if (file_printf(ms, ", %sstripped", stripped ? "" : "not ") == -1) return -1; if (cap_hw1) { const cap_desc_t *cdp; switch (mach) { case EM_SPARC: case EM_SPARC32PLUS: case EM_SPARCV9: cdp = cap_desc_sparc; break; case EM_386: case EM_IA_64: case EM_AMD64: cdp = cap_desc_386; break; default: cdp = NULL; break; } if (file_printf(ms, ", uses") == -1) return -1; if (cdp) { while (cdp->cd_name) { if (cap_hw1 & cdp->cd_mask) { if (file_printf(ms, " %s", cdp->cd_name) == -1) return -1; cap_hw1 &= ~cdp->cd_mask; } ++cdp; } if (cap_hw1) if (file_printf(ms, " unknown hardware capability 0x%" INT64_T_FORMAT "x", (unsigned long long)cap_hw1) == -1) return -1; } else { if (file_printf(ms, " hardware capability 0x%" INT64_T_FORMAT "x", (unsigned long long)cap_hw1) == -1) return -1; } } if (cap_sf1) { if (cap_sf1 & SF1_SUNW_FPUSED) { if (file_printf(ms, (cap_sf1 & SF1_SUNW_FPKNWN) ? ", uses frame pointer" : ", not known to use frame pointer") == -1) return -1; } cap_sf1 &= ~SF1_SUNW_MASK; if (cap_sf1) if (file_printf(ms, ", with unknown software capability 0x%" INT64_T_FORMAT "x", (unsigned long long)cap_sf1) == -1) return -1; } return 0; } /* * Look through the program headers of an executable image, searching * for a PT_INTERP section; if one is found, it's dynamically linked, * otherwise it's statically linked. */ private int dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, size_t size, off_t fsize, int *flags, int sh_num) { Elf32_Phdr ph32; Elf64_Phdr ph64; const char *linking_style = "statically"; const char *shared_libraries = ""; unsigned char nbuf[BUFSIZ]; ssize_t bufsize; size_t offset, align; if (size != xph_sizeof) { if (file_printf(ms, ", corrupted program header size") == -1) return -1; return 0; } for ( ; num; num--) { if (lseek(fd, off, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } if (read(fd, xph_addr, xph_sizeof) == -1) { file_badread(ms); return -1; } off += size; /* Things we can determine before we seek */ switch (xph_type) { case PT_DYNAMIC: linking_style = "dynamically"; break; case PT_INTERP: shared_libraries = " (uses shared libs)"; break; default: if (xph_offset > fsize) { /* Maybe warn here? */ continue; } break; } /* Things we can determine when we seek */ switch (xph_type) { case PT_NOTE: if ((align = xph_align) & 0x80000000UL) { if (file_printf(ms, ", invalid note alignment 0x%lx", (unsigned long)align) == -1) return -1; align = 4; } if (sh_num) break; /* * This is a PT_NOTE section; loop through all the notes * in the section. */ if (lseek(fd, xph_offset, SEEK_SET) == (off_t)-1) { file_badseek(ms); return -1; } bufsize = read(fd, nbuf, ((xph_filesz < sizeof(nbuf)) ? xph_filesz : sizeof(nbuf))); if (bufsize == -1) { file_badread(ms); return -1; } offset = 0; for (;;) { if (offset >= (size_t)bufsize) break; offset = donote(ms, nbuf, offset, (size_t)bufsize, clazz, swap, align, flags); if (offset == 0) break; } break; default: break; } } if (file_printf(ms, ", %s linked%s", linking_style, shared_libraries) == -1) return -1; return 0; } protected int file_tryelf(struct magic_set *ms, int fd, const unsigned char *buf, size_t nbytes) { union { int32_t l; char c[sizeof (int32_t)]; } u; int clazz; int swap; struct stat st; off_t fsize; int flags = 0; Elf32_Ehdr elf32hdr; Elf64_Ehdr elf64hdr; - uint16_t type; + uint16_t type, phnum, shnum; if (ms->flags & (MAGIC_MIME|MAGIC_APPLE)) return 0; /* * ELF executables have multiple section headers in arbitrary * file locations and thus file(1) cannot determine it from easily. * Instead we traverse thru all section headers until a symbol table * one is found or else the binary is stripped. * Return immediately if it's not ELF (so we avoid pipe2file unless needed). */ if (buf[EI_MAG0] != ELFMAG0 || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1) || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) return 0; /* * If we cannot seek, it must be a pipe, socket or fifo. */ if((lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE)) fd = file_pipe2file(ms, fd, buf, nbytes); if (fstat(fd, &st) == -1) { file_badread(ms); return -1; } fsize = st.st_size; clazz = buf[EI_CLASS]; switch (clazz) { case ELFCLASS32: #undef elf_getu #define elf_getu(a, b) elf_getu32(a, b) #undef elfhdr #define elfhdr elf32hdr #include "elfclass.h" case ELFCLASS64: #undef elf_getu #define elf_getu(a, b) elf_getu64(a, b) #undef elfhdr #define elfhdr elf64hdr #include "elfclass.h" default: if (file_printf(ms, ", unknown class %d", clazz) == -1) return -1; break; } return 0; } #endif Index: stable/9/contrib/file/softmagic.c =================================================================== --- stable/9/contrib/file/softmagic.c (revision 275668) +++ stable/9/contrib/file/softmagic.c (revision 275669) @@ -1,2024 +1,2027 @@ /* * Copyright (c) Ian F. Darwin 1986-1995. * Software written by Ian F. Darwin and others; * maintained 1995-present by Christos Zoulas and others. * * 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 immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * softmagic - interpret variable magic from MAGIC */ #include "file.h" #ifndef lint FILE_RCSID("@(#)$File: softmagic.c,v 1.147 2011/11/05 15:44:22 rrt Exp $") #endif /* lint */ #include "magic.h" #include #include #include #include private int match(struct magic_set *, struct magic *, uint32_t, const unsigned char *, size_t, int, int, int); private int mget(struct magic_set *, const unsigned char *, struct magic *, size_t, unsigned int, int, int); private int magiccheck(struct magic_set *, struct magic *); private int32_t mprint(struct magic_set *, struct magic *); private int32_t moffset(struct magic_set *, struct magic *); private void mdebug(uint32_t, const char *, size_t); private int mcopy(struct magic_set *, union VALUETYPE *, int, int, const unsigned char *, uint32_t, size_t, size_t); private int mconvert(struct magic_set *, struct magic *); private int print_sep(struct magic_set *, int); private int handle_annotation(struct magic_set *, struct magic *); private void cvt_8(union VALUETYPE *, const struct magic *); private void cvt_16(union VALUETYPE *, const struct magic *); private void cvt_32(union VALUETYPE *, const struct magic *); private void cvt_64(union VALUETYPE *, const struct magic *); #define OFFSET_OOB(n, o, i) ((n) < (o) || (i) > ((n) - (o))) + +#define MAX_RECURSION_LEVEL 10 + /* * softmagic - lookup one file in parsed, in-memory copy of database * Passed the name and FILE * of one file to be typed. */ /*ARGSUSED1*/ /* nbytes passed for regularity, maybe need later */ protected int file_softmagic(struct magic_set *ms, const unsigned char *buf, size_t nbytes, size_t level, int mode, int text) { struct mlist *ml; int rv; for (ml = ms->mlist->next; ml != ms->mlist; ml = ml->next) if ((rv = match(ms, ml->magic, ml->nmagic, buf, nbytes, mode, text, level)) != 0) return rv; return 0; } /* * Go through the whole list, stopping if you find a match. Process all * the continuations of that match before returning. * * We support multi-level continuations: * * At any time when processing a successful top-level match, there is a * current continuation level; it represents the level of the last * successfully matched continuation. * * Continuations above that level are skipped as, if we see one, it * means that the continuation that controls them - i.e, the * lower-level continuation preceding them - failed to match. * * Continuations below that level are processed as, if we see one, * it means we've finished processing or skipping higher-level * continuations under the control of a successful or unsuccessful * lower-level continuation, and are now seeing the next lower-level * continuation and should process it. The current continuation * level reverts to the level of the one we're seeing. * * Continuations at the current level are processed as, if we see * one, there's no lower-level continuation that may have failed. * * If a continuation matches, we bump the current continuation level * so that higher-level continuations are processed. */ private int match(struct magic_set *ms, struct magic *magic, uint32_t nmagic, const unsigned char *s, size_t nbytes, int mode, int text, int recursion_level) { uint32_t magindex = 0; unsigned int cont_level = 0; int need_separator = 0; int returnval = 0, e; /* if a match is found it is set to 1*/ int firstline = 1; /* a flag to print X\n X\n- X */ int printed_something = 0; int print = (ms->flags & (MAGIC_MIME|MAGIC_APPLE)) == 0; if (file_check_mem(ms, cont_level) == -1) return -1; for (magindex = 0; magindex < nmagic; magindex++) { int flush = 0; struct magic *m = &magic[magindex]; if ((IS_STRING(m->type) && ((text && (m->str_flags & (STRING_BINTEST | STRING_TEXTTEST)) == STRING_BINTEST) || (!text && (m->str_flags & (STRING_TEXTTEST | STRING_BINTEST)) == STRING_TEXTTEST))) || (m->flag & mode) != mode) { /* Skip sub-tests */ while (magic[magindex + 1].cont_level != 0 && ++magindex < nmagic) continue; continue; /* Skip to next top-level test*/ } ms->offset = m->offset; ms->line = m->lineno; /* if main entry matches, print it... */ switch (mget(ms, s, m, nbytes, cont_level, text, recursion_level + 1)) { case -1: return -1; case 0: flush = m->reln != '!'; break; default: if (m->type == FILE_INDIRECT) returnval = 1; switch (magiccheck(ms, m)) { case -1: return -1; case 0: flush++; break; default: flush = 0; break; } break; } if (flush) { /* * main entry didn't match, * flush its continuations */ while (magindex < nmagic - 1 && magic[magindex + 1].cont_level != 0) magindex++; continue; } if ((e = handle_annotation(ms, m)) != 0) return e; /* * If we are going to print something, we'll need to print * a blank before we print something else. */ if (*m->desc) { need_separator = 1; printed_something = 1; if (print_sep(ms, firstline) == -1) return -1; } if (print && mprint(ms, m) == -1) return -1; ms->c.li[cont_level].off = moffset(ms, m); /* and any continuations that match */ if (file_check_mem(ms, ++cont_level) == -1) return -1; while (magic[magindex+1].cont_level != 0 && ++magindex < nmagic) { m = &magic[magindex]; ms->line = m->lineno; /* for messages */ if (cont_level < m->cont_level) continue; if (cont_level > m->cont_level) { /* * We're at the end of the level * "cont_level" continuations. */ cont_level = m->cont_level; } ms->offset = m->offset; if (m->flag & OFFADD) { ms->offset += ms->c.li[cont_level - 1].off; } #ifdef ENABLE_CONDITIONALS if (m->cond == COND_ELSE || m->cond == COND_ELIF) { if (ms->c.li[cont_level].last_match == 1) continue; } #endif switch (mget(ms, s, m, nbytes, cont_level, text, recursion_level + 1)) { case -1: return -1; case 0: if (m->reln != '!') continue; flush = 1; break; default: if (m->type == FILE_INDIRECT) returnval = 1; flush = 0; break; } switch (flush ? 1 : magiccheck(ms, m)) { case -1: return -1; case 0: #ifdef ENABLE_CONDITIONALS ms->c.li[cont_level].last_match = 0; #endif break; default: #ifdef ENABLE_CONDITIONALS ms->c.li[cont_level].last_match = 1; #endif if (m->type != FILE_DEFAULT) ms->c.li[cont_level].got_match = 1; else if (ms->c.li[cont_level].got_match) { ms->c.li[cont_level].got_match = 0; break; } if ((e = handle_annotation(ms, m)) != 0) return e; /* * If we are going to print something, * make sure that we have a separator first. */ if (*m->desc) { if (!printed_something) { printed_something = 1; if (print_sep(ms, firstline) == -1) return -1; } } /* * This continuation matched. Print * its message, with a blank before it * if the previous item printed and * this item isn't empty. */ /* space if previous printed */ if (need_separator && ((m->flag & NOSPACE) == 0) && *m->desc) { if (print && file_printf(ms, " ") == -1) return -1; need_separator = 0; } if (print && mprint(ms, m) == -1) return -1; ms->c.li[cont_level].off = moffset(ms, m); if (*m->desc) need_separator = 1; /* * If we see any continuations * at a higher level, * process them. */ if (file_check_mem(ms, ++cont_level) == -1) return -1; break; } } if (printed_something) { firstline = 0; if (print) returnval = 1; } if ((ms->flags & MAGIC_CONTINUE) == 0 && printed_something) { return returnval; /* don't keep searching */ } } return returnval; /* This is hit if -k is set or there is no match */ } private int check_fmt(struct magic_set *ms, struct magic *m) { regex_t rx; int rc; if (strchr(m->desc, '%') == NULL) return 0; rc = regcomp(&rx, "%[-0-9\\.]*s", REG_EXTENDED|REG_NOSUB); if (rc) { char errmsg[512]; (void)regerror(rc, &rx, errmsg, sizeof(errmsg)); file_magerror(ms, "regex error %d, (%s)", rc, errmsg); return -1; } else { rc = regexec(&rx, m->desc, 0, 0, 0); regfree(&rx); return !rc; } } #ifndef HAVE_STRNDUP char * strndup(const char *, size_t); char * strndup(const char *str, size_t n) { size_t len; char *copy; for (len = 0; len < n && str[len]; len++) continue; if ((copy = malloc(len + 1)) == NULL) return NULL; (void)memcpy(copy, str, len); copy[len] = '\0'; return copy; } #endif /* HAVE_STRNDUP */ private int32_t mprint(struct magic_set *ms, struct magic *m) { uint64_t v; float vf; double vd; int64_t t = 0; char buf[128]; union VALUETYPE *p = &ms->ms_value; switch (m->type) { case FILE_BYTE: v = file_signextend(ms, m, (uint64_t)p->b); switch (check_fmt(ms, m)) { case -1: return -1; case 1: (void)snprintf(buf, sizeof(buf), "%c", (unsigned char)v); if (file_printf(ms, m->desc, buf) == -1) return -1; break; default: if (file_printf(ms, m->desc, (unsigned char) v) == -1) return -1; break; } t = ms->offset + sizeof(char); break; case FILE_SHORT: case FILE_BESHORT: case FILE_LESHORT: v = file_signextend(ms, m, (uint64_t)p->h); switch (check_fmt(ms, m)) { case -1: return -1; case 1: (void)snprintf(buf, sizeof(buf), "%hu", (unsigned short)v); if (file_printf(ms, m->desc, buf) == -1) return -1; break; default: if ( file_printf(ms, m->desc, (unsigned short) v) == -1) return -1; break; } t = ms->offset + sizeof(short); break; case FILE_LONG: case FILE_BELONG: case FILE_LELONG: case FILE_MELONG: v = file_signextend(ms, m, (uint64_t)p->l); switch (check_fmt(ms, m)) { case -1: return -1; case 1: (void)snprintf(buf, sizeof(buf), "%u", (uint32_t)v); if (file_printf(ms, m->desc, buf) == -1) return -1; break; default: if (file_printf(ms, m->desc, (uint32_t) v) == -1) return -1; break; } t = ms->offset + sizeof(int32_t); break; case FILE_QUAD: case FILE_BEQUAD: case FILE_LEQUAD: v = file_signextend(ms, m, p->q); if (file_printf(ms, m->desc, (uint64_t) v) == -1) return -1; t = ms->offset + sizeof(int64_t); break; case FILE_STRING: case FILE_PSTRING: case FILE_BESTRING16: case FILE_LESTRING16: if (m->reln == '=' || m->reln == '!') { if (file_printf(ms, m->desc, m->value.s) == -1) return -1; t = ms->offset + m->vallen; } else { if (*m->value.s == '\0') p->s[strcspn(p->s, "\n")] = '\0'; if (file_printf(ms, m->desc, p->s) == -1) return -1; t = ms->offset + strlen(p->s); if (m->type == FILE_PSTRING) t += file_pstring_length_size(m); } break; case FILE_DATE: case FILE_BEDATE: case FILE_LEDATE: case FILE_MEDATE: if (file_printf(ms, m->desc, file_fmttime(p->l, 1)) == -1) return -1; t = ms->offset + sizeof(time_t); break; case FILE_LDATE: case FILE_BELDATE: case FILE_LELDATE: case FILE_MELDATE: if (file_printf(ms, m->desc, file_fmttime(p->l, 0)) == -1) return -1; t = ms->offset + sizeof(time_t); break; case FILE_QDATE: case FILE_BEQDATE: case FILE_LEQDATE: if (file_printf(ms, m->desc, file_fmttime((uint32_t)p->q, 1)) == -1) return -1; t = ms->offset + sizeof(uint64_t); break; case FILE_QLDATE: case FILE_BEQLDATE: case FILE_LEQLDATE: if (file_printf(ms, m->desc, file_fmttime((uint32_t)p->q, 0)) == -1) return -1; t = ms->offset + sizeof(uint64_t); break; case FILE_FLOAT: case FILE_BEFLOAT: case FILE_LEFLOAT: vf = p->f; switch (check_fmt(ms, m)) { case -1: return -1; case 1: (void)snprintf(buf, sizeof(buf), "%g", vf); if (file_printf(ms, m->desc, buf) == -1) return -1; break; default: if (file_printf(ms, m->desc, vf) == -1) return -1; break; } t = ms->offset + sizeof(float); break; case FILE_DOUBLE: case FILE_BEDOUBLE: case FILE_LEDOUBLE: vd = p->d; switch (check_fmt(ms, m)) { case -1: return -1; case 1: (void)snprintf(buf, sizeof(buf), "%g", vd); if (file_printf(ms, m->desc, buf) == -1) return -1; break; default: if (file_printf(ms, m->desc, vd) == -1) return -1; break; } t = ms->offset + sizeof(double); break; case FILE_REGEX: { char *cp; int rval; cp = strndup((const char *)ms->search.s, ms->search.rm_len); if (cp == NULL) { file_oomem(ms, ms->search.rm_len); return -1; } rval = file_printf(ms, m->desc, cp); free(cp); if (rval == -1) return -1; if ((m->str_flags & REGEX_OFFSET_START)) t = ms->search.offset; else t = ms->search.offset + ms->search.rm_len; break; } case FILE_SEARCH: if (file_printf(ms, m->desc, m->value.s) == -1) return -1; if ((m->str_flags & REGEX_OFFSET_START)) t = ms->search.offset; else t = ms->search.offset + m->vallen; break; case FILE_DEFAULT: if (file_printf(ms, m->desc, m->value.s) == -1) return -1; t = ms->offset; break; case FILE_INDIRECT: t = ms->offset; break; default: file_magerror(ms, "invalid m->type (%d) in mprint()", m->type); return -1; } return (int32_t)t; } private int32_t moffset(struct magic_set *ms, struct magic *m) { switch (m->type) { case FILE_BYTE: return CAST(int32_t, (ms->offset + sizeof(char))); case FILE_SHORT: case FILE_BESHORT: case FILE_LESHORT: return CAST(int32_t, (ms->offset + sizeof(short))); case FILE_LONG: case FILE_BELONG: case FILE_LELONG: case FILE_MELONG: return CAST(int32_t, (ms->offset + sizeof(int32_t))); case FILE_QUAD: case FILE_BEQUAD: case FILE_LEQUAD: return CAST(int32_t, (ms->offset + sizeof(int64_t))); case FILE_STRING: case FILE_PSTRING: case FILE_BESTRING16: case FILE_LESTRING16: if (m->reln == '=' || m->reln == '!') return ms->offset + m->vallen; else { union VALUETYPE *p = &ms->ms_value; uint32_t t; if (*m->value.s == '\0') p->s[strcspn(p->s, "\n")] = '\0'; t = CAST(uint32_t, (ms->offset + strlen(p->s))); if (m->type == FILE_PSTRING) t += file_pstring_length_size(m); return t; } case FILE_DATE: case FILE_BEDATE: case FILE_LEDATE: case FILE_MEDATE: return CAST(int32_t, (ms->offset + sizeof(time_t))); case FILE_LDATE: case FILE_BELDATE: case FILE_LELDATE: case FILE_MELDATE: return CAST(int32_t, (ms->offset + sizeof(time_t))); case FILE_QDATE: case FILE_BEQDATE: case FILE_LEQDATE: return CAST(int32_t, (ms->offset + sizeof(uint64_t))); case FILE_QLDATE: case FILE_BEQLDATE: case FILE_LEQLDATE: return CAST(int32_t, (ms->offset + sizeof(uint64_t))); case FILE_FLOAT: case FILE_BEFLOAT: case FILE_LEFLOAT: return CAST(int32_t, (ms->offset + sizeof(float))); case FILE_DOUBLE: case FILE_BEDOUBLE: case FILE_LEDOUBLE: return CAST(int32_t, (ms->offset + sizeof(double))); case FILE_REGEX: if ((m->str_flags & REGEX_OFFSET_START) != 0) return CAST(int32_t, ms->search.offset); else return CAST(int32_t, (ms->search.offset + ms->search.rm_len)); case FILE_SEARCH: if ((m->str_flags & REGEX_OFFSET_START) != 0) return CAST(int32_t, ms->search.offset); else return CAST(int32_t, (ms->search.offset + m->vallen)); case FILE_DEFAULT: return ms->offset; case FILE_INDIRECT: return ms->offset; default: return 0; } } #define DO_CVT(fld, cast) \ if (m->num_mask) \ switch (m->mask_op & FILE_OPS_MASK) { \ case FILE_OPAND: \ p->fld &= cast m->num_mask; \ break; \ case FILE_OPOR: \ p->fld |= cast m->num_mask; \ break; \ case FILE_OPXOR: \ p->fld ^= cast m->num_mask; \ break; \ case FILE_OPADD: \ p->fld += cast m->num_mask; \ break; \ case FILE_OPMINUS: \ p->fld -= cast m->num_mask; \ break; \ case FILE_OPMULTIPLY: \ p->fld *= cast m->num_mask; \ break; \ case FILE_OPDIVIDE: \ p->fld /= cast m->num_mask; \ break; \ case FILE_OPMODULO: \ p->fld %= cast m->num_mask; \ break; \ } \ if (m->mask_op & FILE_OPINVERSE) \ p->fld = ~p->fld \ private void cvt_8(union VALUETYPE *p, const struct magic *m) { DO_CVT(b, (uint8_t)); } private void cvt_16(union VALUETYPE *p, const struct magic *m) { DO_CVT(h, (uint16_t)); } private void cvt_32(union VALUETYPE *p, const struct magic *m) { DO_CVT(l, (uint32_t)); } private void cvt_64(union VALUETYPE *p, const struct magic *m) { DO_CVT(q, (uint64_t)); } #define DO_CVT2(fld, cast) \ if (m->num_mask) \ switch (m->mask_op & FILE_OPS_MASK) { \ case FILE_OPADD: \ p->fld += cast m->num_mask; \ break; \ case FILE_OPMINUS: \ p->fld -= cast m->num_mask; \ break; \ case FILE_OPMULTIPLY: \ p->fld *= cast m->num_mask; \ break; \ case FILE_OPDIVIDE: \ p->fld /= cast m->num_mask; \ break; \ } \ private void cvt_float(union VALUETYPE *p, const struct magic *m) { DO_CVT2(f, (float)); } private void cvt_double(union VALUETYPE *p, const struct magic *m) { DO_CVT2(d, (double)); } /* * Convert the byte order of the data we are looking at * While we're here, let's apply the mask operation * (unless you have a better idea) */ private int mconvert(struct magic_set *ms, struct magic *m) { union VALUETYPE *p = &ms->ms_value; switch (m->type) { case FILE_BYTE: cvt_8(p, m); return 1; case FILE_SHORT: cvt_16(p, m); return 1; case FILE_LONG: case FILE_DATE: case FILE_LDATE: cvt_32(p, m); return 1; case FILE_QUAD: case FILE_QDATE: case FILE_QLDATE: cvt_64(p, m); return 1; case FILE_STRING: case FILE_BESTRING16: case FILE_LESTRING16: { /* Null terminate and eat *trailing* return */ p->s[sizeof(p->s) - 1] = '\0'; return 1; } case FILE_PSTRING: { char *ptr1 = p->s, *ptr2 = ptr1 + file_pstring_length_size(m); size_t len = file_pstring_get_length(m, ptr1); if (len >= sizeof(p->s)) len = sizeof(p->s) - 1; while (len--) *ptr1++ = *ptr2++; *ptr1 = '\0'; return 1; } case FILE_BESHORT: p->h = (short)((p->hs[0]<<8)|(p->hs[1])); cvt_16(p, m); return 1; case FILE_BELONG: case FILE_BEDATE: case FILE_BELDATE: p->l = (int32_t) ((p->hl[0]<<24)|(p->hl[1]<<16)|(p->hl[2]<<8)|(p->hl[3])); cvt_32(p, m); return 1; case FILE_BEQUAD: case FILE_BEQDATE: case FILE_BEQLDATE: p->q = (uint64_t) (((uint64_t)p->hq[0]<<56)|((uint64_t)p->hq[1]<<48)| ((uint64_t)p->hq[2]<<40)|((uint64_t)p->hq[3]<<32)| ((uint64_t)p->hq[4]<<24)|((uint64_t)p->hq[5]<<16)| ((uint64_t)p->hq[6]<<8)|((uint64_t)p->hq[7])); cvt_64(p, m); return 1; case FILE_LESHORT: p->h = (short)((p->hs[1]<<8)|(p->hs[0])); cvt_16(p, m); return 1; case FILE_LELONG: case FILE_LEDATE: case FILE_LELDATE: p->l = (int32_t) ((p->hl[3]<<24)|(p->hl[2]<<16)|(p->hl[1]<<8)|(p->hl[0])); cvt_32(p, m); return 1; case FILE_LEQUAD: case FILE_LEQDATE: case FILE_LEQLDATE: p->q = (uint64_t) (((uint64_t)p->hq[7]<<56)|((uint64_t)p->hq[6]<<48)| ((uint64_t)p->hq[5]<<40)|((uint64_t)p->hq[4]<<32)| ((uint64_t)p->hq[3]<<24)|((uint64_t)p->hq[2]<<16)| ((uint64_t)p->hq[1]<<8)|((uint64_t)p->hq[0])); cvt_64(p, m); return 1; case FILE_MELONG: case FILE_MEDATE: case FILE_MELDATE: p->l = (int32_t) ((p->hl[1]<<24)|(p->hl[0]<<16)|(p->hl[3]<<8)|(p->hl[2])); cvt_32(p, m); return 1; case FILE_FLOAT: cvt_float(p, m); return 1; case FILE_BEFLOAT: p->l = ((uint32_t)p->hl[0]<<24)|((uint32_t)p->hl[1]<<16)| ((uint32_t)p->hl[2]<<8) |((uint32_t)p->hl[3]); cvt_float(p, m); return 1; case FILE_LEFLOAT: p->l = ((uint32_t)p->hl[3]<<24)|((uint32_t)p->hl[2]<<16)| ((uint32_t)p->hl[1]<<8) |((uint32_t)p->hl[0]); cvt_float(p, m); return 1; case FILE_DOUBLE: cvt_double(p, m); return 1; case FILE_BEDOUBLE: p->q = ((uint64_t)p->hq[0]<<56)|((uint64_t)p->hq[1]<<48)| ((uint64_t)p->hq[2]<<40)|((uint64_t)p->hq[3]<<32)| ((uint64_t)p->hq[4]<<24)|((uint64_t)p->hq[5]<<16)| ((uint64_t)p->hq[6]<<8) |((uint64_t)p->hq[7]); cvt_double(p, m); return 1; case FILE_LEDOUBLE: p->q = ((uint64_t)p->hq[7]<<56)|((uint64_t)p->hq[6]<<48)| ((uint64_t)p->hq[5]<<40)|((uint64_t)p->hq[4]<<32)| ((uint64_t)p->hq[3]<<24)|((uint64_t)p->hq[2]<<16)| ((uint64_t)p->hq[1]<<8) |((uint64_t)p->hq[0]); cvt_double(p, m); return 1; case FILE_REGEX: case FILE_SEARCH: case FILE_DEFAULT: return 1; default: file_magerror(ms, "invalid type %d in mconvert()", m->type); return 0; } } private void mdebug(uint32_t offset, const char *str, size_t len) { (void) fprintf(stderr, "mget @%d: ", offset); file_showstr(stderr, str, len); (void) fputc('\n', stderr); (void) fputc('\n', stderr); } private int mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir, const unsigned char *s, uint32_t offset, size_t nbytes, size_t linecnt) { /* * Note: FILE_SEARCH and FILE_REGEX do not actually copy * anything, but setup pointers into the source */ if (indir == 0) { switch (type) { case FILE_SEARCH: ms->search.s = RCAST(const char *, s) + offset; ms->search.s_len = nbytes - offset; ms->search.offset = offset; return 0; case FILE_REGEX: { const char *b; const char *c; const char *last; /* end of search region */ const char *buf; /* start of search region */ const char *end; size_t lines; if (s == NULL) { ms->search.s_len = 0; ms->search.s = NULL; return 0; } buf = RCAST(const char *, s) + offset; end = last = RCAST(const char *, s) + nbytes; /* mget() guarantees buf <= last */ for (lines = linecnt, b = buf; lines && b < end && ((b = CAST(const char *, memchr(c = b, '\n', CAST(size_t, (end - b))))) || (b = CAST(const char *, memchr(c, '\r', CAST(size_t, (end - c)))))); lines--, b++) { last = b; if (b[0] == '\r' && b[1] == '\n') b++; } if (lines) last = RCAST(const char *, s) + nbytes; ms->search.s = buf; ms->search.s_len = last - buf; ms->search.offset = offset; ms->search.rm_len = 0; return 0; } case FILE_BESTRING16: case FILE_LESTRING16: { const unsigned char *src = s + offset; const unsigned char *esrc = s + nbytes; char *dst = p->s; char *edst = &p->s[sizeof(p->s) - 1]; if (type == FILE_BESTRING16) src++; /* check for pointer overflow */ if (src < s) { file_magerror(ms, "invalid offset %u in mcopy()", offset); return -1; } for (/*EMPTY*/; src < esrc; src += 2, dst++) { if (dst < edst) *dst = *src; else break; if (*dst == '\0') { if (type == FILE_BESTRING16 ? *(src - 1) != '\0' : *(src + 1) != '\0') *dst = ' '; } } *edst = '\0'; return 0; } case FILE_STRING: /* XXX - these two should not need */ case FILE_PSTRING: /* to copy anything, but do anyway. */ default: break; } } if (offset >= nbytes) { (void)memset(p, '\0', sizeof(*p)); return 0; } if (nbytes - offset < sizeof(*p)) nbytes = nbytes - offset; else nbytes = sizeof(*p); (void)memcpy(p, s + offset, nbytes); /* * the usefulness of padding with zeroes eludes me, it * might even cause problems */ if (nbytes < sizeof(*p)) (void)memset(((char *)(void *)p) + nbytes, '\0', sizeof(*p) - nbytes); return 0; } private int mget(struct magic_set *ms, const unsigned char *s, struct magic *m, size_t nbytes, unsigned int cont_level, int text, int recursion_level) { uint32_t offset = ms->offset; uint32_t count = m->str_range; union VALUETYPE *p = &ms->ms_value; - if (recursion_level >= 20) { + if (recursion_level >= MAX_RECURSION_LEVEL) { file_error(ms, 0, "recursion nesting exceeded"); return -1; } if (mcopy(ms, p, m->type, m->flag & INDIR, s, offset, nbytes, count) == -1) return -1; if ((ms->flags & MAGIC_DEBUG) != 0) { mdebug(offset, (char *)(void *)p, sizeof(union VALUETYPE)); #ifndef COMPILE_ONLY file_mdump(m); #endif } if (m->flag & INDIR) { int off = m->in_offset; if (m->in_op & FILE_OPINDIRECT) { const union VALUETYPE *q = CAST(const union VALUETYPE *, ((const void *)(s + offset + off))); switch (m->in_type) { case FILE_BYTE: off = q->b; break; case FILE_SHORT: off = q->h; break; case FILE_BESHORT: off = (short)((q->hs[0]<<8)|(q->hs[1])); break; case FILE_LESHORT: off = (short)((q->hs[1]<<8)|(q->hs[0])); break; case FILE_LONG: off = q->l; break; case FILE_BELONG: case FILE_BEID3: off = (int32_t)((q->hl[0]<<24)|(q->hl[1]<<16)| (q->hl[2]<<8)|(q->hl[3])); break; case FILE_LEID3: case FILE_LELONG: off = (int32_t)((q->hl[3]<<24)|(q->hl[2]<<16)| (q->hl[1]<<8)|(q->hl[0])); break; case FILE_MELONG: off = (int32_t)((q->hl[1]<<24)|(q->hl[0]<<16)| (q->hl[3]<<8)|(q->hl[2])); break; } } switch (m->in_type) { case FILE_BYTE: if (OFFSET_OOB(nbytes, offset, 1)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = p->b & off; break; case FILE_OPOR: offset = p->b | off; break; case FILE_OPXOR: offset = p->b ^ off; break; case FILE_OPADD: offset = p->b + off; break; case FILE_OPMINUS: offset = p->b - off; break; case FILE_OPMULTIPLY: offset = p->b * off; break; case FILE_OPDIVIDE: offset = p->b / off; break; case FILE_OPMODULO: offset = p->b % off; break; } } else offset = p->b; if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_BESHORT: if (OFFSET_OOB(nbytes, offset, 2)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = (short)((p->hs[0]<<8)| (p->hs[1])) & off; break; case FILE_OPOR: offset = (short)((p->hs[0]<<8)| (p->hs[1])) | off; break; case FILE_OPXOR: offset = (short)((p->hs[0]<<8)| (p->hs[1])) ^ off; break; case FILE_OPADD: offset = (short)((p->hs[0]<<8)| (p->hs[1])) + off; break; case FILE_OPMINUS: offset = (short)((p->hs[0]<<8)| (p->hs[1])) - off; break; case FILE_OPMULTIPLY: offset = (short)((p->hs[0]<<8)| (p->hs[1])) * off; break; case FILE_OPDIVIDE: offset = (short)((p->hs[0]<<8)| (p->hs[1])) / off; break; case FILE_OPMODULO: offset = (short)((p->hs[0]<<8)| (p->hs[1])) % off; break; } } else offset = (short)((p->hs[0]<<8)| (p->hs[1])); if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_LESHORT: if (OFFSET_OOB(nbytes, offset, 2)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = (short)((p->hs[1]<<8)| (p->hs[0])) & off; break; case FILE_OPOR: offset = (short)((p->hs[1]<<8)| (p->hs[0])) | off; break; case FILE_OPXOR: offset = (short)((p->hs[1]<<8)| (p->hs[0])) ^ off; break; case FILE_OPADD: offset = (short)((p->hs[1]<<8)| (p->hs[0])) + off; break; case FILE_OPMINUS: offset = (short)((p->hs[1]<<8)| (p->hs[0])) - off; break; case FILE_OPMULTIPLY: offset = (short)((p->hs[1]<<8)| (p->hs[0])) * off; break; case FILE_OPDIVIDE: offset = (short)((p->hs[1]<<8)| (p->hs[0])) / off; break; case FILE_OPMODULO: offset = (short)((p->hs[1]<<8)| (p->hs[0])) % off; break; } } else offset = (short)((p->hs[1]<<8)| (p->hs[0])); if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_SHORT: if (OFFSET_OOB(nbytes, offset, 2)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = p->h & off; break; case FILE_OPOR: offset = p->h | off; break; case FILE_OPXOR: offset = p->h ^ off; break; case FILE_OPADD: offset = p->h + off; break; case FILE_OPMINUS: offset = p->h - off; break; case FILE_OPMULTIPLY: offset = p->h * off; break; case FILE_OPDIVIDE: offset = p->h / off; break; case FILE_OPMODULO: offset = p->h % off; break; } } else offset = p->h; if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_BELONG: case FILE_BEID3: if (OFFSET_OOB(nbytes, offset, 4)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) & off; break; case FILE_OPOR: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) | off; break; case FILE_OPXOR: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) ^ off; break; case FILE_OPADD: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) + off; break; case FILE_OPMINUS: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) - off; break; case FILE_OPMULTIPLY: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) * off; break; case FILE_OPDIVIDE: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) / off; break; case FILE_OPMODULO: offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])) % off; break; } } else offset = (int32_t)((p->hl[0]<<24)| (p->hl[1]<<16)| (p->hl[2]<<8)| (p->hl[3])); if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_LELONG: case FILE_LEID3: if (OFFSET_OOB(nbytes, offset, 4)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) & off; break; case FILE_OPOR: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) | off; break; case FILE_OPXOR: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) ^ off; break; case FILE_OPADD: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) + off; break; case FILE_OPMINUS: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) - off; break; case FILE_OPMULTIPLY: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) * off; break; case FILE_OPDIVIDE: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) / off; break; case FILE_OPMODULO: offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])) % off; break; } } else offset = (int32_t)((p->hl[3]<<24)| (p->hl[2]<<16)| (p->hl[1]<<8)| (p->hl[0])); if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_MELONG: if (OFFSET_OOB(nbytes, offset, 4)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) & off; break; case FILE_OPOR: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) | off; break; case FILE_OPXOR: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) ^ off; break; case FILE_OPADD: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) + off; break; case FILE_OPMINUS: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) - off; break; case FILE_OPMULTIPLY: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) * off; break; case FILE_OPDIVIDE: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) / off; break; case FILE_OPMODULO: offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])) % off; break; } } else offset = (int32_t)((p->hl[1]<<24)| (p->hl[0]<<16)| (p->hl[3]<<8)| (p->hl[2])); if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; case FILE_LONG: if (OFFSET_OOB(nbytes, offset, 4)) return 0; if (off) { switch (m->in_op & FILE_OPS_MASK) { case FILE_OPAND: offset = p->l & off; break; case FILE_OPOR: offset = p->l | off; break; case FILE_OPXOR: offset = p->l ^ off; break; case FILE_OPADD: offset = p->l + off; break; case FILE_OPMINUS: offset = p->l - off; break; case FILE_OPMULTIPLY: offset = p->l * off; break; case FILE_OPDIVIDE: offset = p->l / off; break; case FILE_OPMODULO: offset = p->l % off; break; } } else offset = p->l; if (m->in_op & FILE_OPINVERSE) offset = ~offset; break; } switch (m->in_type) { case FILE_LEID3: case FILE_BEID3: offset = ((((offset >> 0) & 0x7f) << 0) | (((offset >> 8) & 0x7f) << 7) | (((offset >> 16) & 0x7f) << 14) | (((offset >> 24) & 0x7f) << 21)) + 10; break; default: break; } if (m->flag & INDIROFFADD) { offset += ms->c.li[cont_level-1].off; } if (mcopy(ms, p, m->type, 0, s, offset, nbytes, count) == -1) return -1; ms->offset = offset; if ((ms->flags & MAGIC_DEBUG) != 0) { mdebug(offset, (char *)(void *)p, sizeof(union VALUETYPE)); #ifndef COMPILE_ONLY file_mdump(m); #endif } } /* Verify we have enough data to match magic type */ switch (m->type) { case FILE_BYTE: if (OFFSET_OOB(nbytes, offset, 1)) return 0; break; case FILE_SHORT: case FILE_BESHORT: case FILE_LESHORT: if (OFFSET_OOB(nbytes, offset, 2)) return 0; break; case FILE_LONG: case FILE_BELONG: case FILE_LELONG: case FILE_MELONG: case FILE_DATE: case FILE_BEDATE: case FILE_LEDATE: case FILE_MEDATE: case FILE_LDATE: case FILE_BELDATE: case FILE_LELDATE: case FILE_MELDATE: case FILE_FLOAT: case FILE_BEFLOAT: case FILE_LEFLOAT: if (OFFSET_OOB(nbytes, offset, 4)) return 0; break; case FILE_DOUBLE: case FILE_BEDOUBLE: case FILE_LEDOUBLE: if (OFFSET_OOB(nbytes, offset, 8)) return 0; break; case FILE_STRING: case FILE_PSTRING: case FILE_SEARCH: if (OFFSET_OOB(nbytes, offset, m->vallen)) return 0; break; case FILE_REGEX: if (nbytes < offset) return 0; break; case FILE_INDIRECT: if (offset == 0) return 0; if ((ms->flags & (MAGIC_MIME|MAGIC_APPLE)) == 0 && file_printf(ms, "%s", m->desc) == -1) return -1; if (nbytes < offset) return 0; return file_softmagic(ms, s + offset, nbytes - offset, recursion_level, BINTEST, text); case FILE_DEFAULT: /* nothing to check */ default: break; } if (!mconvert(ms, m)) return 0; return 1; } private uint64_t file_strncmp(const char *s1, const char *s2, size_t len, uint32_t flags) { /* * Convert the source args to unsigned here so that (1) the * compare will be unsigned as it is in strncmp() and (2) so * the ctype functions will work correctly without extra * casting. */ const unsigned char *a = (const unsigned char *)s1; const unsigned char *b = (const unsigned char *)s2; uint64_t v; /* * What we want here is v = strncmp(s1, s2, len), * but ignoring any nulls. */ v = 0; if (0L == flags) { /* normal string: do it fast */ while (len-- > 0) if ((v = *b++ - *a++) != '\0') break; } else { /* combine the others */ while (len-- > 0) { if ((flags & STRING_IGNORE_LOWERCASE) && islower(*a)) { if ((v = tolower(*b++) - *a++) != '\0') break; } else if ((flags & STRING_IGNORE_UPPERCASE) && isupper(*a)) { if ((v = toupper(*b++) - *a++) != '\0') break; } else if ((flags & STRING_COMPACT_WHITESPACE) && isspace(*a)) { a++; if (isspace(*b++)) { if (!isspace(*a)) while (isspace(*b)) b++; } else { v = 1; break; } } else if ((flags & STRING_COMPACT_OPTIONAL_WHITESPACE) && isspace(*a)) { a++; while (isspace(*b)) b++; } else { if ((v = *b++ - *a++) != '\0') break; } } } return v; } private uint64_t file_strncmp16(const char *a, const char *b, size_t len, uint32_t flags) { /* * XXX - The 16-bit string compare probably needs to be done * differently, especially if the flags are to be supported. * At the moment, I am unsure. */ flags = 0; return file_strncmp(a, b, len, flags); } private int magiccheck(struct magic_set *ms, struct magic *m) { uint64_t l = m->value.q; uint64_t v; float fl, fv; double dl, dv; int matched; union VALUETYPE *p = &ms->ms_value; switch (m->type) { case FILE_BYTE: v = p->b; break; case FILE_SHORT: case FILE_BESHORT: case FILE_LESHORT: v = p->h; break; case FILE_LONG: case FILE_BELONG: case FILE_LELONG: case FILE_MELONG: case FILE_DATE: case FILE_BEDATE: case FILE_LEDATE: case FILE_MEDATE: case FILE_LDATE: case FILE_BELDATE: case FILE_LELDATE: case FILE_MELDATE: v = p->l; break; case FILE_QUAD: case FILE_LEQUAD: case FILE_BEQUAD: case FILE_QDATE: case FILE_BEQDATE: case FILE_LEQDATE: case FILE_QLDATE: case FILE_BEQLDATE: case FILE_LEQLDATE: v = p->q; break; case FILE_FLOAT: case FILE_BEFLOAT: case FILE_LEFLOAT: fl = m->value.f; fv = p->f; switch (m->reln) { case 'x': matched = 1; break; case '!': matched = fv != fl; break; case '=': matched = fv == fl; break; case '>': matched = fv > fl; break; case '<': matched = fv < fl; break; default: matched = 0; file_magerror(ms, "cannot happen with float: invalid relation `%c'", m->reln); return -1; } return matched; case FILE_DOUBLE: case FILE_BEDOUBLE: case FILE_LEDOUBLE: dl = m->value.d; dv = p->d; switch (m->reln) { case 'x': matched = 1; break; case '!': matched = dv != dl; break; case '=': matched = dv == dl; break; case '>': matched = dv > dl; break; case '<': matched = dv < dl; break; default: matched = 0; file_magerror(ms, "cannot happen with double: invalid relation `%c'", m->reln); return -1; } return matched; case FILE_DEFAULT: l = 0; v = 0; break; case FILE_STRING: case FILE_PSTRING: l = 0; v = file_strncmp(m->value.s, p->s, (size_t)m->vallen, m->str_flags); break; case FILE_BESTRING16: case FILE_LESTRING16: l = 0; v = file_strncmp16(m->value.s, p->s, (size_t)m->vallen, m->str_flags); break; case FILE_SEARCH: { /* search ms->search.s for the string m->value.s */ size_t slen; size_t idx; if (ms->search.s == NULL) return 0; slen = MIN(m->vallen, sizeof(m->value.s)); l = 0; v = 0; for (idx = 0; m->str_range == 0 || idx < m->str_range; idx++) { if (slen + idx > ms->search.s_len) break; v = file_strncmp(m->value.s, ms->search.s + idx, slen, m->str_flags); if (v == 0) { /* found match */ ms->search.offset += idx; break; } } break; } case FILE_REGEX: { int rc; regex_t rx; char errmsg[512]; if (ms->search.s == NULL) return 0; l = 0; rc = regcomp(&rx, m->value.s, REG_EXTENDED|REG_NEWLINE| ((m->str_flags & STRING_IGNORE_CASE) ? REG_ICASE : 0)); if (rc) { (void)regerror(rc, &rx, errmsg, sizeof(errmsg)); file_magerror(ms, "regex error %d, (%s)", rc, errmsg); v = (uint64_t)-1; } else { regmatch_t pmatch[1]; #ifndef REG_STARTEND #define REG_STARTEND 0 size_t l = ms->search.s_len - 1; char c = ms->search.s[l]; ((char *)(intptr_t)ms->search.s)[l] = '\0'; #else pmatch[0].rm_so = 0; pmatch[0].rm_eo = ms->search.s_len; #endif rc = regexec(&rx, (const char *)ms->search.s, 1, pmatch, REG_STARTEND); #if REG_STARTEND == 0 ((char *)(intptr_t)ms->search.s)[l] = c; #endif switch (rc) { case 0: ms->search.s += (int)pmatch[0].rm_so; ms->search.offset += (size_t)pmatch[0].rm_so; ms->search.rm_len = (size_t)(pmatch[0].rm_eo - pmatch[0].rm_so); v = 0; break; case REG_NOMATCH: v = 1; break; default: (void)regerror(rc, &rx, errmsg, sizeof(errmsg)); file_magerror(ms, "regexec error %d, (%s)", rc, errmsg); v = (uint64_t)-1; break; } regfree(&rx); } if (v == (uint64_t)-1) return -1; break; } case FILE_INDIRECT: return 1; default: file_magerror(ms, "invalid type %d in magiccheck()", m->type); return -1; } v = file_signextend(ms, m, v); switch (m->reln) { case 'x': if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%" INT64_T_FORMAT "u == *any* = 1\n", (unsigned long long)v); matched = 1; break; case '!': matched = v != l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%" INT64_T_FORMAT "u != %" INT64_T_FORMAT "u = %d\n", (unsigned long long)v, (unsigned long long)l, matched); break; case '=': matched = v == l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%" INT64_T_FORMAT "u == %" INT64_T_FORMAT "u = %d\n", (unsigned long long)v, (unsigned long long)l, matched); break; case '>': if (m->flag & UNSIGNED) { matched = v > l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%" INT64_T_FORMAT "u > %" INT64_T_FORMAT "u = %d\n", (unsigned long long)v, (unsigned long long)l, matched); } else { matched = (int64_t) v > (int64_t) l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%" INT64_T_FORMAT "d > %" INT64_T_FORMAT "d = %d\n", (long long)v, (long long)l, matched); } break; case '<': if (m->flag & UNSIGNED) { matched = v < l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%" INT64_T_FORMAT "u < %" INT64_T_FORMAT "u = %d\n", (unsigned long long)v, (unsigned long long)l, matched); } else { matched = (int64_t) v < (int64_t) l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "%" INT64_T_FORMAT "d < %" INT64_T_FORMAT "d = %d\n", (long long)v, (long long)l, matched); } break; case '&': matched = (v & l) == l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "((%" INT64_T_FORMAT "x & %" INT64_T_FORMAT "x) == %" INT64_T_FORMAT "x) = %d\n", (unsigned long long)v, (unsigned long long)l, (unsigned long long)l, matched); break; case '^': matched = (v & l) != l; if ((ms->flags & MAGIC_DEBUG) != 0) (void) fprintf(stderr, "((%" INT64_T_FORMAT "x & %" INT64_T_FORMAT "x) != %" INT64_T_FORMAT "x) = %d\n", (unsigned long long)v, (unsigned long long)l, (unsigned long long)l, matched); break; default: matched = 0; file_magerror(ms, "cannot happen: invalid relation `%c'", m->reln); return -1; } return matched; } private int handle_annotation(struct magic_set *ms, struct magic *m) { if (ms->flags & MAGIC_APPLE) { if (file_printf(ms, "%.8s", m->apple) == -1) return -1; return 1; } if ((ms->flags & MAGIC_MIME_TYPE) && m->mimetype[0]) { if (file_printf(ms, "%s", m->mimetype) == -1) return -1; return 1; } return 0; } private int print_sep(struct magic_set *ms, int firstline) { if (ms->flags & MAGIC_MIME) return 0; if (firstline) return 0; /* * we found another match * put a newline and '-' to do some simple formatting */ return file_printf(ms, "\n- "); }