diff --git a/contrib/bind/CHANGES b/contrib/bind/CHANGES index cdc1c3f63af7..203a4ef65bfa 100644 --- a/contrib/bind/CHANGES +++ b/contrib/bind/CHANGES @@ -1,3904 +1,4152 @@ + + --- 8.3.6-REL released --- (Sun Jun 8 15:11:32 PDT 2003) + +1547. [port] cope with spurious EINVAL from evRead. + +1538. [port] linux: not all distributions define IF_NAMESIZE. + +1502. [port] some IPv6 references were not protected. + + --- 8.3.5-REL released --- (Mon Jun 2 03:15:53 PDT 2003) + +1540. [bug] remove potential memory leak from net_data_create(). + +1537. [bug] dig buffer overrun with large command lines. + +1535. [bug] winnt: large zone transfers failed. + +1536. [cleanup] use NS_MAXMSG to define TCP buffers. + +1534. [func] The advertised EDNS UDP buffer size can now be set + via named.conf (edns-udp-size). + +1533. [bug] don't artificially restrict the update message size. + +1532. [bug] use maximum sized answer buffers in res_findzonecut(). + +1530. [bug] nslookup computed incorrect reverse lookup for IPv6. + +1529. [lint] unused variable in dnsquery.c::main(). + +1528. [bug] getaddrinfo() incorrectly rejected a numeric service + under certian circumstances. + +1527. [proto] add ns_t_apl (42). + +1526. [doc] res_{get,set}servers(). + +1523. [bug] getipnodebyname with AI_ADDRCONFIG set was broken + on HPUX 11.11. Detect IPv6 interfaces under linux. + +1519. [port] decunix: conflicting setnetgrent() and innetgr() + prototypes. + +1518. [cleanup] silence "No root nameservers for class XX" when + "forward only;" is set in options. + +1517. [cleanup] stop using putshort/putlong internally. + +1513. [bug] use ipnodes.{byname,byaddr} for IPv6 NIS lookups. + Add support for "YP_MULTI_". + +1511. [cleanup] don't use argument names in function prototypes. + +1510. [port] openbsd uses /bsd not /kernel. + +1506. [bug] named could sometimes set tc incorrectly. + +1505. [bug] potential overflow if pointer arithmetic wrapped. + +1503. [bug] named could make unnecessary queries for glue if the + additional section was full. + +1501. [port] decunix: OSF 3.2 does not have native 64 bit support. + +1500. [port] linux: namespace collision. + +1499. [port] linux: #include bin/dig/dig.c + +1498. [bug] ns_makecanon() could under read its destination buffer + by one character and fail to properly canonicalise. + +1497. [bug] res_mkupdate() used compression pointers when it + shouldn't. + +1496. [bug] res_mkupdate() didn't support NAPTR. + +1494. [bug] memory leak on thread destruction if gethostbyname() / + getnetbyname() have been called by the thread. + +1493. [bug] check scope for link local servers. + +1492. [placeholder] + +1491. [cleanup] indentation problems. + +1490. [bug] the seek offset was miscalculated when truncating + the ixfr log. + +1489. [func] named no longer queries for missing additional A6 + records. + +1488. [port] decunix: TruCluster support. + See port/decunix/TruCluster. + +1487. [bug] getnetgroup() takes (char **) not (const char **). + +1486. [func] res_query() now generates more/better debug on failure + +1485. [func] res_send() records the nameserver the response came + from. Dig retrieves this rather than reporting the + first address. + +1484. [bug] dig use sin.sin_port for IPv4. + +1483. [bug] nslookup could dereference a NULL pointer under certain + circumstances. + +1482. [bug] provide local storage for localtime_r result. + +1481. [bug] tv.tv_sec and time_t are not always the same type. + +1480. [bug] gethostbyname(), getaddrinfo() could drop address + if the previous call contained one of the new + addresses. + +1479. [func] try known lame servers if all other servers have + failed. + +1478. [cleanup] libbind: don't look for A6 records, don't follow + DNAME record (use the CNAMES), remove some bitstring + related functions. + +1477. [cleanup] libbind: namespace cleanup (irs_* to __irs*, + dst_* to __dst_* and tree_* to __tree*) + +1476. [bug] dig wasn't using a random query id. + +1475. [bug] "query-source address port *" + failed to use a system assigned port as documented. + +1474. [bug] named wasn't seeing cached NODATA CNAME records. + +1473. [bug] nslookup: buffer overrun when looking up reverse + IPv6 addresses under IP6.INT when not found under + IP6.ARPA. + +1472. [port] freebsd; current has pselect(). + +1471. [port] 'dig -P' failed on some platforms. + +1470. [bug] J.ROOT-SERVERS.NET is now 192.58.128.30. + +1467. [deleted] + +1461. [func] return referrals for glue (NS/A/AAAA) if recursion is + disabled (recursion no;). + +1460. [bug] NS_MD5RSA_MAX_BITS was not correct. + +1459. [bug] ns_sign2() could fail to compute a correct signature + if the TSIG ownername was compressed. + +1458. [bug] host: spurious "Unknown algorithm" message with default + zone listing. missing white space before '(' in SOA + format. + +1457. [bug] bison didn't like ns_parser.y. + +1456. [doc] document auth-nxdomain default is "no" (see # 524). + +1455. [bug] named failed to allow a cached NODATA response for + a ANY query to be retrieved. + +1454. [contrib] nsverifier from Bob.Whelton@qwest.com. + +1453. [bug] SOA answers should only be cached for the current + tick. + +1452. [bug] don't cache -ve response SOA record. + +1451. [port] bsdos: maybe_fix_includes is not required. + +1450. [bug] hint zones don't need to be reloaded when a "child" + zone is removed. + +1449. [bug] it was possible to orphan glue records. this could + lead to panics in stale(). + +1438. [bug] glue from a parent zone beneath a child zone could + be deleted by loading a child zone. + +1437. [bug] linux: probe_ipv6 was broken. + +1436. [port] decunix: update sys/bitypes.h + +1435. [func] named-xfer: log the zone name when reporting query + sent. + +1434. [doc] the man page for dn_expand failed to document eomorig. + +1433. [lint] remove unused variable. + +1432. [func] log TSIG key name if used with zone transfer. + +1431. [func] new category "update-security". + +1430. [func] libbind: the default nameservers now include ::1/:: + as well as 127.0.0.1/0.0.0.0 if none are specified in + resolv.conf. + +1429. [port] libbind: use strlcat/strlcpy if available. + +1428. [port] eventlib.c: cast tv_sec to long when calling *printf(). + +1427. [func] define INT8SZ + +1426. [port] res_dprintf() now supports format checking w/ gcc. + +1425. [bug] 'aa' was not being set appropriately with cross zone + CNAMES. + +1424. [cleanup] ip6_str2scopeid() now returns u_int32_t. + +1423. [bug] 'ndc restart' could fail to restart named if there + were no arguments to named. + +1422. [cleanup] optarg() etc. are declared in unistd.h. + +1421. [bug] clear and check errno when calling strtoul(). + +1420. [cleanup] use %p instead of %#x for printing pointers. + +1419. [cleanup] getinfo(): kill buflen manipulation. + +1418. [port] cast pointers to (size_t) when aligning. + +1417. [cleanup] make1101inaddr(): kill size manipulation. + +1416. [port] log_vwrite() now supports format checking w/ gcc. + +1415. [port] irix: probe for in6addr_any. + +1414. [bug] strtoul() cast (char*) to (unsigned char*). + +1413. [bug] host: soa values are not signed. + +1412. [bug] fix numeric port range check in getaddrinfo(). + +1411. [port] freebsd/netbsd/openbsd: #define USE_IFNAMELINKID. + +1410. [port] probe for sin6_scope_id when probing for IPv6 structs. + +1409. [bug] dig: reverse6 computed a incorrect nibble string. + +1408. [cleanup] res_mkquery.c: kill buflen manipulation. + +1407. [port] namespace clash EV_ERR -> EV_SETERR + --- 8.3.4-REL released --- (Thu Nov 14 05:45:26 PST 2002) 1469. [bug] buffer length calculation for PX was wrong. 1468. [bug] ns_name_ntol() could overwite a zero length buffer. 1466. [bug] large ENDS UDP buffer size could trigger a assertion. 1465. [bug] possible NULL pointer dereference in db_sec.c 1464. [bug] the buffer used to construct the -ve record was not big enough for all possible SOA records. use pointer arithmetic to calculate the remaining size in this buffer. 1463. [bug] use serial space arithmetic to determine if a SIG is too old, in the future or has internally constistant times. 1462. [bug] write buffer overflow in make_rr(). --- 8.3.3-REL released --- (Wed Jun 26 21:15:43 PDT 2002) -1301. [func] log attempts to exploit #1300. +1406. [func] log attempts to exploit #1405. -1300. [bug] Remote buffer overrun. +1405. [bug] Remote buffer overrun. -1299. [func] Log to xfer-in when a named-xfer fails and one of +1404. [func] Log to xfer-in when a named-xfer fails and one of the masters returned REFUSED. -1298. [bug] named could leak a OPT record when returning a +1403. [bug] named could leak a OPT record when returning a negative response. -1297. [func] 'ndc restart' will now attempt to preserve the +1402. [func] 'ndc restart' will now attempt to preserve the arguments named was started with. Use 'ndc restart --' to clear the arguements. Use 'ndc restart ' to restart named with a alternate set of arguements. -1296. [bug] delay setting need_restart until the response to +1401. [bug] delay setting need_restart until the response to ndc exec has been sent. -1295. [func] new ndc command 'args'. returns the arguements that +1400. [func] new ndc command 'args'. returns the arguements that named was started with %xx escaped. -1294. [bug] #1272 broke linkage for those OS's using -lfl (flex). +1399. [bug] #1377 broke linkage for those OS's using -lfl (flex). Move -ll/-lfl to LIBL for all platforms. --- 8.3.2-REL released --- (Mon Jun 17 20:24:32 PDT 2002) -1293. [doc] update man pages for 'dig', 'dnsquery' and 'host' +1398. [doc] update man pages for 'dig', 'dnsquery' and 'host' to reflect IPv6 capabilities (nslookup and nsupdate were already IPv4/IPv6 agnostic). -1292. [func] host: the default lookups now also include AAAA +1397. [func] host: the default lookups now also include AAAA records. -1291. [func] 'dig -x ' now works. +1396. [func] 'dig -x ' now works. -1290. [bug] 'dig @server' fail to report the IPv6 address of the +1395. [bug] 'dig @server' fail to report the IPv6 address of the server in error messages. -1289. [contrib] normalize_zone now handles $TTL. +1394. [contrib] normalize_zone now handles $TTL. -1288. [bug] named: -t and -w could not be used together. +1393. [bug] named: -t and -w could not be used together. -1287. [func] report serial(s) in out going transfer operation. +1392. [func] report serial(s) in out going transfer operation. --- 8.3.2-RC1 released --- (Thu May 30 23:06:11 PDT 2002) -1286. [func] libbind: no longer attempts bit string labels for +1391. [func] libbind: no longer attempts bit string labels for IPv6 reverse resolution. Try IP6.ARPA then IP6.INT for nibble style resolution. -1285. [port] linux: ss_family vs __ss_family in sockaddr_storage. +1390. [port] linux: ss_family vs __ss_family in sockaddr_storage. -1284. [port] freebsd: 5.0 uses gid_t rather that int for +1389. [port] freebsd: 5.0 uses gid_t rather that int for GETGROUPLIST_ARGS -1283. [port] bsdi: 4.2 also has struct sockaddr_storage. +1388. [port] bsdi: 4.2 also has struct sockaddr_storage. -1282. [bug] nslookup was using inet_ntoa() to print out a IPv6 +1387. [bug] nslookup was using inet_ntoa() to print out a IPv6 address. -1281. [bug] escape '(' and ')' when coverting to presentation +1386. [bug] escape '(' and ')' when coverting to presentation form. -1280. [func] server { edns yes_or_no; } is now supported. +1385. [func] server { edns yes_or_no; } is now supported. -1279. [bug] nslookup: partial coversion to similar style outputs +1384. [bug] nslookup: partial coversion to similar style outputs for both -type=aaaa and -type=a. -1278. [bug] free() of non-malloced memory in nslookup. +1383. [bug] free() of non-malloced memory in nslookup. -1277. [port] cast all instances of yytext in commands.l to (char*) +1382. [port] cast all instances of yytext in commands.l to (char*) to silence compilers on OS's where lex declares it as (unsigned char *). -1276. [port] hpux 11.22: ansify GetAnswer in getinfo.c to +1381. [port] hpux 11.22: ansify GetAnswer in getinfo.c to silence compiler. -1275. [bug] bad declaration of si in tsig_key_from_addr(). +1380. [bug] bad declaration of si in tsig_key_from_addr(). -1274. [port] hpux 11.22: ansify hexstring() and display() in +1379. [port] hpux 11.22: ansify hexstring() and display() in addr.c to silence compiler. -1273. [bug] const pointer conficts in res_debug.c. +1378. [bug] const pointer conficts in res_debug.c. -1272. [port] hpux 11.22: don't link against -ll unless required. +1377. [port] hpux 11.22: don't link against -ll unless required. -1272. [bug] main_need_num was not last entry in enum. +1376. [bug] main_need_num was not last entry in enum. main_need_tick nolonger required. -1271. [port] hpux: treat all hpux systems as BIG_ENDIAN, don't +1375. [port] hpux: treat all hpux systems as BIG_ENDIAN, don't include . -1270. [port] hpux 11.22 namespace clash DATASIZE -> BIND_DATASIZE. +1374. [port] hpux 11.22 namespace clash DATASIZE -> BIND_DATASIZE. -1269. [port] hpux 11.11 interface scaning. +1373. [port] hpux 11.11 interface scaning. -1268. [port] solaris: 64 bit binary compatability. +1372. [port] solaris: 64 bit binary compatability. -1267. [bug] aix4: missing IN6_IS_ADDR_V4COMPAT macro. +1371. [bug] aix4: missing IN6_IS_ADDR_V4COMPAT macro. -1266. [bug] If you are using TSIG #1261 introduced the potential +1370. [bug] If you are using TSIG #1365 introduced the potential of a infinite loop. -1265. [bug] nslookup failed on platforms that don't have BSD 43 +1369. [bug] nslookup failed on platforms that don't have BSD 43 style sockets. -1264. [bug] LINK_INIT and UNLINK were not compatible with +1368. [bug] LINK_INIT and UNLINK were not compatible with C++, use LINK_INIT_TYPE and UNLINK_TYPE instead. --- 8.3.2-T1B released --- (Tue May 7 18:49:58 PDT 2002) -1263. [bug] gethostans() could get out of sync parsing the +1367. [bug] gethostans() could get out of sync parsing the response if there was a very long CNAME chain. -1262. [bug] winnt: dumpdb and stats should now work reliably. +1366. [bug] winnt: dumpdb and stats should now work reliably. -1261. [bug] using a valid TSIG with a compressed ownername could +1365. [bug] using a valid TSIG with a compressed ownername could result a INSIST() failure. -1260. [func] "notify explicit;" from BIND 9. +1364. [func] "notify explicit;" from BIND 9. -1259. [misc] leave the verification of the OPT options to the +1363. [misc] leave the verification of the OPT options to the caller. -1258. [func] accept SOA MNAME field as legitimate additional +1362. [func] accept SOA MNAME field as legitimate additional data. -1257. [bug] malformed response to query w/ tsig + edns. +1361. [bug] malformed response to query w/ tsig + edns. -1256. [port] darwin: probe for IPv6 support. +1360. [port] darwin: probe for IPv6 support. -1255. [bug] xfers_running could become out of sync if a zone +1359. [bug] xfers_running could become out of sync if a zone was removed while it was being transfered. -1254. [func] nsupdate can now update IPv6 servers. +1358. [func] nsupdate can now update IPv6 servers. -1253. [func] host now accepts IPv6 addresses. +1357. [func] host now accepts IPv6 addresses. -1253. [bug] reserve space for the signature when performing a +1356. [bug] reserve space for the signature when performing a zone transfer. -1252. [func] dnsquery now accepts IPv6 addresses. +1355. [func] dnsquery now accepts IPv6 addresses. -1251. [bug] win32: it was possible to call RegCloseKey() on a +1354. [bug] win32: it was possible to call RegCloseKey() on a invalid key. -1250 [func] nslookup now accepts IPv6 addresses. +1353. [func] nslookup now accepts IPv6 addresses. -1249. [func] dig now accepts IPv6 addresses. +1352. [func] dig now accepts IPv6 addresses. -1248. [doc] correct some typos in named.conf.5 and corresponding +1351. [doc] correct some typos in named.conf.5 and corresponding html. -1247. [bug] get_salen() IPv6 support was broken for OSs w/o sa_len. +1350. [bug] get_salen() IPv6 support was broken for OSs w/o sa_len. -1246. [support] add highly dangerous compile time option +1349. [support] add highly dangerous compile time option NXDOMAIN_ON_DENIAL. it should not be used except in testing. -1245. [bug] if we don't have enough file descriptors to open +1348. [bug] if we don't have enough file descriptors to open a socket attempt to close a idle tcp client. -1244. [port] bsdi: 4.3 has struct sockaddr_storage. +1347. [port] bsdi: 4.3 has struct sockaddr_storage. -1243. [bug] SERVFAIL can have too many other causes to be used +1346. [bug] SERVFAIL can have too many other causes to be used say whether a server supports EDNS or not. -1242. [port] 64k answer buffers were causing stack space to be +1345. [port] 64k answer buffers were causing stack space to be exceeded for certian OS. Use heap space instead. -1241. [bug] getnameinfo() failed to lookup IPv4 mapped / +1344. [bug] getnameinfo() failed to lookup IPv4 mapped / compatible addresses. -1340. [bug] reference after free for included conf file name. +1343. [bug] reference after free for included conf file name. -1339. [bug] doaddinfo would not always attempt to fetch missing +1342. [bug] doaddinfo would not always attempt to fetch missing glue when it should have. -1338. [bug] an IPv6 only nameserver could generate spurious +1341. [bug] an IPv6 only nameserver could generate spurious sysquery errors. -1337. [port] linux: IN6ADDR_LOOPBACK_INIT, IN6ADDR_ANY_INIT and +1340. [port] linux: IN6ADDR_LOOPBACK_INIT, IN6ADDR_ANY_INIT and sockaddr_storage not declared by early kernels. -1336. [bug] getaddrinfo() could call freeaddrinfo() with an +1339. [bug] getaddrinfo() could call freeaddrinfo() with an invalid pointer. -1335. [bug] res_nupdate() failed to update the name servers +1338. [bug] res_nupdate() failed to update the name servers addresses before sending the update. -1334. [bug] A6 is expected in the additional section. +1337. [bug] A6 is expected in the additional section. --- 8.3.1-REL released --- (Thu Jan 31 21:28:59 PST 2002) -1333. [bug] cached NXDOMAIN/NODATA responses were being ignored +1336. [bug] cached NXDOMAIN/NODATA responses were being ignored when when fetching missing additional data. -1332. [func] "allow-query" is now supported for forward zones. +1335. [func] "allow-query" is now supported for forward zones. #define FORWARD_ALLOWS in bin/named/named.h to enable. -1331. [bug] rrsetadd() should explictly attach to the databuf +1334. [bug] rrsetadd() should explictly attach to the databuf rather than inheriting the reference count. -1330. [bug] potential reference after free. +1333. [bug] potential reference after free. -1329. [port] linux: ensure that CC is correctly propgated to +1332. [port] linux: ensure that CC is correctly propgated to probe_ipv6. -1328. [port] linux: res_init.c failed to compile on certian older +1331. [port] linux: res_init.c failed to compile on certian older machines. -1327. [contrib] add mysqlBind reference. +1330. [contrib] add mysqlBind reference. -1326. [bug] named-xfer could incorrectly report a fopen() failure +1329. [bug] named-xfer could incorrectly report a fopen() failure as a fdopen() failure. -1325. [bug] if fdopen() failed empty files could be left behind. +1328. [bug] if fdopen() failed empty files could be left behind. -1324. [bug] certian bad delegations could result in a DNS storm. +1327. [bug] certian bad delegations could result in a DNS storm. --- 8.3.0-REL released --- (Fri Jan 11 04:00:00 PST 2002) -1323. [bug] don't assume statp->_u._ext.ext is valid unless +1326. [bug] don't assume statp->_u._ext.ext is valid unless RES_INIT is set when calling res_*init(). -1322. [bug] call res_ndestroy() if RES_INIT is set when +1325. [bug] call res_ndestroy() if RES_INIT is set when res_*init() is called. -1321. [cleanup] YPKLUDGE in no longer default. +1324. [cleanup] YPKLUDGE in no longer default. -1320. [port] winnt: namespace collision #undef the system's EV_ERR. +1323. [port] winnt: namespace collision #undef the system's EV_ERR. -1319. [port] winnt: make __res_nopt() visible externally. +1322. [port] winnt: make __res_nopt() visible externally. -1318. [port] Tru64 UNIX V5.1 can return spurious EINVAL on +1321. [port] Tru64 UNIX V5.1 can return spurious EINVAL on close() after connect() failure. -1317. [bug] NULL used where zero was required. +1320. [bug] NULL used where zero was required. --- 8.3.0-RC2 released --- (Wed Jan 2 17:14:23 PST 2002) -1316. [bug] memory leak when a ixfr update or a re-applied update +1319. [bug] memory leak when a ixfr update or a re-applied update was rejected. -1315. [bug] the wrong options level check-names value was used +1318. [bug] the wrong options level check-names value was used when merging ixfr updates. -1314. [bug] corrupt update / ixfr updates should result in +1317. [bug] corrupt update / ixfr updates should result in merge errors being reported. -1313. [bug] set current_serial in db_ixfr.c:ixfr_getdelta(). +1316. [bug] set current_serial in db_ixfr.c:ixfr_getdelta(). -1312. [bug] use serial space arithmetic when selecting deltas for +1315. [bug] use serial space arithmetic when selecting deltas for IXFR. -1311. [contrib] mdnkit update to 2.2.3 +1314. [contrib] mdnkit update to 2.2.3 -1310. [bug] TSIG signed IXFR's wern't correctly verified. +1313. [bug] TSIG signed IXFR's wern't correctly verified. -1309. [port] winnt: re-order fclose/unlink so that the unlink +1312. [port] winnt: re-order fclose/unlink so that the unlink succeeds. -1308. [doc] miscellaneous nroff fixes. +1311. [doc] miscellaneous nroff fixes. -1307. [func] preferred-glue can now be used to partially order +1310. [func] preferred-glue can now be used to partially order the additional section. -1306. [bug] It was possible to trigger an INSIST failure with +1309. [bug] It was possible to trigger an INSIST failure with certian EDNS responses. -1305. [bug] named-xfer could return a false up-to-date status. +1308. [bug] named-xfer could return a false up-to-date status. -1304. [bug] "named-xfer -x" didn't attempt to use the specified +1307. [bug] "named-xfer -x" didn't attempt to use the specified address for all possible masters if earlier connects failed. -1303. [contrib] nslint upgraded from 2.0.2 to 2.1a3 (by author). +1306. [contrib] nslint upgraded from 2.0.2 to 2.1a3 (by author). -1302. [bug] res_nametotype() and res_nametoclass() were broken. +1305. [bug] res_nametotype() and res_nametoclass() were broken. -1301. [bug] "ndc reload zone" without class was broken. +1304. [bug] "ndc reload zone" without class was broken. -1300. [port] Tru64 UNIX 5.1 does not like __align as a element name. +1303. [port] Tru64 UNIX 5.1 does not like __align as a element name. -1299. [bug] host failed to handle "." in search list correctly. +1302. [bug] host failed to handle "." in search list correctly. -1298. [bug] max-ixfr-log-size default was unreasonable. +1301. [bug] max-ixfr-log-size default was unreasonable. -1297. [bug] read buffer overflows. +1300. [bug] read buffer overflows. -1296. [protocol] NAPTR additional section processing. +1299. [protocol] NAPTR additional section processing. --- 8.3.0-RC1 released --- (Wed Nov 21 20:41:32 PST 2001) -1295. [bug] be more aggressive at dropping messages from certian +1298. [bug] be more aggressive at dropping messages from certian well known ports. -1294. [bug] hint zones and root stub zones were not being removed +1297. [bug] hint zones and root stub zones were not being removed correctly. -1293. [port] MPE/iX workaround recvfrom() not supporting larger +1296. [port] MPE/iX workaround recvfrom() not supporting larger address buffers required for IPv6. -1292. [bug] memory leak: free_flushset(). +1295. [bug] memory leak: free_flushset(). -1291. [func] accept and generate EDNS0 queries. +1294. [func] accept and generate EDNS0 queries. -1290. [func] allow initial notifies on startup to be suppressed. +1293. [func] allow initial notifies on startup to be suppressed. [Kenneth Karoliussen, kenneth@activeisp.com] -1289. [func] allow keys to be specified in the masters clause. +1292. [func] allow keys to be specified in the masters clause. -1288. [func] named-xfer report if response was signed. +1291. [func] named-xfer report if response was signed. -1287. [bug] named-xfer could report false TSIG failures under +1290. [bug] named-xfer could report false TSIG failures under certian conditions. -1286. [bug] don't allow rtt estimates to drop to zero. +1289. [bug] don't allow rtt estimates to drop to zero. -1285. [bug] non-answering servers rtt estimates were not always +1288. [bug] non-answering servers rtt estimates were not always penalised. -1284. [bug] struct __res_state was still broken after #1265. +1287. [bug] struct __res_state was still broken after #1268. -1283. [port] addition platform support for _r functions. +1286. [port] addition platform support for _r functions. -1282. [port] pw->pw_class is platform dependant. +1285. [port] pw->pw_class is platform dependant. -1281. [port] namespace collision: dprintf -> res_dprintf, +1284. [port] namespace collision: dprintf -> res_dprintf, ERR -> EV_ERR, OK -> EV_OK. -1280. [cleanup] gai_strerror() re-written. +1283. [cleanup] gai_strerror() re-written. -1279. [bug] non-glue was not being reported on reload. +1282. [bug] non-glue was not being reported on reload. -1278. [bug] Remove the INSIST at the end if zone_maint(). +1281. [bug] Remove the INSIST at the end if zone_maint(). -1277. [func] hostname.bind/txt/chaos now returns the name of +1280. [func] hostname.bind/txt/chaos now returns the name of the machine hosting the nameserver. This is useful in diagnosing problems with anycast servers. -1276. [bug] dns_ho.c:add_hostent() was not returning the amount +1279. [bug] 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 wern't suppressing duplicate addresses. -1275. [port] sunos: old sprintf. +1278. [port] sunos: old sprintf. -1274. [port] MPE/iX C.70 +1277. [port] MPE/iX C.70 -1273. [bug] host was sorting multiple RRs into the wrong order +1276. [bug] host was sorting multiple RRs into the wrong order prior to verifying. --- 8.3.0-T2A released --- (Thu Sep 27 18:15:00 PDT 2001) -1272. [bug] "ndc trace 0" should behave like "ndc notrace". +1275. [bug] "ndc trace 0" should behave like "ndc notrace". -1271. [func] inet_{cidr,net}_{pton,ntop}() now have IPv6 support. +1274. [func] inet_{cidr,net}_{pton,ntop}() now have IPv6 support. -1270. [bug] AXFR style IXFR responses were not handled properly, +1273. [bug] AXFR style IXFR responses were not handled properly, transfer-format single-answer. -1269. [bug] misc: more string format fixes, open va_end(), +1272. [bug] misc: more string format fixes, open va_end(), call exit() with positive values, include . -1268. [func] Resolver, dig: "DNSSEC OK" (DO) support. +1271. [func] Resolver, dig: "DNSSEC OK" (DO) support. -1267. [port] HP e3000 MPE is big-endian. +1270. [port] HP e3000 MPE is big-endian. -1266. [func] dig +[no]tr: use relative names when printing +1269. [func] dig +[no]tr: use relative names when printing out a zone transfer ([NO]TRuncate). -1265. [bug] struct __res_state to large on 64 bit arch. +1268. [bug] struct __res_state to large on 64 bit arch. -1264. [port] winnt: pass the root zone as "." to named-xfer +1267. [port] winnt: pass the root zone as "." to named-xfer rather than "". -1263. [port] #1227 broke sunos. +1266. [port] #1230 broke sunos. -1262. [func] log type as well as class for denied queries. +1265. [func] log type as well as class for denied queries. -1261. [bug] get*by*() failed to check if res_init() had been +1264. [bug] get*by*() failed to check if res_init() had been called. -1260. [func] resolver option: no-tld-query. disables trying +1263. [func] resolver option: no-tld-query. disables trying unqualified as a tld. no_tld_query is also supported for FreeBSD compatability. -1259. [func] enable NOADDITIONAL code by default. +1262. [func] enable NOADDITIONAL code by default. -1258. [func] treat class ANY as class IN for access control for +1261. [func] treat class ANY as class IN for access control for non-xfr queries. -1257. [func] increase nameserver chaining distance from 1 to 3 +1260. [func] increase nameserver chaining distance from 1 to 3 (NS_MAX_DISTANCE). -1256. [bug] increased loop avoidance with well known ports. +1259. [bug] increased loop avoidance with well known ports. -1255. [bug] cached NXDOMAIN responses were being ignored when +1258. [bug] cached NXDOMAIN responses were being ignored when selecting servers to query. -1254. [support] improved support for parallel make. +1257. [support] improved support for parallel make. -1253. [port] winnt: support for change #1218 +1256. [port] winnt: support for change #1221 -1252. [port] winnt: the stat structure does not have st_blksize. +1255. [port] winnt: the stat structure does not have st_blksize. -1251. [bug] AXFR style IXFR responses were not handled properly. +1254. [bug] AXFR style IXFR responses were not handled properly. -1250. [doc] document support-ixfr. +1253. [doc] document support-ixfr. -1249. [func] add support gcc's format string checking. +1252. [func] add support gcc's format string checking. -1248. [bug] string formats. +1251. [bug] string formats. -1247. [bug] dig -t axfr attempted to use UDP not TCP. +1250. [bug] dig -t axfr attempted to use UDP not TCP. -1246. [doc] miscellaneous nroff fixes. +1249. [doc] miscellaneous nroff fixes. -1245. [port] winnt: named-xfer failures. improved recvfrom() +1248. [port] winnt: named-xfer failures. improved recvfrom() emulation. -1244. [port] winnt: bug in tracking "file" file descriptors +1247. [port] winnt: bug in tracking "file" file descriptors extend "file" file descriptors support to 2048. -1243. [cleanup] defining REPORT_ERRORS in lib/dst caused compile time +1246. [cleanup] defining REPORT_ERRORS in lib/dst caused compile time errors. -1242. [bug] inet_pton() failed to reject octal input. +1245. [bug] inet_pton() failed to reject octal input. -1241. [bug] memory leaks: zone forwarder table, server key list, +1244. [bug] memory leaks: zone forwarder table, server key list, global forwarder table, query tsig info, unused channels, key names, xfr tsig info. -1240. [bug] restore message if ns_forw() fails. +1243. [bug] restore message if ns_forw() fails. -1239. [bug] call res_ndestroy() in net_data_destroy(). +1242. [bug] call res_ndestroy() in net_data_destroy(). -1238. [func] named-bootconf now supports HP's "no-round-robin". +1241. [func] named-bootconf now supports HP's "no-round-robin". -1237. [bug] buffer overrun, access mode read. +1240. [bug] buffer overrun, access mode read. -1236. [bug] ignore white space after address of nameserver. +1239. [bug] ignore white space after address of nameserver. -1235. [port] solaris 2.4: use ioctl(FIONBIO) rather than fcntl(). +1238. [port] solaris 2.4: use ioctl(FIONBIO) rather than fcntl(). --- 8.3.0-T1A released --- (Wed Jun 20 19:05:01 PDT 2001) -1234. [bug] memory leak with "use-id-pool no;". +1237. [bug] memory leak with "use-id-pool no;". -1233. [func] res_setservers(), res_getservers(). +1236. [func] res_setservers(), res_getservers(). -1232. [bug] don't assume the forwarder has dropped bogus records. +1235. [bug] don't assume the forwarder has dropped bogus records. -1231. [bug] always restart a query if we dropped records when +1234. [bug] always restart a query if we dropped records when parsing. -1230. [func] report the address the server learnt the record from +1233. [func] report the address the server learnt the record from in lame server messages. -1229. [func] opaque rdata support for unknown types. +1232. [func] opaque rdata support for unknown types. -1228. [protocol] IXFR don't test for AA, don't check that the question +1231. [protocol] IXFR don't test for AA, don't check that the question is returned. -1227. [port] solaris: sys_nerr and sys_errlist do not exist when +1230. [port] solaris: sys_nerr and sys_errlist do not exist when compiling in 64 bit mode. -1226. [placeholder] +1229. [placeholder] -1225. [bug] don't send cross class additional records. +1228. [bug] don't send cross class additional records. -1224. [bug] use after realloc(), non portable pointer arithmetic in +1227. [bug] use after realloc(), non portable pointer arithmetic in grmerge(). -1223. [bug] allow all KEY records in parent zone at bottom of zone +1226. [bug] allow all KEY records in parent zone at bottom of zone cut. -1222. [bug] HMAC-MD5 key files are now mode 0600. +1225. [bug] HMAC-MD5 key files are now mode 0600. -1221. [port] aix: 4.3.3.0 (4.3.2.0?) and later have strndup(). +1224. [port] aix: 4.3.3.0 (4.3.2.0?) and later have strndup(). -1220. [port] winnt: isc_movefile() and syslog() updated. +1223. [port] winnt: isc_movefile() and syslog() updated. -1219. [bug] winnt: zone transfers could fail. +1222. [bug] winnt: zone transfers could fail. -1218. [func] add IPv6 transport support to the resolver from the +1221. [func] add IPv6 transport support to the resolver from the KAME project. Includes initial bitstring support. -1217. [bug] #1186 broke the command-line programs that relied on +1220. [bug] #1189 broke the command-line programs that relied on the fact that HMAC-MD5 keys have keyid 0. -1216. [bug] #1180 completely broke inet_nsap_addr(), +1219. [bug] #1183 completely broke inet_nsap_addr(), inet_nsap_ntoa() still didn't emit the leading 0x. -1215. [bug] long UNIX domain control socket names were not being +1218. [bug] long UNIX domain control socket names were not being printed in full. -1214. [bug] getrgnam()/getgrgid() leaked memory. +1217. [bug] getrgnam()/getgrgid() leaked memory. -1213. [bug] #1199 introduced a reference after free bug. +1216. [bug] #1202 introduced a reference after free bug. -1212. [bug] some function declarations wern't protected by +1215. [bug] some function declarations wern't protected by __BEGIN_DECL/__END_DECL in resolv.h. -1211. [port] next: openstep is now supported as well as nextstep. +1214. [port] next: openstep is now supported as well as nextstep. -1210. [port] add: SCO Unix 5.0.6. +1213. [port] add: SCO Unix 5.0.6. -1209. [port] winnt: issues. +1212. [port] winnt: issues. -1208. [func] close "dynamic" file channels when debug is set to +1211. [func] close "dynamic" file channels when debug is set to zero allowing them to be removed and re-opened. -1207. [contrib] new h2n from author. +1210. [contrib] new h2n from author. -1206. [bug] ixfr_getdelta() uninitialised variable used. +1209. [bug] ixfr_getdelta() uninitialised variable used. -1205. [bug] parse_cert_rr() passes wrong buffer size to b64_pton(). +1208. [bug] parse_cert_rr() passes wrong buffer size to b64_pton(). -1204. [bug] memory leak: pathname leaked specifing controls unix. +1207. [bug] memory leak: pathname leaked specifing controls unix. -1203. [bug] detect corrupted ixfr logs. +1206. [bug] detect corrupted ixfr logs. -1202. [bug] memory leak: dynamic update was leaking. +1205. [bug] memory leak: dynamic update was leaking. -1201. [bug] ISO/IEC 9945-1 versions of getpwuid_r(), getpwnam_r(), +1204. [bug] ISO/IEC 9945-1 versions of getpwuid_r(), getpwnam_r(), getgrgid_r() and getgrnam_r() were not ISO/IEC 9945-1 compliant. -1200. [bug] memory leak: when following CNAMES from of req_query(). +1203. [bug] memory leak: when following CNAMES from of req_query(). -1199. [bug] memory leak: when defining keys. +1202. [bug] memory leak: when defining keys. -1198. [func] reference count all databuf activity. +1201. [func] reference count all databuf activity. -1197. [func] deallocate-on-exit yes; will call abort() if there +1200. [func] deallocate-on-exit yes; will call abort() if there is still active memory. -1196. [func] memactive(): report if there is still active memory. +1199. [func] memactive(): report if there is still active memory. -1195. [bug] memory leak: include in named.conf leaked file name. +1198. [bug] memory leak: include in named.conf leaked file name. -1194. [port] MPE/IX port updated by Mark Bixby of the +1197. [port] MPE/IX port updated by Mark Bixby of the HP CSY MPE/iX Internet & Interoperability Team. -1193. [port] winnt: path separator. +1196. [port] winnt: path separator. -1192. [bug] winnt: fix accept failures. +1195. [bug] winnt: fix accept failures. -1191. [port] winnt: a CLI tool for controling named 'BINDcmd' now +1194. [port] winnt: a CLI tool for controling named 'BINDcmd' now exists. -1190. [contrib] nslint upgraded from 1.5.1 to 2.0.2 (by author). +1193. [contrib] nslint upgraded from 1.5.1 to 2.0.2 (by author). -1189. [port] redo #1146 to cope w/ differing gettimeofday() +1192. [port] redo #1147 to cope w/ differing gettimeofday() function signatures. -1188. [bug] memory leak when removing multiple records via dynamic +1191. [bug] memory leak when removing multiple records via dynamic updates. --- 8.2.4-T1B released --- (Thu Apr 19 14:38:30 PDT 2001) -1187. [support] Don't accept in a query names which would be rejected +1190. [support] Don't accept in a query names which would be rejected in responses. -1186. [bug] DNSSEC key ids were computed incorrectly. +1189. [bug] DNSSEC key ids were computed incorrectly. -1185. [bug] remember if a notify came in while a zone transfer is +1188. [bug] remember if a notify came in while a zone transfer is in progress and perform a refresh check after the transfer completes. --- 8.2.4-T1A released --- (Sun Apr 1 12:15:48 PDT 2001) -1184. [support] notify delay limit now "nzones" rather than "nzones/5". +1187. [support] notify delay limit now "nzones" rather than "nzones/5". -1183. [port] new port/cygwin contributed by s_c_biggs@bigfoot.com. +1186. [port] new port/cygwin contributed by s_c_biggs@bigfoot.com. -1182. [contrib] new contrib/mdnkit (V1.3) from author. +1185. [contrib] new contrib/mdnkit (V1.3) from author. -1181. [bug] dig -T was only delaying after the first batched query. +1184. [bug] dig -T was only delaying after the first batched query. -1180. [bug] NSAP processing did not support leading 0x as required +1183. [bug] NSAP processing did not support leading 0x as required by RFC 1706. -1179. [contrib] new contrib/adm from official ftp site. +1182. [contrib] new contrib/adm from official ftp site. -1178. [contrib] new contrib/host from author. +1181. [contrib] new contrib/host from author. -1177. [contrib] new contrib/dnsp from author. +1180. [contrib] new contrib/dnsp from author. -1176. [bug] fix memory and file descriptor leaks. +1179. [bug] fix memory and file descriptor leaks. -1175. [bug] statp->nsort could fail to be re-initialised if +1178. [bug] statp->nsort could fail to be re-initialised if resolv.conf goes away. -1174. [port] winnt: missing call to sockout() in close(). +1177. [port] winnt: missing call to sockout() in close(). -1173. [bug] suppress repeated notifies when a nameserver is a +1176. [bug] suppress repeated notifies when a nameserver is a CNAME. -1172. [bug] allow res_{n}update to take a single unlinked element. +1175. [bug] allow res_{n}update to take a single unlinked element. -1171. [doc] rfc2308-type1 applies to answers from the cache. +1174. [doc] rfc2308-type1 applies to answers from the cache. -1170. [port] winnt: does not have unix domain sockets. +1173. [port] winnt: does not have unix domain sockets. -1171. [bug] xfers_deferred could become out of sync. +1172. [bug] xfers_deferred could become out of sync. -1170. [bug] check the family before using a cached result from +1171. [bug] check the family before using a cached result from gethostbyname*(). -1169. [cleanup] namespace cleanup of prand_conf. +1170. [cleanup] namespace cleanup of prand_conf. -1168. [port] fix ctk ORD_32 problem on some HPUX 10.20 systems. +1169. [port] fix ctk ORD_32 problem on some HPUX 10.20 systems. -1167. [support] note possible HAVE_STRNDUP need for AIX4. +1168. [support] note possible HAVE_STRNDUP need for AIX4. -1166. [bug] bad $TTL could kill the contributed dns_signer. +1167. [bug] bad $TTL could kill the contributed dns_signer. -1165. [func] INITIALZONES tuning for large servers (> 1000 zones). +1166. [func] INITIALZONES tuning for large servers (> 1000 zones). -1164. [bug] the resolver could leak a file descriptors under +1165. [bug] the resolver could leak a file descriptors under certain conditions. -1163. [func] ns_critical() is now available. +1164. [func] ns_critical() is now available. -1162. [port] winnt: add strerror. +1163. [port] winnt: add strerror. -1161. [support] log out of memory during zone transfers. +1162. [support] log out of memory during zone transfers. -1160. [support] extend "Response from unexpected source" message. +1161. [support] extend "Response from unexpected source" message. -1159. [doc] query-source is used for TCP and that the port is +1160. [doc] query-source is used for TCP and that the port is ignored. -1158. [func] add A6, DNAME, SINK and OPT to list of known RR types +1159. [func] add A6, DNAME, SINK and OPT to list of known RR types in res_debug. -1157. [perf] add more ns_wouldlog() calls on mainline. +1158. [perf] add more ns_wouldlog() calls on mainline. -1156. [bug] don't use a known bogus key name. +1157. [bug] don't use a known bogus key name. -1155. [support] log possible "TSIG BUG" exploit attempts. Requires +1156. [support] log possible "TSIG BUG" exploit attempts. Requires LOG_TSIG_BUG to be defined in bin/named/named.h. -1154. [bug] RFC2317 support was broken in bin/host command again. +1155. [bug] RFC2317 support was broken in bin/host command again. -1153. [support] be more consist with the use of slave vs secondary. +1154. [support] be more consist with the use of slave vs secondary. -1152. [bug] ixfr processing could leave Z_XFER_RUNNING set. +1153. [bug] ixfr processing could leave Z_XFER_RUNNING set. -1151. [bug] failed to correctly parse the orginal ttl in SIG +1152. [bug] failed to correctly parse the orginal ttl in SIG records. -1150. [bug] forwarders: it was possible to use freed memory. +1151. [bug] forwarders: it was possible to use freed memory. -1149. [support] zone rejected message to error level. +1150. [support] zone rejected message to error level. -1148. [bug] non-glue now logged to category load. +1149. [bug] non-glue now logged to category load. -1147. [bug] handle notify w/ SOA records better. +1148. [bug] handle notify w/ SOA records better. -1146. [support] be more gentle in handling bad system clocks. +1147. [support] be more gentle in handling bad system clocks. -1145. [port] solaris: 2.4 does not have pthreads. +1146. [port] solaris: 2.4 does not have pthreads. -1144. [support] log class w/ denied messages. +1145. [support] log class w/ denied messages. -1143. [bug] only use the query-source address, not port, for TCP +1144. [bug] only use the query-source address, not port, for TCP connections. -1142. [doc] document change #924 better (doc/html/controls.html). +1143. [doc] document change #924 better (doc/html/controls.html). -1141. [bug] DESTDIR is not part of the nslookup help file path. +1142. [bug] DESTDIR is not part of the nslookup help file path. -1140. [bug] only reset interval timers if the value has changed. +1141. [bug] only reset interval timers if the value has changed. --- 8.2.3-REL released --- -1139. [bug] inet_{net_,}ntop() had an off-by-one error. +1140. [bug] inet_{net_,}ntop() had an off-by-one error. -1138. [bug] purge_nonglue() should only be fatal on master +1139. [bug] purge_nonglue() should only be fatal on master servers. 1138. [port] add include/errs.h to various ports. winnt: #1130 caused linkage failures. --- 8.2.3-RC5 released --- 1137. [bug] rfc1034 escape sequences not processed when replaying updates. 1136. [port] winnt: named nolonger creates resolv.conf. 1135. [bug] fixup from #1130/1132. 1134. [port] winnt: SIOCGIFADDR, SIOCGIFFLAGS, SIOCGIFDSTADDR and mkstemp() fixes. 1133. [bug] sorting of SIG/non-SIG records prior to rrset ordering of was broken. --- 8.2.3-RC4 released --- 1132. [lint] more #1130. 1131. [support] TTL 0 is now allowed in zone files. 1130. [lint] massive, massive delinting from "gcc -Wall". 1129. [support] "max_log_size_ixfr" is now a scaled number (4m, etc). 1128. [contrib] updated mdnkit. 1127. [port] winnt: support for more interfaces, dnskeygen. 1126. [bug] resolver: close cached file descriptors when socket() fails. 1125. [bug] when ns_addr_list is rotated, rotate cached file descriptors. 1124. [bug] the select() timeout was not always being correctly computed. 1123. [bug] changes to ns_addr_list were not being reflected into our private copy. 1122. [port] sco: DESTRUN and DESTSBIN can't be the same. 1121. [cleanup] re-word "server is ??? priming" status message. 1120. [bug] more #1108 fine tuning. 1119. [bug] "delete all" RRs were not being printed correctly. 1118. [port] winnt: always install the named executable 1117. [port] linux: turn off returning ICMP port unreachables. 1116. [bug] minor tweak to #1108 1115. [bug] fail if tsig transfers are requested but we can't communicate the keys to named-xfer. 1114. [bug] remove extraneous semi-colon from ns_parser.y --- 8.2.3-T9B released --- 1113. [support] show config file name and age in "ndc status" 1112. [support] "ndc status" no longer mentions loading of config. 1111. [port] some versions of sunos don't have _POSIX_PATH_MAX 1110. [bug] zones with Null keys at delegation incorreclty rejected. 1109. [support] named-xfer was bombing on non-TSIG'd zones 1108. [support] ignore queries that come in during long synch ops 1107. [func] allow the default syslog facility to be set by adding -DISC_FACILITY= to CDEBUG in Makefile.set. 1106. [func] host statistics can now be cleared after they are dumped. Use "ndc stats clear". 1105. [func] host-statistics-max can be used to set a upper bound on the number hosts we collect statistics against. 1104. [func] the source of a record is no longer dependent on setting "host-statistics yes;" 1103. [doc] winnt: updated port specific notes. 1102. [port] winnt: BINDctrl fixes 1101. [port] winnt: install fixes 1100. [bug] named-xfer some memory allocations were not checked. 1099. [bug] more missing INIT_LINK's. 1098. [support] force gmake to fail if the sub-shell fails. 1097. [port] winnt: lower the logging level so that BINDCtrl status checks do not cause the eventlog to fillup. 1096. [bug] don't pass '-i' to named-xfer unless we are going to attempt a IXFR. 1095. [bug] dig: report missing arguements. 1094. [port] winnt: more cylink fixes, updated install. 1093. [bug] winnt: build lib cylink correctly 1092. [cleanup] winnt: snmpmib.c is nolonger required 1091. [support] winnt: workout the install directory. 1090. [bug] winnt: install was copying old over new. 1089. [bug] winnt: fix copyright for nameserver.c winnt: snmpmib.c not needed in libbind.dsp 1088. [bug] #1053 still contained NAPTR problems. --- 8.2.3-T8B released --- 1087. [port] sunos/gcc _POSIX_PATH_MAX isn't defined when it should be. 1086. [doc] malformed man page for heap. 1085. [bug] ixfr responses to zones we don't server were malformed. 1084. [bug] INIT_LINK before APPEND in four more places. 1083. [support] only log "no options before zone" config error before FIRST zone [kjd]. 1082. [bug] have client-side IXFR work in single answer mode [kjd]. 1081. [bug] have server-side IXFR work in single answer mode [kjd]. 1080. [support] still do IXFR's even when a file name is not specified for zone [kjd]. 1079. [support] need to have a file name for a hints zone [kjd]. 1078. [port] WinNT interface enumeration fixes from Danny Mayer. 1077. [support] format string audit. 1076. [port] now recognize RH7.0's "strndup()" 1075. [contrib] add contrib/resparse-1.3 [Henning Schulzrinne @CU] 1074. [support] INSIST that lists are correctly managed. 1073. [port] Win/NT port work from Danny Mayer. Dig, host and nslookup have been added. 1072. [port] work around a gcc bug on solaris. 1071. [bug] memory leak in res_nsendsigned(). 1070. [bug] We were accepting non syntactically valis SOA records. 1069. [port] movefile() is now part of libbind as isc_movefile(), remaining rename() calls converted to isc_movefile(). 1068. [bug] purge the zone from memory if an error is detected on loading. 1067. [bug] reload the parent zone if loading the child zone fails, the parent zone may otherwise be corrupted. 1066. [bug] refresh/retry timer need to be reset after IXFR 1065. [bug] IXFR change list could be freed to early. 1064. [bug] unchecked memget in sx_send_ixfr(). 1063. [bug] fix #1041 was incomplete. 1062. [bug] host printed out address records multiple times if they were at the end of a CNAME chain. 1061. [bug] host failed to look for A records for the second an subsequent entries in the search list when using the default lookup. 1060. [bug] $GENERATE did not reject a out of zone LHS. 1059. [bug] res_findzonecut() contained a bad debugging printf. 1058. [bug] possible NULL pointer de-reference in dst_key_to_buffer(). 1057. [doc] document that bogus causes anti-alias processing. 1056. [bug] ns_sprintrrf() could incorrectly print "." as "@". 1055. [bug] aa was being cleared on notify "queries" prior to testing. 1054. [bug] NAPTR records were using name compression. 1053. [bug] NAPTR records were not being printed correctly. 1052. [bug] UPDATES w/ NAPTR records were failing. 1051. [contrib] YADDAS: Yet another DNS database awk script. 1050. [bug] named-bootconf did not handle cacheless secondary/stub zones. NOTE cacheless secondary/stub zones are not recommended. 1049. [bug] buffer overruns by 1 in getnameinfo(). 1048. [bug] ns_ctl_install() was corrupting the server_controls list. 1047. [bug] req_iquery() wasn't doing a final update on buflenp. 1046. [port] Win/NT port improved by its author. --- 8.2.3-T7B released --- 1045. [bug] forwarded and initiated TCP queries weren't affected by the "query-source" config option, and weren't being set nonblocking. 1044. [support] add HITCOUNTS compile-time option (from lamont@hp.com). 1043. [bug] dnsquery's command line args could overflow buffers. 1042. [doc] maintain-ixfr-base had wrong description in named.conf(5). 1041. [bug] host assumed axfr returned "one-answer" responses. 1040. [bug] add d_rcnt processing to update processing. 1039. [bug] qcomp wasn't stable. 1038. [port] solaris needs a strerror that does not return NULL, call isc_strerror instead. 1037. [support] soften #1025 -- continue to accept !AA notify req's. 1036. [debug] add TKEY debugging support. 1035. [bug] ndc's "help" command worked in signal but not channel mode. 1034. [bug] loc_ntoa() failed to correctly print altitudes in the range [-0.99 .. -0.01]. 1033. [port] Win/NT portability infusion from Larry @NortelNetworks. 1032. [bug] fix minor signal buglet introduced in #1029. 1031. [bug] nslookup now correctly refuses to accept qtypes AXFR or IXFR. (use nslookup "ls", not queries, for this.) 1030. [protocol] nslookup "ls" command now uses writev() rather than two write()'s, to get msglen and query into same tcp seg. --- 8.2.3-T6B released --- 1029. [bug] incredibly busy systems could starve handle_needs(). 1028. [protocol] unrecognized TSIG was returning NOERROR (now NOTAUTH). 1027. [support] INSIST(), ENSURE(), et al, now always have sideeffects. 1026. [port] some kernels bogusly return tv_usec>1000000 from gettimeofday(). panic and dump core when this happens. 1025. [proto] NOTIFY messages should have AA. 1024. [bug] we were unwilling to use the last 10 octets of a response buffer in certain transaction types. 1023. [port] HP-UX 10.20 was looping inside contrib/dnssigner. 1022. [port] ensure that all handled signals are unblocked. 1021. [bug] the "host" command wasn't properly printing SRV RR's. 1020. [contrib] new "updatehosts" (V1.1.0) contributed by author. 1019. [port] separate CFLAGS and CPPFLAGS for unusual builds. 1018. [bug] When maintain_ixfr_base is set to "no" a zones IXFR file was still being written too. 1017. [doc] resolver(3) was out of date with respect to recent API changes. 1016. [bug] nslookup wasn't properly printing SIG RR's. 1015. [bug] when merging group information gr_name and gr_passwd could be left pointing at freed memory. 1014. [bug] iquery: DoS (potential), information leak. 1013. [bug] mangled hostent structures returned by gethostbyname_r() and friends. 1012. [doc] add named-bootconf example to INSTALL. 1011. [bug] if spawnxfer() fails we should return immediately. 1010. [bug] bad responses to the initial IXFR/SOA query could result in using an uninitalised variable. 1009. [port] Add support for darwin / Mac OS X 1008. [doc] specify allow-query default in named.conf. 1007. [bug] only set STREAM_AXFRIXFR if the original query is an IXFR. --- 8.2.3-T5B (RC3) released --- 1006. [port] Windows/NT does not have fchown(). 1005. [bug] RD was sometimes left set, inappropriately. 1004. [bug] cached NXT's were corrupted. 1003. [bug] correction to #997. 1002. [bug] file descriptor leak in res_nclose(). 1001. [port] some builds were too fast. --- 8.2.3-T4B (RC2) released --- 1000. [bug] #996 was wrongly implemented; replacement fix. --- 8.2.3-T3B released --- 999. [support] named now makes an effort to create its files with ownership as specified by -u and -g command options. 998. [support] show version number in NOTIFY log messages. 997. [support] forwarders are now used in order by measured RTT. 996. [protocol] if answering ixfr with full zone, used qtype axfr. 995. [bug] "dig -b" was broken due to missing switch "break;" 994. [bug] named-xfer did not handle empty question sections. 993. [bug] TSIG AXFR was completely broken in DiG. 992. [bug] OPTION_USE_IXFR and OPTION_MAINTAIN_IXFR_BASE had non-single-bit flag values in src/bin/named/ns_defs.h. 991. [protocol] send A6 glue records in xfr. 990. [bug] we could loose track of a bottom of zone cut if the write buffer filled up at just the correct moment. 989. [bug] apply to "fetch-glue no;" to notify processing. need to add A records that would be found this way w/ also-notify. 988. [support] report expired zones when detected in maintainence pass. 987. [feature] "ndc reconfig -noexpired" skip attempts to load expired zoned when reconfiguring. 986. [bug] pushlev only needs to be called for axfr/zxfr not ixfr. --- 8.2.3-T2B released --- 985. [support] remove "view" command from nslookup (it used mktemp()). 984. [bug] always restart processing query from scratch if we have chased a CNAME as we might still have the answer in the cache once the CNAME has been resolved. 983. [support] "notify from non-master server" is now debug, not info. 982. [bug] rollback the compression pointers array when a RRset/RR does not fit. 981. [port] decunix: typedef (u_)int#m_t 980. [bug] mishandled memget failure w/ TCP connections. 979. [bug] we were failing to call ns_stopxfrs() before calling purge_zone() in some cases. 978. [port] sco50: setsockopt(SO_REUSEADDR) fails on unix domain sockets 977. [bug] we should be returning notimpl for update forwarding rather than refused. a client receiving refused should terminate the update attempt. notimpl should just cause the client to skip to the next server. 976. [bug] some stats weren't getting incremented, & added a few. 975. [support] SLAVE_FORWARD is now redundant and has been removed. 974. [port] ultrix with vendor's y2k patch explicitly desupported. 973. [bug] some field names added in #935 conflicted with macros. 972. [support] restore heartbeat notifies. 971. [bug] out of order updates in log. 970. [port] solaris: add ipv6 interface scanning support. 969. [bug] post process a zone load to remove any non-glue at or below bottom of zone. 968. [bug] TSIGs failed to verify if the key name was compressed. 967. [bug] zones signed by the BIND 9 signer failed to load. --- 8.2.3-T1A released --- 966. [bug] nslookup and dig misprinted root zone in $ORIGIN. 965. [feature] dig's command line input buffer was rather small. 964. [bug] make res_nsearch() behave like res_search() of olde. 963. [bug] res_debug::do_section() can no longer spin all VM. 962. [bug] another almost-complete rewrite of IXFR from kjd (462) 961. [bug] acl "none" now fails to match but doesn't end search. 960. [bug] more hesiod library fixes from danny. 959. [doc] christos fixed several man page typos and brainos. 958. [bug] getnameinfo() should accept experimental/multicast. 957. [port] ultrix again. "cd" now presumed to be silent again. 956. [bug] multiline was not being cleared correctly. 955. [bug] explicit TTL on SOA records were being replaced with soa minimum. 954. [bug] cannot load a signed root zone. 953. [bug] memory overrun in set_zone_ixfr_file(). 952. [bug] errs was not being correctly adjusted if the included master file did not exist in db_load(). 951. [bug] contrib/dns_signer/signer: write_trim_name array bounds write error. 950. [bug] hesiod: ctx->res was not being initalised. 949. [port] aix32: add prand_conf.h and define WCOREDUMP 948. [bug] fixed logic error in a number of expressions causing res_ninit() not to be called when it should be. 947. [bug] sanity check in dst_read_key() wasn't. 946. [port] freebsd: threaded library support. 945. [bug] wrong file name logged in ixfr_have_log(). 944. [doc] add forwarders to zone types master/slave/stub in named.conf man page. 943. [bug] raise CNAME and OTHER / multiple CNAME logging to warning. 942. [bug] bad referrals logged for forwarders. 941. [bug] lame server detection wasn't checking for SOA record. 940. [clarity] unapproved -> denied in log messages. 939. [bug] reload_master and purgeandload should write the zone if it has been updated. 938. [bug] update and ixfr logs could get corrupted. fseek() before ftell() on fopen(, "a+") file. 937. [support] allow parallel makes to work. 936. [protocol] add preliminary A6 glue recognition in ns_req. 935. [cleanup] res_nsend() segmented into multiple functions for readability. also fixed two file descriptor leaks. CAN_RECONNECT is gone, keep one socket per nameserver. 934. [bug] Perror and Aerror where incorrect if DEBUG is not defined. 933. [port] cygwin port added 932. [port] sco42 does not have unix domain sockets or gethostid. 931. [bug] eventlib was not handling unix domain sockets correctly. 930. [bug] we wern't using all the potential compression pointers in the question section. 929. [bug] we were accepting updates (adds) with illegal ttls. 928. [bug] if we manage to get a illegal ttl stored, print it unsigened. 927. [port] hpux: (11.* 10.30) Makefile.set.gcc 926. [port] hpux10: gcc needs -D_HPUX_SOURCE and -fPIC 925. [protocol] when a slave loads it should notify others (RFC 1996). 924. [port] sunos solaris: #define NEED_SECURE_DIRECTORY to secure the directory containing unix domain socket rather than the socket itself. 923. [support] shutup "make clean" about missing threaded directories. 922. [bug] removing an cached zone file then performing a "ndc reload zone" should force a zone transfer. 921. [bug] nsupdate: listuprec was not being initalised. 920. [port] aix4: Makefile.set.gcc aix4: __P was being defined by 919 [port] linux: remove one level of symbolic linkage when performing make links on port/linux/include 918. [bug] update prerequisite could match w/ wildcard. 917. [port] irix: make the current IRIX release (6.5) work by not patching res_debug.c. see INSTALL if you have problems with 6.3. 916. [bug] removing / changing a zone type could result in Z_NOTIFY being cleared / tested against the wrong zone. 915. [bug] evNewWaitList() was not maintaining the prev chain. 914. [bug] signal EWOULDBLOCK if EV_POLL'ing with no timers. 913. [bug] input could get lost on the server side of a ctl sock. 912. [bug] nsupdate now allows explicit 0 TTL's on added RR's. 911. [bug] gethostbyname() should not return duplicate addresses. 910. [bug] address-sorting logic was exiting early. 909. [bug] dig wasn't respecting the +ti and +ret arguments. 908. [contrib] Tony Stoneley sent us an updated misc/makezones. 907. [port] winnt fixes from Larry at Nortel. 906. [bug] res_findzonecut() failed if the NS referred to a CNAME. 905. [doc] Minor fix to doc/man/Makefile for getnameinfo 904. [bug] bin/host wasn't looking up MX records if no -t flags were passed to it. --- 8.2.2-P6 released --- 903. [bug] divide by zero bug when querying for SIG records from a secure zone. 902. [support] don't attempt to set q_fzone if we won't be using it. 901. [support] delay notify timer setting until all zones have been loaded. 900. [port] hpux10 fix call to bison; sco call bison consistenly. 899. [bug] dynamically allocate buffer used to display RR rather than uses a fixed sized one. grow as needed. 898. [bug] if truncation caused no RR's to appear in the answer we mis-classified the answer on a NODATA. 897. [support] descriptors used by named should not be inherited by named-xfer. 896. [contrib] add contrib/adm/adm-nxt, an exploit for the NXT bug in 8.2 and 8.2.1. as before, we do not recommend its use, and we do recommend that you run the latest BIND. --- 8.2.2-P5 released --- 895. [port] minor NT build and documentation improvements. 894. [bug] incorrect "key" statements in named.conf weren't handled properly. --- 8.2.2-P4 released --- 893. [bug] DNSSEC logic in bin/host broke -t any 892. [bug] multiple SOA on AXFR bug --- 8.2.2-P3 released --- 891. [bug] options { also-notify { ... }; }; resulted in wrong pointer being memput with the wrong size on reload. 890. [port] A/UX portability improved. 889. [port] added IPv6 portability for OpenBSD, NetBSD, FreeBSD. --- 8.2.2-P2 released (internal release) --- 888. [support] add default: all tag to top src/Makefile so that "make" will work properly in some OS'. 887. [bug] "dig ... axfr" was printing spurious "TSIG ok" msgs. 886. [support] top-level Makefile now included in all tarballs. 885. [support] IXFR improvements. 884. [bug] some deprecated NXT RR forms weren't ignored properly. 883. [support] "host" command can now try to verify dnssec signatures. 882. [contrib] dns_signer/ had some last minute problems (by author). 881. [bug] possible sprintf() overflow prevented. 880. [support] minor tweak to bin/dig/dig.c TSIG code to clarify whether res_nsend or res_nsendsigned is being used. 879. [support] add "noesw" target to top-level Makefile (for PL1). 878. [port] aix4 HAS_INET6_STRUCTS was not being set based on the existance of _IN6_ADDR_STRUCT. 877. [port] freebsd + KAME need a different Makefile.set see INSTALL notes. 876. [port] IPv6 probe for MPE/IX, NetBSD. 875. [bug] bad NAPTR RRs could be loaded from zone files. 874. [port] update irix_patch in irix port. 873. [port] add SRC/tools to sco's make [std]links. --- 8.2.2-REL released --- 872. [bug] named-xfer could free() a string twice. 871. [port] linux support for broken IPv6. 870. [port] more NT fixes and improvements from larry at bay. 869. [bug] disable client side IXFR (in named-xfer) for now. 868. [bug] updated named-bootconf to handle case insensitive parts of named.boot. added stubs support. class was not being reset. 867. [support] updated INSTALL notes. 866. [port] More NT fixes from larry at bay. 865. [port] add #include to next's port_before.h 864. [port] change solaris' Makefile.set files to use yacc and lex. also clean up install and binary paths. 863. [bug] lib/isc/ctl_srvr.c needed fcntl.h #included --- 8.2.2-T8B (RC2) released --- 862. [port] another NT infusion from larry over at bay. 861. [support] improve support for tsig'd updates. 860. [port] add IPv6 probing to: decunix hpux irix lynxos mpe netbsd qnx rhapsody sco50 859. [bug] set control sockets to close-on-exec; potential file descriptor leaks in ctl_srvr. 858. [bug] make ns_samename() and use it instead of strcasecmp(). 857. [bug] unset update-log can lead to debugging msg mishaps. --- 8.2.2-T7B (RC1) released --- 856. [bug] IXFR finally works and is reenabled. 855. [port] more win/nt changes from bay. 854. [bug] /etc/hosts lines longer than 8K can crash gethostent(). 853. [bug] another linked list bug shaken out of ns_update. 852. [bug] compiled in pathname for nslookup help file was wrong. 851. [bug] ns_update had an off by 2 bug when checking names in SRV records causing unexpected failures. 850. [bug] empty updates triggered an overambitious INSIST(). --- 8.2.2-T6B released --- 849. [support] print rcode on failed UPDATE messages. 848. [port] paths.h and port_before.h tweaks from SCO for unixware7. 847. [port] add SRC/irix_patch to make links in IRIX 846. [support] restore some diagnotics lost when #634 was done. 845. [support] WATSQ patch from Ted Rule of Flextech Television. 844. [support] added src/DNSSEC with a note about BIND-8.1.2 interop. 843. [bug] IXFR fixes. 842. [bug] pointer arithmetic on (void *) not ANSI C. 841. [port] sco50: make install: libport.a not longer exists. 840. [bug] turning on touch_timer() in ctl_clnt.c found a bug. 839. [contrib] new version of contrib/host (from author). 838. [support] improve error reporting; remove lint. 837. [bug] bin/host/host.c was not RFC2317 compliant. 836. [port] hpux portability and speed improvements. 835. [port] some shell's "cd" produce output - fix in port/systype. --- 8.2.2-T5A released --- 834. [support] massive changes to dynupd API. 833. [port] more Win/NT. 832. [feature] boolean: treat-cr-as-space. If yes, BIND will treat '\r' the same as it treats ' ' and '\t' in zone files. 831. [bugs] DNSSEC/CAIRN workshop results (in addition to #826): - invalid size passed into b64_ntop in SIG parser - Invalid TSIG keys are now logged and ignored instead of panicing. - trusted-keys didn't work if a trailing dot was present - a DST problem that occurs when one of the multiprecision integers begins with a 0 byte. - TSIG signed truncated responses were mishandled. - minor RFC2535 changes. 830. [doc] Minor updates to INSTALL 829. [support] we need to cache SOA NXDOMAIN queries if only for a clock tick. 828. [support] multiple zone warning clearer. 827. [bug] the ctl interface was clearing already-cleared timers. 826. [contrib] various improvements to contrib/dns_signer (from TIS). 825. [support] change __NAMESER and __RES to 19991006. 824. [port] sco50 needed #define __BIND_RES_TEXT in port_after.h 823. [bug] named-xfer missed a SIG text format change 822. [bug] TSIG signed truncated responses crashed the server 821. [bug] potential reference after free bugs. 820. [port] ultrix finally works again. 819. [bug] removed test for missing glue from nslookup() as it got false matches. There is no simple test for missing glue. 818. [bug] back out #790, there was no memory leak. 817. [port] Solaris needed #define BSD_COMP in port_before.h. --- 8.2.2-T4B released --- 816. [bug] you could not raise the number of available file descriptors after the first call to res_send() and make use of them. 815. [feature] report version via command line option (-v). 814. [feature] getipnodebyname, getipnodebyaddr and freehostent added. These are RFC 2553 newcomers to the RFC 2133 set. 813. [support] better diagnostics when trying to clean up old unix control socket. 812. [bug] uninitalised variable. 811. [port] sco50 make links was not linking resolv.h.diffs 810. [bug] zone transfer did not transfer all DNSSEC records at delegation points. 809. [support] res_[n]sendupdate has died before it could be used. 808. [bug] res_send() wasn't checking for EINTR after select(). 807. [support] it's now possible to send TSIG'd updates. 806. [support] ns_parserr() was uncompressing from the wrong base in a certain corner case trod on by res_findzonecut(). 805. [bug] only set SO_LINGER if required by the OS, #define DO_SO_LINGER to do so. 804. [bug] another swath of IXFR fixes. 803. [port] Compaq Tru64 UNIX 4.0B with ZK3's experimental IPv6 kit installed will at least build, but hasn't been tested. 802. [support] we no longer cache NXDOMAIN if the QTYPE was SOA. 801. [bug] our negative caching logic would log spurious errors if the response had an empty question section. 800. [bug] #764 was too aggressive in one case. 799. [port] ultrix is a still-moving target. 798. [support] QRYLOG now logs the QCLASS 797. [bug] closing a thread which had called get*by*() would leak memory. 796. [support] deallocate_on_exit now frees memory allocated by irs. 795. [port] solaris 2.4 SO_REUSEADDR generates errors on unix domain sockets. 794. [bug] ixfr_have_log() was logging wrong file name. 793. [bug] clean_cache() was not alway removing complete RRsets. 792. [bug] deallocate-on-exit caused references to freed memory. 791. [support] MEMCLUSTER_DEBUG had an array size error. 790. [bug] fix minor memory leak in ixfr code. 789. [bug] #669 was too aggressive. more than cached data was removed. 788. [bugs] improvements to tsig and dnssec. 787. [port] win/nt lint. 786. [port] IRIX and emul_ioctl(). 785. [bug] #780 broke A record update support. 784. [bugs] still trying to get IXFR working again. --- 8.2.2-T3B released --- 783. [support] make res_send() more friendly to the java scheduler. 782. [support] dangling cnames aren't errors, stop logging them. 781. [support] add -n option to ndc command, to run nonstandard named. 780. [bug] UPDATE did not support the AAAA RR. 779. [bug] miscellaneous IXFR fixes. 778. [support] don't complain to syslog about negative caching RRs. --- 8.2.2-T2B released --- 777. [bug] getword() didn't increment lineno at EOF. 776. [bug] the NOERROR_NODATA cookie overlapped a valid rcode. 775. [protocol] we weren't sending properly formated FORMERR responses. 774. [bug] UPDATE did not support the SRV RR. 773. [bug] named-xfer was calling inet_ntoa in one printf. 772. [typo] Typo in ns_parser.y on maybe_zero_port: line. 771. [lint] UNLINK now performs a INIT_LINK so explicit INIT_LINK's are nolonger needed after UNLINK. 770. [protocol] dynamic update prerequisites were inappropiately matching wildcards, at variance with RFC 2136. 769. [bug] ordering of CNAMES was driven by original query type. 768. [support] MINROOTS is now a configuration option "min-roots". 767. [clarity] adjust XFR log messages to be more clear about cause. 766. [support] add "serial-queries" option to dynamify MAXQSERIAL. 765. [feature] added evInitID() and evTestID() for NOTIFY work. 764. [bug] DNSSEC changed the semantics of match() without changing all the call sites that cared about it. 763. [bug] NOTIFY events caused by dynamic update weren't being deferred, and multiple NOTIFY events weren't being coalesced. 762. [support] don't rotate log file versions on server startup. 761. [port] named-xfer's openlog() was unconditionally using the LOG_CONS option. now it does what named does. --- 8.2.2-T1A released --- 760. [port] preliminary win/nt from baynetworks (thanks!) 759. [support] new compile time option BIND_IXFR, defaults to "off", since our testing has shown up some problems with it. 758. [feature] new "ndc reconfig" command only finds new/gone zones, doesn't stat() master files or qserial() slave SOA's. 757. [support] FORCED_RELOAD is no longer optional. 756. [support] fixed output format of hmac keys; removed DST chaff. 755. [feature] "also-notify" is now a global option. 754. [bug] the control socket was not checked for event lib compatability. 753. [feature] "ndc help" now returns one line command summaries. 752. [feature] "ndc trace" now takes an optional "level" argument. 751. [support] debugging output could segfault in ns_print.c::addstr. 750. [port] A/UX 3.1.1. 749. [port] #9 has now been done for all Makefiles. 748. [feature] "transfer-source" is now a global option. 747. [support] SORT_RESPONSE is no longer a compile time option, since the behaviour can be turned off at runtime with the "rrset_order fixed;" option. 746. [bug] don't bother rescanning the interfaces if setuid!=root. 745. [protocol] IXFR transmission was just plain wrong in some cases. 744. [support] allow the calling location of strings to be recorded. 743. [feature] $GENERATE now supports more record types, and options. 742. [port] port/sco50 was using /usr/local/etc for its ndc socket. 741. [port] HPUX needed __BIND_RES_TEXT. 740. [bug] #634 had the unfortunate side effect of disabling IXFR. 739. [port] probe for IPv6 structures, solaris openbsd freebsd 738. [bug] invalidate pointers back into linked list when element is removed. 737. [port] solaris: expr is sensitive to LC_COLLATE 736. [bug] potential single file descriptor leak opening /dev/random. 735. [bug] memory leak: having rrset-order set and reconfiguring the server results in a memory leak. 734. [port] linux only fills in as many entries as will fill the buffer with SIOCGIFCONF. 733. [bug] RD is not being set on first message to first forwarder resulting in false "Lame Server" reports and degraded service. 732. [bug] errors reading keys from master files could cause the the server to drop core. 731. [bug] highestFD was not reflecting the highest value the library could cope with. 730. [port] rand() does not modify the LSB on BSD based systems. 729. [bug] allow-query responses were dependent upon cache contents. 728. [bug] it wasn't possible to specify the flags of trusted keys in hex, which was inconvenient since dig prints hex. 727. [bug] TSIG keys weren't properly shared with named-xfer if the zone named contained a slash (/). 726. [bug] TSIG keys weren't reloaded correctly with 'ndc reload'. 725. [bug] only the first key in an acl was matched correctly. 724. [bug] "ndc restart" needed a short delay before checking for the health of a newly started name server. 723. [bug] TSIG signed zone transfer failed on especially large zones. 722. [doc] the example named.conf file had invalid TSIG usage. 721. [bug] duplicate records were tripping the cname-and-otherdata test, which wasn't necessary since they'll be ignored. 720. [port] solaris doesn't have gethostid() the way we build. 719. [lint] lots of lint fixed by bob and paul. 718. [bug] multiple CNAME support was not cycling the cnames in an RRset properly. 717. [bug] wrong /bin/ps flags in solaris prand_conf.h. minor tweak to ports/prand_conf/prand_conf.c to ensure proper flags in future ports. 716. [bug] log files are now closed/reopened on a size basis. 715. [clarity] root servers don't need to be primed. 714. [typo] extra "q" in a message in ns_maint.c. --- 8.2.1 released --- 713. [bug] don't loop on untimely eof within config file. 712. [port] hp-ux signals; aix bit types. 711. [perf] don't call find_zone() four times from within qnew(). --- 8.2.1-t7b released --- 710. [bug] can fetch zone from own address if port is different. 709. [bug] make sure zones are properly reinited when they die. 708. [bug] end marker or sizeof, but not both please. --- 8.2.1-t7a released --- 707. [port] AIX, HPUX, SunOS. 706. [feature] zone forwarding can now be applied to master, slave and stub zones as well as forward zones. 705. [bug] some zone options were not being copied. 704. [bug] very obscure problem fixed in res_update(). 703. [bug] single-zone reload was stomping freed memory. --- 8.2.1-t6b released --- 702. [port] solaris vs. enum; linux vs. IPv6. 701. [bug] NOTIFY rejection logic still wasn't correct. 700. [bug] complete #697 --- 8.2.1-t5b (rc2) released --- 699. [bug] if getting the ixfr change log fails send a axfr style response. 698. [bug] res_notify() was rejecting valid NOTIFY messages. re-organise code so that logged messages are more appropriate. 697. [port] linux. some versions define _GNU_SOURCE in features.h some version require the compiler to set the byte order when probing for IPv6 structures. 696. [bug] don't use NULL file pointer if IXFR transaction log cannot be opened due to permission errors. 695. [lint] another considerable amount of lint was removed. 694. [bug] only the last two forwarders would be used. 693. [bug] nsfwdadd() needed to continue outer loop. 692. [bug] RD was not being cleared by ns_forw(). this could cause DNS storms between lame servers. 691. [bug] We still had some leftover named-xfer ixfr tmp files. 690. [bug] return IXFR in question section of AXFR style IXFR response. 689. [bug] we now return "up to date" response to IXFR queries when required. 688. [bug] UDP IXFR now tells the client to use TCP. 687. [bug] IXFR was incorrectly reporting errors on DNSSEC RRs. 686. [port] hpux Makefile.set improvement (+O2 -> +ESlit). 685. [feature] mark recursive queries in query log. 684. [bug] named-xfer now ignores out-of-class glue. --- 8.2.1-t4b (RC1) released --- 683. [lint] considerable lint was removed. 682. [perf] another round of performance tweaks from HP (thanks!). 681. [bug] SIG wasn't being ignored when generating NOTIFY msgs. 680. [feature] delay parent reload as long as we can after removing child zone to save multiple parent reloads. 679. [port] port probe now recognizes SCO 5.0.5. 678. [doc] not all man pages were being installed. 677. [feature] lost feature "allow-recursion" added back in. 676. [bug] "100" was too small for ndc message sizes. 675. [bug] we weren't storing a (needed) extra copy of the zname. 674. [bug] SIGTERM wasn't working the first time it was sent. --- 8.2.1-t3b released --- 673. [bug] nslookup wasn't accepting _ at the beginning of names. 672. [bug] ndc was only passing the verb across the command channel and not the arguements. Reload of a single zone "really" works now. 671. [feature] you can reload multiple zones with a single ndc reload command. e.g. ndc reload zone1 zone2 ... 670. [bug] db_load did not work unless a RR had the class defined. 669. [bug] the cache is now purged when a forwarder is {re}loaded. 668. [bug] complete #652. 667. [bug] allow-query wasn't being allowed for stub zones. 666. [usability] only try to chown()/chmod() a control socket when the owner or permissions _change_ between reloads. 665. [bug] "options topology" is now possible to set. 664. [security] add important solaris-related security note to README. 663. [bug] "ndc -q" now turns off initial header and EOF printing. --- 8.2.1-t2b released --- 662. [usability] src/conf/ added, containing some of ISC's config files. 661. [protocol] we weren't sending AAAA RR's as AXFR glue. 660. [port] IRIX. 659. [contrib] author-submitted changes to dnssigner, new cider2named. 658. [protocol] print better messages wrt TSIG. add p_rcode(). remove _res_resultcodes[]. improve key handling. 657. [port] apply cpp to /usr/include/netinet/in.h to work out if struct sockaddr_in6 and struct in6_addr/inaddr6 are defined. 656. [bug] Classless IN-ADDR support was broken. 655. [bug] major overhaul of IXFR code. 654. [bug] dynamic update of non top of zone SOA now ZONEERR. 653. [feature] check-names now applied dynamic updates as if the zone was being loaded. REFUSED returned. 652. [port/bug] many operating systems allow more descriptors than their default FD_SETSIZE has room for. we catch this now, both by asking the operating system not to do this and by treating as invalid any out-of-range descriptor. 651. [protocol] any soft failures in res_send() will now cause the final return value to be TRY_AGAIN. previously the last server response received was the one returned. 650. [doc] resolver.5 man page clarified and corrected; res_init() made to do what the man page now says it does. 649. [port] make header files c++ compatible. 648. [bug] multiple options definitions of allow-query / allow-transfer / sortlist / blackist / topology are not allowed. warn rather than silently applying the last definition. 647. [bug] options max-ixfr-log-size was not being applied. 646. [feature] memcluster debugging support improved. -DRECORD_MEMCLUSTER to enable. 645. [bug] memory leaks 644. [bug] res_update() could not delete the first CNAME in a chain. 643. [bug] res_update() did not correctly handle labels with periods. 642. [port] SCO 5.0 portability improved. 641. [feature] $TTL now takes TTLs of the form 1w6d7h32m20s. 640. [bug] was returning NODATA rather than NXDOMAIN after a dynamic update removed the last RR from a childless node. 639. [bug] another fix for "rrset_order fixed". --- 8.2.1-t1a released --- 638. [bug] ixfr was still creating the wrong file names sometimes. 637. [bug] bin/dnsquery/dnsquery.c wasn't init'ing the resolver correctly befloew calling gethostbyname(). 636. [port] inet_ntoa() had to go back to being non-const for now. 635. [bug] AXFR wasn't forcing an autoincrement of SOA.SERIAL following a batch of UPDATE requests. 634. [feature] check all master soa's and use best serial, rather than trying them in order and grabbing the first one who answers with one better than the local one. 633. [port] SunOS 4.1.4 has a broken recvfrom() with non-blocking sockets. 632. [bug] res_mkupdate() signed/unsigned stupidity. 631. [bug] HMAC-MD5 fixes 630. [bug] NSTATS output was spaceless. 629. [misc] improvements to TSIG error logging. 628. [bug] "rrset_order fixed" was LIFO rather than FIFO. 627. [bug] TSIG signed zone transfers broken. 626. [bug] multiple CNAME support was broken. 625. [bug] key names are really domains so they need to be made canonical. 624. [bug] ns_name_pton() accepted domains of the form "example.." when it should have rejected them. 623. [feature] it is occasionally useful to know the local address used to perform a zone transfer. this is now logged. 622. [bug] missing check for malloc() failures in strndup(). 621. [bug] various things were wrong with nslookup's "ls -d" cmd. 620. [feature] forwarders are now retried like queries to the delegated nameservers. forward only should be more robust as a result. 619. [protocol] don't refresh TTL's from delegation information. 618. [feature] ndc is now quiet and verbose when it should be. 617. [bug] SOA counters now have minima as well as maxima. 616. [bug] needs were not always processed in a timely fashion. 615. [bug] ns_shutdown() memput() the wrong amount of memory when freeing the zones array. 614. [feature] ndc can now reload single zones including the root zone. 613. [bug] check for old unix domain socket / fifo prior to attempting to establish control channel. error message no longer just noise. 612. [port] Solaris UNIX domain sockets return different error codes and also may use FIFOs. 611. [bug] extend control timeout to 10 minutes. reloads can take a long time. 610. [bug] when reloading via the control channel we were reporting that we were about to reload after the reload was performed. Ensure message is set prior to reloading. 609. [bug] zoneTypeString() could be called with NULL pointer. 608. [bug] set various pointers to NULL after associated memory has been released to prevent accidental use. 607. [bug] finddata() was returning SIG's inappropriately. 606. [bug] fix two memory leaks in db_sec.c. 605. [feature] better error reporting from named-xfer. 604. [bug] fix a bug in the handling of $TTL's absence. 603. [port] add contributed/untested rhapsody port. 602. [bug] multiple "type hint" zones are now supported. 601. [bug] z_ftime wasn't being reset when fopen() failed. 600. [bug] gen_res_get() was initializing the wrong variable. 599. [bug] "ndc reload" exercised an uninitialized variable. 598. [bug] "nslookup reports danger" was reported ambiguously. 597. [bug] we weren't priming the cache in forward-only mode. 596. [bugs] many small bugs in DNSSEC handling were fixed. 595. [bug] nsupdate failed to support quite a few rr types: sig,key,nxt,eid,numloc,srv,atma,naptr,kx,cert 594. [proto] BADID removed per I-D. 593. [bug] mk_update() didn't support SIG. 592. [bug] lcl_pr and lcl_ho were using uninitialized bufsizes. 591. [port] linux. 590. [port] irix. 589. [doc] hesiod(3) man page contrib'd in 1996 finally put in. 588. [bug] too many lame servers at once was fatal. --- 8.2 released --- 587. [perf] uses about 5% less memory than 8.1.2 now. 586. [perf] faster at tcp, therefore less blocking on udp. 585. [misc] various releng lint. 584. [bug] IXFR wasn't doing DNSSEC RRtypes. 583. [bug] dnskeygen now fully qualifies its names; better usage. 582. [port] irix needed some patches applied during the build. 581. [bug] match_order() could dump core after "ndc reload". 580. [bug] ip_match_is_none() could dump core. 579. [bug] state names were off by one in src/lib/isc/ctl_srvr.c. 578. [misc] try without "transfer-source" if axfr connect() fails. 577. [contrib] sqlbind-8. 576. [bug] insecure updates weren't supported. 575. [doc] better documentation of key, trusted-key, zone pubkey. 574. [bug] was freeing freed memory on exit. 573. [port] nextstep. 572. [misc] centralize the name hashing logic (widen in some cases) 571. [perf] the new db_marshal() code was taking too much memory. 570. [perf] the lame server storage was taking too much memory. 569. [bug] src/lib/isc/ctl_srvr.c had an incomplete assertion. 568. [doc] Brent Baccala contributed an nsupdate man page. 567. [port] mpe, nextstep. 566. [protocol] upgrade to tsig draft 08. 565. [lint] use right relative paths for dnssafe includes in dst. 564. [bug] default security level for update rr's wasn't set. 563. [bug] debugging output in dprint_key_info() could panic us. 562. [perf] 8.2-t6b used 30% more memory on root name servers than 8.1.2 did. most of that was db_marshal hash tables. --- 8.2-T6B released --- 561. [bug] DST more graceful in handling unsupported algorithms. 560. [feature] lame server ttl now a configuration option. Re-enable lame server negative caching. 559. [bug] sysquery() was still using the child's name when it switched to using the parent's NS list causing false lame server reports. 558. [bug] disable lame server negative caching for the present. 557. [bug] undersized tcp messages are now detected early. 556. [bug] DNSSEC fine tuning. 555. [bug] the named.conf lexer was depending on two characters worth of putback buffer, ansi c guarantees one char. 554. [port] port to "next" contributed by jack bryans. 553. [contrib] added "snoof", another script kiddie toy. 552. [bug] allow-query didn't interact well with external cnames. 551. [bug] validate_zone could crash the server. 550. [lint] ns_maint was using ns_log_default, not ns_log_in_xfer. 549. [port] netbsd and openbsd improved. prand_conf improved. 548. [bug] ns_resp was using the wrong logging category. 547. [bug] dig was reinit'ing its resolver flags incorrectly. 546. [bug] nsupdate didn't handle HINFO,ISDN,TXT,X25 correctly. 545. [feature] added dnssafe back in. 544. [feature] removed DES encryption support. 543. [port] cleaned cylink of non used definitons in headerfiles. 542. [bug] include/dst no longer needed 541. [bug] CERT records are allowed to have alg == 0. 540. [doc] Removed outdated doc/secure, updated dnssigner documentation, updated dnskeygen.1 539. [bug] db_dump() was misparsing CERT records. 538. [feature] The KEY set is along with SOA, NS, A, AAAA records. 537. [bug] Multiple signatures are handled correctly. 536. [bug] SIG record expiration should be checked when the SIG is verified. 535. [bug] Queries for SIG records of non-authoritative names should not look in the cache or cache the results. 534. [bug] DNSSEC SIG records are dropped when they don't sign any data correctly. 533. [bug] SIG and NXT records are correctly handled when received in responses by named 532. [bug] dynamic update data is now always considered insecure, rather than having no security status. 531. [bug] dynamic update can again remove all data associated with a name (type ANY, class ANY). 530. [lint] downgraded "ctl: unexpected eof" from error to debug. 529. [port] unixware 7 port received. 528. [bug] timeouts could make ctl_srvr dump core. 527. [bug] we were not reliably reaping our children. 526. [bug] Cached CNAMES pointing to servers returning Type 3/4 NXDOMAIN are translated to Type 3 NODATA responses. 525. [bug] nscount could be short if we had to recurse after following a cname and we got a negative response. NS rrset got split between AU and AD sections. 524. [protocol] RFC 2308 support added. 523. [feature] mark lame servers as such and don't use them for NTTL. 522. [port] solaris 7 is now known to work. 521. [port] sunos4 should be supported now. 520. [bug] inet_pton() was allowing some bad ipv6 addresses in. 519. [bug] refuse duplicate also-notify's; optimize logging. 518. [port] hpux portability fixes. 517. [contrib] dnswalk wasn't copying with 8.* "dig" output. 516. [port] MPE portability fix. --- 8.2-T5B released --- 515. [security] lib/dnssafe code removed; now a separate patch. 514. [port] freebsd patches. 513. [bug] memory leak in res_mkupdate(). 512. [bug] $GENERATE could use an unset ttl. 511. [bug] $TTL warning test was wrong. 510. [port] bugs and things found by the netbsd folks. 509. [bug] The labels field in the SIG record may be less than the number of labels in the domain name if the owner of the SIG is a wildcard. 508. [bug] rrset ordering contained an off-by-one error 507. [bug] NXT set processing was not distinguishing between the upper and lower sets at delegation points. 506. [contrib] more script-kiddie toys, this time contrib/adm. 505. [bug] the ixfr changes to named-xfer destabilized stubs. 504. [port] some IRIX problems fixed. 503. [bug] ixfr wasn't correctly setting up its qsp. --- 8.2-T4A released --- 502. [bug] some config file parsing was still using malloc(). 501. [feature] named sets the AD bit in the header when returning authenticated data 500. [bug] dst_verify_data returns the documented error codes 499. [bug] verify_set now verifies the correct data 498. [bug] ixfr was not completely finished. 497. [bug] don't put zone 0 on the free list. 496. [bug] Losing all but last RR of RRset. 495. [port] random portability noise. 494. [bug] sysquery() should not let nlookup() change its data. 493. [feature] add "options ... rrset_order ... cyclic|random|etc". this allows round robin to be turned off selectively, or replaced with pseudorandom ordering, or whatever. 492. [bug] src/bin/named/db_sec.c was memputting objects twice. 491. [feature] add IRP (Information Retrieval Protocol) and daemon. this is functionally similar to solaris "nscd". 490. [bug] lib/isc/ctl_srvr.c couldn't overlap read and write. (also: add session context set/get.) 489. [bug] "cname and other data" was more complex than thought. 488. [port] some netbsd portability stuff. (still not working?) 487. [port] digital unix 3.2 wasn't working (4.0d was though). 486. [feature] add "sortlist", which may yet be merged/renamed into the "topology" verb. 485. [bug] do not complain about default TTLs unless a master. 484. [contrib] add contrib/z0ne, a useful tool for crackers. 483. [contrib] add contrib/query-loc[-*] to look up LOC RR's. 482. [bug] all RR's must now be of the same class as the zone. 481. [bug] outbound zone transfers are killed on any UPDATE. --- 8.2-T3A released --- 480. [bug] ns_update was corrupting TXT records 479. [bug] res_mkupdate was not handling WKS, HINFO, TXT, X25, ISDN, NSAP and LOC records. 478. [bug] name_pack could leave a bad compression pointer. 477. [port] improved support for FreeBSD 3.0. 476. [bug] BSDI contributed some fixes to the /etc/group parsing. 475. [bug] another memory leak in hesiod_resolve(). 474. [bug] SRV RR names were being compressed on output. 473. [feature] IXFR is no longer optional and has been cleaned up. 472. [bug] IXFR was disabling USE_PID_FILE. 471. [feature] add support for CERT records. 470. [bug] rrset_db_upgrade was updating the wrong cache. 469. [performance] use a free list for unused zones. 468. [feature] add getaddrinfo, courtesy of WIDE. 467. [lint] include/dst/dst.h moved to include/isc/dst.h. 466. [bug] fix core dump introduced with tsig glue. --- 8.2-T2A released --- 465. [bug] ref counting bug in ns_xfr. 464. [bug] correct cut&pasteo in IXFR config syntax. 463. [lint] clean psf files after top level "make tar". --- 8.2-T1A released --- 462. [feature] we now use randomized query id's. 461. [feature] new option "version" added. 460. [feature] add initial IXFR support from Check Point Technologies. 459. [bug] res_update() was putting debugging info on stderr. 458. [doc] add named.conf(5), improve doc/html. 457. [feature] named-bootconf is now written in /bin/sh and it is now installed in ${DESTSBIN}. 456. [bug] res->defdname[] wasn't always properly \0 terminated. 455. [bug] _PATH_MEMSTATS was never being used. 454. [doc] the html docs weren't clear about logging having to be specified first in the named.conf file. 453. [feature] add zone type "forward" for selective forwarding (sometimes called "split horizon" or "fake root"). 452. [bug] lib/irs/* was generally not coping with oversized lines and files not ending in \n. 451. [port] BSD/OS 2.* is now a separate port. 450. [Feature] added DNS key generator in bin/dnskeygen. 449. [contrib] added DNS zone signer in contrib/dns_signer. 448. [doc] sample named.conf and html documentation include examples of DNSSEC / TSIG configurations. 447. [feature] named verifies TSIG records on incoming messages, and generates TSIG records on outgoing messages. 446. [feature] res_nsendsigned, res_nfindprimary, res_nsendupdate provide TSIG aware resolver functions. 445. [feature] ns_sign and ns_verify generate/authenticate TSIG signatures on DNS messages. ns_sign_tcp, ns_sign_tcp_init, ns_verify_tcp, and ns_verify_tcp_init are used for tcp transfers. 444. [feature] acls can now include shared key names. 443. [feature] added DNSSEC verification of zone data on load and partial verification of signed data received over the wire. 442. [feature] lib/dst (TIS digital signature toolkit), lib/dnssafe, and lib/cylink added to provide functionality needed for DNSSEC and transaction signatures. 441. [bug] fixed memory leak in hesoid support. 440. [bug] support for res in lib irs was a mess. _res now controls the behaviour of get*by*() again. 439. [bug] fix *END_RESULT macros in port/solaris/port_before.h. 438. [feature] permit the install user and group to be overridden. 437. [feature] TCP truncation now reports IP address of the server. 436. [bug] memory leaks in nsupdate. 435. [doc] updated resolver.3 434. [bug] named.run was not always being created when ndc trace was run. 433. [bug] req_notify required the slave zone to have been loaded. this may not be the case when a zone has expired or is being established over a dial on demand link. 432. [feature] blackhole queries from these nets. do not use these nets to resolve queries. 431. [feature] loop breaking with UDP based well known services. 430. [bug] memory leaks in dispatch_message. 429. [feature] fast retries on host/net unreachable. 428. [bug] CNAME and other data is now a hard error. 427. [feature] support very large numbers of virtual interfaces. 426. [bug] bring named closer into line with the data ranking in RFC 2181, Section 5.4.1. 425. [bug] removed spurious debug statment that generated a lot false bug reports. 424. [bug] closed file descriptor leaks in ns_update. 423. [feature] loc_ntoa() can now accept NULL like other _ntoa's. 422. [feature] you can now specify a port on the master statement to allow transfers from a non standard port. 421. [feature] warn when the root hints do not match reality. 420. [misc] added support for bcc (bounds checking compiler). 419. [feature] bring negative caching into RFC 2308 compliance. 418. [bug] expire now behaviour now as per RFC 1034/1035. 417. [bug] updates and zone transfers weren't locking eachother. 416. [port] support added for HPUX B.11.* 415. [feature] ndc is a C program now, uses new "controls" subsystem. 414. [feature] "controls" element of named.conf now live and working. 413. [feature] octal and hexadecimal numbers now parsed in named.conf. 412. [bug] we now support 2**24-1 (16M) zones. (need namespaces!) 411. [bug] fix *END_RESULT macros in port/bsdos/port_before.h 410. [feature] added support for dial on demand links between servers. 409. [port] remove aggregious use of snprintf(). 408. [feature] add -b option to dig to set srcaddr of tcp connects. 407. [feature] added $GENERATE to generate sets of RR's that only differ by an interator. 406. [doc] added manpage for inet_cidr_ntop() inet_cidr_pton(). 405. [bug] res_nsend() closed sockets unnecessarily on timeout. handle change NS list and RES_STAYOPEN generically. 404. [bug] inet_addr/inet_aton/inet_network accepted illegal inputs as legal. Also enforce octal input. 403. [bug] inet_cidr_ntop() was not producing correct output for all possible inputs. 402. [bug] fix retry/retransmit logic in face of network errors. 401. [doc] the "transfer-source" zone option wasn't documented. 400. [bug] bin/host was dumping core - converted to use getopt. 399. [port] use time() rather than gettimeofday() in dig. 398. [bug] named could exit silently on assertion failures, now assertion failures are logged using INSIST. 397. [port] add an AIX 3.2 port (requires GNU utilities). 396. [bug] dig and nslookup allowed sscanf/sprintf overflows. 395. [bug] dig and nslookup were unable to deal with 64KB answers. 394. [feature] add RES_NOCHECKNAME and "options no-check-names" (in resolv.conf) to turn off modern host/mail name checks. 393. [bug] lib/isc/tree.c was missing a critical \ (#if DEBUG). 392. [bug] inet_aton() wasn't requiring nonterminal octets to be in the range of octets, i.e., 1.300.1.1. 391. [bug] fix bug in MAX_XFERS_RUNNING logic. 390. [bug] ns_update() was capable of renaming an open file. 389. [feature] libbind.a now has a "ctl" subsystem, which is planned to replace signals as a the communication path between "ndc" and "named". preliminary support is in "named". 388. [feature] preliminary/nonfunctional/nonstandard ZXFR support. 387. [feature] inet_cidr_pton() and inet_cidr_ntop() added. 386. [bug] inet_net_pton() was not parsing hex correctly. 385. [feature] three new options for the RES_OPTIONS environment var or for the "options" directive in /etc/resolv.conf: attempts:NN default res.retry timeout:NN default res.retrans rotate use ALL listed nameservers 384. [feature] there is now a nearly-thread-safe resolver API, with the old non-thread-safe API being a set of stubs on top of this. it is possible to program without _res. note: the documentation has not been updated. also note: IRS is a thread-ready API, get*by*() is not. (see ../contrib/manyhosts for an example application.) 383. [contrib] bsdi contributed an /etc/services.db hack, which is currently conditionalized for bsd/os but would work on any modern BSD-derived system (DB, snprintf, etc). 382. [port] bsd/os 4.0 defines its own pselect(), which differs from the one we simulated. we now simulate the right one, and use the right one. 381. [contrib] added contrib/srv, the beginnings of SRV client side. --- 8.1.2 released --- 380. [bug] Replaying the dynamic update log could trigger an INSIST. 379. [port] Updated IRIX port. 378. [bug] The declaration for res_freeupdrec() in resolv.h didn't use __P(). 377. [func] The server now sets SO_SNDBUF on UDP sockets. 376. [port] The malloc() implementation on many systems didn't like memcluster.c's 4KB block allocations, sometimes causing huge amounts of memory to be wasted. memcluster.c now allocates bigger chunks and makes its own 4KB blocks. 375. [bug] If more than (sizeof u_long) gets occurred for a particular memory bucket, an INSIST about puts < gets might have been erroneously trigged. Now total gets and outstanding gets are counted. 374. [port] SCO 3.2v4.2 doesn't have initgroups(), so we do not want to define CAN_CHANGE_ID. 373. [port] Updated LynxOS port. 372. [port] Updated SCO 3.2v5.0.x port. 371. [bug] "make install" could fail on some Linux systems because src/port/linux/include/net/Makefile didn't cope with an empty HFILES variable. 370. [bug] Trying to update an expired slave zone would cause the server to panic. 369. [bug] The Makefile for named-xfer didn't try to create ${DESTDIR}${DESTEXEC} if it didn't exist. 368. [bug] Interface scanning could get confused on BSD-like systems if the sa_len of the address was less than sizeof (struct sockaddr). 367. [func] The default value for the host-statistics option has been changed to "no". --- 8.1.2-T3B released --- 366. [bug] Z_AUTH was set on the cache zone do_reload(). 365. [security] Missing bounds checking in inverse query handling allowed an attacker to overwrite the server's stack. 364. [port] Added support for HP MPE. 363. [bug] named-xfer automatically restarts the transfer if the SOA changes during the transfer. There was no limit on the number of restarts, resulting in a lot of wasted effort if the SOA was constantly changing. The number of restarts is now limited. 362. [security] Requesting a zone transfer for a domain name which had a resource record of a certain format would cause the server to abort(). 361. [bug] named-xfer tries to close files named might have had open. On Solaris, sysconf(_SC_OPEN_MAX) can return RLIM_INFINITY, and if it did named-xfer would try to close all those files. named-xfer now applies an upper limit of FD_SETSIZE. 360. [port] Solaris 2.5 systems needed to be included in port_after.h to get rlim_t. --- 8.1.2-T3A released --- 359. [func] IRS group support is now controlled by the WANT_IRS_GR define in port_before.h. 358. [port] Updated IRIX port. 357. [port] Added support for QNX. 356. [func] Added -u (set user id), -g (set group id), and -t (chroot) command line options to 'named'. 355. [func] If getnetconf() fails because it can't create the socket used to get the interface list, the server will log an error if it is doing a periodic interface scan, and panic otherwise. Previous versions of the server always panicked. 354. [security] Bounds checking in named-xfer, dig, host, and nslookup had problems similar to those in item 293. Added a few more bounds checks to the server. 353. [port] Paths are no longer overridden in port_after.h, and are now generated from the various DEST paths in Makefile.set. 352. [bug] Because of problems with setting an infinite rlim_max for RLIMIT_NOFILE on some systems, previous versions of the server implemented "limit files unlimited" by setting the limit to the value returned by sysconf(_SC_OPEN_MAX). The server will now use RLIM_INFINITY on systems which allow it. 351. [port] Updated HP/UX 10.x port. 350. [bug] errno could be changed by certain signal handlers. These signal handlers now save errno on entry and restore it on exit. This changes eliminates the need for the SPURIOUS_ECHILD #define. 349. [bug] hesiod.h wasn't installed. 348. [port] Added support for LynxOS. 347. [bug] res_update() leaked the zone section it allocated. This leak no longer occurs on normal returns, but still occurs when there is an abnormal return. This will be addressed in a future fix. 346. [bug] Fix 303 fixed one thing and broke another, resulting in a nonfunctional grscan(). 345. [bug] Fix 328 was bad, causing the root zone to be purged every time a toplevel domain was reloaded. 344. [bug] The priming fix in change 330 erroneously called unsched() twice, causing a core dump if priming failed. The priming fix could also erroneously query [0.0.0.0].0. 343. [bug] The REQUIRE() in free_rrecp() was wrong, and was triggered by an unapproved update. 342. [port] Added support for SCO UNIX 3.2v5.0.4. --- 8.1.2-T2A released --- 341. [port] The LOG_CONS option to openlog() does not work as documented on some systems. The server will now use LOG_CONS only if USE_LOG_CONS is defined by the port. Currently the bsdos, decunix, freebsd, linux, and netbsd ports define USE_LOG_CONS. 340. [bug] The pid file was updated before the configuration file had been read. 339. [port] #define HAVE_GETRUSAGE for Solaris >= 2.5. 338. [func] 'host' can now print AAAA records. 337. [bug] rm_datum() erroneously set dp->d_next to NULL when savedpp wasn't NULL. Given a dynamic update operation that deleted more than one RR, this bug would cause all but one of the RRs to be leaked, and would prevent correct rollback if the update failed. 336. [bug] Make sure 's' isn't negative in res_send(). This shouldn't happen, but there have been some reports suggesting it can happen. 335. [lint] Cleaned up more gcc warnings. 334. [port] Added support for HP-UX 9.x. 333. [bug] db_glue.c didn't compile if DEBUG wasn't defined. 332. [bug] named-bootconf.pl didn't convert secondary lines that didn't contain a filename correctly. 331. [bug] If the server was configured with forwarders (but not in forward-only mode), and a query ran out of forwarders and had no nameservers, then the server would erroneously forward the request to [0.0.0.0].0. 330. [bug] If priming of the root servers failed, recovery could take a long time. If using forwarders to prime and the query expired, the first forwarder would always be skipped on subsequent attempts. The server complained about priming problems in forward-only mode, even though it doesn't matter. 329. [port] Some versions of Linux apparently need SPURIOUS_ECHILD. 328. [bug] purge_zone() didn't recurse if given the root zone, causing old data and new data for the root zone to be merged. 327. [func] Add log_check() and log_check_channel(). 326. [func] Add r_prev field to ns_updrec in . 325. [bug] Rollback of a failed dynamic update was done in FIFO order instead of LIFO order. 324. [bug] evTryAccept() closed the wrong fd if getsockname() failed. 323. [bug] eventlib didn't clear bits that had been serviced or deselected out of ctx->{rd,wr,ex}Last. 322. [bug] evDestroy() destroyed the files list before destroying the streams list. If there were any active streams, this would cause a double destroy of the streams' file objects, very likely triggering an 'insist'. 321. [bug] The correct error code for a failed asynchronous connect was not reported. It now is, at least on systems that have the SO_ERROR socket option. 320. [func] Allow multiple pending accepts. evTryAccept() now reports the errno if an error was queued. 319. [bug] The toplevel Makefile passed MARGS before $settings, which prevented overriding a port's Makefile.set from the command line. 318. [bug] The Solaris port_after.h checked for SUNOS_2_5_1 instead of SUNOS_5_5_1. 317. [unused] [This change number was allocated but not used.] 316. [bug] evTryAccept() didn't append to the done list correctly if connLast wasn't NULL. 315. [bug] The dynamic update code was incorrectly converted to clean up ns_updrec structures using the new clustered memory allocator, and this would cause an 'insist' to be triggered some time after a dynamic update had been processed. Instead of freeing the ns_updrec fields directly in ns_update.c, res_freeupdrec() was added to the resolver. 314. [bug] Adding and then deleting an RR in a single dynamic update request would crash the server. 313. [bug] The nameserver would only try zone transfers from the master that answered its SOA query. If a master for some reason can answer the SOA but not the AXFR, the other masters (if any) should be tried. 312. [security] Bounds checking in the resolver and dynamic update code had problems similar to those in item 293. Added more checks to ns_resp.c. 311. [bug] The s_wbuf in the qstream structure was leaked in certain zone transfer failures. 310. [bug] If the server ran out of memory in ns_xfr(), the subsequent connection cleanup could modify the z_numxfrs field of zone 0 instead of the zone being transferred, causing an 'insist' to be triggered later. 309. [bug] NAMELEN() could return a negative length. 308. [func] Don't log ECONNRESET in stream_getlen(). 307. [bug] include/isc/assertions.h and include/isc/list.h weren't installed. 306. [bug] Timewarping into the future would cause repeating timers to generate an event for every interval between the previous time and the new time. Repeating timers are now rescheduled based on the last event time, not their due time. Idle timers now use the last event time to compute the idle interval instead of the due time. 305. [bug] The BOUNDS_CHECK() for the 5 32-bit integers in the SOA RR was wrong. 304. [bug] lib/isc/assertions.c and lib/isc/memcluster.c did not follow the port_{before/after}.h convention. memcluster.c #included eventlib.h but did not need it. --- 8.1.2-T1A released --- 303. [bug] 'bp' in grscan() in lib/irs/lcl_gr.c was incorrectly validated, potentially causing corrupt data to be read. 302. [port] #define HAVE_GETRUSAGE for Solaris >= 2.5.1. 301. [port] Added support for Solaris 2.6. 300. [bug] The space for the pathname of named-xfer in the options block leaked. 299. [bug] wasn't in the include directory, and wasn't included before "port_after.h". 298. [func] Added "deallocate-on-exit" and "memstatistics-file" options. If deallocate-on-exit is "yes", the server will painstakingly deallocate every object it allocated. This is slower than letting the OS clean things up, but is helpful in detecting memory leaks. 297. [port] GNU libc 2.0 doesn't have so in the Linux port we now provide a stub nlist.h that includes the real nlist.h if GNU libc < 2.0 and does nothing if >= 2.0. 296. [bug] "make stdlinks" didn't "mkdir /var/obj" if /var/obj didn't exist. 295. [bug] Specifying a query-source with and address and port that the server was listening to didn't work. 294. [security] The server was willing to answer queries on its forwarding sockets. 293. [security] rrextract() did insufficient bounds checking which could cause it to crash the server by reading from an invalid memory location. 292. [bug] The server sometimes leaked the flushset (ns_resp.c). 291. [bug] The server did not detect oversized UDP packets, causing useless retries. 290. [bug] free_listen_info_list() leaked the IP matching lists; the leak occurred when the config file was reloaded. 289. [bug] [This bug number was allocated for something that turned out not to be a bug.] 288. [func] Add new list and assertion code to the ISC library. 287. [bug] "dig +sort" doesn't do anything, but was mentioned in dig's usage message, as well as in the man page. 286. [bug] Some systems have a default FD_SETSIZE much smaller than the number of files that can be opened. This could cause problems in the resolver and eventlib. FD_SETSIZE may now be set in port/*/include/fd_setsize.h. 285. [bug] If OS probing failed to match any of the supported ports, the build would try to continue with BSD 4.4 settings, with poor results in most situations. An error message is now printed if probing fails. 284. [func] The interface list is now doubly-linked. 283. [bug] The server would panic if binding to an interface that it had discovered failed. Simply not listening to the interface is a better solution. 282. [bug] The nslookup Makefile didn't prefix DESTHELP with DESTDIR when setting DEFS. 281. [bug] A socket() called in ns_main.c used PF_INET instead of AF_INET. 280. [bug] The sample named.conf used "clean-interval" instead of "cleaning-interval". 279. [bug] Some panic() messages in the IP matching code in ns_config.c were wrong. 278. [bug] Setting an interval to zero (e.g. interface-interval) eventually caused random timer destruction. 277. [bug] ns_panic() used "args" twice, but only called va_start() and va_end() once. 276. [bug] nslookup's "ls" command always listed all records instead of behaving the way its man page describes. 275. [bug] add_related_additional() leaked memory if the name was already in the related array. 274. [bug] If a timer was cleared while in executing its callback, and a new non-repeating timer was created afterwards (but still in the callback), the new timer was erroneously destroyed when the callback completed. 273. [func] Added transfer-source and host-statistics options. 272. [func] The zone number is now unsigned, allowing up to 65536 zones instead of the previous limit of 32768. 271. [func] Added evDefer(). 270. [bug] The meaning of the count returned by select() varies somewhat by operating system. Under certain circumstances, this confused eventlib's accounting and caused the server to spin. 269. [func] Added evLastEventTime(). 268. [bug] Connections weren't cleaned up when the eventlib context was destroyed. 267. [func] Added evTimeRW() and evUntimeRW() to control idle timer usage in the eventlib streams module. 266. [func] Added file descriptor table to ev_files.c to improve performance of evSelect() and evDeselect(). 265. [func] Added evHold(), evUnhold(), and evTryAccept(). 264. [func] Double-link many eventlib lists to allow faster removal of list elements. 263. [bug] Remember the previous non-blocking status of sockets given to evListen(). 262. [func] Added idle timers to eventlib. 261. [func] Added clustered memory allocator to eventlib; eventlib and named now use this allocator. 260. [func] The value of FD_SETSIZE that eventlib uses can be set by changing include/fd_setsize.h. 259. [bug] Notification of hosts on the also-notify list stopped after the first successful notification. --- 8.1.1 released --- 258. [bug] Setting SO_SNDLOWAT to 0 in ns_xfr() wasn't doing what it was intended to do, and could trigger a kernel bug on various systems derived from BSD 4.4. 257. [bug] In lib/irs/dns_ho.c, variable needsort was used in addrsort() before it was initialized. 256. [func] Ignore ECHILD from select() if SPURIOUS_ECHILD is defined. 255. [bug] The contents of libport.a needed to be in libbind.a. libport.a has been removed. 254. [install] Install library and .h files under /usr/local/bind instead of /usr/local. When the include files were in /usr/local/include, some compilers would automatically use them. The clients would typically not link with -lbind, causing unresolved symbols at link time. 253. [port] Removed change 216. 252. [port] Added port for UnixWare 2.0.x. 251. [doc] Added a documentation on installing to non-default locations. 250. [bug] The Makefiles for the binaries didn't create the installation target directories if they didn't exist. 249. [bug] Change HAS_SA_LEN to HAVE_SA_LEN in the AIX 4 port. 248. [security] The server now caches only those response records that are within the current query domain. 247. [bug] Forwarding of dynamic update requests sent to a slave for the zone is broken. This will be fixed in a future release, but in the meantime the server will simply refuse the request. Cleaned up the way some update code indicated that the request should be refused. --- 8.1.1-T2B released --- 246. [bug] process_prereq() could core dump if the name being processed wasn't known. 245. [bug] It was possible to evSelectFD the same event bits on the same fd more than once. 244. [bug] eventlib didn't decrement fdCount correctly if the eventmask matched in multiple descriptor sets. 243. [lint] Improved comment in stale(). 242. [port] Added port for OpenBSD. 241. [bug] evConnect() didn't evDeselect() the fd if connect() failed, which would cause us to call select() with a mask that included a closed file. select() would then return EBADF and trigger an 'insist'. 240. [bug] evCancelConn() closed the fd. 239. [port] SunOS doesn't supply RAND_MAX. 238. [bug] fakeaddr() called inet_aton() which wasn't strict enough. inet_pton() is now used. 237. [port] Added UnixWare 2.1.2 port. 236. [bug] The buffer in res_querydomain could overflow. 235. [bug] Fixed memory allocation problems in lib/irs/nis_gr.c. 234. [bug] evDeselectFD didn't restore the fd's previous nonblocking status correctly. 233. [func] Define SPURIOUS_ECHILD in Solaris port. Don't complain about getting ECHILD from recvfrom() if SPURIOUS_ECHILD is defined. 232. [func] named-bootconf.pl now supplies a commented out query-source directive and instructions to use it if there's a firewall involved. 231. [bug] Changed a few strdup() calls in rrextract() into savestr() calls. This prevents "related" checking from being turned off if the server runs out of memory. 230. [bug] If the query control structure was reset in ns_resp.c, we leaked the memory used for the previous qp->q_domain. 229. [func] Added the "dump-file" and "statistics-file" options. 228. [bug] named.conf called "statistics-interval" "stats-interval". 227. [func] demoted "zones changed" and "zones shrunk" messages in tryxfer() to debug level 3. --- 8.1.1-T1A released --- 226. [bug] evCancelConn trashed the connections list if the first element was removed. This could cause a seg fault or trigger an 'insist'. 225. [bug] In the "cannot redefine listen-on for port ..." error message, the port was not converted to host byte order before being printed. 224. [port] Added port for AIX 4. 223. [bug] The dynamic update routine findzone() didn't match class, so if you had two zones with the same name but different classes (e.g. IN and HS), then the wrong allow-update ACL could be used, and the wrong zone could be updated. 222. [bug] If a dynamic master zone was updated and then was made non-dynamic by removing the allow-update ACL or changing it to "none" before the zone had been dumped, then the master file would not reflect the update. 221. [func] added 'also-notify'. 220. [func] revised HAVE_GETRUSAGE ifdefs in ns_config.c. The "cannot set resource limits on this system" message on systems without HAVE_GETRUSAGE will now be logged once per options block, and the message severity is now "info" instead of "warning". 219. [bug] If the root name was encoded in a message using a compression pointer, dn_expand() would erroneously return "." as the name instead of "". 218. [bug] when gethostans() in dns_ho.c encountered a CNAME while processing a PTR query, it erroneously required that the CNAME target pass the res_hnok() test (i.e. that it be an RFC 952 hostname). 217. [bug] dnsquery didn't work because it tried to use the obsolete and broken p_query() call instead of fp_nquery(). 216. [port] set SH=bash in port/freebsd/Makefile.set. 215. [port] #define ts_sec and ts_nsec to tv_sec and tv_nsec respectively in port/freebsd/include/port_before.h. 214. [bug] the clarification TTL changes (see change 145 below) set the SOA minimum field to zero if the MSB was set. The server now leaves the SOA RR alone, but sets z_minimum to zero if the MSB is set. 213. [bug] if the SOA refresh or retry fields of a slave zone were 0, an 'insist' would be triggered when zone maintenance was performed. The server still leaves the SOA RR alone, but now imposes a minimum value for z_refresh and z_retry. 212. [func] added the clean-interval, interface-interval, and statistics-interval options. 211. [func] scan for new or deleted interfaces periodically. 210. [func] the _PATH_DUMPFILE default is now "named_dump.db". 209. [bug] and were #included after port_after.h. They are now #included before it, since they #include system header files. ns_lexer.h was #including and ns_parseutil.h. Now it #includes neither one. These changes required that the definition of struct timespec be moved from port_after.h to port_before.h in the ULTRIX, SunOS, and A/UX ports. 208. [port] removed HAVE_GETRUSAGE from the Solaris port, since Solaris only has it if a Berkeley compatibility package is installed. 207. [bug] abortxfer() always used SIGKILL, which didn't give named-xfer a chance to clean up after itself. Now abortxfer() does a SIGTERM first. If the SIGTERM isn't successful, it will use SIGKILL. 206. [bug] If two zones with the same name but different classes (e.g. IN and HS) were defined, then a zone transfer of whichever zone loaded first would work normally, but a zone transfer of the second would give only the NS and SOA RRs. 205. [bug] certain operating systems (notably Solaris) return error codes the server didn't expect, and thus treated as fatal to the interface. More error codes are now recognized. The server will now log unrecognized errors, but will not delete the interface. Certain error results from recvfrom() and accept() now panic the server. 204. [bug] stub zone transfers would fail if there were no NS records in the SOA response. The stub logic now works as intended and has more error checking. 203. [bug] we logged a failure of bind() in opensocket_d() twice. 202. [port] Linux defines AF_INET6 as 10, so we use that value in port/linux/include/port_after.h. 201. [bug] library Makefiles want to press on if linking of an individual module fails. The 'ld' rule was set up to do this, but the subsequent 'mv' rule was not, causing the make to stop if the 'ld' failed. Now the 'mv' is done only if the 'ld' succeeds. 200. [bug] the value of timeout.tv_sec was printed in SendRequest (bin/nslookup/send.c). select() on some systems (such as Linux) modifies the value of the timeout, so printing it is useless since it will always be 0. 199. [func] if s is too big for FD_SETSIZE in res_send, complain and try another nameserver. 198. [bug] sysnotify() was too strict in requiring an NS RR for the server named in the SOA MNAME field. RFCs 1996 and 2136 say the NS RR is optional. 197. [bug] The parser erroneously freed zone_name if a zone redefinition was attempted. This would cause the server to dump core if a zone appeared more than once in a configuration file. 196. [bug] Makefiles below port/*/include had "fi \" followed by "done" on the next line. This made bash 2.0 unhappy. The "fi" is now followed by a ";". 195. [port] ULTRIX's sh doesn't like an empty "for x in ..." list, and that was causing "make install" to fail in the src/port/ultrix/include/rpc directory. 194. [port] add SH variable to toplevel Makefile, document the need to use SH=bash on systems where /bin/sh is derived from "ash". 193. [bug] named-bootconf.pl could repeat end-of-line comments 192. [bug] ndc was being installed in DESTBIN instead of DESTSBIN. 191. [bug] block delivery of all other signals when in SIGTERM handler in named-xfer. 190. [bug] named-bootconf.pl didn't handle non-masked xfrnets correctly if the network was class B or class C. --- 8.1-REL released --- 189. [port] update to the port/sco50 directory rcvd from author. 188. [func] to avoid potentially confusing log messages, don't set Z_DYNAMIC if "allow-update { none; };" is specified in the config file. 187. [bug] a panic() in new_ip_match_mask() erroneously referred to the function as "new_ip_match_pattern". 186. [bug] transfers-in couldn't be set higher than the default. It may now be set as high as 20. 185. [doc] add a stub example to named.conf. 184. [bug] the usage message was out-of-date. 183. [port] some systems don't define AF_INET6, so we define it if necessary in all port_after.h --- 8.1-T5B released --- 182. [bug] fix the way bindname is allocated in hesiod_to_bind(). 181. [bug] MAXHOSTNAMELEN wasn't defined on Solaris. 180. [bug] a check for zptr != NULL in res_update was wrong. It should have been zptr == NULL. 179. [bug] sq_remove() and sq_done() were calling ns_freexfr() when any stream was removed, resulting in a panic when the server was reloaded. ns_freexfr() is now only called when a zone transfer stream is removed. --- 8.1-T4B released --- 178. [bug] if the server was reloaded and then a zone was deleted and the server reloaded again, all within a short period of time, then pending NOTIFY messages would cause the server to panic when they ran. 177. [lint] replaced BUFSIZ with a more appropriate size in several places. 176. [func] change MAXDATA to 2*MAXDNAME + 5*INT32SZ. 175. [security] libirs now limits hostnames to MAXHOSTNAMELEN characters. 174. [bug] we called ns_refreshtime() instead of ns_retrytime() in the Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL case in zone_maint(). 173. [bug] the server didn't clear the Z_NEED_RELOAD flag in zoneinit(). 172. [bug] if a server was a slave for a zone, and an outbound transfer ever hung or terminated unusually, regular zone maintenance would cease for the zone. 171. [port] work around a bug in the Digital UNIX 4.0B SIOCGIFCONF ioctl. 170. [func] the message logged when a zone is loaded now indicates the class of the zone. 169. [func] the message logged when a zone is removed now indicates both the type and class of the zone. 168. [bug] if a zone's type changed from master to slave on a server reload, the server erroneously deleted the new zone data as part of cleaning up the old zone data. 167. [func] when converting from wire format to printable format, represent special characters ".;\@$ by escaping them with \ instead of converting them to \DDD. 166. [bug] when a slave zone expired, it was not scheduled for immedidate maintenance. 165. [port] added port for SCO OSE 5.0.2, renamed port for SCO UNIX 3.2v4.2. 164. [func] created the "response-checks" logging category. 163. [port] don't define AF_INET6 in nameser_compat.h. 162. [bug] the server panicked if a dynamic update request was sent for a dynamic zone which had not loaded because of syntax errors in the master file. The server now returns NOTAUTH. 161. [bug] debugging messages in process_prereq() referred to process_updates() instead of process_prereq(). 160. [bug] hp was not reset after a realloc() in named-xfer.c 159. [bug] named-bootconf.pl didn't translate stub zones. 158. [lint] cast a number of "no effect" statements to void so that gcc doesn't complain when invoked with -Wall -W -Wno-unused 157. [lint] a number of uses of the %lu printf() format were converted to %u; the corresponding casts to u_long were removed. 156. [lint] converted z_deferupdcnt and z_updatecnt from int to u_int32_t. 155. [func] maint_interval is now gone; SOA sanity checking related to it is gone too. 154. [bug] in named-xfer, unsigned 32-bit integers were sometimes stored in signed 32-bit variables and then printed using a cast to u_long and printf() format %lu. This would cause problems on 64-bit systems if the MSB of the 32-bit integer was set. The variable declarations have been changed to u_int32_t, and the printf format is now %u. 153. [bug] log_open_stream() had two syslogs that said the failing function was log_vwrite() instead of log_open_stream(). 152. [lint] made class, type, and dlen in rrextract() and named-xfer.c/print_output() u_int16_t. 151. [bug] the server was incrementing nssSentFErr in the formerr: code in ns_resp.c even though it wasn't sending FORMERR to anyone. 150. [func] in "check-names response fail" mode, instead of just dropping a failing response, we now send REFUSED to the client and drop the query. 149. [bug] if there wasn't a space between the SOA minimum value and a following ')' in a master file, the server would generate an error when it tried to parse the minimum value, causing the zone load to fail. 148. [func] the list of supported syslog facilities has been increased; the following facilities may now be used, provided they're available on the system 'named' is being built on: kern, user, mail, daemon, auth, syslog, lpr, news, uucp, cron, authpriv, ftp, local[0-7]. 147. [bug] the maybe_syslog_facility, logging_opt, channel_severity, address_name, key_ref, key_stmt, acl_stmt, zone_stmt, optional_class, and size_spec rules in the parser either leaked memory or could leak memory. 146. [func] if an RR set in a reply differed from an RR set in the cache only in the TTL, we would not update the TTL of the RR set in the cache. We now update this TTL to that of the reply RR set if the reply RR set's TTL is greater. 145. [func] follow the direction of the clarification draft and treat TTLs as unsigned 32-bit integers, with a maximum value of 2^31 - 1. TTLs greater than the maximum will be converted to 0. A warning will be issued if this conversion occurs while loading a master zone or during inbound zone transfer. 144. [func] "dig version.bind. txt chaos" now returns only the version number (e.g. "8.1-T4B"). 143. [lint] fixed various mismatches between printf() format string components and their corresponding arguments. 142. [lint] SendRequest_close() in nslookup/send.c had a return type of int instead of void. 141. [port] converted bcopy() to memcpy() or memmove() as appropriate. 140. [bug] certain buffer size checking comparisons in rdata_expand() weren't working because they were checking to see if an unsigned value was < 0. 139. [func] convert a few address comparisons from == to using ina_equal(). 138. [bug] an address comparison used in marking a server as bad was done incorrectly in ns_resp.c because the comparison used = instead of ==. 137. [lint] cleaned up warnings caused by assignment used as truth-value in various source files. 136. [func] changed eventlib-related INSIST statements into INSIST_ERR, so that we can print out strerror(errno). 135. [lint] replaced _getshort() with ns_get16() and _getlong() with ns_get32() in various source files. 134. [lint] findzone() and rdata_expand() were used before they were declared in ns_update.c, and were not declared static. 133. [lint] merge_logs() was not declared in ns_func.h. 132. [lint] Linux port_after.h didn't declare daemon(). We now do so, but only if GNU libc < 2.0. 131. [lint] set_boolean_option() was not declared in ns_func.h. 130. [lint] yyparse() was not declared in ns_parser.y. 129. [lint] ns_lexer.h didn't declare lexer_end_file(). 128. [lint] db_dump.c, db_lookup.c, db_update.c, db_glue.c, db_save.c, ns_ncache.c, ns_req.c, ns_stats.c, and ns_xfr.c didn't #include 127. [lint] logging.c, ev_connects.c, ns_maint.c, ns_glue.c, ns_update.c, dig/dig.c, nslookup/list.c, nslookup/send.c, host/host.c, and dnsquery/dnsquery.c didn't #include . 126. [lint] res_update.c, heap.c, db_load.c, db_save.c, db_glue.c, ns_lexer.c, ns_forw.c, ns_maint.c, ns_req.c, ns_stats.c, ns_xfr.c, ns_glue.c, ns_config.c, ns_update.c, host/host.c, nslookup/list.c, and nslookup/getinfo.c didn't #include . 125. [lint] res_mkupdate.c, ns_update.c, nsupdate.c, ns_print.c, didn't #include . 124. [port] replaced bcmp() with memcmp(). 123. [func] while not required, it's nice to preserve the order of RRs as received when ROUND_ROBIN isn't on, so we now do so. 122. [bug] under certain improbable conditions, the server could erroneously set a maintenance timer for a master zone. When the timer went off, it would trigger the INSIST() in zone_maint(). 121. [port] replaced bzero() with memset(). 120. [func] added multiple-cnames option. 119. [bug] the timeout: code in ns_resp.c didn't clean up TCP connections. 118. [port] added port for IRIX 5.3, 6.2, 6.4 117. [bug] removed declaration of getnum_error from db_load.c, since it is now declared in ns_glob.h. 116. [bug] GNU libc 2.0 doesn't have a , so in the Linux port we now provide a stub net/route.h that includes the real if GNU libc < 2.0 and does nothing if >= 2.0. 115. [func] on Linux systems, avoid an often unnecessary 'ranlib' and the subsequent relinking of all binaries by using the 's' flag of 'ar'. 114. [bug] 'make install' didn't work on HP/UX because the path to the install script was wrong in many cases. 113. [bug] named-xfer didn't clean up properly when sent SIGTERM. 112. [bug] named-xfer didn't clean up properly if an error occured in print_output(). 111. [func] added "max-transfer-time-in" option. The server used to allow a maximum of 2 hours for an inbound zone transfer to complete. This time can now be set globally or on a per-zone basis. The parameter is the number of minutes a transfer can take. 110. [func] moved declaration of d_ns in struct databuf to improve structure alignment. 109. [bug] addname() in ns_print.c didn't write an "@" for RRs that contained a domain name which was the same as the zone origin (it wrote nothing). 108. [bug] the server didn't check for EINTR in readable() and writable() in ev_streams.c. 107. [bug] check for both EWOULDBLOCK and EAGAIN after certain system calls instead of using PORT_WOULDBLK. This fixes partial zone transfer problems reported on Sun systems. 106. [bug] db_load() couldn't read SOAs with ( ) that were only one line. 105. [bug] fixed typo in Linux Makefile.set MANROFF definition. 104. [func] move various rrset debugging messages, rm_datum, and nsfree messages to debug level 3. Moved a few rrset debugging messages to debug level 2. 103. [bug] d_rcnt could overflow; to prevent this it has been increased to 32 bits. d_mark was made unsigned and decreased to 12 bits. 102. [func] added macro DRCNTDEC to go along with DRCNTINC. 101. [bug] clean_cache() didn't count deleted RRs, so it always reported "Cleaned cache of 0 RRs". 100. [bug] heap_for_each() didn't return a status, and didn't check for a NULL context or a NULL action. heap_element() didn't set errno to EINVAL when given invalid arguments. 99. [bug] the category rule in the parser leaked memory. 98. [bug] "notify" was not recognized as a valid category name. 97. [security] zone access control wasn't applied correctly to names that didn't exist, allowing an attacker to determine whether or not a given name exists in a zone. 96. [bug] we didn't recognize certain non-fatal errno values when recvfrom() failed; this would result in us dropping an interface unnecessarily. --- 8.1-T3B released --- 95. [bug] named-bootconf.pl didn't process xfrnets correctly (if no netmask was specifed, it assumed a mask of 255.255.255.255 instead of the natural netmask for the class of the address). 94. [bug] named-bootconf.pl didn't handle lines ending in a comment. 93. [bug] if rename() failed in merge_logs(), we would return garbage instead of -1. 92. [bug] writemsg() in named-xfer.c was returning a random value instead of the number of bytes written. 91. [bug] schedretry() could set retry times in the past because it was relying on 'tt' which hadn't been updated. It now calls gettime(&tt). 90. [bug] 'tt' might not have been current when clean_cache() was called. 89. [bug] ns_lexer.h didn't #include 88. [cleanup] removed some relics of the early days of BIND 8's new logging system from the parser and ns_config.c. 87. [bug] when writing to a TCP socket, the server didn't handle errors from the write() correctly. Under the right circumstances, this will cause the server to spin. The most common trigger would be a large outbound zone transfer where the far end died. 86. [cleanup] fixed comment in dig.c that messed up font-lock mode in emacs. 85. [bug] inet_lnaof, inet_makeaddr, inet_netof, and inet_network were missing from lib/inet. 84. [func] improved log_channel creation and use by making the type more opaque. The logging API provides a more complete set of services. Added the LOG_CHANNEL_OFF flag. 83. [func] removed statistics_channel; it wasn't being used. 82. [lint] a few handler functions were declared as void (*)() instead of void (*)(void). All now have the latter declaration. 81. [port] added port for A/UX 3.1.1. 80. [port] added port for SCO UNIX 3.2v4.2. 79. [bug] when processsing slave zones during a config file reload, in the "backup file changed" (or missing) case we were calling purge_zone() and do_reload() even if we had never successfully transferred and loaded the zone. 78. [cleanup] moved writemsg() to named-xfer.c. 77. [cleanup] removed doupdate() from ns_resp.c. 76. [bug] writev() in lib/bsd would keep going if there was a partial write; this could cause incorrect output. 75. [func] added readv() to lib/bsd. 74. [bug] if evConnect() failed in tcp_send() we were aborting the server instead of just returning an error. 73. [port] automatically fix getgrgid() declaration in ULTRIX 4.5 grp.h. 72. [func] make port/*/Makefile invoke SUBDIR make in include. Add/modify include and include/sys Makefiles. 71. [port] added utimes() to lib/bsd. 70. [doc] README broken up into INSTALL, TODO, port/README. Added more info about many topics. 69. [bug] NOTIFY didn't handle an unknown NS target. E.g. if we had "test.domain NS unknown.name" and "unknown.name" was not known, NOTIFY wasn't doing an "A" query for "unknown.name". 68. [lint] tweaks to ERR() and OK() in eventlib_p.h. 67. [bug] 'ch' in main() was a char instead of an int. 66. [bug] in bin/named/Makefile, pathnames wasn't getting linked with ${LIBBIND}, ${LIBPORT}, and ${SYSLIBS}. 65. [port] automatically fix timespec in BSD/OS 2.1 includes. 64. [func] lib/isc/heap.c now includes port_before.h and port_after.h. Fix 58 (below) has been undone; with port_after.h we'll now use __ansi_realloc() from Fix 59. 63. [bug] STRIP and PS were missing from MARGS in bin/Makefile. 62. [func] RRs in the additional data section must relate to RRs in the answer and authority sections. Only certain RR types are allowed in the authority and additional data sections. 61. [bug] Dynamic update didn't understand SRV records. 60. [bug] SRV records weren't decoded properly. --- 8.1-T3A released --- 59. [bug] The IRS library also wanted an ANSI C realloc(). port/sunos now provides __ansi_realloc(). 58. [bug] SunOS didn't like heap.c doing realloc() on a NULL pointer (in ANSI C that is equivalent to malloc()), so we malloc() instead. 57. [bug] interface discovery complained about bogus interfaces on ULTRIX, SunOS, and HP/UX because SIOCGIFCONF_ADDR wasn't defined in their port_after.h. 56. [API] created lib/nameser/ns_name.c and moved a lot of the functionality from lib/resolv/res_comp.c into it. functions older than 8.1 were stubbed out, but new functions from 8.1 were just renamed/removed. 55. [bug] findzone in ns_update wasn't ignoring z_nil zones. 54. [bug] if the named-xfer exec() failed, a misleading message was printed. 53. [bug] interface discovery didn't work on NetBSD because HAVE_SA_LEN wasn't defined in port_after.h. 52. [func] log the host we got a NOTIFY message from 51. [bug] we weren't sending out NOTIFY messages if the SOA was changed as the result of a dynamic update. 50. [bug] req_notify() wasn't calling sched_zone_maint() after it called qserial_query(). 49. [bug] initial_{data,stack,core}_size and initial_num_files weren't in an #ifdef HAVE_GETRUSAGE block. 48. [func] use sysconf(_SC_OPEN_MAX) instead of getdtablesize() in all cases when USE_POSIX is defined. 47. [bug] printupdatelog() was printing the post-update serial number in the zone section instead of the pre-update serial number. 46. [bug] zp->z_serial wasn't being updated if a dynamic update changed the zone serial number. 45. [bug] the SEQ_GT test in db_update was backwards. 44. [func] merge_logs() didn't work because a 'break' wasn't removed when class and type lookups were converted to sym_ston. 43. [func] evResetTimer() added to eventlib. 42. [bug] incr_serial() doesn't need to call schedule_dump(). 41. [bug] reset_retrytimer() could clear a timer that had already been cleared. 40. [bug] some zone data structures weren't freed if the zone was removed. 39. [func] The eventlib timers module now uses a heap to implement the timer queue. 38. [bug] dynamic zones weren't dumped if they were removed from the configuration file. 37. [func] created the "load" logging category. 36. [func] find_zone now uses a hash table instead of a linear search. 35. [bug] we weren't scheduling a retry for dumps or soa serial increments that failed. 34. [func] instead of doing all NOTIFY messages five seconds after loading completes, we now spread them out over up to fifteen minutes (the maximum delay depends on how many zones there are). 33. [func] if there are too many qserials running, we'll try again in five to thirty seconds. 32. [bug] z_dumptime wasn't getting set to zero after a zone dump. 31. [func] Each zone now has a maintenance timer. sched_maint() is gone. The new programming rule: if you change zp->z_time, it's your reponsibility to ensure sched_zone_maint(zp) gets called. 30. [func] short circuit PrintTimers evPrintfs if not debugging at a level where PrintTimers would print something. 29. [bug] if a log message with a non-default category was logged to a default category channel which had print-category on, "default" was printed instead of the category name. 28. [func] the performance of the main loop has been improved. 27. [bug] NOTIFY messages weren't being delayed after a zone load. 26. [bug] the eventlib category wasn't working if the channel wasn't the default debugging channel. 25. [func] added the "maintenance" logging category. 24. [func] periodic statistics dumps are now done using an eventlib timer instead of in ns_maint(). 23. [bug] names which have multiple CNAME records are illegal, but the server was allowing them. 22. [func] convert to POSIX signals from eventlib signal handling; the eventlib API no longer provides signal support. 21. [func] converted assert() to INSIST() so that the logging system (category "insist") will be used if a consistency check fails. 20. [bug] the server could exit when it shouldn't, and without leaving a message or a core file, because it wasn't handling SIGPIPE. 19. [port] Solaris has trouble if the size of the buffer used for IP_OPTIONS processing isn't 40 bytes. 18. [bug] library Makefiles we were using 'ld' instead of ${LD}. Added LD_LIBFLAGS. 17. [bug] on at least one OS, ctime() can return NULL and this can cause problems. We now call checked_ctime() in ns_glue.c, which returns "\n" if ctime() fails. 16. [bug] some signal handlers were calling library routines which POSIX does not designate as safe for use by signal handlers. 15. [func] finished conversion to new options scheme of name checking and inbound zone transfer parameters. 14. [func] added os_change_directory(). 13. [bug] write_open() in ns_config.c wasn't checking if the file was regular before unlinking. 12. [func] added "os" logging category. 11. [bug] named-bootconf.pl used the deprecated channel name "default" instead of "default_syslog". 10. [bug] named-bootconf.pl didn't understand continuation lines. 9. [bug] remove -p from mkdep command in Makefiles for bin/named and bin/nslookup. 8. [bug] add CDEBUG to Makefiles that link using ${CC}. 7. [bug] timestamp and level were printed twice for file channels in lib/isc/logging.c. 6. [bug] off by one with on level_text subscript in lib/isc/logging.c. 5. [bug] broken channels sometimes weren't marked as broken in lib/isc/logging.c. 4. [bug] didn't set foundname=0 after try_again: in ns_resp.c. 3. [bug] update_pid_file() didn't put a newline after the pid. 2. [func] minor log message tweaks in ns_config.c. 1. [bug] zone names needed to be canonicalized in the parser. --- 8.1-T2B released --- diff --git a/contrib/bind/README b/contrib/bind/README index 09e0bf849183..856be7d4fdf5 100644 --- a/contrib/bind/README +++ b/contrib/bind/README @@ -1,250 +1,256 @@ This is the source portion of BIND version 8. Its companions are "doc" and "contrib" so you are probably not missing anything. See the CHANGES file for a detailed listing of all changes. See the INSTALL file for information on building and installing BIND. See the SUPPORT file for information on obtaining commercial support for ISC artifacts including BIND, INN, and DHCP. Note that BIND 8 is in "end-of-life", having been replaced by BIND 9. See http://www.isc.org/ for more details. +BIND 8.3.6 Highlights + Maintenance release. + +BIND 8.3.5 Highlights + Maintenance release. + BIND 8.3.4 Highlights Security Fix DoS and buffer overrun. BIND 8.3.3 Highlights Security Fix libbind. All applications linked against libbind need to relinked. 'rndc restart' now preserves named's arguements BIND 8.3.2 Highlights dig, nslookup, host and nsupdate have improved IPv6 support. BIND 8.3.1 Highlights Critical bug fix to prevent DNS storms. If you have BIND 8.3.0 you need to upgrade. BIND 8.3.0 Highlights IPv6 transport support in resolver (from KAME). Opaque rdata support. EDNS0 support. Glue ordering to help non-ENDS0 aware clients (servers) cope with larger responses as a result of IPv6 by allowing A records to be added first to the additional section. IPv6 capable clients are expected to use EDNS0 to allow larger responses to be sent. Bug Fixes, includes BIND 8.2.5 changes. BIND 8.2.4 Highlights NSAP processing was not RFC 1706 compliant. NOTE: OLD MASTER FILES NEED TO BE CORRECTED AND CACHE FILES REMOVED. Fixes long-standing protocol incompatibility in DNSSEC support. Avoids fwd'ing to root name servers if response will be rejected. new port/cygwin contributed by s_c_biggs@bigfoot.com. new contrib/mdnkit (V1.3) from author. new contrib/adm from official ftp site. new contrib/host from author. new contrib/dnsp from author. fixed file descriptor leak in resolver. fixed a major memory leak in the processing of dynamic updates. numerous portability improvements. numerous bug fixes. BIND 8.2.3 Highlights Improved support for Windows NT and Windows 2000. Host stats are no longer required to track the source of a record. IXFR improvements. Forwarders track and use RTT to select fastest. Unix domain sockets implementions that require the directory to be secure, are now secured. Many minor problems fixed. Linux DoS removed. BIND 8.2.2 patchlevel 5 Highlights Bug in named-xfer (from patchlevel 4). Portability to IPv6 versions of FreeBSD, OpenBSD, NetBSD. Portability improvements (A/UX, AIX, IRIX, NetBSD, SCO, MPE/IX, NT). "also-notify" option could cause memory allocation errors. IXFR improvements (though client-side is still disabled). Contributed software upgraded (including TIS's "dns_signer"). Several latent denial-of-service bugs fixed (from audits, not abuse). New "make noesw" top-level target for removing encumbered components. BIND 8.2.2 Highlights Interoperability with MS-Win2K has been improved. Server-side IXFR is now known to work even under high load. Support for Windows/NT (thanks to BayNetworks). More fixes, especially to DNSSEC, TSIG, IXFR, and selective forwarding. More portability improvements and lint removal (A/UX 3.1.1, SCO 5.0). Better NOTIFY behaviour, especially with large update volume. Better UPDATE handling, including SRV RR support and RFC compliance. Fix for "ndc reload ZONENAME" (specific zone reload) problems. Fix for round robin when multiple CNAMEs are in use. New "min-roots" (MINROOTS) and "serial-queries" (MAXQSERIAL) options. Log files are no longer auto-rotated every time the server starts up. New "ndc reconfig" command only finds new/deleted zones, no stat()ing. New global options for "transfer-source" and "also-notify". $GENERATE now supports more record types, and options. BIND 8.2.1 Highlights Bug fixes, especially to DNSSEC, TSIG, IXFR, and selective forwarding. Portability improvements and lint removal. Use best SOA rather than first-better when selecting an AXFR master. $TTL now accepts symbolic time values (such as "$TTL 1h30m"). "ndc reload" now accepts a zone argument, for single-zone reloads. ndc is better behaved; is verbose or quiet when appropriate. event and error reporting improvements. BIND 8.2 Highlights RFC 2308 (Negative Caching) RFC 2181 (DNS Clarifications) RFC 2065 (DNS Security) TSIG (Transaction SIGnatures) support for multiple virtual name servers NDC uses a "control channel" now (no more signals) "Split DNS" via zone type "forward". Many bug fixes Documentation improvements Performance enhancements BIND 8.1.2 Highlights Security fixes for a number of problems including: An attacker could overwrite the stack if inverse query support was enabled. A number of denial of service attacks where malformed packets could cause the server to crash. The server was willing to answer queries on its forwarding sockets. Several memory leaks have been plugged. The server no longer panics if a periodic interface scan fails due to no file descriptors being available. Updates to a number of ports. New ports for QNX, LynxOS, HP-UX 9.x, and HP MPE. "files unlimited" now works as expected on systems where setting an infinite rlim_max for RLIMIT_NOFILE works. Adding and deleting the same record in the same dynamic update no longer crashes the server. If a dynamic update fails, rollback is now done in LIFO order instead of FIFO order. Better behavior when priming of the root servers fails. purge_zone() didn't work correctly for the root zone, allowing old data to persist after loading the zone. Improved handling of oversized UDP packets. All hosts on the also-notify list are now notified. The meaning of the count returned by select() varies somewhat by operating system, and this could cause previous releases of the server to spin. Per-host statistics may be disabled by specifying 'host-statistics no' in named.conf. The maximum number of zones has been increased from 32768 to 65536. query-source may specify an address and port that the server is already listening on. BIND 8.1.1 required that either the address or port be wild. E.g., you can now say: listen-on port 53 { 10.0.0.1; }; query-source address 10.0.0.1 port 53; The value of FD_SETSIZE to use may be specified. Experimental -u (set user id), -g (set group id), and -t (chroot) command line options. See the INSTALL file for details. BIND 8 Features -> DNS Dynamic Updates (RFC 2136) -> DNS Change Notification (RFC 1996) -> Completely new configuration syntax -> Flexible, categorized logging system -> IP-address-based access control for queries, zone transfers, and updates that may be specified on a zone-by-zone basis -> More efficient zone transfers -> Improved performance for servers with thousands of zones -> The server no longer forks for outbound zone transfers -> Many bug fixes File and Directory Overview CHANGES history of added features and fixed bugs INSTALL how to build and install README this file TODO features planned but not yet written Version the version number of this release bin/* source for executables, including the nameserver include/* public .h files lib/* the resolver and various BIND support libraries port/* ports to various operating systems Kits, Questions, Comments, and Bug Reports current non-test release latest public test kit using BIND DNS operations in general DNS standards in general gw'd to u:c.p.d.bind gw'd to u:c.p.d.std code warriors only please the BIND home page bug reports To Support the Effort Note that BIND is supported by the Internet Software Consortium, and although it is free for use and redistribution and incorporation into vendor products and export and anything else you can think of, it costs money to produce. That money comes from ISPs, hardware and software vendors, companies who make extensive use of the software, and generally kind hearted folk such as yourself. The Internet Software Consortium has also commissioned a DHCP server implementation, has taken over official support/release of the INN system, and has supported the Kerberos Version 5 effort at MIT. You can learn more about the ISC's goals and accomplishments from the web page at . diff --git a/contrib/bind/Version b/contrib/bind/Version index e9273f2d8702..68b8c56cef46 100644 --- a/contrib/bind/Version +++ b/contrib/bind/Version @@ -1 +1 @@ -8.3.4-REL +8.3.6-REL diff --git a/contrib/bind/bin/dig/dig.c b/contrib/bind/bin/dig/dig.c index 5bb6e7aee06c..e18249f16d22 100644 --- a/contrib/bind/bin/dig/dig.c +++ b/contrib/bind/bin/dig/dig.c @@ -1,1806 +1,1790 @@ #ifndef lint -static const char rcsid[] = "$Id: dig.c,v 8.57 2002/06/18 02:26:49 marka Exp $"; +static const char rcsid[] = "$Id: dig.c,v 8.62.6.3 2003/06/02 10:06:30 marka Exp $"; #endif /* * Copyright (c) 1989 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /*********************** Notes for the BIND 4.9 release (Paul Vixie, DEC) * dig 2.0 was written by copying sections of libresolv.a and nslookup * and modifying them to be more useful for a general lookup utility. * as of BIND 4.9, the changes needed to support dig have mostly been * incorporated into libresolv.a and nslookup; dig now links against * some of nslookup's .o files rather than #including them or maintaining * local copies of them. * * while merging dig back into the BIND release, i made a number of * structural changes. for one thing, i put all of dig's private * library routines into this file rather than maintaining them in * separate, #included, files. i don't like to #include ".c" files. * i removed all calls to "bcopy", replacing them with structure * assignments. i removed all "extern"'s of standard functions, * replacing them with #include's of standard header files. this * version of dig is probably as portable as the rest of BIND. * * i had to remove the query-time and packet-count statistics since * the current libresolv.a is a lot harder to modify to maintain these * than the 4.8 one (used in the original dig) was. for consolation, * i added a "usage" message with extensive help text. * * to save my (limited, albeit) sanity, i ran "indent" over the source. * i also added the standard berkeley/DEC copyrights, since this file now * contains a fair amount of non-USC code. note that the berkeley and * DEC copyrights do not prohibit redistribution, with or without fee; * we add them only to protect ourselves (you have to claim copyright * in order to disclaim liability and warranty). * * Paul Vixie, Palo Alto, CA, April 1993 **************************************************************************** ****************************************************************** * DiG -- Domain Information Groper * * * * dig.c - Version 2.1 (7/12/94) ("BIND takeover") * * * * Developed by: Steve Hotz & Paul Mockapetris * * USC Information Sciences Institute (USC-ISI) * * Marina del Rey, California * * 1989 * * * * dig.c - * * Version 2.0 (9/1/90) * * o renamed difftime() difftv() to avoid * * clash with ANSI C * * o fixed incorrect # args to strcmp,gettimeofday * * o incorrect length specified to strncmp * * o fixed broken -sticky -envsa -envset functions * * o print options/flags redefined & modified * * * * Version 2.0.beta (5/9/90) * * o output format - helpful to `doc` * * o minor cleanup * * o release to beta testers * * * * Version 1.1.beta (10/26/89) * * o hanging zone transer (when REFUSED) fixed * * o trailing dot added to domain names in RDATA * * o ISI internal * * * * Version 1.0.tmp (8/27/89) * * o Error in prnttime() fixed * * o no longer dumps core on large pkts * * o zone transfer (axfr) added * * o -x added for inverse queries * * (i.e. "dig -x 128.9.0.32") * * o give address of default server * * o accept broadcast to server @255.255.255.255 * * * * Version 1.0 (3/27/89) * * o original release * * * * DiG is Public Domain, and may be used for any purpose as * * long as this notice is not removed. * ******************************************************************/ /* Import. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include /* time(2), ctime(3) */ #include "port_after.h" #include #include "../nslookup/res.h" /* Global. */ #define VERSION 83 #define VSTRING "8.3" #define PRF_DEF (RES_PRF_STATS | RES_PRF_CMD | RES_PRF_QUES | \ RES_PRF_ANS | RES_PRF_AUTH | RES_PRF_ADD | \ RES_PRF_HEAD1 | RES_PRF_HEAD2 | RES_PRF_TTLID | \ RES_PRF_HEADX | RES_PRF_REPLY | RES_PRF_TRUNC) #define PRF_MIN (RES_PRF_QUES | RES_PRF_ANS | RES_PRF_HEAD1 | \ RES_PRF_HEADX | RES_PRF_REPLY | RES_PRF_TRUNC) #define PRF_ZONE (RES_PRF_STATS | RES_PRF_CMD | RES_PRF_QUES | \ RES_PRF_ANS | RES_PRF_AUTH | RES_PRF_ADD | \ RES_PRF_TTLID | RES_PRF_REPLY | RES_PRF_TRUNC) #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 256 #endif #define SAVEENV "DiG.env" #define DIG_MAXARGS 30 +#ifndef DIG_PING +#define DIG_PING "ping" +#endif +#ifndef DIG_TAIL +#define DIG_TAIL "tail" +#endif +#ifndef DIG_PINGFMT +#define DIG_PINGFMT "%s -s %s 56 3 | %s -3" +#endif + static int eecode = 0; static FILE * qfp; -static char *defsrv, *srvmsg; -static char defbuf[40] = "default -- "; -static char srvbuf[1024]; static char myhostname[MAXHOSTNAMELEN]; static struct sockaddr_in myaddress; static struct sockaddr_in6 myaddress6; static u_int32_t ixfr_serial; +static char ubuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:123.123.123.123")]; /* stuff for nslookup modules */ struct __res_state res; FILE *filePtr; jmp_buf env; HostInfo *defaultPtr = NULL; HostInfo curHostInfo, defaultRec; int curHostValid = FALSE; int queryType, queryClass; extern int StringToClass(), StringToType(); /* subr.c */ #if defined(BSD) && BSD >= 199006 && !defined(RISCOS_BSD) FILE *yyin = NULL; void yyrestart(FILE *f) { UNUSED(f); } #endif char *pager = NULL; /* end of nslookup stuff */ /* Forward. */ static void Usage(void); static int setopt(const char *); static void res_re_init(void); static int xstrtonum(char *); static int printZone(ns_type, const char *, const struct sockaddr_in *, ns_tsig_key *); static int print_axfr(FILE *output, const u_char *msg, size_t msglen); static struct timeval difftv(struct timeval, struct timeval); static void prnttime(struct timeval); static void stackarg(char *, char **); static void reverse6(char *, struct in6_addr *); /* Public. */ int main(int argc, char **argv) { short port = htons(NAMESERVER_PORT); short lport; /* Wierd stuff for SPARC alignment, hurts nothing else. */ union { HEADER header_; u_char packet_[PACKETSZ]; } packet_; #define header (packet_.header_) #define packet (packet_.packet_) - u_char answer[64*1024]; + u_char answer[NS_MAXMSG]; int n; char doping[90]; char pingstr[50]; char *afile; char *addrc, *addrend, *addrbegin; time_t exectime; struct timeval tv1, tv2, start_time, end_time, query_time; char *srv; int anyflag = 0; int sticky = 0; int tmp; int qtypeSet; int addrflag = 0; ns_type xfr = ns_t_invalid; int bytes_out, bytes_in; char cmd[512]; char domain[MAXDNAME]; char msg[120], **vtmp; char *args[DIG_MAXARGS]; char **ax; int once = 1, dofile = 0; /* batch -vs- interactive control */ char fileq[384]; int fp; int wait=0, delay; int envset=0, envsave=0; struct __res_state res_x, res_t; int r; struct in6_addr in6; ns_tsig_key key; char *keyfile = NULL, *keyname = NULL; + const char *pingfmt = NULL; res_ninit(&res); res.pfcode = PRF_DEF; qtypeSet = 0; memset(domain, 0, sizeof domain); gethostname(myhostname, (sizeof myhostname)); #ifdef HAVE_SA_LEN myaddress.sin_len = sizeof(struct sockaddr_in); #endif myaddress.sin_family = AF_INET; myaddress.sin_addr.s_addr = INADDR_ANY; myaddress.sin_port = 0; /*INPORT_ANY*/; #ifdef HAVE_SA_LEN myaddress6.sin6_len = sizeof(struct sockaddr_in6); #endif myaddress6.sin6_family = AF_INET6; myaddress6.sin6_addr = in6addr_any; myaddress6.sin6_port = 0; /*INPORT_ANY*/; - defsrv = strcat(defbuf, inet_ntoa(res.nsaddr.sin_addr)); res_x = res; /* * If LOCALDEF in environment, should point to file * containing local favourite defaults. Also look for file * DiG.env (i.e. SAVEENV) in local directory. */ if ((((afile = (char *) getenv("LOCALDEF")) != (char *) NULL) && ((fp = open(afile, O_RDONLY)) > 0)) || ((fp = open(SAVEENV, O_RDONLY)) > 0)) { read(fp, (char *)&res_x, (sizeof res_x)); close(fp); res = res_x; } /* * Check for batch-mode DiG; also pre-scan for 'help'. */ vtmp = argv; ax = args; while (*vtmp != NULL) { if (strcmp(*vtmp, "-h") == 0 || strcmp(*vtmp, "-help") == 0 || strcmp(*vtmp, "-usage") == 0 || strcmp(*vtmp, "help") == 0) { Usage(); exit(0); } if (strcmp(*vtmp, "-f") == 0) { dofile++; once=0; if ((qfp = fopen(*++vtmp, "r")) == NULL) { fflush(stdout); perror("file open"); fflush(stderr); exit(10); } } else { if (ax - args == DIG_MAXARGS) { fprintf(stderr, "dig: too many arguments\n"); exit(10); } *ax++ = *vtmp; } vtmp++; } - res.id = 1; gettimeofday(&tv1, NULL); /* * Main section: once if cmd-line query * while !EOF if batch mode */ *fileq = '\0'; while ((dofile && fgets(fileq, sizeof fileq, qfp) != NULL) || (!dofile && once--)) { if (*fileq == '\n' || *fileq == '#' || *fileq==';') { printf("%s", fileq); /* echo but otherwise ignore */ continue; /* blank lines and comments */ } /* * "Sticky" requests that before current parsing args * return to current "working" environment (X******). */ if (sticky) { printf(";; (using sticky settings)\n"); res = res_x; } /* * Concat cmd-line and file args. */ stackarg(fileq, ax); /* defaults */ queryType = ns_t_ns; queryClass = ns_c_in; xfr = ns_t_invalid; *pingstr = 0; srv = NULL; sprintf(cmd, "\n; <<>> DiG %s <<>> ", VSTRING); argv = args; argc = ax - args; /* * More cmd-line options than anyone should ever have to * deal with .... */ while (*(++argv) != NULL && **argv != '\0') { + if (strlen(cmd) + strlen(*argv) + 2 > sizeof (cmd)) { + fprintf(stderr, + "Argument too large for input buffer\n"); + exit(1); + } strcat(cmd, *argv); strcat(cmd, " "); if (**argv == '@') { srv = (*argv+1); continue; } if (**argv == '%') continue; if (**argv == '+') { setopt(*argv+1); continue; } if (**argv == '=') { ixfr_serial = strtoul(*argv+1, NULL, 0); continue; } if (strncmp(*argv, "-nost", 5) == 0) { sticky = 0; continue; } else if (strncmp(*argv, "-st", 3) == 0) { sticky++; continue; } else if (strncmp(*argv, "-envsa", 6) == 0) { envsave++; continue; } else if (strncmp(*argv, "-envse", 6) == 0) { envset++; continue; } if (**argv == '-') { switch (argv[0][1]) { case 'T': if (*++argv == NULL) printf("; no arg for -T?\n"); else wait = atoi(*argv); break; case 'c': if(*++argv == NULL) printf("; no arg for -c?\n"); else if ((tmp = atoi(*argv)) || *argv[0] == '0') { queryClass = tmp; } else if ((tmp = StringToClass(*argv, 0, NULL) ) != 0) { queryClass = tmp; } else { printf( "; invalid class specified\n" ); } break; case 't': if (*++argv == NULL) printf("; no arg for -t?\n"); else if ((tmp = atoi(*argv)) || *argv[0]=='0') { if (ns_t_xfr_p(tmp)) { xfr = tmp; } else { queryType = tmp; qtypeSet++; } } else if ((tmp = StringToType(*argv, 0, NULL) ) != 0) { if (ns_t_xfr_p(tmp)) { xfr = tmp; } else { queryType = tmp; qtypeSet++; } } else { printf( "; invalid type specified\n" ); } break; case 'x': if (!qtypeSet) { queryType = T_ANY; qtypeSet++; } if ((addrc = *++argv) == NULL) { printf("; no arg for -x?\n"); break; } r = inet_pton(AF_INET6, addrc, &in6); if (r > 0) { reverse6(domain, &in6); break; } addrend = addrc + strlen(addrc); if (*addrend == '.') *addrend = '\0'; *domain = '\0'; while ((addrbegin = strrchr(addrc,'.'))) { strcat(domain, addrbegin+1); strcat(domain, "."); *addrbegin = '\0'; } strcat(domain, addrc); strcat(domain, ".in-addr.arpa."); break; case 'p': if (argv[0][2] != '\0') port = htons(atoi(argv[0]+2)); else if (*++argv == NULL) printf("; no arg for -p?\n"); else port = htons(atoi(*argv)); break; case 'P': - if (argv[0][2] != '\0') + if (argv[0][2] != '\0') { strcpy(pingstr, argv[0]+2); - else - strcpy(pingstr, "ping -s"); + pingfmt = + "%s %s 56 3 | %s -3"; + } else { + strcpy(pingstr, DIG_PING); + pingfmt = DIG_PINGFMT; + } break; case 'n': if (argv[0][2] != '\0') res.ndots = atoi(argv[0]+2); else if (*++argv == NULL) printf("; no arg for -n?\n"); else res.ndots = atoi(*argv); break; case 'b': { char *a, *p; if (argv[0][2] != '\0') a = argv[0]+2; else if (*++argv == NULL) { printf("; no arg for -b?\n"); break; } else a = *argv; if ((p = strchr(a, ':')) != NULL) { *p++ = '\0'; lport = htons(atoi(p)); } else lport = htons(0); if (inet_pton(AF_INET6, a, &myaddress6.sin6_addr) == 1) { myaddress6.sin6_port = lport; } else if (!inet_aton(a, &myaddress.sin_addr)) { fprintf(stderr, ";; bad -b addr\n"); exit(1); } else myaddress.sin_port = lport; } break; case 'k': /* -k keydir:keyname */ if (argv[0][2] != '\0') keyfile = argv[0]+2; else if (*++argv == NULL) { printf("; no arg for -k?\n"); break; } else keyfile = *argv; keyname = strchr(keyfile, ':'); if (keyname == NULL) { fprintf(stderr, "key option argument should be keydir:keyname\n"); exit(1); } *keyname++='\0'; break; } /* switch - */ continue; } /* if '-' */ if ((tmp = StringToType(*argv, -1, NULL)) != -1) { if ((T_ANY == tmp) && anyflag++) { queryClass = C_ANY; continue; } if (ns_t_xfr_p(tmp) && (tmp == ns_t_axfr || (res.options & RES_USEVC) != 0) ) { res.pfcode = PRF_ZONE; xfr = (ns_type)tmp; } else { queryType = tmp; qtypeSet++; } } else if ((tmp = StringToClass(*argv, -1, NULL)) != -1) { queryClass = tmp; } else { memset(domain, 0, sizeof domain); sprintf(domain,"%s",*argv); } } /* while argv remains */ /* process key options */ if (keyfile) { #ifdef PARSE_KEYFILE int i, n1; char buf[BUFSIZ], *p; FILE *fp = NULL; int file_major, file_minor, alg; fp = fopen(keyfile, "r"); if (fp == NULL) { perror(keyfile); exit(1); } /* Now read the header info from the file. */ i = fread(buf, 1, BUFSIZ, fp); if (i < 5) { fclose(fp); exit(1); } fclose(fp); p = buf; n=strlen(p); /* get length of strings */ n1=strlen("Private-key-format: v"); if (n1 > n || strncmp(buf, "Private-key-format: v", n1)) { fprintf(stderr, "Invalid key file format\n"); exit(1); /* not a match */ } p+=n1; /* advance pointer */ sscanf((char *)p, "%d.%d", &file_major, &file_minor); /* should do some error checking with these someday */ while (*p++!='\n'); /* skip to end of line */ n=strlen(p); /* get length of strings */ n1=strlen("Algorithm: "); if (n1 > n || strncmp(p, "Algorithm: ", n1)) { fprintf(stderr, "Invalid key file format\n"); exit(1); /* not a match */ } p+=n1; /* advance pointer */ if (sscanf((char *)p, "%d", &alg)!=1) { fprintf(stderr, "Invalid key file format\n"); exit(1); } while (*p++!='\n'); /* skip to end of line */ n=strlen(p); /* get length of strings */ n1=strlen("Key: "); if (n1 > n || strncmp(p, "Key: ", n1)) { fprintf(stderr, "Invalid key file format\n"); exit(1); /* not a match */ } p+=n1; /* advance pointer */ pp=p; while (*pp++!='\n'); /* skip to end of line, * terminate it */ *--pp='\0'; key.data=malloc(1024*sizeof(char)); key.len=b64_pton(p, key.data, 1024); strcpy(key.name, keyname); strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT"); #else /* use the dst* routines to parse the key files * * This requires that both the .key and the .private * files exist in your cwd, so the keyfile parmeter * here is assumed to be a path in which the * K*.{key,private} files exist. */ DST_KEY *dst_key; char cwd[PATH_MAX+1]; if (getcwd(cwd, PATH_MAX)==NULL) { perror("unable to get current directory"); exit(1); } if (chdir(keyfile)<0) { fprintf(stderr, "unable to chdir to %s: %s\n", keyfile, strerror(errno)); exit(1); } dst_init(); dst_key = dst_read_key(keyname, 0 /* not used for priv keys */, KEY_HMAC_MD5, DST_PRIVATE); if (!dst_key) { fprintf(stderr, "dst_read_key: error reading key\n"); exit(1); } key.data=malloc(1024*sizeof(char)); dst_key_to_buffer(dst_key, key.data, 1024); key.len=dst_key->dk_key_size; strcpy(key.name, keyname); strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT"); if (chdir(cwd)<0) { fprintf(stderr, "unable to chdir to %s: %s\n", cwd, strerror(errno)); exit(1); } #endif } if (res.pfcode & 0x80000) printf("; pfcode: %08lx, options: %08lx\n", res.pfcode, res.options); /* * Current env. (after this parse) is to become the * new "working" environmnet. Used in conj. with sticky. */ if (envset) { res_x = res; envset = 0; } /* * Current env. (after this parse) is to become the * new default saved environmnet. Save in user specified * file if exists else is SAVEENV (== "DiG.env"). */ if (envsave) { afile = (char *) getenv("LOCALDEF"); if ((afile && ((fp = open(afile, O_WRONLY|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE)) > 0)) || ((fp = open(SAVEENV, O_WRONLY|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE)) > 0)) { write(fp, (char *)&res, (sizeof res)); close(fp); } envsave = 0; } if (res.pfcode & RES_PRF_CMD) printf("%s\n", cmd); addrflag = anyflag = 0; /* * Find address of server to query. If not dot-notation, then * try to resolve domain-name (if so, save and turn off print * options, this domain-query is not the one we want. Restore * user options when done. * Things get a bit wierd since we need to use resolver to be * able to "put the resolver to work". */ - srvbuf[0] = 0; - srvmsg = defsrv; if (srv != NULL) { int nscount = 0; union res_sockaddr_union u[MAXNS]; struct addrinfo *answer = NULL; struct addrinfo *cur = NULL; struct addrinfo hint; memset(u, 0, sizeof(u)); res_t = res; res_ninit(&res); res.pfcode = 0; res.options = RES_DEFAULT; memset(&hint, 0, sizeof(hint)); hint.ai_socktype = SOCK_DGRAM; if (!getaddrinfo(srv, NULL, &hint, &answer)) { res = res_t; cur = answer; for (cur = answer; cur != NULL; cur = cur->ai_next) { if (nscount == MAXNS) break; switch (cur->ai_addr->sa_family) { case AF_INET6: u[nscount].sin6 = *(struct sockaddr_in6*)cur->ai_addr; u[nscount++].sin6.sin6_port = port; break; case AF_INET: u[nscount].sin = *(struct sockaddr_in*)cur->ai_addr; - u[nscount++].sin6.sin6_port = + u[nscount++].sin.sin_port = port; break; } } - if (nscount != 0) { - char buf[80]; + if (nscount != 0) res_setservers(&res, u, nscount); - srvmsg = strcat(srvbuf, srv); - strcat(srvbuf, " "); - buf[0] = '\0'; - switch (u[0].sin.sin_family) { - case AF_INET: - inet_ntop(AF_INET, - &u[0].sin.sin_addr, - buf, sizeof(buf)); - break; - case AF_INET6: - inet_ntop(AF_INET6, - &u[0].sin6.sin6_addr, - buf, sizeof(buf)); - break; - } - strcat(srvbuf, buf); - } freeaddrinfo(answer); } else { res = res_t; fflush(stdout); fprintf(stderr, "; Bad server: %s -- using default server and timer opts\n", srv); fflush(stderr); - srvmsg = defsrv; srv = NULL; } printf("; (%d server%s found)\n", res.nscount, (res.nscount==1)?"":"s"); res.id += res.retry; } if (ns_t_xfr_p(xfr)) { int i; int nscount; union res_sockaddr_union u[MAXNS]; nscount = res_getservers(&res, u, MAXNS); - for (i = 0; i < res.nscount; i++) { + for (i = 0; i < nscount; i++) { int x; if (keyfile) x = printZone(xfr, domain, &u[i].sin, &key); else x = printZone(xfr, domain, &u[i].sin, NULL); if (res.pfcode & RES_PRF_STATS) { - char buf[80]; exectime = time(NULL); - buf[0] = '\0'; - switch (u[i].sin.sin_family) { - case AF_INET: - inet_ntop(AF_INET, - &u[i].sin.sin_addr, - buf, sizeof(buf)); - break; - case AF_INET6: - inet_ntop(AF_INET6, - &u[i].sin6.sin6_addr, - buf, sizeof(buf)); - break; - } printf(";; FROM: %s to SERVER: %s\n", myhostname, - buf); + p_sockun(u[RES_GETLAST(res)], + ubuf, sizeof(ubuf))); printf(";; WHEN: %s", ctime(&exectime)); } if (!x) break; /* success */ } fflush(stdout); continue; } if (*domain && !qtypeSet) { queryType = T_A; qtypeSet++; } bytes_out = n = res_nmkquery(&res, QUERY, domain, queryClass, queryType, NULL, 0, NULL, packet, sizeof packet); if (n < 0) { fflush(stderr); printf(";; res_nmkquery: buffer too small\n\n"); fflush(stdout); continue; } if (queryType == T_IXFR) { HEADER *hp = (HEADER *) packet; u_char *cpp = packet + bytes_out; hp->nscount = htons(1+ntohs(hp->nscount)); n = dn_comp(domain, cpp, (sizeof packet) - (cpp - packet), NULL, NULL); cpp += n; PUTSHORT(T_SOA, cpp); /* type */ PUTSHORT(C_IN, cpp); /* class */ PUTLONG(0, cpp); /* ttl */ PUTSHORT(22, cpp); /* dlen */ *cpp++ = 0; /* mname */ *cpp++ = 0; /* rname */ PUTLONG(ixfr_serial, cpp); PUTLONG(0xDEAD, cpp); /* Refresh */ PUTLONG(0xBEEF, cpp); /* Retry */ PUTLONG(0xABCD, cpp); /* Expire */ PUTLONG(0x1776, cpp); /* Min TTL */ bytes_out = n = cpp - packet; }; #if defined(RES_USE_EDNS0) && defined(RES_USE_DNSSEC) if (n > 0 && (res.options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) bytes_out = n = res_nopt(&res, n, packet, sizeof(packet), 4096); #endif eecode = 0; if (res.pfcode & RES_PRF_HEAD1) fp_resstat(&res, stdout); (void) gettimeofday(&start_time, NULL); if (keyfile) n = res_nsendsigned(&res, packet, n, &key, answer, sizeof answer); else n = res_nsend(&res, packet, n, answer, sizeof answer); if ((bytes_in = n) < 0) { fflush(stdout); n = 0 - n; - msg[0]=0; if (keyfile) - strcat(msg,";; res_nsendsigned to server "); + strcpy(msg, ";; res_nsendsigned"); else - strcat(msg,";; res_nsend to server "); - strcat(msg,srvmsg); + strcat(msg, ";; res_nsend"); perror(msg); fflush(stderr); if (!dofile) { if (eecode) exit(eecode); else exit(9); } } (void) gettimeofday(&end_time, NULL); if (res.pfcode & RES_PRF_STATS) { + union res_sockaddr_union u[MAXNS]; + + (void) res_getservers(&res, u, MAXNS); query_time = difftv(start_time, end_time); printf(";; Total query time: "); prnttime(query_time); putchar('\n'); exectime = time(NULL); - printf(";; FROM: %s to SERVER: %s\n", - myhostname, srvmsg); + printf(";; FROM: %s to SERVER: %s\n", myhostname, + p_sockun(u[RES_GETLAST(res)], + ubuf, sizeof(ubuf))); printf(";; WHEN: %s", ctime(&exectime)); printf(";; MSG SIZE sent: %d rcvd: %d\n", bytes_out, bytes_in); } fflush(stdout); /* * Argh ... not particularly elegant. Should put in *real* ping code. * Would necessitate root priviledges for icmp port though! */ - if (*pingstr) { - sprintf(doping,"%s %s 56 3 | tail -3",pingstr, - (srv==NULL)?(defsrv+10):srv); + if (*pingstr && srv != NULL) { + sprintf(doping, pingfmt, pingstr, srv, DIG_TAIL); system(doping); } putchar('\n'); /* * Fairly crude method and low overhead method of keeping two * batches started at different sites somewhat synchronized. */ gettimeofday(&tv2, NULL); delay = (int)(tv2.tv_sec - tv1.tv_sec); if (delay < wait) { sleep(wait - delay); } tv1 = tv2; } return (eecode); } /* Private. */ static void Usage() { fputs("\ usage: dig [@server] [domain] [q-type] [q-class] {q-opt} {d-opt} [%comment]\n\ where: server,\n\ domain are names in the Domain Name System\n\ q-class is one of (in,any,...) [default: in]\n\ q-type is one of (a,any,mx,ns,soa,hinfo,axfr,txt,...) [default: a]\n\ ", stderr); fputs("\ q-opt is one of:\n\ -x dot-notation-address (shortcut to in-addr.arpa lookups)\n\ -f file (batch mode input file name)\n\ -T time (batch mode time delay, per query)\n\ -p port (nameserver is on this port) [53]\n\ -b addr[:port] (bind to this tcp address) [*]\n\ -P[ping-string] (see man page)\n\ -t query-type (synonym for q-type)\n\ -c query-class (synonym for q-class)\n\ -k keydir:keyname (sign the query with this TSIG key)\n\ -envsav,-envset (see man page)\n\ -[no]stick (see man page)\n\ ", stderr); fputs("\ d-opt is of the form ``+keyword=value'' where keyword is one of:\n\ [no]debug [no]d2 [no]recurse retry=# time=# [no]ko [no]vc\n\ [no]defname [no]search domain=NAME [no]ignore [no]primary\n\ [no]aaonly [no]cmd [no]stats [no]Header [no]header [no]trunc\n\ [no]ttlid [no]cl [no]qr [no]reply [no]ques [no]answer\n\ [no]author [no]addit [no]dnssec pfdef pfmin\n\ pfset=# pfand=# pfor=#\n\ ", stderr); fputs("\ notes: defname and search don't work; use fully-qualified names.\n\ this is DiG version " VSTRING "\n\ - $Id: dig.c,v 8.57 2002/06/18 02:26:49 marka Exp $\n\ + $Id: dig.c,v 8.62.6.3 2003/06/02 10:06:30 marka Exp $\n\ ", stderr); } static int setopt(const char *string) { char option[NAME_LEN], *ptr; int i; i = pickString(string, option, sizeof option); if (i == 0) { fprintf(stderr, ";*** Invalid option: %s\n", string); /* this is ugly, but fixing the caller to behave properly with an error return value would require a major cleanup. */ exit(9); } if (strncmp(option, "aa", 2) == 0) { /* aaonly */ res.options |= RES_AAONLY; } else if (strncmp(option, "noaa", 4) == 0) { res.options &= ~RES_AAONLY; } else if (strncmp(option, "deb", 3) == 0) { /* debug */ res.options |= RES_DEBUG; } else if (strncmp(option, "nodeb", 5) == 0) { res.options &= ~(RES_DEBUG | RES_DEBUG2); } else if (strncmp(option, "ko", 2) == 0) { /* keepopen */ res.options |= (RES_STAYOPEN | RES_USEVC); } else if (strncmp(option, "noko", 4) == 0) { res.options &= ~RES_STAYOPEN; } else if (strncmp(option, "d2", 2) == 0) { /* d2 (more debug) */ res.options |= (RES_DEBUG | RES_DEBUG2); } else if (strncmp(option, "nod2", 4) == 0) { res.options &= ~RES_DEBUG2; } else if (strncmp(option, "def", 3) == 0) { /* defname */ res.options |= RES_DEFNAMES; } else if (strncmp(option, "nodef", 5) == 0) { res.options &= ~RES_DEFNAMES; } else if (strncmp(option, "dn", 2) == 0) { /* dnssec */ res.options |= RES_USE_DNSSEC; } else if (strncmp(option, "nodn", 4) == 0) { res.options &= ~RES_USE_DNSSEC; } else if (strncmp(option, "sea", 3) == 0) { /* search list */ res.options |= RES_DNSRCH; } else if (strncmp(option, "nosea", 5) == 0) { res.options &= ~RES_DNSRCH; } else if (strncmp(option, "do", 2) == 0) { /* domain */ ptr = strchr(option, '='); if (ptr != NULL) { i = pickString(++ptr, res.defdname, sizeof res.defdname); if (i == 0) { /* value's too long or non-existant. This actually shouldn't happen due to pickString() above */ fprintf(stderr, "*** Invalid domain: %s\n", ptr) ; exit(9); /* see comment at previous call to exit()*/ } } } else if (strncmp(option, "ti", 2) == 0) { /* timeout */ ptr = strchr(option, '='); if (ptr != NULL) sscanf(++ptr, "%d", &res.retrans); } else if (strncmp(option, "ret", 3) == 0) { /* retry */ ptr = strchr(option, '='); if (ptr != NULL) sscanf(++ptr, "%d", &res.retry); } else if (strncmp(option, "i", 1) == 0) { /* ignore */ res.options |= RES_IGNTC; } else if (strncmp(option, "noi", 3) == 0) { res.options &= ~RES_IGNTC; } else if (strncmp(option, "pr", 2) == 0) { /* primary */ res.options |= RES_PRIMARY; } else if (strncmp(option, "nop", 3) == 0) { res.options &= ~RES_PRIMARY; } else if (strncmp(option, "rec", 3) == 0) { /* recurse */ res.options |= RES_RECURSE; } else if (strncmp(option, "norec", 5) == 0) { res.options &= ~RES_RECURSE; } else if (strncmp(option, "v", 1) == 0) { /* vc */ res.options |= RES_USEVC; } else if (strncmp(option, "nov", 3) == 0) { res.options &= ~RES_USEVC; } else if (strncmp(option, "pfset", 5) == 0) { ptr = strchr(option, '='); if (ptr != NULL) res.pfcode = xstrtonum(++ptr); } else if (strncmp(option, "pfand", 5) == 0) { ptr = strchr(option, '='); if (ptr != NULL) res.pfcode = res.pfcode & xstrtonum(++ptr); } else if (strncmp(option, "pfor", 4) == 0) { ptr = strchr(option, '='); if (ptr != NULL) res.pfcode |= xstrtonum(++ptr); } else if (strncmp(option, "pfmin", 5) == 0) { res.pfcode = PRF_MIN; } else if (strncmp(option, "pfdef", 5) == 0) { res.pfcode = PRF_DEF; } else if (strncmp(option, "an", 2) == 0) { /* answer section */ res.pfcode |= RES_PRF_ANS; } else if (strncmp(option, "noan", 4) == 0) { res.pfcode &= ~RES_PRF_ANS; } else if (strncmp(option, "qu", 2) == 0) { /* question section */ res.pfcode |= RES_PRF_QUES; } else if (strncmp(option, "noqu", 4) == 0) { res.pfcode &= ~RES_PRF_QUES; } else if (strncmp(option, "au", 2) == 0) { /* authority section */ res.pfcode |= RES_PRF_AUTH; } else if (strncmp(option, "noau", 4) == 0) { res.pfcode &= ~RES_PRF_AUTH; } else if (strncmp(option, "ad", 2) == 0) { /* addition section */ res.pfcode |= RES_PRF_ADD; } else if (strncmp(option, "noad", 4) == 0) { res.pfcode &= ~RES_PRF_ADD; } else if (strncmp(option, "tt", 2) == 0) { /* TTL & ID */ res.pfcode |= RES_PRF_TTLID; } else if (strncmp(option, "nott", 4) == 0) { res.pfcode &= ~RES_PRF_TTLID; } else if (strncmp(option, "tr", 2) == 0) { /* TTL & ID */ res.pfcode |= RES_PRF_TRUNC; } else if (strncmp(option, "notr", 4) == 0) { res.pfcode &= ~RES_PRF_TRUNC; } else if (strncmp(option, "he", 2) == 0) { /* head flags stats */ res.pfcode |= RES_PRF_HEAD2; } else if (strncmp(option, "nohe", 4) == 0) { res.pfcode &= ~RES_PRF_HEAD2; } else if (strncmp(option, "H", 1) == 0) { /* header all */ res.pfcode |= RES_PRF_HEADX; } else if (strncmp(option, "noH", 3) == 0) { res.pfcode &= ~(RES_PRF_HEADX); } else if (strncmp(option, "qr", 2) == 0) { /* query */ res.pfcode |= RES_PRF_QUERY; } else if (strncmp(option, "noqr", 4) == 0) { res.pfcode &= ~RES_PRF_QUERY; } else if (strncmp(option, "rep", 3) == 0) { /* reply */ res.pfcode |= RES_PRF_REPLY; } else if (strncmp(option, "norep", 5) == 0) { res.pfcode &= ~RES_PRF_REPLY; } else if (strncmp(option, "cm", 2) == 0) { /* command line */ res.pfcode |= RES_PRF_CMD; } else if (strncmp(option, "nocm", 4) == 0) { res.pfcode &= ~RES_PRF_CMD; } else if (strncmp(option, "cl", 2) == 0) { /* class mnemonic */ res.pfcode |= RES_PRF_CLASS; } else if (strncmp(option, "nocl", 4) == 0) { res.pfcode &= ~RES_PRF_CLASS; } else if (strncmp(option, "st", 2) == 0) { /* stats*/ res.pfcode |= RES_PRF_STATS; } else if (strncmp(option, "nost", 4) == 0) { res.pfcode &= ~RES_PRF_STATS; } else { fprintf(stderr, "; *** Invalid option: %s\n", option); return (ERROR); } res_re_init(); return (SUCCESS); } /* * Force a reinitialization when the domain is changed. */ static void res_re_init() { static char localdomain[] = "LOCALDOMAIN"; u_long pfcode = res.pfcode, options = res.options; unsigned ndots = res.ndots; int retrans = res.retrans, retry = res.retry; char *buf; /* * This is ugly but putenv() is more portable than setenv(). */ buf = malloc((sizeof localdomain) + strlen(res.defdname) +10/*fuzz*/); sprintf(buf, "%s=%s", localdomain, res.defdname); putenv(buf); /* keeps the argument, so we won't free it */ res_ninit(&res); res.pfcode = pfcode; res.options = options; res.ndots = ndots; res.retrans = retrans; res.retry = retry; } /* * convert char string (decimal, octal, or hex) to integer */ static int xstrtonum(char *p) { int v = 0; int i; int b = 10; int flag = 0; while (*p != 0) { if (!flag++) if (*p == '0') { b = 8; p++; continue; } if (isupper(*p)) *p = tolower(*p); if (*p == 'x') { b = 16; p++; continue; } if (isdigit(*p)) { i = *p - '0'; } else if (isxdigit(*p)) { i = *p - 'a' + 10; } else { fprintf(stderr, "; *** Bad char in numeric string..ignored\n"); i = -1; } if (i >= b) { fprintf(stderr, "; *** Bad char in numeric string..ignored\n"); i = -1; } if (i >= 0) v = v * b + i; p++; } return (v); } typedef union { HEADER qb1; u_char qb2[PACKETSZ]; } querybuf; static int printZone(ns_type xfr, const char *zone, const struct sockaddr_in *sin, ns_tsig_key *key) { static u_char *answer = NULL; static int answerLen = 0; querybuf buf; int msglen, amtToRead, numRead, result, sockFD, len; int count, type, class, rlen, done, n; int numAnswers, numRecords, soacnt; u_char *cp, tmp[NS_INT16SZ]; char dname[2][NS_MAXDNAME]; enum { NO_ERRORS, ERR_READING_LEN, ERR_READING_MSG, ERR_PRINTING } error; pid_t zpid = -1; u_char *newmsg; int newmsglen; ns_tcp_tsig_state tsig_state; int tsig_ret, tsig_required, tsig_present; switch (xfr) { case ns_t_axfr: case ns_t_zxfr: break; default: fprintf(stderr, ";; %s - transfer type not supported\n", p_type(xfr)); return (ERROR); } /* * Create a query packet for the requested zone name. */ msglen = res_nmkquery(&res, ns_o_query, zone, queryClass, ns_t_axfr, NULL, 0, 0, buf.qb2, sizeof buf); if (msglen < 0) { if (res.options & RES_DEBUG) fprintf(stderr, ";; res_nmkquery failed\n"); return (ERROR); } /* * Sign the message if a key was sent */ if (key == NULL) { newmsg = (u_char *)&buf; newmsglen = msglen; } else { DST_KEY *dstkey; int bufsize, siglen; u_char sig[64]; int ret; /* ns_sign() also calls dst_init(), but there is no harm * doing it twice */ dst_init(); bufsize = msglen + 1024; newmsg = (u_char *) malloc(bufsize); if (newmsg == NULL) { errno = ENOMEM; return (-1); } memcpy(newmsg, (u_char *)&buf, msglen); newmsglen = msglen; if (strcmp(key->alg, NS_TSIG_ALG_HMAC_MD5) != 0) dstkey = NULL; else dstkey = dst_buffer_to_key(key->name, KEY_HMAC_MD5, NS_KEY_TYPE_AUTH_ONLY, NS_KEY_PROT_ANY, key->data, key->len); if (dstkey == NULL) { errno = EINVAL; if (key) free(newmsg); return (-1); } siglen = sizeof(sig); /* newmsglen++; */ ret = ns_sign(newmsg, &newmsglen, bufsize, NOERROR, dstkey, NULL, 0, sig, &siglen, 0); if (ret < 0) { if (key) free (newmsg); if (ret == NS_TSIG_ERROR_NO_SPACE) errno = EMSGSIZE; else if (ret == -1) errno = EINVAL; return (ret); } ns_verify_tcp_init(dstkey, sig, siglen, &tsig_state); } /* * Set up a virtual circuit to the server. */ if ((sockFD = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { int e = errno; perror(";; socket"); return (e); } switch (sin->sin_family) { case AF_INET: if (bind(sockFD, (struct sockaddr *)&myaddress, sizeof myaddress) < 0){ int e = errno; - fprintf(stderr, ";; bind(%s:%u): %s\n", + fprintf(stderr, ";; bind(%s port %u): %s\n", inet_ntoa(myaddress.sin_addr), ntohs(myaddress.sin_port), strerror(e)); (void) close(sockFD); sockFD = -1; return (e); } if (connect(sockFD, (const struct sockaddr *)sin, sizeof *sin) < 0) { int e = errno; perror(";; connect"); (void) close(sockFD); sockFD = -1; return (e); } break; case AF_INET6: if (bind(sockFD, (struct sockaddr *)&myaddress6, sizeof myaddress6) < 0){ int e = errno; char buf[80]; - fprintf(stderr, ";; bind(%s:%u): %s\n", + fprintf(stderr, ";; bind(%s port %u): %s\n", inet_ntop(AF_INET6, &myaddress6.sin6_addr, buf, sizeof(buf)), ntohs(myaddress6.sin6_port), strerror(e)); (void) close(sockFD); sockFD = -1; return (e); } if (connect(sockFD, (const struct sockaddr *)sin, sizeof(struct sockaddr_in6)) < 0) { int e = errno; perror(";; connect"); (void) close(sockFD); sockFD = -1; return (e); } break; } /* * Send length & message for zone transfer */ ns_put16(newmsglen, tmp); if (write(sockFD, (char *)tmp, NS_INT16SZ) != NS_INT16SZ || write(sockFD, (char *)newmsg, newmsglen) != newmsglen) { int e = errno; if (key) free (newmsg); perror(";; write"); (void) close(sockFD); sockFD = -1; return (e); } else if (key) free (newmsg); /* * If we're compressing, push a gzip into the pipeline. */ if (xfr == ns_t_zxfr) { enum { rd = 0, wr = 1 }; int z[2]; if (pipe(z) < 0) { int e = errno; perror(";; pipe"); (void) close(sockFD); sockFD = -1; return (e); } zpid = vfork(); if (zpid < 0) { int e = errno; perror(";; fork"); (void) close(sockFD); sockFD = -1; return (e); } else if (zpid == 0) { /* Child. */ (void) close(z[rd]); (void) dup2(sockFD, STDIN_FILENO); (void) close(sockFD); (void) dup2(z[wr], STDOUT_FILENO); (void) close(z[wr]); execlp("gzip", "gzip", "-d", "-v", NULL); perror(";; child: execlp(gunzip)"); _exit(1); } /* Parent. */ (void) close(z[wr]); (void) dup2(z[rd], sockFD); (void) close(z[rd]); } result = 0; numAnswers = 0; numRecords = 0; soacnt = 0; error = NO_ERRORS; dname[0][0] = '\0'; for (done = 0; !done; (void)NULL) { /* * Read the length of the response. */ cp = tmp; amtToRead = INT16SZ; while (amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0) { cp += numRead; amtToRead -= numRead; } if (numRead <= 0) { error = ERR_READING_LEN; break; } len = ns_get16(tmp); if (len == 0) break; /* nothing left to read */ /* * The server sent too much data to fit the existing buffer -- * allocate a new one. */ if (len > answerLen) { if (answerLen != 0) free(answer); answerLen = len; answer = (u_char *)Malloc(answerLen); } /* * Read the response. */ amtToRead = len; cp = answer; while (amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0) { cp += numRead; amtToRead -= numRead; } if (numRead <= 0) { error = ERR_READING_MSG; break; } result = print_axfr(stdout, answer, len); if (result != 0) { error = ERR_PRINTING; break; } numRecords += htons(((HEADER *)answer)->ancount); numAnswers++; /* Header. */ cp = answer + HFIXEDSZ; /* Question. */ for (count = ntohs(((HEADER *)answer)->qdcount); count > 0; count--) { n = dn_skipname(cp, answer + len); if (n < 0) { error = ERR_PRINTING; done++; break; } cp += n + QFIXEDSZ; if (cp > answer + len) { error = ERR_PRINTING; done++; break; } } /* Answer. */ for (count = ntohs(((HEADER *)answer)->ancount); count > 0 && !done; count--) { n = dn_expand(answer, answer + len, cp, dname[soacnt], sizeof dname[0]); if (n < 0) { error = ERR_PRINTING; done++; break; } cp += n; if (cp + 3 * INT16SZ + INT32SZ > answer + len) { error = ERR_PRINTING; done++; break; } GETSHORT(type, cp); GETSHORT(class, cp); cp += INT32SZ; /* ttl */ GETSHORT(rlen, cp); cp += rlen; if (cp > answer + len) { error = ERR_PRINTING; done++; break; } if (type == T_SOA && soacnt++ && ns_samename(dname[0], dname[1]) == 1) { done++; break; } } /* * Verify the TSIG */ if (key) { if (ns_find_tsig(answer, answer + len) != NULL) tsig_present = 1; else tsig_present = 0; if (numAnswers == 1 || soacnt > 1) tsig_required = 1; else tsig_required = 0; tsig_ret = ns_verify_tcp(answer, &len, &tsig_state, tsig_required); if (tsig_ret == 0) { if (tsig_present) printf("; TSIG ok\n"); } else printf("; TSIG invalid\n"); } } printf(";; Received %d answer%s (%d record%s).\n", numAnswers, (numAnswers != 1) ? "s" : "", numRecords, (numRecords != 1) ? "s" : ""); (void) close(sockFD); sockFD = -1; /* * If we were uncompressing, reap the uncompressor. */ if (xfr == ns_t_zxfr) { pid_t pid; int status; pid = wait(&status); if (pid < 0) { int e = errno; perror(";; wait"); return (e); } if (pid != zpid) { fprintf(stderr, ";; wrong pid (%lu != %lu)\n", (u_long)pid, (u_long)zpid); return (ERROR); } printf(";; pid %lu: exit %d, signal %d, core %c\n", (u_long)pid, WEXITSTATUS(status), WIFSIGNALED(status) ? WTERMSIG(status) : 0, WCOREDUMP(status) ? 't' : 'f'); } switch (error) { case NO_ERRORS: return (0); case ERR_READING_LEN: return (EMSGSIZE); case ERR_PRINTING: return (result); case ERR_READING_MSG: return (EMSGSIZE); default: return (EFAULT); } } static int print_axfr(FILE *file, const u_char *msg, size_t msglen) { ns_msg handle; if (ns_initparse(msg, msglen, &handle) < 0) { fprintf(file, ";; ns_initparse: %s\n", strerror(errno)); return (ns_r_formerr); } if (ns_msg_getflag(handle, ns_f_rcode) != ns_r_noerror) return (ns_msg_getflag(handle, ns_f_rcode)); /* * We are looking for info from answer resource records. * If there aren't any, return with an error. We assume * there aren't any question records. */ if (ns_msg_count(handle, ns_s_an) == 0) return (NO_INFO); #ifdef PROTOCOLDEBUG printf(";;; (message of %d octets has %d answers)\n", msglen, ns_msg_count(handle, ns_s_an)); #endif for (;;) { static char origin[NS_MAXDNAME], name_ctx[NS_MAXDNAME]; const char *name; char buf[2048]; /* XXX need to malloc/realloc. */ ns_rr rr; if (ns_parserr(&handle, ns_s_an, -1, &rr)) { if (errno != ENODEV) { fprintf(file, ";; ns_parserr: %s\n", strerror(errno)); return (FORMERR); } break; } name = ns_rr_name(rr); if (origin[0] == '\0' && name[0] != '\0') { if (strcmp(name, ".") != 0) strcpy(origin, name); fprintf(file, "$ORIGIN %s.\n", origin); if (strcmp(name, ".") == 0) strcpy(origin, name); if (res.pfcode & RES_PRF_TRUNC) strcpy(name_ctx, "@"); } if (ns_sprintrr(&handle, &rr, (res.pfcode & RES_PRF_TRUNC) ? name_ctx : NULL, (res.pfcode & RES_PRF_TRUNC) ? origin : NULL, buf, sizeof buf) < 0) { fprintf(file, ";; ns_sprintrr: %s\n", strerror(errno)); return (FORMERR); } strcpy(name_ctx, name); fputs(buf, file); fputc('\n', file); } return (SUCCESS); } static struct timeval difftv(struct timeval a, struct timeval b) { static struct timeval diff; diff.tv_sec = b.tv_sec - a.tv_sec; if ((diff.tv_usec = b.tv_usec - a.tv_usec) < 0) { diff.tv_sec--; diff.tv_usec += 1000000; } return (diff); } static void prnttime(struct timeval t) { printf("%lu msec", (u_long)(t.tv_sec * 1000 + (t.tv_usec / 1000))); } /* * Take arguments appearing in simple string (from file or command line) * place in char**. */ static void stackarg(char *l, char **y) { int done = 0; while (!done) { switch (*l) { case '\t': case ' ': l++; break; case '\0': case '\n': done++; *y = NULL; break; default: *y++ = l; while (!isspace(*l)) l++; if (*l == '\n') done++; *l++ = '\0'; *y = NULL; } } } static void reverse6(char *domain, struct in6_addr *in6) { sprintf(domain, "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa", in6->s6_addr[15] & 0x0f, (in6->s6_addr[15] >> 4) & 0x0f, in6->s6_addr[14] & 0x0f, (in6->s6_addr[14] >> 4) & 0x0f, in6->s6_addr[13] & 0x0f, (in6->s6_addr[13] >> 4) & 0x0f, in6->s6_addr[12] & 0x0f, (in6->s6_addr[12] >> 4) & 0x0f, in6->s6_addr[11] & 0x0f, (in6->s6_addr[11] >> 4) & 0x0f, in6->s6_addr[10] & 0x0f, (in6->s6_addr[10] >> 4) & 0x0f, in6->s6_addr[9] & 0x0f, (in6->s6_addr[9] >> 4) & 0x0f, in6->s6_addr[8] & 0x0f, (in6->s6_addr[8] >> 4) & 0x0f, in6->s6_addr[7] & 0x0f, (in6->s6_addr[7] >> 4) & 0x0f, in6->s6_addr[6] & 0x0f, (in6->s6_addr[6] >> 4) & 0x0f, in6->s6_addr[5] & 0x0f, (in6->s6_addr[5] >> 4) & 0x0f, in6->s6_addr[4] & 0x0f, (in6->s6_addr[4] >> 4) & 0x0f, - in6->s6_addr[6] & 0x0f, (in6->s6_addr[3] >> 4) & 0x0f, + in6->s6_addr[3] & 0x0f, (in6->s6_addr[3] >> 4) & 0x0f, in6->s6_addr[2] & 0x0f, (in6->s6_addr[2] >> 4) & 0x0f, in6->s6_addr[1] & 0x0f, (in6->s6_addr[1] >> 4) & 0x0f, in6->s6_addr[0] & 0x0f, (in6->s6_addr[0] >> 4) & 0x0f); } diff --git a/contrib/bind/bin/dnsquery/dnsquery.c b/contrib/bind/bin/dnsquery/dnsquery.c index cbc1e85e2378..b972658a6c6d 100644 --- a/contrib/bind/bin/dnsquery/dnsquery.c +++ b/contrib/bind/bin/dnsquery/dnsquery.c @@ -1,232 +1,232 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: dnsquery.c,v 8.19 2002/04/12 03:03:48 marka Exp $"; +static const char rcsid[] = "$Id: dnsquery.c,v 8.19.10.1 2003/06/02 09:15:45 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include extern int errno; extern int h_errno; extern char *h_errlist[]; struct __res_state res; static int newserver(char *srv, union res_sockaddr_union *u, int ns, int max) { struct addrinfo *answer = NULL; struct addrinfo *cur = NULL; struct addrinfo hint; short port = htons(NAMESERVER_PORT); memset(&hint, 0, sizeof(hint)); hint.ai_socktype = SOCK_DGRAM; if (!getaddrinfo(srv, NULL, &hint, &answer)) { for (cur = answer; cur != NULL; cur = cur->ai_next) { if (ns >= max) break; switch (cur->ai_addr->sa_family) { case AF_INET6: u[ns].sin6 = *(struct sockaddr_in6*)cur->ai_addr; u[ns++].sin6.sin6_port = port; break; case AF_INET: u[ns].sin = *(struct sockaddr_in*)cur->ai_addr; u[ns++].sin6.sin6_port = port; break; } } freeaddrinfo(answer); } else { fprintf(stderr, "Bad nameserver (%s)\n", srv); exit(1); } return (ns); } int main(int argc, char *argv[]) { char name[MAXDNAME]; u_char answer[8*1024]; int c, n; int nameservers = 0, class, type, len; union res_sockaddr_union q_nsaddr[MAXNS]; - extern int optind, opterr; + extern int optind; extern char *optarg; int stream = 0, debug = 0; /* set defaults */ len = MAXDNAME; gethostname(name, len); class = C_IN; type = T_ANY; /* if no args, exit */ if (argc == 1) { fprintf(stderr, "Usage: %s [-h] host [-n ns] [-t type] [-c class] [-r retry] [-p period] [-s] [-v] [-d] [-a]\n", argv[0]); exit(1); } /* handle args */ while ((c = getopt(argc, argv, "c:dh:n:p:r:st:u:v")) != -1) { switch (c) { case 'r' : res.retry = atoi(optarg); break; case 'p' : res.retrans = atoi(optarg); break; case 'h' : if (strlen(optarg) >= sizeof(name)) { fprintf(stderr, "Domain name too long (%s)\n", optarg); exit(1); } else strcpy(name, optarg); break; case 'c' : { int success, proto_class; proto_class = sym_ston(__p_class_syms, optarg, &success); if (success) class = proto_class; else { fprintf(stderr, "Bad class (%s)\n", optarg); exit(1); } } break; case 't' : { int success, proto_type; proto_type = sym_ston(__p_type_syms, optarg, &success); if (success) type = proto_type; else { fprintf(stderr, "Bad type (%s)\n", optarg); exit(1); } } break; case 'd' : debug++; break; case 's' : case 'v' : stream++; break; case 'n' : /* * If we set some nameservers here without * using gethostbyname() first, then they will * get overwritten when we do the first query. * So, we must init the resolver before any * of this. */ if (!(res.options & RES_INIT)) if (res_ninit(&res) == -1) { fprintf(stderr, "res_ninit() failed\n" ); exit(1); } nameservers = newserver(optarg, q_nsaddr, nameservers, MAXNS); break; default : fprintf(stderr, "\tUsage: %s [-n ns] [-h host] [-t type] [-c class] [-r retry] [-p period] [-s] [-v] [-d] [-a]\n", argv[0]); exit(1); } } if (optind < argc) { if (strlen(argv[optind]) >= sizeof(name)) { fprintf(stderr, "Domain name too long (%s)\n", argv[optind]); exit(1); } else { strcpy(name, argv[optind]); } } len = sizeof(answer); if (!(res.options & RES_INIT)) if (res_ninit(&res) == -1) { fprintf(stderr, "res_ninit() failed\n"); exit(1); } /* * set these here so they aren't set for a possible call to * gethostbyname above */ if (debug) res.options |= RES_DEBUG; if (stream) res.options |= RES_USEVC; /* if the -n flag was used, add them to the resolver's list */ if (nameservers != 0) res_setservers(&res, q_nsaddr, nameservers); /* * if the -h arg is fully-qualified, use res_query() since * using res_search() will lead to use of res_querydomain() * which will strip the trailing dot */ if (name[strlen(name) - 1] == '.') { n = res_nquery(&res, name, class, type, answer, len); if (n < 0) { fprintf(stderr, "Query failed (h_errno = %d) : %s\n", h_errno, h_errlist[h_errno]); exit(1); } } else if ((n = res_nsearch(&res, name, class, type, answer, len)) < 0) { fprintf(stderr, "Query failed (h_errno = %d) : %s\n", h_errno, h_errlist[h_errno]); exit(1); } res_pquery(&res, answer, n, stdout); exit(0); } diff --git a/contrib/bind/bin/host/host.c b/contrib/bind/bin/host/host.c index 5bac200670e5..7c0fa72de011 100644 --- a/contrib/bind/bin/host/host.c +++ b/contrib/bind/bin/host/host.c @@ -1,2140 +1,2136 @@ #ifndef lint -static const char rcsid[] = "$Id: host.c,v 8.53 2002/06/18 02:34:02 marka Exp $"; +static const char rcsid[] = "$Id: host.c,v 8.55.8.1 2003/06/02 09:24:38 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1986 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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 lint static const char copyright[] = "@(#) Copyright (c) 1986 Regents of the University of California.\n\ Portions Copyright (c) 1993 Digital Equipment Corporation.\n\ Portions Copyright (c) 1996-1999 Internet Software Consortium.\n\ All rights reserved.\n"; #endif /* not lint */ /* * Actually, this program is from Rutgers University, however it is * based on nslookup and other pieces of named tools, so it needs * the above copyright notices. */ /* Import. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include /* Global. */ #ifndef PATH_SEP #define PATH_SEP '/' #endif #define SIG_RDATA_BY_NAME 18 #define NS_HEADERDATA_SIZE 10 /* type + class + ttl + rdlen */ #define NUMNS 8 #define NUMNSADDR 16 #define NUMMX 50 #define NUMRR 127 /* max rr's per node to verify signatures for */ #define SUCCESS 0 #define TIME_OUT -1 #define NO_INFO -2 #ifdef ERROR #undef ERROR #endif #define ERROR -3 #define NONAUTH -4 -#define MY_PACKETSZ 64*1024 /* need this to hold tcp answers */ +#define MY_PACKETSZ NS_MAXMSG typedef union { HEADER qb1; u_char qb2[MY_PACKETSZ]; } querybuf; #define SD_RR 1 #define SD_SIG 2 #define SD_BADSIG 4 typedef struct { u_char data[MY_PACKETSZ]; size_t len; } rrstruct; static char chase_domain[NS_MAXDNAME]; static int chase_class; static int chase_type; static char chase_sigorigttl[NS_INT32SZ]; static rrstruct chase_rr[NUMRR]; static int chase_rr_num; static char chase_lastgoodkey[NS_MAXDNAME]; static char chase_signer[NS_MAXDNAME]; static u_char chase_sigrdata[MY_PACKETSZ]; static size_t chase_sigrdata_len; static u_char chase_signature[MY_PACKETSZ]; static size_t chase_signature_len; static int chase_step; static int sigchase; static char cnamebuf[NS_MAXDNAME]; static u_char hostbuf[NS_MAXDNAME]; static int sockFD; static FILE *filePtr; static struct __res_state res; static char *cname = NULL; static const char *progname = "amnesia"; static int getclass = ns_c_in, verbose = 0, list = 0; static int server_specified = 0; static int gettype = 0; static int querytype = 0; static char getdomain[NS_MAXDNAME]; /* Forward. */ static int parsetype(const char *s); static int parseclass(const char *s); static void hperror(int errnum); static int addrinfo(struct sockaddr_storage *addr); static int gethostinfo(char *name); static int getdomaininfo(const char *name, const char *domain); static int getinfo(const char *name, const char *domain, int type); static int printinfo(const querybuf *answer, const u_char *eom, int filter, int isls, int isinaddr); static const u_char * pr_rr(const u_char *cp, const u_char *msg, FILE *file, int filter); static const char * pr_type(int type); static const char * pr_class(int class); static const u_char * pr_cdname(const u_char *cp, const u_char *msg, char *name, int namelen); static int ListHosts(char *namePtr, int queryType); static const char * DecodeError(int result); static void usage(const char *msg) { fprintf(stderr, "%s: usage error (%s)\n", progname, msg); fprintf(stderr, "\ Usage: %s [-adlrwv] [-t querytype] [-c class] host [server]\n\ \t-a is equivalent to '-v -t *'\n\ \t-c class to look for non-Internet data\n\ \t-d to turn on debugging output\n\ \t-l to turn on 'list mode'\n\ \t-r to disable recursive processing\n\ \t-s recursively chase signature found in answers\n\ \t-t querytype to look for a specific type of information\n\ \t-v for verbose output\n\ \t-w to wait forever until reply\n\ ", progname); exit(1); } /* Public. */ int main(int argc, char **argv) { struct sockaddr_storage addr; - struct hostent *hp; + int ok = 0; char *s; int waitmode = 0; int ncnames, ch; int nkeychains; struct addrinfo *answer = NULL; struct addrinfo *cur = NULL; struct addrinfo hint; int ip = 0; dst_init(); if ((progname = strrchr(argv[0], PATH_SEP)) == NULL) progname = argv[0]; else progname++; res_ninit(&res); res.retrans = 5; while ((ch = getopt(argc, argv, "ac:dlrst:vw")) != -1) { switch (ch) { case 'a': verbose = 1; querytype = ns_t_any; break; case 'c': getclass = parseclass(optarg); break; case 'd': res.options |= RES_DEBUG; break; case 'l': list = 1; break; case 'r': res.options &= ~RES_RECURSE; break; case 's': sigchase = 1; break; case 't': querytype = parsetype(optarg); break; case 'v': verbose = 1; break; case 'w': res.retry = 1; res.retrans = 15; waitmode = 1; break; default: usage("unrecogized switch"); /*NOTREACHED*/ } } if ((querytype == 0) && (sigchase)) { if (verbose) printf ("Forcing `-t a' for signature trace.\n"); querytype = ns_t_a; } argc -= optind; argv += optind; if (argc < 1) usage("missing host argument"); strncpy(getdomain, *argv++, NS_MAXDNAME); getdomain[NS_MAXDNAME-1] = 0; argc--; if (argc > 1) usage("extra undefined arguments"); if (argc == 1) { union res_sockaddr_union u[MAXNS]; int nscount; s = *argv++; argc--; server_specified++; memset(&hint, 0, sizeof(hint)); hint.ai_flags = AI_CANONNAME; hint.ai_family = PF_UNSPEC; hint.ai_socktype = SOCK_DGRAM; if (!getaddrinfo(s, NULL, &hint, &answer)) { nscount = 0; if (answer->ai_canonname != NULL) { printf("Using domain server:\n"); printf("Name: %s\n", answer->ai_canonname); printf("Addresses:"); } else printf("Using domain server"); for (cur = answer; cur != NULL; cur = cur->ai_next) { char buf[80]; struct sockaddr_in6 *sin6; struct sockaddr_in *sin; switch (cur->ai_addr->sa_family) { case AF_INET6: sin6 = (struct sockaddr_in6 *)cur->ai_addr; inet_ntop(cur->ai_addr->sa_family, &sin6->sin6_addr, buf, sizeof(buf)); printf(" %s", buf); if (nscount >= MAXNS) break; u[nscount].sin6 = *sin6; u[nscount++].sin6.sin6_port = htons(NAMESERVER_PORT); break; case AF_INET: sin = (struct sockaddr_in*)cur->ai_addr; inet_ntop(cur->ai_addr->sa_family, &sin->sin_addr, buf, sizeof(buf)); printf(" %s", buf); if (nscount >= MAXNS) break; u[nscount].sin = *sin; u[nscount++].sin6.sin6_port = htons(NAMESERVER_PORT); break; } } if (nscount != 0) { res_setservers(&res, u, nscount); } if (answer->ai_canonname != NULL) printf("\n\n"); else printf(":\n\n"); freeaddrinfo(answer); } else { fprintf(stderr, "Error in looking up server name:\n"); exit(1); } res.retry = 2; } memset(&hint, 0, sizeof(hint)); hint.ai_flags = AI_NUMERICHOST; hint.ai_socktype = SOCK_DGRAM; if(!getaddrinfo(getdomain, NULL, &hint, &answer)) { memset(&addr, 0, sizeof(addr)); switch (answer->ai_family) { case AF_INET: memcpy(&addr, answer->ai_addr, sizeof(struct sockaddr_in)); ip = 1; break; case AF_INET6: memcpy(&addr, answer->ai_addr, sizeof(struct sockaddr_in6)); ip = 1; break; } freeaddrinfo(answer); } - hp = NULL; res.res_h_errno = TRY_AGAIN; /* * We handle default domains ourselves, thank you. */ res.options &= ~RES_DEFNAMES; if (list) exit(ListHosts(getdomain, querytype ? querytype : ns_t_a)); ncnames = 5; nkeychains = 18; - while (hp == NULL && res.res_h_errno == TRY_AGAIN) { + while (ok == 0 && res.res_h_errno == TRY_AGAIN) { if (!ip) { cname = NULL; - hp = (struct hostent *)gethostinfo(getdomain); + ok = gethostinfo(getdomain); getdomain[0] = 0; /* clear this query */ if (sigchase && (chase_step & SD_RR)) { if (nkeychains-- == 0) { printf("Too many sig/key chains. Loop?\n"); exit(1); } if (chase_step & SD_SIG) { /* start new query, for KEY */ strcpy (getdomain, chase_signer); strcat (getdomain, "."); querytype = ns_t_key; } else if (!(chase_step & SD_BADSIG)) { /* start new query, for SIG */ strcpy (getdomain, chase_domain); strcat (getdomain, "."); querytype = ns_t_sig; - } else if (hp && !(chase_step & SD_SIG) && + } else if (ok != 0 && !(chase_step & SD_SIG) && (chase_step & SD_BADSIG)) { printf ("%s for %s not found, last verified key %s\n", chase_step & SD_SIG ? "Key" : "Signature", chase_step & SD_SIG ? chase_signer : chase_domain, chase_lastgoodkey[0] ? chase_lastgoodkey : "None"); } } if (!getdomain[0] && cname) { if (ncnames-- == 0) { printf("Too many cnames. Loop?\n"); exit(1); } strcpy(getdomain, cname); strcat(getdomain, "."); } if (getdomain[0]) { if (chase_step & SD_SIG) { printf ("Locating key for %s\n", getdomain); } else if (chase_step & SD_SIG) { printf ("Locating signature for %s record(s) on %s\n", sym_ntos(__p_type_syms, chase_type, NULL), getdomain); } - hp = NULL; + ok = 0; res.res_h_errno = TRY_AGAIN; continue; } - } else { - if (addrinfo(&addr) == 0) - hp = NULL; - else - hp = (struct hostent *)1; /* XXX */ - } + } else + ok = addrinfo(&addr); if (!waitmode) break; } - if (hp == NULL) { + if (ok == 0) { hperror(res.res_h_errno); exit(1); } exit(0); } /* Private. */ static int parsetype(const char *s) { int type, success; type = sym_ston(__p_type_syms, s, &success); if (success) return (type); if (strcmp(s, "*") == 0) return (ns_t_any); if (atoi(s)) return (atoi(s)); fprintf(stderr, "Invalid query type: %s\n", s); exit(2); /*NOTREACHED*/ } static int parseclass(const char *s) { int class, success; class = sym_ston(__p_class_syms, s, &success); if (success) return (class); if (atoi(s)) return (atoi(s)); fprintf(stderr, "Invalid query class: %s\n", s); exit(2); /*NOTREACHED*/ } static void hperror(int errnum) { switch(errnum) { case HOST_NOT_FOUND: fprintf(stderr, "Host not found.\n"); break; case TRY_AGAIN: fprintf(stderr, "Host not found, try again.\n"); break; case NO_RECOVERY: fprintf(stderr, "No recovery, Host not found.\n"); break; case NO_ADDRESS: fprintf(stderr, "There is an entry for this host, but it doesn't have " ); switch (gettype) { case ns_t_a: fprintf(stderr, "an Internet address.\n"); break; case ns_t_ns: fprintf(stderr, "a Name Server.\n"); break; case ns_t_md: fprintf(stderr, "a Mail Destination.\n"); break; case ns_t_mf: fprintf(stderr, "a Mail Forwarder.\n"); break; case ns_t_cname: fprintf(stderr, "a Canonical Name.\n"); break; case ns_t_soa: fprintf(stderr, "a Start of Authority record.\n"); break; case ns_t_mb: fprintf(stderr, "a Mailbox Domain Name.\n"); break; case ns_t_mg: fprintf(stderr, "a Mail Group Member.\n"); break; case ns_t_mr: fprintf(stderr, "a Mail Rename Name.\n"); break; case ns_t_null: fprintf(stderr, "a Null Resource record.\n"); break; case ns_t_wks: fprintf(stderr, "any Well Known Service information.\n"); break; case ns_t_ptr: fprintf(stderr, "a Pointer record.\n"); break; case ns_t_hinfo: fprintf(stderr, "any Host Information.\n"); break; case ns_t_minfo: fprintf(stderr, "any Mailbox Information.\n"); break; case ns_t_mx: fprintf(stderr, "a Mail Exchanger record.\n"); break; case ns_t_txt: fprintf(stderr, "a Text record.\n"); break; case ns_t_rp: fprintf(stderr, "a Responsible Person.\n"); break; case ns_t_srv: fprintf(stderr, "a Server Selector.\n"); break; case ns_t_naptr: fprintf(stderr, "a URN Naming Authority.\n"); break; default: fprintf(stderr, "the information you requested.\n"); break; } break; } } static int addrinfo(struct sockaddr_storage *addr) { char name[NS_MAXDNAME]; unsigned char *p; struct in6_addr *addr6; switch(addr->ss_family) { case AF_INET: p = (unsigned char*)&((struct sockaddr_in *)addr)->sin_addr; mapped: sprintf(name, "%u.%u.%u.%u.IN-ADDR.ARPA.", p[3], p[2], p[1], p[0]); break; case AF_INET6: addr6 = &((struct sockaddr_in6 *)addr)->sin6_addr; p = (unsigned char *)addr6; if (IN6_IS_ADDR_V4MAPPED(addr6) || IN6_IS_ADDR_V4COMPAT(addr6)) { p += 12; goto mapped; } sprintf(name, "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." "IP6.ARPA", p[15] & 0xf, (p[15] >> 4) & 0xf, p[14] & 0xf, (p[14] >> 4) & 0xf, p[13] & 0xf, (p[13] >> 4) & 0xf, p[12] & 0xf, (p[12] >> 4) & 0xf, p[11] & 0xf, (p[11] >> 4) & 0xf, p[10] & 0xf, (p[10] >> 4) & 0xf, p[9] & 0xf, (p[9] >> 4) & 0xf, p[8] & 0xf, (p[8] >> 4) & 0xf, p[7] & 0xf, (p[7] >> 4) & 0xf, p[6] & 0xf, (p[6] >> 4) & 0xf, p[5] & 0xf, (p[5] >> 4) & 0xf, p[4] & 0xf, (p[4] >> 4) & 0xf, p[3] & 0xf, (p[3] >> 4) & 0xf, p[2] & 0xf, (p[2] >> 4) & 0xf, p[1] & 0xf, (p[1] >> 4) & 0xf, p[0] & 0xf, (p[0] >> 4) & 0xf); break; default: abort(); } return (getinfo(name, NULL, ns_t_ptr)); } static int gethostinfo(char *name) { char *cp, **domain; char tmp[NS_MAXDNAME]; const char *tp; int hp; int asis = 0; u_int n; if (strcmp(name, ".") == 0) return (getdomaininfo(name, NULL)); for (cp = name, n = 0; *cp; cp++) if (*cp == '.') n++; if (n && cp[-1] == '.') { if (cp[-1] == '.') cp[-1] = 0; hp = getdomaininfo(name, (char *)NULL); if (cp[-1] == 0) cp[-1] = '.'; return (hp); } if (n == 0 && (tp = res_hostalias(&res, name, tmp, sizeof tmp))) { if (verbose) printf("Aliased to \"%s\"\n", tp); res.options |= RES_DEFNAMES; return (getdomaininfo(tp, (char *)NULL)); } if (n >= res.ndots) { asis = 1; if (verbose) printf("Trying null domain\n"); hp = getdomaininfo(name, (char*)NULL); if (hp) return (hp); } for (domain = res.dnsrch; *domain; domain++) { if (verbose) printf("Trying domain \"%s\"\n", *domain); hp = getdomaininfo(name, *domain); if (hp) return (hp); } if (res.res_h_errno != HOST_NOT_FOUND || (res.options & RES_DNSRCH) == 0) return (0); if (!asis) return (0); if (verbose) printf("Trying null domain\n"); return (getdomaininfo(name, (char *)NULL)); } static int getdomaininfo(const char *name, const char *domain) { int val1, val2, val3; if (querytype) return (getinfo(name, domain, gettype=querytype)); else { val1 = getinfo(name, domain, gettype=ns_t_a); if (cname || verbose) return (val1); val2 = getinfo(name, domain, gettype=ns_t_aaaa); val3 = getinfo(name, domain, gettype=ns_t_mx); return (val1 || val2 || val3); } } static int getinfo(const char *name, const char *domain, int type) { u_char *eom; querybuf buf, answer; int n; char host[NS_MAXDNAME]; if (domain == NULL || (domain[0] == '.' && domain[1] == '\0')) sprintf(host, "%.*s", NS_MAXDNAME, name); else sprintf(host, "%.*s.%.*s", NS_MAXDNAME, name, NS_MAXDNAME, domain); n = res_nmkquery(&res, QUERY, host, getclass, type, NULL, 0, NULL, buf.qb2, sizeof buf); if (n < 0) { if (res.options & RES_DEBUG) printf("res_nmkquery failed\n"); res.res_h_errno = NO_RECOVERY; return (0); } n = res_nsend(&res, buf.qb2, n, answer.qb2, sizeof answer); if (n < 0) { if (res.options & RES_DEBUG) printf("res_nsend failed\n"); res.res_h_errno = TRY_AGAIN; return (0); } eom = answer.qb2 + n; return (printinfo(&answer, eom, ns_t_any, 0, (type == ns_t_ptr))); } static int printinfo(const querybuf *answer, const u_char *eom, int filter, int isls, int isinaddr) { int n, nmx, ancount, nscount, arcount, qdcount, buflen, savesigchase; const u_char *bp, *cp; const HEADER *hp; /* * Find first satisfactory answer. */ hp = (const HEADER *) answer; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); nscount = ntohs(hp->nscount); arcount = ntohs(hp->arcount); if (res.options & RES_DEBUG || (verbose && isls == 0)) printf("rcode = %d (%s), ancount=%d\n", hp->rcode, DecodeError(hp->rcode), ancount); if (hp->rcode != NOERROR || (ancount+nscount+arcount) == 0) { switch (hp->rcode) { case NXDOMAIN: res.res_h_errno = HOST_NOT_FOUND; return (0); case SERVFAIL: res.res_h_errno = TRY_AGAIN; return (0); - case NOERROR: - res.res_h_errno = NO_DATA; - return (0); - case FORMERR: - case NOTIMP: - case REFUSED: - res.res_h_errno = NO_RECOVERY; - return (0); + case NOERROR: + res.res_h_errno = NO_DATA; + return (0); + case FORMERR: + case NOTIMP: + case REFUSED: + res.res_h_errno = NO_RECOVERY; + return (0); } return (0); } bp = hostbuf; nmx = 0; buflen = sizeof(hostbuf); cp = answer->qb2 + HFIXEDSZ; if (qdcount > 0) { while (qdcount-- > 0) { n = dn_skipname(cp, eom); if (n < 0) { printf("Form error.\n"); return (0); } cp += n + QFIXEDSZ; if (cp > eom) { printf("Form error.\n"); return (0); } } } if (ancount) { if (!hp->aa) if (verbose && isls == 0) printf( "The following answer is not authoritative:\n" ); if (!hp->ad) if (verbose && isls == 0) printf("The following answer is not verified as authentic by the server:\n"); while (--ancount >= 0 && cp && cp < eom) { cp = pr_rr(cp, answer->qb2, stdout, filter); /* * When we ask for address and there is a CNAME, it * seems to return both the CNAME and the address. * Since we trace down the CNAME chain ourselves, we * don't really want to print the address at this * point. */ if (cname && (!verbose) && (!isinaddr)) return (1); } } if (!verbose) return (1); /* don't chase signatures for non-answer stuff */ savesigchase = sigchase; sigchase = 0; if (nscount) { printf("For authoritative answers, see:\n"); while (--nscount >= 0 && cp && cp < eom) cp = pr_rr(cp, answer->qb2, stdout, filter); } if (arcount) { printf("Additional information:\n"); while (--arcount >= 0 && cp && cp < eom) cp = pr_rr(cp, answer->qb2, stdout, filter); } /* restore sigchase value */ sigchase = savesigchase; return (1); } static void print_hex_field (u_int8_t field[], int length, int width, const char *pref) { /* Prints an arbitrary bit field, from one address for some number of bytes. Output is formatted via the width, and includes the raw hex value and (if printable) the printed value underneath. "pref" is a string used to start each line, e.g., " " to indent. This is very useful in gdb to see what's in a memory field. */ int i, start, stop; start=0; do { stop=(start+width)= 0) cname = cnamebuf; case ns_t_mb: case ns_t_mg: case ns_t_mr: case ns_t_ns: case ns_t_ptr: { const u_char *startrdata = cp; u_char cdname[NS_MAXCDNAME]; cp = pr_cdname(cp, msg, name, sizeof name); if (doprint) fprintf(file, "%c%s", punc, name); /* Extract DNSSEC canonical RR. */ n = ns_name_unpack(msg, msg+MY_PACKETSZ, startrdata, cdname, sizeof cdname); if (n >= 0) n = ns_name_ntol(cdname, cdname, sizeof cdname); if (n >= 0) { /* Copy header. */ memcpy(canonrr, cp1 - NS_HEADERDATA_SIZE, NS_HEADERDATA_SIZE); /* Overwrite length field. */ ns_put16(n, canonrr + NS_HEADERDATA_SIZE - NS_INT16SZ); /* Copy unpacked name. */ memcpy(canonrr + NS_HEADERDATA_SIZE, cdname, n); canonrr_len = NS_HEADERDATA_SIZE + n; } break; } case ns_t_hinfo: case ns_t_isdn: { const u_char *cp2 = cp + dlen; n = *cp++; if (n != 0) { if (doprint) fprintf(file,"%c%.*s", punc, n, cp); cp += n; } if ((cp < cp2) && (n = *cp++)) { if (doprint) fprintf(file,"%c%.*s", punc, n, cp); cp += n; } else if (type == ns_t_hinfo) if (doprint) fprintf(file, "\n; *** Warning *** OS-type missing" ); } break; case ns_t_soa: { const u_char *startname = cp; u_char cdname[NS_MAXCDNAME]; cp = pr_cdname(cp, msg, name, sizeof name); if (doprint) fprintf(file, "\t%s", name); n = ns_name_unpack(msg, msg + 512, startname, cdname, sizeof cdname); if (n >= 0) n = ns_name_ntol(cdname, cdname, sizeof cdname); if (n >= 0) { /* Copy header. */ memcpy(canonrr, cp1 - NS_HEADERDATA_SIZE, NS_HEADERDATA_SIZE); /* Copy expanded name. */ memcpy(canonrr + NS_HEADERDATA_SIZE, cdname, n); canonrr_len = NS_HEADERDATA_SIZE + n; } startname = cp; cp = pr_cdname(cp, msg, name, sizeof name); if (doprint) fprintf(file, " %s", name); n = ns_name_unpack(msg, msg + 512, startname, cdname, sizeof cdname); if (n >= 0) n = ns_name_ntol(cdname, cdname, sizeof cdname); if (n >= 0) { /* Copy expanded name. */ memcpy(canonrr + canonrr_len, cdname, n); canonrr_len += n; /* Copy rest of SOA. */ memcpy(canonrr + canonrr_len, cp, 5 * INT32SZ); canonrr_len += 5 * INT32SZ; /* Overwrite length field. */ ns_put16(canonrr_len - NS_HEADERDATA_SIZE, canonrr + NS_HEADERDATA_SIZE - NS_INT16SZ); } if (doprint) - fprintf(file, "(\n\t\t\t%lu\t;serial (version)", + fprintf(file, " (\n\t\t\t%lu\t;serial (version)", ns_get32(cp)); cp += INT32SZ; if (doprint) fprintf(file, "\n\t\t\t%lu\t;refresh period", ns_get32(cp)); cp += INT32SZ; if (doprint) fprintf(file, "\n\t\t\t%lu\t;retry refresh this often", ns_get32(cp)); cp += INT32SZ; if (doprint) fprintf(file, "\n\t\t\t%lu\t;expiration period", ns_get32(cp)); cp += INT32SZ; if (doprint) fprintf(file, "\n\t\t\t%lu\t;minimum TTL\n\t\t\t)", ns_get32(cp)); cp += INT32SZ; break; } case ns_t_mx: case ns_t_afsdb: case ns_t_rt: { const u_char *startrdata = cp; u_char cdname[NS_MAXCDNAME]; if (doprint) { if (type == ns_t_mx && !verbose) fprintf(file," (pri=%d) by ", ns_get16(cp)); else if (verbose) fprintf(file,"\t%d ", ns_get16(cp)); else fprintf(file," "); } cp += sizeof(u_short); cp = pr_cdname(cp, msg, name, sizeof(name)); if (doprint) fprintf(file, "%s", name); n = ns_name_unpack(msg, msg+512, startrdata + sizeof(u_short), cdname, sizeof cdname); if (n >= 0) n = ns_name_ntol(cdname, cdname, sizeof cdname); if (n >= 0) { /* Copy header. */ memcpy(canonrr, cp1 - NS_HEADERDATA_SIZE, NS_HEADERDATA_SIZE); /* Overwrite length field. */ ns_put16(sizeof(u_short) + n, canonrr + NS_HEADERDATA_SIZE - NS_INT16SZ); /* Copy u_short. */ memcpy(canonrr + NS_HEADERDATA_SIZE, startrdata, sizeof(u_short)); /* Copy expanded name. */ memcpy(canonrr + NS_HEADERDATA_SIZE + sizeof(u_short), cdname, n); canonrr_len = NS_HEADERDATA_SIZE + sizeof(u_short) + n; } break; } case ns_t_srv: if (doprint) fprintf(file," %d", ns_get16(cp)); cp += sizeof(u_short); if (doprint) fprintf(file," %d", ns_get16(cp)); cp += sizeof(u_short); if (doprint) fprintf(file," %d", ns_get16(cp)); cp += sizeof(u_short); cp = pr_cdname(cp, msg, name, sizeof(name)); if (doprint) fprintf(file, " %s", name); break; case ns_t_naptr: /* order */ if (doprint) fprintf(file, " %d", ns_get16(cp)); cp += sizeof(u_short); /* preference */ if (doprint) fprintf(file, " %d", ns_get16(cp)); cp += NS_INT16SZ; /* Flags */ n = *cp++; if (doprint) { if (n) fprintf(file, "%c%.*s", punc, n, cp); else fprintf(file, "%c\"\"",punc); } cp += n; /* Service */ n = *cp++; if (doprint) { if (n) fprintf(file, "%c%.*s", punc, n, cp); else fprintf(file,"%c\"\"",punc); } cp += n; /* Regexp */ n = *cp++; if (doprint) { if (n) fprintf(file, "%c%.*s", punc, n, cp); else fprintf(file, "%c\"\"",punc); } cp += n; /* replacement */ cp = pr_cdname(cp, msg, name, sizeof(name)); if (doprint) fprintf(file, "%s", name); break; case ns_t_minfo: case ns_t_rp: cp = pr_cdname(cp, msg, name, sizeof name); if (doprint) { if (type == ns_t_rp) { char *p; p = strchr(name, '.'); if (p != NULL) *p = '@'; } fprintf(file, "%c%s", punc, name); } cp = pr_cdname(cp, msg, name, sizeof(name)); if (doprint) fprintf(file, " %s", name); break; case ns_t_x25: n = *cp++; if (n != 0) { if (doprint) fprintf(file, "%c%.*s", punc, n, cp); cp += n; } break; case ns_t_txt: { int n, j; const u_char *end = cp + dlen; while (cp < end) { if (doprint) (void) fputs(" \"", file); n = *cp++; if (n != 0) for (j = n; j > 0 && cp < end ; j --) { if (doprint) { if (*cp == '\n' || *cp == '"' || *cp == '\\') putc('\\', file); putc(*cp, file); } cp++; } if (doprint) putc('"', file); } } break; case ns_t_wks: if (dlen < INT32SZ + 1) break; memcpy(&inaddr, cp, INADDRSZ); cp += INT32SZ; proto = *cp++; protop = getprotobynumber(proto); if (doprint) { if (protop) fprintf(file, "%c%s %s", punc, inet_ntoa(inaddr), protop->p_name); else fprintf(file, "%c%s %d", punc, inet_ntoa(inaddr), proto); } n = 0; while (cp < cp1 + dlen) { c = *cp++; do { if (c & 0200) { servp = NULL; if (protop) servp = getservbyport(htons(n), protop-> p_name); if (doprint) { if (servp) fprintf(file, " %s", servp->s_name); else fprintf(file, " %d", n); } } c <<= 1; } while (++n & 07); } break; case ns_t_nxt: { const u_char *startrdata = cp; u_char cdname[NS_MAXCDNAME]; size_t bitmaplen; cp = pr_cdname(cp, msg, name, sizeof name); if (doprint) fprintf(file, "%c%s", punc, name); bitmaplen = dlen - (cp - startrdata); /* extract dnssec canonical rr */ n = ns_name_unpack(msg, msg+MY_PACKETSZ, startrdata, cdname, sizeof cdname); if (n >= 0) n = ns_name_ntol(cdname, cdname, sizeof cdname); if (n >= 0) { /* Copy header. */ memcpy(canonrr, cp1 - NS_HEADERDATA_SIZE, NS_HEADERDATA_SIZE); /* Overwrite length field. */ ns_put16(n + bitmaplen, canonrr + NS_HEADERDATA_SIZE - NS_INT16SZ); /* Copy expanded name. */ memcpy(canonrr + NS_HEADERDATA_SIZE, cdname, n); /* Copy type bit map. */ memcpy(canonrr + NS_HEADERDATA_SIZE + n, cp, bitmaplen); canonrr_len = NS_HEADERDATA_SIZE + n + bitmaplen; } cp += bitmaplen; break; } case ns_t_sig: { int tc; const u_char *origttl; /* type covered */ tc = ns_get16(cp); if (doprint && verbose) fprintf(file, "%c%s", punc, sym_ntos(__p_type_syms, tc, NULL)); cp += sizeof(u_short); /* algorithm */ if (doprint && verbose) fprintf(file, " %d", *cp); cp++; /* labels */ if (doprint && verbose) fprintf(file, " %d", *cp); cp++; /* original ttl */ origttl = cp; if (doprint && verbose) - fprintf(file, " %ld", ns_get32(cp)); + fprintf(file, " %lu", ns_get32(cp)); cp += INT32SZ; /* signature expiration */ if (doprint && verbose) - fprintf(file, " %ld", ns_get32(cp)); + fprintf(file, " %lu", ns_get32(cp)); cp += INT32SZ; /* time signed */ if (doprint && verbose) - fprintf(file, " %ld", ns_get32(cp)); + fprintf(file, " %lu", ns_get32(cp)); cp += INT32SZ; /* key footprint */ if (doprint && verbose) fprintf(file, " %d", ns_get16(cp)); cp += sizeof(u_short); /* signer's name */ cp = pr_cdname(cp, msg, name, sizeof(name)); if (doprint && verbose) fprintf(file, " %s", name); else if (doprint && !verbose) fprintf (file, " %s for type %s", name, sym_ntos(__p_type_syms, tc, NULL)); /* signature */ { char str[MY_PACKETSZ]; size_t len = cp1-cp+dlen; b64_ntop (cp, len, str, MY_PACKETSZ-1); if (sigchase && !(chase_step & SD_SIG) && strcmp (chase_domain, thisdomain) == 0 && chase_class == class && chase_type == tc) { u_char cdname[NS_MAXCDNAME]; if (doprint && !verbose) fprintf(file, " (chasing key)"); strcpy(chase_signer, name); memcpy(&chase_sigorigttl[0], origttl, NS_INT32SZ); n = ns_name_ntol(cp1 + SIG_RDATA_BY_NAME, cdname, sizeof cdname); if (n >= 0) { memcpy(chase_sigrdata, cp1, SIG_RDATA_BY_NAME); memcpy(chase_sigrdata + SIG_RDATA_BY_NAME, cdname, n); chase_sigrdata_len = SIG_RDATA_BY_NAME + n; memcpy(chase_signature, cp, len); chase_signature_len = len; chase_step |= SD_SIG; } } else if (sigchase) { chase_step |= SD_BADSIG; } cp += len; if (doprint && verbose) fprintf (file, " %s", str); } break; } case ns_t_key: /* flags */ if (doprint && verbose) fprintf(file, "%c%d", punc, ns_get16(cp)); cp += sizeof(u_short); /* protocol */ if (doprint && verbose) fprintf(file, " %d", *cp); cp++; /* algorithm */ n = *cp; if (doprint && verbose) fprintf(file, " %d", *cp); cp++; switch (n) { case 1: /* MD5/RSA */ { char str[MY_PACKETSZ]; size_t len = cp1-cp+dlen; b64_ntop (cp, len, str, MY_PACKETSZ-1); cp += len; if (doprint && verbose) fprintf (file, " %s", str); break; } default: - fprintf (stderr, "Unknown algorithm %d\n", n); + if (doprint && verbose) + fprintf (stderr, "Unknown algorithm %d\n", n); cp = cp1 + dlen; break; } if (sigchase && (chase_step & (SD_SIG|SD_RR)) && strcmp (getdomain, name) == 0 && getclass == class && gettype == type) { DST_KEY *dstkey; int rc, len, i, j; /* convert dnskey to dstkey */ dstkey = dst_dnskey_to_key (name, cp1, dlen); /* fix ttl in rr */ for (i = 0; i < NUMRR && chase_rr[i].len; i++) { len = dn_skipname(chase_rr[i].data, chase_rr[i].data + chase_rr[i].len); if (len>=0) memcpy(chase_rr[i].data + len + NS_INT16SZ + NS_INT16SZ, &chase_sigorigttl, INT32SZ); } /* sort rr's (qsort() is too slow) */ for (i = 0; i < NUMRR && chase_rr[i].len; i++) for (j = i + 1; j < NUMRR && chase_rr[j].len; j++) if (memcmp(chase_rr[i].data, chase_rr[j].data, MY_PACKETSZ) < 0) memswap(&chase_rr[i], &chase_rr[j], sizeof(rrstruct)); /* append rr's to sigrdata */ for (i = 0; i < NUMRR && chase_rr[i].len; i++) { memcpy (chase_sigrdata + chase_sigrdata_len, chase_rr[i].data, chase_rr[i].len); chase_sigrdata_len += chase_rr[i].len; } /* print rr-data and signature */ if (verbose) { fprintf(file, "\n"); print_hex_field(chase_sigrdata, chase_sigrdata_len, 21,"DATA: "); print_hex_field(chase_signature, chase_signature_len, 21,"SIG: "); } /* do the works */ if (dstkey) rc = dst_verify_data(SIG_MODE_ALL, dstkey, NULL, chase_sigrdata, chase_sigrdata_len, chase_signature, chase_signature_len); else rc = 1; dst_free_key(dstkey); if (verbose) { fprintf(file, "\nVerification %s", rc == 0 ? "was SUCCESSFULL" : "FAILED"); } else { fprintf (file, " that %s verify our %s " "record(s) on %s", rc == 0 ? "successfully" : "DOES NOT", sym_ntos(__p_type_syms, chase_type, NULL), chase_domain); } if (rc == 0) { strcpy (chase_lastgoodkey, name); } else { /* don't trace further after a failure */ sigchase = 0; } chase_step = 0; chase_signature_len = 0; chase_sigrdata_len = 0; memset(chase_sigorigttl, 0, NS_INT32SZ); memset(chase_rr, 0, sizeof(chase_rr)); chase_rr_num = 0; } break; default: if (doprint) fprintf(file, "%c???", punc); cp += dlen; break; } if (cp != cp1 + dlen) fprintf(file, "packet size error (%p != %p)\n", cp, cp1 + dlen); if (sigchase && !(chase_step & SD_SIG) && strcmp (getdomain, thisdomain) == 0 && getclass == class && gettype == type && type != ns_t_sig) { u_char cdname[NS_MAXCDNAME]; if (doprint && !verbose) fprintf(file, " (chasing signature)"); /* unpack rr */ n = ns_name_unpack(msg, msg + MY_PACKETSZ, savecp, cdname, sizeof cdname); if (n >= 0) n = ns_name_ntol(cdname, cdname, sizeof cdname); if (n >= 0) { memcpy(chase_rr[chase_rr_num].data, cdname, n); memcpy(chase_rr[chase_rr_num].data + n, canonrr_len ? canonrr : cp1 - NS_HEADERDATA_SIZE, canonrr_len ? canonrr_len : dlen + NS_HEADERDATA_SIZE); chase_rr[chase_rr_num].len = n + (canonrr_len != 0 ? canonrr_len : dlen + NS_HEADERDATA_SIZE); strcpy(chase_domain, getdomain); chase_class = class; chase_type = type; chase_step |= SD_RR; chase_rr_num++; } } if (doprint) fprintf(file, "\n"); return (cp); } /* * Return a string for the type. A few get special treatment when * not in verbose mode, to make the program more chatty and easier to * understand. */ static const char * pr_type(int type) { if (!verbose) switch (type) { case ns_t_a: case ns_t_aaaa: return ("has address"); case ns_t_cname: return ("is a nickname for"); case ns_t_mx: return ("mail is handled"); case ns_t_txt: return ("descriptive text"); case ns_t_sig: return ("has a signature signed by"); case ns_t_key: return ("has a key"); case ns_t_nxt: return ("next valid name"); case ns_t_afsdb: return ("DCE or AFS service from"); } if (verbose) return (sym_ntos(__p_type_syms, type, NULL)); else return (sym_ntop(__p_type_syms, type, NULL)); } /* * Return a mnemonic for class */ static const char * pr_class(int class) { static char spacestr[20]; if (!verbose) switch (class) { case ns_c_in: /* internet class */ return (""); case ns_c_hs: /* hesiod class */ return (""); } spacestr[0] = ' '; strcpy(&spacestr[1], p_class(class)); return (spacestr); } static const u_char * pr_cdname(const u_char *cp, const u_char *msg, char *name, int namelen) { int n = dn_expand(msg, msg + MY_PACKETSZ, cp, name, namelen - 2); if (n < 0) return (NULL); if (name[0] == '\0') { name[0] = '.'; name[1] = '\0'; } return (cp + n); } static void add(union res_sockaddr_union *u, int type, void *p) { memset(u, 0, sizeof(*u)); switch (type) { case ns_t_a: memcpy(&u->sin.sin_addr, p, NS_INADDRSZ); u->sin.sin_family = AF_INET; u->sin.sin_port = htons(NAMESERVER_PORT); #ifdef HAVE_SA_LEN u->sin.sin_len = sizeof(u->sin); #endif break; case ns_t_aaaa: memcpy(&u->sin6.sin6_addr, p, 16); u->sin6.sin6_family = AF_INET6; u->sin6.sin6_port = htons(NAMESERVER_PORT); #ifdef HAVE_SA_LEN u->sin6.sin6_len = sizeof(u->sin6); #endif break; } } static int salen(union res_sockaddr_union *u) { switch (u->sin.sin_family) { case AF_INET6: return (sizeof(u->sin6)); case AF_INET: return (sizeof(u->sin)); } return (0); } static int ListHosts(char *namePtr, int queryType) { querybuf buf, answer; struct sockaddr_in sin; const HEADER *headerPtr; enum { NO_ERRORS, ERR_READING_LEN, ERR_READING_MSG, ERR_PRINTING } error = NO_ERRORS; int msglen, amtToRead, numRead, i, len, dlen, type, nscount, n; int numAnswers = 0, soacnt = 0, result = 0; u_char tmp[NS_INT16SZ]; char name[NS_MAXDNAME], dname[2][NS_MAXDNAME], domain[NS_MAXDNAME]; u_char *cp, *nmp, *eom; /* Names and addresses of name servers to try. */ char nsname[NUMNS][NS_MAXDNAME]; int nshaveaddr[NUMNS]; union res_sockaddr_union nsipaddr[NUMNSADDR]; int numns, numnsaddr, thisns; int qdcount, ancount; /* * Normalize to not have trailing dot. We do string compares below * of info from name server, and it won't have trailing dots. */ i = strlen(namePtr); if (namePtr[i-1] == '.') namePtr[i-1] = 0; if (server_specified) numnsaddr = res_getservers(&res, nsipaddr, NUMNSADDR); else { /* * First we have to find out where to look. This needs a NS * query, possibly followed by looking up addresses for some * of the names. */ msglen = res_nmkquery(&res, ns_o_query, namePtr, ns_c_in, ns_t_ns, NULL, 0, NULL, buf.qb2, sizeof buf); if (msglen < 0) { printf("res_nmkquery failed\n"); return (ERROR); } msglen = res_nsend(&res, buf.qb2, msglen, answer.qb2, sizeof answer); if (msglen < 0) { printf("Cannot find nameserver -- try again later\n"); return (ERROR); } if (res.options & RES_DEBUG || verbose) printf("rcode = %d (%s), ancount=%d\n", answer.qb1.rcode, DecodeError(answer.qb1.rcode), ntohs(answer.qb1.ancount)); /* * Analyze response to our NS lookup. */ nscount = ntohs(answer.qb1.ancount) + ntohs(answer.qb1.nscount) + ntohs(answer.qb1.arcount); if (answer.qb1.rcode != NOERROR || nscount == 0) { switch (answer.qb1.rcode) { case NXDOMAIN: /* Check if it's an authoritive answer */ if (answer.qb1.aa) printf("No such domain\n"); else printf("Unable to get information about domain -- try again later.\n"); break; case SERVFAIL: printf("Unable to get information about that domain -- try again later.\n"); break; case NOERROR: printf("That domain exists, but seems to be a leaf node.\n"); break; case FORMERR: case NOTIMP: case REFUSED: printf("Unrecoverable error looking up domain name.\n"); break; } return (0); } cp = answer.qb2 + HFIXEDSZ; eom = answer.qb2 + msglen; qdcount = ntohs(answer.qb1.qdcount); while (qdcount-- > 0) { n = dn_skipname(cp, eom); if (n < 0) { printf("Form error.\n"); return (ERROR); } cp += n + QFIXEDSZ; if (cp > eom) { printf("Form error.\n"); return (ERROR); } } numns = 0; numnsaddr = 0; /* * Look at response from NS lookup for NS and A records. */ for ((void)NULL; nscount; nscount--) { cp += dn_expand(answer.qb2, answer.qb2 + msglen, cp, domain, sizeof(domain)); if (cp + 3 * INT16SZ + INT32SZ > eom) { printf("Form error.\n"); return (ERROR); } type = ns_get16(cp); cp += INT16SZ + INT16SZ + INT32SZ; dlen = ns_get16(cp); cp += INT16SZ; if (cp + dlen > eom) { printf("Form error.\n"); return (ERROR); } if (type == ns_t_ns) { if (dn_expand(answer.qb2, eom, cp, name, sizeof(name)) >= 0) { if (numns < NUMNS && ns_samename((char *)domain, namePtr) == 1) { for (i = 0; i < numns; i++) if (ns_samename( nsname[i], (char *)name ) == 1) /* duplicate */ break; if (i >= numns) { strncpy(nsname[numns], (char *)name, sizeof(name)); nshaveaddr[numns] = 0; numns++; } } } } else if ((type == ns_t_a || type == ns_t_aaaa) && numnsaddr < NUMNSADDR) { for (i = 0; i < numns; i++) { if (ns_samename(nsname[i], (char *)domain) != 1) continue; nshaveaddr[i]++; add(&nsipaddr[numnsaddr++], type, cp); break; } } cp += dlen; } /* * Usually we'll get addresses for all the servers in the * additional info section. But in case we don't, look up * their addresses. */ for (i = 0; i < numns; i++) { struct addrinfo *answer = NULL; struct addrinfo *cur = NULL; struct addrinfo hint; memset(&hint, 0, sizeof(hint)); hint.ai_family = PF_UNSPEC; hint.ai_socktype = SOCK_STREAM; if (nshaveaddr[i] == 0 && !getaddrinfo(nsname[i], NULL, &hint, &answer)) { int numaddrs = 0; for (cur = answer; cur != NULL; cur = cur->ai_next) { union res_sockaddr_union *u; if (numnsaddr >= NUMNSADDR) break; u = &nsipaddr[numnsaddr]; switch (cur->ai_addr->sa_family) { case AF_INET6: u->sin6 = *(struct sockaddr_in6 *)cur->ai_addr; u->sin6.sin6_port = htons(NAMESERVER_PORT); numnsaddr++; numaddrs++; break; case AF_INET: u->sin = *(struct sockaddr_in*)cur->ai_addr; u->sin6.sin6_port = htons(NAMESERVER_PORT); numnsaddr++; numaddrs++; break; } } if (res.options & RES_DEBUG || verbose) printf( "Found %d addresses for %s by extra query\n", numaddrs, nsname[i]); freeaddrinfo(answer); } else if (res.options & RES_DEBUG || verbose) printf("Found %d addresses for %s\n", nshaveaddr[i], nsname[i]); } } /* * Now nsipaddr has numnsaddr addresses for name servers that * serve the requested domain. Now try to find one that will * accept a zone transfer. */ thisns = 0; again: numAnswers = 0; soacnt = 0; /* * Create a query packet for the requested domain name. */ msglen = res_nmkquery(&res, QUERY, namePtr, getclass, ns_t_axfr, NULL, 0, NULL, buf.qb2, sizeof buf); if (msglen < 0) { if (res.options & RES_DEBUG) fprintf(stderr, "ListHosts: Res_mkquery failed\n"); return (ERROR); } memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_port = htons(NAMESERVER_PORT); /* * Set up a virtual circuit to the server. */ for ((void)NULL; thisns < numnsaddr; thisns++) { if ((sockFD = socket(nsipaddr[thisns].sin.sin_family, SOCK_STREAM, 0)) < 0) { if (errno == EPROTONOSUPPORT) continue; perror("ListHosts"); return (ERROR); } if (res.options & RES_DEBUG || verbose) { char buf[80]; switch (nsipaddr[thisns].sin.sin_family) { case AF_INET: inet_ntop(nsipaddr[thisns].sin.sin_family, &nsipaddr[thisns].sin.sin_addr, buf, sizeof(buf)); break; case AF_INET6: inet_ntop(nsipaddr[thisns].sin6.sin6_family, &nsipaddr[thisns].sin6.sin6_addr, buf, sizeof(buf)); break; } printf("Trying %s\n", buf); } if (connect(sockFD, (struct sockaddr *)&nsipaddr[thisns], salen(&nsipaddr[thisns])) >= 0) break; if (verbose) perror("Connection failed, trying next server"); close(sockFD); sockFD = -1; } if (thisns >= numnsaddr) { printf("No server for that domain responded\n"); if (!verbose) perror("Error from the last server was"); return (ERROR); } /* * Send length & message for zone transfer */ ns_put16(msglen, tmp); if (write(sockFD, (char *)tmp, INT16SZ) != INT16SZ || write(sockFD, (char *)buf.qb2, msglen) != msglen) { perror("ListHosts"); (void) close(sockFD); sockFD = -1; return (ERROR); } filePtr = stdout; for (;;) { /* * Read the length of the response. */ cp = buf.qb2; amtToRead = INT16SZ; while (amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0) { cp += numRead; amtToRead -= numRead; } if (numRead <= 0) { error = ERR_READING_LEN; break; } if ((len = ns_get16(buf.qb2)) == 0) break; /* Protocol violation. */ /* * Read the response. */ amtToRead = len; cp = buf.qb2; while (amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0) { cp += numRead; amtToRead -= numRead; } if (numRead <= 0) { error = ERR_READING_MSG; break; } i = buf.qb1.rcode; if (i != NOERROR || ntohs(buf.qb1.ancount) == 0) { if (thisns + 1 < numnsaddr && (i == SERVFAIL || i == NOTIMP || i == REFUSED)) { if (res.options & RES_DEBUG || verbose) printf( "Server failed, trying next server: %s\n", i != NOERROR ? DecodeError(i) : "Premature end of data"); (void) close(sockFD); sockFD = -1; thisns++; goto again; } printf("Server failed: %s\n", i != NOERROR ? DecodeError(i) : "Premature end of data"); break; } result = printinfo(&buf, cp, queryType, 1, 0); if (! result) { error = ERR_PRINTING; break; } numAnswers++; cp = buf.qb2 + HFIXEDSZ; qdcount = ntohs(buf.qb1.qdcount); while (qdcount-- > 0) { n = dn_skipname(cp, buf.qb2 + len); if (n <= 0) { error = ERR_PRINTING; break; } if (cp + n + QFIXEDSZ > buf.qb2 + len) { error = ERR_PRINTING; break; } cp += n + QFIXEDSZ; } ancount = ntohs(buf.qb1.ancount); while (ancount-- > 0) { nmp = cp; n = dn_skipname(cp, buf.qb2 + len); if (n <= 0) { error = ERR_PRINTING; break; } cp += n; if (cp + INT16SZ > buf.qb2 + len) { error = ERR_PRINTING; break; } type = ns_get16(cp); cp += INT16SZ; if (type == ns_t_soa) { (void) dn_expand(buf.qb2, buf.qb2 + len, nmp, dname[soacnt], sizeof dname[0]); if (soacnt) { if (ns_samename(dname[0], dname[1]) == 1) goto done; } else soacnt++; } if (cp + INT16SZ*2 + INT32SZ > buf.qb2 + len) { error = ERR_PRINTING; break; } cp += INT32SZ + INT16SZ; dlen = ns_get16(cp); cp += INT16SZ; if (cp + dlen > buf.qb2 + len) { error = ERR_PRINTING; break; } cp += dlen; } if (error != NO_ERRORS) break; } done: (void) close(sockFD); sockFD = -1; switch (error) { case NO_ERRORS: return (SUCCESS); case ERR_READING_LEN: return (ERROR); case ERR_PRINTING: fprintf(stderr,"*** Error during listing of %s: %s\n", namePtr, DecodeError(result)); return (result); case ERR_READING_MSG: headerPtr = (HEADER *) &buf; fprintf(stderr,"ListHosts: error receiving zone transfer:\n"); fprintf(stderr, " result: %s, answers = %d, authority = %d, additional = %d\n", p_rcode(headerPtr->rcode), ntohs(headerPtr->ancount), ntohs(headerPtr->nscount), ntohs(headerPtr->arcount)); return (ERROR); default: return (ERROR); } } static const char * DecodeError(int result) { switch(result) { case NOERROR: return ("Success"); case FORMERR: return ("Format error"); case SERVFAIL: return ("Server failed"); case NXDOMAIN: return ("Non-existent domain"); case NOTIMP: return ("Not implemented"); case REFUSED: return ("Query refused"); case NO_INFO: return ("No information"); case ERROR: return ("Unspecified error"); case TIME_OUT: return ("Timed out"); case NONAUTH: return ("Non-authoritative answer"); default: return ("BAD ERROR VALUE"); } /* NOTREACHED */ } diff --git a/contrib/bind/bin/named-xfer/named-xfer.c b/contrib/bind/bin/named-xfer/named-xfer.c index 27d9ac023c2e..696c821f5226 100644 --- a/contrib/bind/bin/named-xfer/named-xfer.c +++ b/contrib/bind/bin/named-xfer/named-xfer.c @@ -1,3252 +1,3255 @@ /* * The original version of named-xfer by Kevin Dunlap. * Completed and integrated with named by David Waitzman * (dwaitzman@bbn.com) 3/14/88. * Modified by M. Karels and O. Kure 10-88. * Modified extensively since then by just about everybody. */ /* * Copyright (c) 1988, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1998 by MetaInfo, Incorporated. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of MetaInfo Incorporated not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND METAINFO INCORPORATED DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL METAINFO INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #if !defined(lint) && !defined(SABER) char copyright[] = "@(#) Copyright (c) 1988, 1990 The Regents of the University of California.\n\ portions Copyright (c) 1993 Digital Equipment Corporation\n\ portions Copyright (c) 1998 MetaInfo, Inc.\n\ portions Copyright (c) 1995, 1996 Internet Software Consorium\n\ All rights reserved.\n"; #endif /* not lint */ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)named-xfer.c 4.18 (Berkeley) 3/7/91"; -static const char rcsid[] = "$Id: named-xfer.c,v 8.121 2002/06/26 03:27:22 marka Exp $"; +static const char rcsid[] = "$Id: named-xfer.c,v 8.122.8.2 2003/06/02 05:59:56 marka Exp $"; #endif /* not lint */ #include "port_before.h" #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 "port_after.h" #ifndef PATH_SEP #define PATH_SEP '/' #endif #define MAIN_PROGRAM #include "../named/named.h" #undef MAIN_PROGRAM #define MAX_XFER_RESTARTS 2 #define ENABLE_IXFR 1 # ifdef SHORT_FNAMES extern long pathconf __P((const char *path, int name)); /* XXX */ # endif static struct zoneinfo zone; /* zone information */ static char *ddtfilename = NULL, *ddtfile = NULL; static char *tmpname = NULL, *tmpiname = NULL, /* temporary file name for ixfr transaction file */ *domain; /* domain being xfered */ static int quiet = 0, read_interrupted = 0, curclass, domain_len; /* strlen(domain) */ static FILE *fp = NULL, *dbfp = NULL, *ixfp = NULL; static char *ProgName; static void usage(const char *); static int tsig_init(const char *); static int getzone(struct zoneinfo *, u_int32_t, int), print_output(struct zoneinfo *, u_int32_t, u_char *, int, u_char *, int), netread(int, char *, int, int), writemsg(int, const u_char *, int); static int ixfr_log(const u_char *msg, int len, int *delete, FILE *file, struct sockaddr_in *sin, u_int32_t *serial_no, int *); static SIG_FN read_alarm(void); static SIG_FN term_handler(void); static const char *soa_zinfo(struct zoneinfo *, u_char *, u_char*), *tsig_rcode(int); struct zoneinfo zp_start, zp_finish; static int restarts = 0; static int check_serial = 0; static int xfr_qtype = T_AXFR; FILE *ddt = NULL; int servermethode[NSMAX]; char *soa_buf; typedef struct _tsig_node { struct in_addr addr; DST_KEY *dst_key; LINK(struct _tsig_node) link; } tsig_node; LIST(tsig_node) tsig_list; /* * Debugging printf. */ -void -dprintf(int level, const char *format, ...) { +static void lprintf(int level, const char *format, ...) ISC_FORMAT_PRINTF(2, 3); +static void +lprintf(int level, const char *format, ...) { va_list ap; va_start(ap, format); if (ddt != NULL && debug >= level) (void) vfprintf(ddt, format, ap); va_end(ap); } static int init_xfer_logging() { log_channel chan; if (log_new_context(ns_log_max_category, NULL, &log_ctx) < 0) { perror("log_new_context"); return (0); } log_option(log_ctx, LOG_OPTION_DEBUG, debug); log_option(log_ctx, LOG_OPTION_LEVEL, debug); log_ctx_valid = 1; chan = log_new_syslog_channel(0, 0, ISC_FACILITY); if (chan == NULL) return (0); if (log_add_channel(log_ctx, ns_log_default, chan) < 0) { perror("log_add_channel syslog"); return (0); } if (debug) { unsigned int flags = LOG_USE_CONTEXT_LEVEL|LOG_REQUIRE_DEBUG; chan = log_new_file_channel(flags, 0, NULL, ddt, 0, ULONG_MAX); if (chan == NULL) return (0); if (log_add_channel(log_ctx, ns_log_default, chan) < 0) { perror("log_add_channel debug"); return (0); } } return (1); } static void cleanup_for_exit(void) { #ifdef DEBUG if (!debug) #endif { (void) unlink(tmpname); if (tmpiname != NULL) (void) unlink(tmpiname); } if(tmpiname) free(tmpiname); tmpiname = NULL; if (ddtfilename != NULL) { free(ddtfilename); if (ddtfilename == ddtfile) ddtfile = NULL; ddtfilename = NULL; } if(tmpname) free(tmpname); tmpname = NULL; if(ddtfile) free(ddtfile); ddtfile = NULL; } int main(int argc, char *argv[]) { struct zoneinfo *zp; struct hostent *hp; struct in_addr axfr_src; char *dbfile = NULL, *tracefile = NULL, *tm = NULL, *tsigfile = NULL; char *ixfrfile = NULL; int dbfd, ddtd, result, c, ixfd = -1; u_int32_t serial_no = 0; u_int port = htons(NAMESERVER_PORT); struct stat statbuf; int stub_only = 0; int class = C_IN; int n; #ifdef _AUX_SOURCE set42sig(); #endif memset(&axfr_src, 0, sizeof axfr_src); ProgName = strrchr(argv[0], PATH_SEP); if (ProgName != NULL) ProgName++; else ProgName = argv[0]; (void) umask(022); ddtfilename = (char *)malloc(strlen(_PATH_TMPXFER) + 1); strcpy(ddtfilename, _PATH_TMPXFER); ddtfile = ddtfilename; #ifdef RENICE nice(-40); /* this is the recommended procedure to */ nice(20); /* reset the priority of the current process */ nice(0); /* to "normal" (== 0) - see nice(3) */ #endif n = LOG_PID; #ifdef LOG_PERROR n |= LOG_PERROR; #endif #if defined(LOG_CONS) && defined(USE_LOG_CONS) n |= LOG_CONS; #endif #ifdef SYSLOG_42BSD openlog(ProgName, n); #else openlog(ProgName, n, ISC_FACILITY); #endif while ((c = getopt(argc, argv, "C:d:l:s:t:z:f:i:p:P:qx:ST:Z")) != -1) switch (c) { case 'C': class = get_class(optarg); break; case 'd': #ifdef DEBUG debug = atoi(optarg); #endif break; case 'l': ddtfile = (char *)malloc(strlen(optarg) + sizeof(".XXXXXX") + 1); if (!ddtfile) panic("malloc(ddtfile)", NULL); #ifdef SHORT_FNAMES filenamecpy(ddtfile, optarg); #else (void) strcpy(ddtfile, optarg); #endif /* SHORT_FNAMES */ (void) strcat(ddtfile, ".XXXXXX"); break; case 's': serial_no = strtoul(optarg, (char **)NULL, 10); check_serial++; break; case 't': tracefile = optarg; break; case 'z': /* zone == domain */ domain = optarg; domain_len = strlen(domain); while ((domain_len > 0) && (domain[domain_len-1] == '.')) domain[--domain_len] = '\0'; break; case 'f': dbfile = optarg; tmpname = (char *)malloc((unsigned)strlen(optarg) + sizeof(".XXXXXX") + 1); if (!tmpname) panic("malloc(tmpname)", NULL); #ifdef SHORT_FNAMES filenamecpy(tmpname, optarg); #else (void) strcpy(tmpname, optarg); #endif /* SHORT_FNAMES */ break; case 'i': #if ENABLE_IXFR ixfrfile = optarg; tmpiname = (char *) malloc(strlen(optarg) + sizeof(".XXXXXX") + 1); if (!tmpiname) panic("malloc(tmpiname)", NULL); #ifdef SHORT_FNAMES filenamecpy(tmpiname, optarg); #else (void) strcpy(tmpiname, optarg); #endif /* SHORT_FNAMES */ #endif /* ENABLE_IXFR */ break; case 'p': port = htons((u_int16_t)atoi(optarg)); break; case 'P': port = (u_int16_t)atoi(optarg); break; case 'S': stub_only = 1; break; case 'q': quiet++; break; case 'x': if (!inet_aton(optarg, &axfr_src)) panic("bad -x addr: %s", optarg); break; case 'T': tsigfile = optarg; break; case 'Z': xfr_qtype = ns_t_zxfr; break; case '?': default: usage("unrecognized argument"); /* NOTREACHED */ } if (!domain || ((!dbfile) && (!ixfrfile)) || optind >= argc) { if (!domain) usage("no domain"); if (!dbfile) usage("no dbfile"); if (optind >= argc) usage("not enough arguments"); /* NOTREACHED */ } if (stat(dbfile, &statbuf) != -1 && !S_ISREG(statbuf.st_mode) && !S_ISFIFO(statbuf.st_mode)) usage("dbfile must be a regular file or FIFO"); if (ixfrfile && (stat(ixfrfile, &statbuf) != -1 && !S_ISREG(statbuf.st_mode) && !S_ISFIFO(statbuf.st_mode))) usage("ixfrfile must be a regular file or FIFO"); if (tsigfile && stat(tsigfile, &statbuf) != -1 && !S_ISREG(statbuf.st_mode) && !S_ISFIFO(statbuf.st_mode)) usage("tsigfile must be a regular file or FIFO"); if (tracefile && (fp = fopen(tracefile, "w")) == NULL) perror(tracefile); (void) strcat(tmpname, ".XXXXXX"); /* tmpname is now something like "/etc/named/named.bu.db.XXXXXX" */ if ((dbfd = mkstemp(tmpname)) == -1) { perror(tmpname); if (!quiet) syslog(LOG_ERR, "can't make tmpfile (%s): %s\n", tmpname, strerror(errno)); exit(XFER_FAIL); } #ifdef HAVE_FCHMOD /* XXX */ if (fchmod(dbfd, 0644) == -1) #else if (chmod(tmpname, 0644) == -1) #endif { perror(tmpname); if (!quiet) syslog(LOG_ERR, "can't [f]chmod tmpfile (%s): %s\n", tmpname, strerror(errno)); close(dbfd); unlink(tmpname); exit(XFER_FAIL); } if ((dbfp = fdopen(dbfd, "r+")) == NULL) { perror(tmpname); if (!quiet) syslog(LOG_ERR, "can't fdopen tmpfile (%s)", tmpname); close(dbfd); unlink(tmpname); exit(XFER_FAIL); } if (ixfrfile) { (void) strcat(tmpiname, ".XXXXXX"); if ((ixfd = mkstemp(tmpiname)) == -1) { perror(tmpiname); if (!quiet) syslog(LOG_ERR, "can't make tmpifile (%s): %s\n", tmpiname, strerror(errno)); (void) fclose(dbfp); (void) close(dbfd); exit(XFER_FAIL); } #ifdef HAVE_FCHMOD /* XXX */ if (fchmod(ixfd, 0644) == -1) #else if (chmod(tmpiname, 0644) == -1) #endif { perror(tmpiname); if (!quiet) syslog(LOG_ERR, "can't [f]chmod tmpifile (%s): %s\n", tmpiname, strerror(errno)); (void) fclose(dbfp); (void) close(dbfd); (void) close(ixfd); exit(XFER_FAIL); } close(ixfd); } #ifdef DEBUG if (debug) { /* ddtfile is now something like "/usr/tmp/xfer.ddt.XXXXXX" */ if ((ddtd = mkstemp(ddtfile)) == -1) { perror(ddtfile); debug = 0; } #ifdef HAVE_FCHMOD else if (fchmod(ddtd, 0644) == -1) #else else if (chmod(ddtfile, 0644) == -1) #endif { perror(ddtfile); close(ddtd); unlink(ddtfile); debug = 0; } else if ((ddt = fdopen(ddtd, "w")) == NULL) { perror(ddtfile); close(ddtd); unlink(ddtfile); debug = 0; } else setvbuf(ddt, NULL, _IOLBF, 0); } #endif if (!init_xfer_logging()) { cleanup_for_exit(); perror("init_xfer_logging"); } /* * Ignore many types of signals that named (assumed to be our parent) * considers important- if not, the user controlling named with * signals usually kills us. */ (void) signal(SIGHUP, SIG_IGN); #ifdef SIGSYS (void) signal(SIGSYS, SIG_IGN); #endif #ifdef DEBUG if (debug == 0) #endif { (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); } (void) signal(SIGILL, SIG_IGN); #if defined(SIGUSR1) && defined(SIGUSR2) (void) signal(SIGUSR1, SIG_IGN); (void) signal(SIGUSR2, SIG_IGN); #else /* SIGUSR1&&SIGUSR2 */ (void) signal(SIGEMT, SIG_IGN); (void) signal(SIGFPE, SIG_IGN); #endif /* SIGUSR1&&SIGUSR2 */ if (dbfile) - dprintf(1, "domain `%s'; file `%s'; serial %u\n", + lprintf(1, "domain `%s'; file `%s'; serial %u\n", domain, dbfile, serial_no); if (ixfrfile) - dprintf(1, "domain `%s'; ixfrfile `%s'; serial %u\n", + lprintf(1, "domain `%s'; ixfrfile `%s'; serial %u\n", domain, ixfrfile, serial_no); if (tsigfile) - dprintf(1, "tsigfile `%s'\n", tsigfile); + lprintf(1, "tsigfile `%s'\n", tsigfile); buildservicelist(); buildprotolist(); if (tsig_init(tsigfile) == -1) { cleanup_for_exit(); return (XFER_FAIL); } /* init zone data */ zp = &zone; if (stub_only) zp->z_type = Z_STUB; else zp->z_type = Z_SECONDARY; zp->z_class = class; zp->z_origin = domain; zp->z_source = dbfile; zp->z_axfr_src = axfr_src; zp->z_addrcnt = 0; - dprintf(1, "zone found (%d): \"%s\", source = %s\n", + lprintf(1, "zone found (%d): \"%s\", source = %s\n", zp->z_type, (zp->z_origin[0] == '\0') ? "." : zp->z_origin, zp->z_source); for (; optind != argc; optind++) { int tmpsupportixfr; tm = argv[optind]; tmpsupportixfr = ISNOTIXFR; if ((optind+1) != argc) { if (strcasecmp("ixfr", argv[optind+1]) == 0) { #if ENABLE_IXFR tmpsupportixfr = ISIXFR; servermethode[zp->z_addrcnt] = tmpsupportixfr; #endif optind++; } else if (strcasecmp("axfr", argv[optind+1]) == 0) { tmpsupportixfr = ISNOTIXFR; optind++; } } if (!inet_aton(tm, &zp->z_addr[zp->z_addrcnt])) { if (strcmp("-ixfr",tm)==0) { #if ENABLE_IXFR tmpsupportixfr = ISIXFR; servermethode[zp->z_addrcnt-1] = tmpsupportixfr; #endif continue; } else if (strcmp("-axfr",tm)==0) { tmpsupportixfr = ISNOTIXFR; continue; } hp = gethostbyname(tm); if (hp == NULL) { syslog(LOG_NOTICE, "uninterpretable server (%s) for %s\n", tm, zp->z_origin); continue; } memcpy(&zp->z_addr[zp->z_addrcnt], hp->h_addr, INADDRSZ); - dprintf(1, "Arg: \"%s\" %s\n", tm,((tmpsupportixfr) ? "IXFR":"AXFR")); + lprintf(1, "Arg: \"%s\" %s\n", tm,((tmpsupportixfr) ? "IXFR":"AXFR")); } if (++zp->z_addrcnt >= NSMAX) { zp->z_addrcnt = NSMAX; - dprintf(1, "NSMAX reached\n"); + lprintf(1, "NSMAX reached\n"); break; } } - dprintf(1, "addrcnt = %d\n", zp->z_addrcnt); + lprintf(1, "addrcnt = %d\n", zp->z_addrcnt); res_ninit(&res); res.options &= ~(RES_DEFNAMES | RES_DNSRCH | RES_RECURSE); result = getzone(zp, serial_no, port); (void) fclose(dbfp); (void) close(dbfd); if (ixfp) (void) my_fclose(ixfp); else close(ixfd); switch (result) { case XFER_SUCCESSAXFR: /* ok exit */ if (tmpiname != NULL) unlink(tmpiname); if (ixfrfile) { /* * An IXFR was requested but we performed an * AXFR. Rename the temporary file to the IXFR * name, named will rename it again to the dbname. */ if (isc_movefile(tmpname, ixfrfile) == -1) { perror("isc_movefile"); #ifdef DEBUG if (debug) (void) unlink(ddtfile); #endif if (!quiet) syslog(LOG_ERR, "rename %s to %s: %s", tmpname, ixfrfile, strerror(errno)); cleanup_for_exit(); exit(XFER_FAIL); }; exit(XFER_SUCCESSAXFRIXFRFILE); } if (isc_movefile(tmpname, dbfile) == -1) { perror("isc_movefile"); if (!quiet) syslog(LOG_ERR, "isc_movefile %s to %s: %m", tmpname, dbfile); cleanup_for_exit(); exit(XFER_FAIL); } exit(XFER_SUCCESSAXFR); case XFER_SUCCESSIXFR: unlink(tmpname); if (isc_movefile(tmpiname, ixfrfile) == -1) { perror("isc_movefile"); if (!quiet) syslog(LOG_ERR, "isc_movefile %s to %s: %m", tmpiname, ixfrfile); cleanup_for_exit(); exit(XFER_FAIL); } cleanup_for_exit(); exit(XFER_SUCCESSIXFR); case XFER_UPTODATE: /* the zone was already uptodate */ (void) unlink(tmpname); if (tmpiname != NULL) (void) unlink(tmpiname); cleanup_for_exit(); exit(XFER_UPTODATE); default: result = XFER_FAIL; /* fall through */ case XFER_REFUSED: case XFER_TIMEOUT: case XFER_FAIL: (void) unlink(tmpname); cleanup_for_exit(); exit(result); /* error or timeout */ } /*NOTREACHED*/ return (0); /* Make gcc happy. */ } static const char *UsageText[] = { "\t-z zone_to_transfer\n", "\t-f db_file\n", "\t[-i ixfr_file]\n", "\t[-s serial_no]\n", "\t[-d debug_level]\n", "\t[-l debug_log_file]\n", "\t[-t trace_file]\n", "\t[-p port]\n", "\t[-S] [-Z]\n", "\t[-C class]\n", "\t[-x axfr-src]\n", "\t[-T tsig_info_file]\n", "\tservers [-ixfr|-axfr]...\n", NULL }; static void usage(const char *msg) { const char **line; fprintf(stderr, "Usage error: %s\n", msg); fprintf(stderr, "Usage: %s\n", ProgName); for (line = UsageText; *line; line++) fputs(*line, stderr); exit(XFER_FAIL); } static int tsig_init(const char *file) { char buf[1024]; FILE *fp; char *s; if (file == NULL) return (0); fp = fopen(file, "r"); if (fp == NULL) return (-1); dst_init(); INIT_LIST(tsig_list); while (1) { tsig_node *n = malloc(sizeof(tsig_node)); int alg, secret_len; char *cp; u_char secret[128]; char *name; if (n == NULL) return (-1); s = fgets(buf, sizeof(buf), fp); if (s == NULL) break; buf[strlen(buf)-1] = 0; inet_aton(buf, &n->addr); fgets(buf, sizeof(buf), fp); buf[strlen(buf)-1] = 0; name = strdup(buf); if (name == NULL) return (-1); fscanf(fp, "%d", &alg); fgets(buf, sizeof(buf), fp); fgets(buf, sizeof(buf), fp); buf[strlen(buf)-1] = 0; cp = buf; while (isspace(*cp)) cp++; secret_len = b64_pton(cp, secret, sizeof(secret)); n->dst_key = dst_buffer_to_key(name, alg, 0, 0, secret, secret_len); free(name); INIT_LINK(n, link); APPEND(tsig_list, n, link); } fclose(fp); unlink(file); return (0); } #define DEF_DNAME '\001' /* '\0' means the root domain */ /* XXX: The following variables should probably all be "static" */ u_int32_t minimum_ttl = 0; int soa_cnt = 0, scdsoa = 0, methode = ISNOTIXFR; int delete_soa = 1; int ixfr_single_answer_mode = 0; u_int32_t final_serial = 0; int ixfr_soa = 0; int ns_cnt = 0; int query_type = 0; int prev_comment = 0; /* was previous record a comment? */ char zone_top[MAXDNAME]; /* the top of the zone */ char prev_origin[MAXDNAME]; /* from most recent $ORIGIN line */ char prev_dname[MAXDNAME] = { DEF_DNAME }; /* from previous record */ char prev_ns_dname[MAXDNAME] = { DEF_DNAME }; /* from most recent NS record */ /* * TSIG state */ static int tsig_signed; static ns_tcp_tsig_state tsig_state; static int make_query(int fd, struct zoneinfo *zp, int type, u_int32_t serial_no, DST_KEY *tsig_key, u_char *buf, u_int bufsize) { HEADER *hp; u_char *cp; int n, ret; time_t timesigned = 0; n = res_nmkquery(&res, QUERY, zp->z_origin, curclass, type, NULL, 0, NULL, buf, bufsize); if (n < 0) { if (!quiet) syslog(LOG_INFO, "zone %s: res_nmkquery %s failed", p_type(query_type), zp->z_origin); return (n); } if (type == T_IXFR) { hp = (HEADER *) buf; cp = buf; - dprintf(1, "len = %d\n", n); + lprintf(1, "len = %d\n", n); hp->nscount = htons(1+ntohs(hp->nscount)); cp += n; n = dn_comp(zp->z_origin, cp, bufsize - (cp - buf), NULL, NULL); if (n < 0) return (n); cp += n; if (cp + 3 * INT16SZ + 6 * INT32SZ + 2 > buf + bufsize) return (-1); PUTSHORT(T_SOA, cp); /* type */ PUTSHORT(C_IN, cp); /* class */ PUTLONG(0, cp); /* ttl */ PUTSHORT(22, cp); /* dlen */ *cp++ = 0; /* mname */ *cp++ = 0; /* rname */ PUTLONG(serial_no, cp); PUTLONG(0xDEAD, cp); /* Refresh */ PUTLONG(0xBEEF, cp); /* Retry */ PUTLONG(0xABCD, cp); /* Expire */ PUTLONG(0x1776, cp); /* Min TTL */ n = cp - buf; - dprintf(1, "len = %d\n", cp-buf); + lprintf(1, "len = %d\n", cp-buf); } tsig_signed = 0; if (tsig_key != NULL) { int siglen; u_char sig[64]; siglen = sizeof(sig); ret = ns_sign(buf, &n, bufsize, NOERROR, tsig_key, NULL, 0, sig, &siglen, timesigned); if (ret == 0) { tsig_signed = 1; ns_verify_tcp_init(tsig_key, sig, siglen, &tsig_state); } } if (debug) res_pquery(&res, buf, n, ddt); if (writemsg(fd, buf, n) < 0) { syslog(LOG_INFO, "writemsg: %m"); return (-1); } return (n); } static u_int readandverify(int fd, u_char **bufp, u_int *bufsizep, struct sockaddr_in *sin, char *z_origin, int sig_req) { u_char *buf = *bufp; u_char *newbuf; u_int bufsize = *bufsizep; u_int len; if (netread(fd, (char *)buf, INT16SZ, XFER_TIMER) < 0) return (0); if ((len = ns_get16(buf)) == 0) return (0); if (len > bufsize) { newbuf = realloc(buf, len); if (newbuf == NULL) { syslog(LOG_INFO, "realloc(%u) failed\n", len); return (0); } *bufp = buf = newbuf; *bufsizep = bufsize = len; } if (netread(fd, (char *)buf, len, XFER_TIMER) < 0) return (0); #ifdef DEBUG if (debug >= 3) { (void)fprintf(ddt,"len = %d\n", len); res_pquery(&res, buf, len, ddt); } if (fp) res_pquery(&res, buf, len, fp); #endif if (tsig_signed) { int ret; ret = ns_verify_tcp(buf, (int *)&len, &tsig_state, sig_req); if (ret != 0) { syslog(LOG_NOTICE, "%s [%s] %s %s: %s (%d)\n", "TSIG verification from server", inet_ntoa(sin->sin_addr), "zone", z_origin, tsig_rcode(ret), ret); return (0); } } return (len); } static void print_comment(int s, struct sockaddr_in *sin, int check_serial, u_int32_t serial_no, DST_KEY *tsig_key) { struct sockaddr_in local; ISC_SOCKLEN_T locallen; const char *l, *nl; gettime(&tt); locallen = sizeof local; if (getsockname(s, (struct sockaddr *)&local, &locallen) < 0) memset(&local, 0, sizeof local); for (l = Version; l; l = nl) { size_t len; if ((nl = strchr(l, '\n')) != NULL) { len = nl - l; nl = nl + 1; } else { len = strlen(l); nl = NULL; } while (isspace((unsigned char) *l)) l++; if (*l) fprintf(dbfp, "; BIND version %.*s\n", (int)len, l); } fprintf(dbfp, check_serial ? "; zone '%s' last serial %u\n" : "; zone '%s' first transfer\n", domain, serial_no); fprintf(dbfp, "; from %s:%d", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); fprintf(dbfp, " (local %s) using %s at %s", inet_ntoa(local.sin_addr), (methode == ISIXFR) ? "IXFR":"AXFR", ctimel(tt.tv_sec)); if (tsig_signed != 0) fprintf(dbfp, "; TSIG verified: key %s.\n", tsig_key->dk_key_name); else fprintf(dbfp, "; NOT TSIG verified\n"); } static int getzone(struct zoneinfo *zp, u_int32_t serial_no, int port) { HEADER *hp; u_int len; int s, n, l, error = 0; int was_ixfr = 0; u_int cnt; u_char *cp, *nmp, *eom, *tmp ; u_char *buf = NULL; u_char *bp; u_int bufsize = 0; u_char *buf2 = NULL; u_int buf2size = 0; char name2[MAXDNAME]; struct sockaddr_in sin; #ifdef POSIX_SIGNALS struct sigaction sv, osv; #else struct sigvec sv, osv; #endif int qdcount, ancount, aucount, arcount, class = 0, type = 0; const char *badsoa_msg = "Nil"; struct sockaddr_in my_addr; char my_addr_text[30]; ISC_SOCKLEN_T alen; int tsig_req; DST_KEY *tsig_key; int ixfr_first = 1; int loop_cnt = 0; u_int32_t query_serial = serial_no; int first_soa_printed; struct in_addr z_axfr_src; int refused = 0; #ifdef DEBUG if (debug) { (void)fprintf(ddt,"getzone() %s ", zp->z_origin); switch (zp->z_type) { case Z_STUB: fprintf(ddt, "stub\n"); break; case Z_SECONDARY: fprintf(ddt, "slave\n"); break; default: fprintf(ddt, "unknown type\n"); } } #endif #ifdef POSIX_SIGNALS memset(&sv, 0, sizeof sv); sv.sa_handler = (SIG_FN (*)()) read_alarm; /* SA_ONSTACK isn't recommended for strict POSIX code */ /* is it absolutely necessary? */ /* sv.sa_flags = SA_ONSTACK; */ sigfillset(&sv.sa_mask); (void) sigaction(SIGALRM, &sv, &osv); memset(&sv, 0, sizeof sv); sv.sa_handler = (SIG_FN (*)()) term_handler; sigfillset(&sv.sa_mask); (void) sigaction(SIGTERM, &sv, &osv); #else memset(&sv, 0, sizeof sv); sv.sv_handler = read_alarm; sv.sv_mask = ~0; (void) sigvec(SIGALRM, &sv, &osv); memset(&sv, 0, sizeof sv); sv.sv_handler = term_handler; sv.sv_mask = ~0; (void) sigvec(SIGTERM, &sv, &osv); #endif strcpy(zone_top, zp->z_origin); if ((l = strlen(zone_top)) != 0 && zone_top[l - 1] == '.') zone_top[l - 1] = '\0'; strcpy(prev_origin, zone_top); for (cnt = 0; cnt < zp->z_addrcnt; cnt++) { methode = servermethode[cnt]; sin.sin_addr = zp->z_addr[cnt]; - dprintf(3, "address [%s] %s\n", + lprintf(3, "address [%s] %s\n", inet_ntoa(sin.sin_addr), (methode == ISIXFR) ? "IXFR":"AXFR"); } for (cnt = 0; cnt < zp->z_addrcnt; cnt++) { methode = ISNOTIXFR; curclass = zp->z_class; /* * If we have been given a serial number and a ixfr log * file name then set methode. */ if (check_serial && tmpiname != NULL) methode = servermethode[cnt]; error = 0; tsig_signed = 0; z_axfr_src = zp->z_axfr_src; if (buf == NULL) { if ((buf = (u_char *)malloc(2 * PACKETSZ)) == NULL) { syslog(LOG_INFO, "malloc(%u) failed", 2 * PACKETSZ); error++; break; } bufsize = 2 * PACKETSZ; } tsig_key = tsig_key_from_addr(sin.sin_addr); try_again: first_soa_printed = 0; if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) { syslog(LOG_INFO, "socket: %m"); error++; break; } if (z_axfr_src.s_addr != 0) { memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_port = 0; /* "ANY" */ sin.sin_addr = z_axfr_src; - dprintf(2, "binding to address [%s]\n", + lprintf(2, "binding to address [%s]\n", inet_ntoa(sin.sin_addr)); if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) syslog(LOG_INFO, "warning: bind(%s) failed", inet_ntoa(zp->z_axfr_src)); } memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_port = port; sin.sin_addr = zp->z_addr[cnt]; - dprintf(2, "connecting to server #%d [%s].%d\n", + lprintf(2, "connecting to server #%d [%s].%d\n", cnt + 1, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { if (z_axfr_src.s_addr != 0) { - dprintf(2, "connect failed, trying w/o -x"); + lprintf(2, "connect failed, trying w/o -x"); z_axfr_src.s_addr = 0; (void) my_close(s); goto try_again; } if (!quiet) syslog(LOG_INFO, "connect(%s) for zone %s failed: %s", inet_ntoa(sin.sin_addr), zp->z_origin, strerror(errno)); error++; (void) my_close(s); continue; } query_type = (methode == ISIXFR && was_ixfr == 0) ? T_IXFR : T_SOA; n = make_query(s, zp, query_type, serial_no, tsig_key, buf, bufsize); if (n < 0) { (void) my_close(s); #ifdef POSIX_SIGNALS (void) sigaction(SIGALRM, &osv, (struct sigaction *)0); #else (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); #endif return (XFER_FAIL); } /* * Get out your butterfly net and catch the answer. */ len = readandverify(s, &buf, &bufsize, &sin, zp->z_origin, 1); if (len == 0) { my_close(s); error++; continue; } if (query_type == T_IXFR && ixfp == NULL) { delete_soa = 1; ixfr_soa = 0; if ((ixfp = fopen(tmpiname, "w+")) == NULL) { perror(tmpiname); if (!quiet) syslog(LOG_ERR, "can't fopen ixfr log (%s)", tmpname); exit(XFER_FAIL); } } hp = (HEADER *) buf; qdcount = ntohs(hp->qdcount); ancount = ntohs(hp->ancount); aucount = ntohs(hp->nscount); arcount = ntohs(hp->arcount); /* * close socket if any of these apply: * 1) rcode != NOERROR * 2) not an authority response * 3) not an answer to our question * 4) both the number of answers and authority count < 1) */ if (hp->rcode != NOERROR || (query_type == T_SOA && (!hp->aa || qdcount != 1)) || (ancount < 1 && aucount < 1)) { #ifndef SYSLOG_42BSD syslog(LOG_NOTICE, "[%s] %s for %s, %s query got rcode %d, aa %d, ancount %d, aucount %d", inet_ntoa(sin.sin_addr), (hp->aa ? (qdcount==1 ?"no SOA found" :"bad response") : "not authoritative"), zp->z_origin[0] != '\0' ? zp->z_origin : ".", p_type(query_type), hp->rcode, hp->aa, ancount, aucount); #endif error++; (void) my_close(s); continue; } zp_start = *zp; if ((int)len < HFIXEDSZ + QFIXEDSZ) { badsoa_msg = "too short"; badsoa: syslog(LOG_INFO, "malformed %s from [%s], zone %s: %s", p_type(query_type), inet_ntoa(sin.sin_addr), zp->z_origin, badsoa_msg); error++; (void) my_close(s); continue; } /* * Step through response. */ tmp = buf + HFIXEDSZ; eom = buf + len; /* Query Section. */ if (qdcount > 1) { badsoa_msg = "question error"; goto badsoa; } if (qdcount < 1) goto no_question; n = dn_expand(buf, eom, tmp, name2, sizeof name2); if (n < 0) { badsoa_msg = "qname error"; goto badsoa; } tmp += n; if (tmp + 2 * INT16SZ > eom) { badsoa_msg = "query error"; goto badsoa; } NS_GET16(type, tmp); NS_GET16(class, tmp); if (class != curclass || ((type != T_SOA) && (type != T_IXFR) && (type != T_AXFR)) || ns_samename(zp->z_origin, name2) != 1) { syslog(LOG_INFO, "wrong query in resp from [%s], zone %s: [%s %s %s]\n", inet_ntoa(sin.sin_addr), zp->z_origin, name2, p_class(class), p_type(type)); error++; (void) my_close(s); continue; } no_question: /* ... Answer Section. * We may have to loop a little, to bypass SIG SOA's in * the response. */ loop_cnt = 0; bp = NULL; do { u_char *cp4, *ocp = tmp; u_short type, class, dlen, olen = len; u_int32_t ttl; n = dn_expand(buf, eom, tmp, name2, sizeof name2); if (n < 0) { badsoa_msg = "aname error"; goto badsoa; } tmp += n; if (loop_cnt == 0) bp = tmp; /* Are type, class, and ttl OK? */ cp4 = tmp; /* Leave tmp pointing to type field */ if (eom - cp4 < 3 * INT16SZ + INT32SZ) { badsoa_msg = "zinfo too short"; goto badsoa; } NS_GET16(type, cp4); NS_GET16(class, cp4); NS_GET32(ttl, cp4); NS_GET16(dlen, cp4); if (cp4 + dlen > eom) { badsoa_msg = "zinfo dlen too big"; goto badsoa; } if (type == T_SOA) { if (was_ixfr) { methode = ISNOTIXFR; break; } if ((methode == ISIXFR) && (loop_cnt == 0)) { soa_cnt++; badsoa_msg = soa_zinfo(&zp_finish, tmp, eom); if (badsoa_msg) goto badsoa; if (ixfp && ixfr_log(buf, len, &delete_soa, ixfp, &sin, &serial_no, &ixfr_first) < 0) { error++; break; } } else { if (methode == ISIXFR) { check_serial = 0; soa_cnt++; break; } break; } } if ((loop_cnt >= 1) && (soa_cnt < 2)) { - dprintf(1, + lprintf(1, "server %s %d rejected IXFR and responded with AXFR\n", inet_ntoa(sin.sin_addr), soa_cnt); methode = ISNOTIXFR; check_serial = 0; was_ixfr++; tmp = bp; break; } /* Skip to next record, if any. */ - dprintf(1, "skipping %s %s RR in response\n", + lprintf(1, "skipping %s %s RR in response\n", name2, p_type(type)); tmp = cp4 + dlen; loop_cnt++; if (loop_cnt == 1) { badsoa_msg = soa_zinfo(&zp_start, bp, eom); if (badsoa_msg) goto badsoa; if (check_serial && !SEQ_GT(zp_start.z_serial, query_serial)) { (void) my_close(s); - dprintf(1, + lprintf(1, "zone up-to-date, serial %u\n", zp_start.z_serial); if (ixfp) { (void) fclose(ixfp); (void) unlink (tmpiname); ixfp = NULL; } return (XFER_UPTODATE); } } if (ancount == 1 && loop_cnt == 1) { if (buf2 == NULL) { if ((buf2 = (u_char *)malloc(2 * PACKETSZ)) == NULL) { syslog(LOG_INFO, "malloc(%u) failed", 2 * PACKETSZ); error++; break; } buf2size = 2 * PACKETSZ; } len = readandverify(s, &buf2, &buf2size, &sin, zp->z_origin, 0); if (len == 0) { error++; tmp = bp; check_serial = 0; break; } hp = (HEADER *) buf2; qdcount = ntohs(hp->qdcount); ancount = ntohs(hp->ancount); aucount = ntohs(hp->nscount); arcount = ntohs(hp->arcount); tmp = buf2 + HFIXEDSZ; eom = buf2 + len; /* Query Section. */ if (qdcount > 1) { badsoa_msg = "question error"; goto badsoa; } else if (qdcount == 1) { n = dn_skipname(tmp, eom); if (n < 0) { badsoa_msg = "qname error"; goto badsoa; } tmp += n; if (tmp + 2 * INT16SZ > eom) { badsoa_msg = "query error"; goto badsoa; } tmp += 2 * INT16SZ; } /* answer section */ if (ancount < 1) { badsoa_msg = "empty answer"; goto badsoa; } n = dn_expand(buf2, eom, tmp, name2, sizeof name2); if (n < 0) { badsoa_msg = "qname error"; goto badsoa; } tmp += n; bp = tmp; if (tmp + 2 * INT16SZ > eom) { badsoa_msg = "query error"; goto badsoa; } NS_GET16(type, tmp); NS_GET16(class, tmp); if (class != curclass || ns_samename(zp->z_origin, name2) != 1) { syslog(LOG_INFO, "wrong query in resp from [%s], zone %s: [%s %s %s]\n", inet_ntoa(sin.sin_addr), zp->z_origin, name2, p_class(class), p_type(type)); error++; tmp = bp; check_serial = 0; break; } if (type == T_SOA) { ixfr_single_answer_mode = 1; if (ixfp && ixfr_log(buf2, len, &delete_soa, ixfp, &sin, &serial_no, &ixfr_first) < 0) { error++; break; } free(buf); buf = buf2; bufsize = buf2size; buf2 = NULL; break; } else { methode = ISNOTIXFR; was_ixfr++; check_serial = 0; cp = buf + HFIXEDSZ; n = print_output(zp, serial_no, buf, olen, ocp, 3); first_soa_printed = 1; free(buf); buf = buf2; buf2 = NULL; bufsize = buf2size; break; } } if (loop_cnt > 1) { tmp = bp; check_serial = 0; break; } } while (1); if (error != 0) { (void) my_close(s); continue; } if (ns_samename(zp->z_origin, name2) != 1) { syslog(LOG_INFO, "wrong answer in resp from [%s], zone %s: [%s %s %s]\n", inet_ntoa(sin.sin_addr), zp->z_origin, name2, p_class(class), p_type(type)); error++; (void) my_close(s); continue; } if (loop_cnt < 1) { badsoa_msg = soa_zinfo(&zp_start, tmp, eom); if (badsoa_msg) goto badsoa; } if (methode == ISNOTIXFR) { if (SEQ_GT(zp_start.z_serial, serial_no) || !check_serial) { if (soa_cnt) { if (!first_soa_printed) soa_cnt = 0; goto axfr_response; } - dprintf(1, "need update, serial %u\n", + lprintf(1, "need update, serial %u\n", zp_start.z_serial); soa_cnt = 0; hp = (HEADER *) buf; ns_cnt = 0; print_comment(s, &sin, check_serial, serial_no, tsig_key); for (;;) { if ((soa_cnt == 0) || (zp->z_type == Z_STUB)) { if (zp->z_type == Z_STUB) { if (soa_cnt == 1 && ns_cnt == 0) query_type = T_NS; else query_type = T_SOA; } else if (methode == ISIXFR) query_type = T_IXFR; else query_type = xfr_qtype; n = make_query(s, zp, query_type, serial_no, tsig_key, buf, bufsize); syslog(LOG_INFO, - "send %s query %d to %s", - p_type(query_type), - cnt, inet_ntoa(sin.sin_addr)); - dprintf(1, + "send %s query %d to %s for %s", + p_type(query_type), + cnt, inet_ntoa(sin.sin_addr), + (*zp->z_origin != '\0') ? + zp->z_origin : "."); + lprintf(1, "send %s query to %s\n", p_type(query_type), inet_ntoa(sin.sin_addr)); - dprintf(1,"bufsize = %d\n", bufsize); + lprintf(1,"bufsize = %d\n", bufsize); if (n < 0) { if (!quiet) { if (zp->z_type == Z_STUB) syslog(LOG_INFO, "zone %s: res_nmkquery %s failed", p_type(query_type), zp->z_origin); else syslog(LOG_INFO, "zone %s: res_nmkquery %s failed", zp->z_origin, p_type(query_type)); } (void) my_close(s); #ifdef POSIX_SIGNALS sigaction(SIGALRM, &osv, (struct sigaction *)0); #else sigvec(SIGALRM, &osv, (struct sigvec *)0); #endif return (XFER_FAIL); } } /*XXX ZXFR*/ receive: /* * Receive length & response */ tsig_req = (soa_cnt == 0); len = readandverify(s, &buf, &bufsize, &sin, zp->z_origin, tsig_req); if (len == 0) { error++; break; } hp = (HEADER *)buf; eom = buf + len; if (len < HFIXEDSZ) { badrec: error++; alen = sizeof my_addr; if (getsockname(s, (struct sockaddr *) &my_addr, &alen) < 0) sprintf(my_addr_text, "[errno %d]", errno); else sprintf(my_addr_text, "[%s].%u", inet_ntoa(my_addr. sin_addr), ntohs(my_addr.sin_port) ); if ((len >= HFIXEDSZ) && (hp->rcode == REFUSED)) { syslog(LOG_INFO, "[%s] transfer refused from [%s], zone %s\n", my_addr_text, inet_ntoa(sin.sin_addr), zp->z_origin); refused = 1; } else { syslog(LOG_INFO, "[%s] record too short from [%s], zone %s\n", my_addr_text, inet_ntoa(sin.sin_addr), zp->z_origin); } break; } axfr_response: if (query_type == T_IXFR) if (hp->rcode != NOERROR) { - dprintf(1, + lprintf(1, "server %s did not support IXFR\n", inet_ntoa(sin.sin_addr)); methode = ISNOTIXFR; continue; }; cp = buf + HFIXEDSZ; if (ntohs(hp->qdcount) == 1) { if ((query_type == T_IXFR) && (methode == ISIXFR)) { - dprintf(1, + lprintf(1, "server %s rejected IXFR and responded with AXFR\n", inet_ntoa(sin.sin_addr)); methode = ISNOTIXFR; } n = dn_skipname(cp, eom); if ((n == -1) || ((n + QFIXEDSZ) >= (eom - cp))) goto badrec; cp += n + QFIXEDSZ; } nmp = cp; if ((n = dn_skipname(cp, eom)) == -1) goto badrec; tmp = cp + n; if (zp->z_type == Z_STUB) { ancount = ntohs(hp->ancount); n = 0; for (cnt = 0; cnt < (u_int)ancount; cnt++) { n = print_output(zp, serial_no, buf, len, cp, 0); if (n < 0) break; cp += n; } /* * If we've processed the answer * section and didn't get any useful * answers, bail out. */ if (query_type == T_SOA && soa_cnt == 0) { syslog(LOG_ERR, "stubs: no SOA in answer"); error++; break; } if (query_type == T_NS && ns_cnt == 0) { syslog(LOG_ERR, "stubs: no NS in answer"); error++; break; } if (n >= 0 && hp->nscount) { ancount = ntohs(hp->nscount); for (cnt = 0; cnt < (u_int)ancount; cnt++) { n = print_output(zp, serial_no, buf, len, cp, 0); if (n < 0) break; cp += n; } } ancount = ntohs(hp->arcount); for (cnt = 0; n > 0 && cnt < (u_int)ancount; cnt++) { n = print_output(zp, serial_no, buf, len, cp, 0); cp += n; } if (n < 0) { syslog(LOG_INFO, "print_output: unparseable answer (%d), zone %s", hp->rcode, zp->z_origin); error++; break; } if (cp != eom) { syslog(LOG_INFO, "print_output: short answer (%d, %d), zone %s", cp - buf, eom - buf, zp->z_origin); error++; break; } } else { ancount = ntohs(hp->ancount); if (query_type == T_IXFR && methode == ISIXFR && ixfr_log(buf, len, &delete_soa, ixfp, &sin, &serial_no, &ixfr_first) < 0 ){ error++; break; } for (n = cnt = 0; cnt < (u_int)ancount; cnt++) { n = print_output(zp, serial_no, buf, len, cp, 0); if (n < 0) break; cp += n; } if (n < 0) { syslog(LOG_INFO, "print_output: unparseable answer (%d), zone %s", hp->rcode, zp->z_origin); error++; break; } if (cp != eom) { syslog(LOG_INFO, "print_output: short answer (%d, %d), zone %s", cp - buf, eom - buf, zp->z_origin); error++; break; } } if ((soa_cnt >= 2) && (methode == ISNOTIXFR)) break; if ((soa_cnt == -1) && (methode == ISIXFR)) break; } (void) my_close(s); if (error == 0) { #ifdef POSIX_SIGNALS (void) sigaction(SIGALRM, &osv, (struct sigaction *)0); #else (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); #endif if (ixfp) { (void) fclose(ixfp); ixfp = NULL; } return (XFER_SUCCESSAXFR); } if (ixfp) { (void) fclose(ixfp); ixfp = NULL; } - dprintf(2, "error receiving zone transfer\n"); + lprintf(2, "error receiving zone transfer\n"); } else if (zp_start.z_serial == serial_no) { (void) my_close(s); - dprintf(1, "zone up-to-date, serial %u\n", + lprintf(1, "zone up-to-date, serial %u\n", zp_start.z_serial); if (ixfp) { (void) fclose(ixfp); (void) unlink (tmpiname); ixfp = NULL; } return (XFER_UPTODATE); } else { (void) my_close(s); if (!quiet) syslog(LOG_NOTICE, "serial from [%s], zone %s: %u lower than current: %u\n", inet_ntoa(sin.sin_addr), zp->z_origin, zp_start.z_serial, serial_no); return (XFER_FAIL); } } else { if (zp_finish.z_serial == query_serial) { (void) my_close(s); - dprintf(1, "zone up-to-date, serial %u\n", + lprintf(1, "zone up-to-date, serial %u\n", zp_start.z_serial); if (ixfp) { (void) fclose(ixfp); (void) unlink (tmpiname); ixfp = NULL; } return (XFER_UPTODATE); } if (SEQ_GT(query_serial, zp_finish.z_serial)) { if (!quiet) syslog(LOG_NOTICE, "serial from [%s], zone %s: %u lower than current: %u\n", inet_ntoa(sin.sin_addr), zp->z_origin, zp_finish.z_serial, query_serial); - dprintf(1, + lprintf(1, "serial from [%s], zone %s: %u lower than current: %u\n", inet_ntoa(sin.sin_addr), zp->z_origin, zp_finish.z_serial, query_serial); if (ixfp) { (void) fclose(ixfp); (void) unlink (tmpiname); ixfp = NULL; } if (was_ixfr == 0) { was_ixfr++; n = make_query(s, zp, T_AXFR, serial_no, tsig_key, buf, bufsize); if (n < 0) { (void) my_close(s); #ifdef POSIX_SIGNALS (void) sigaction(SIGALRM, &osv, (struct sigaction *)0); #else (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); #endif return (XFER_FAIL); } methode = ISNOTIXFR; check_serial = 0; soa_cnt = 0; was_ixfr = 0; goto receive; } (void) my_close(s); return (XFER_FAIL); } if (soa_cnt > 2) { methode = ISNOTIXFR; check_serial = 0; soa_cnt = 0; goto axfr_response; } - dprintf(1, "We have an IXFR\n"); + lprintf(1, "We have an IXFR\n"); loop_cnt = 0; while (SEQ_GT(zp_finish.z_serial, serial_no)) { /* * Receive length & response */ tsig_req = (soa_cnt == 0); len = readandverify(s, &buf, &bufsize, &sin, zp->z_origin, 1); if (len == 0) { error++; break; } hp = (HEADER *)buf; eom = buf + len; if (len < HFIXEDSZ) { error++; alen = sizeof my_addr; if (getsockname(s, (struct sockaddr *) &my_addr, &alen) < 0) sprintf(my_addr_text, "[errno %d]", errno); else sprintf(my_addr_text, "[%s].%u", inet_ntoa(my_addr. sin_addr), ntohs(my_addr.sin_port)); if ((hp->rcode == REFUSED) && (len >= HFIXEDSZ)) { syslog(LOG_INFO, "[%s] transfer refused from [%s], zone %s\n", my_addr_text, inet_ntoa(sin.sin_addr), zp->z_origin); refused = 1; } else { syslog(LOG_INFO, "[%s] record too short from [%s], zone %s\n", my_addr_text, inet_ntoa(sin.sin_addr), zp->z_origin); } break; } if (ixfp && ixfr_log(buf, len, &delete_soa, ixfp, &sin, &serial_no, &ixfr_first) < 0) { error++; break; } } (void) my_close(s); if (!error) { fprintf(ixfp, "update:\t{add} "); if (soa_buf) fputs(soa_buf, ixfp); fprintf(ixfp, "[END_DELTA]\n"); return (XFER_SUCCESSIXFR); } } } #ifdef POSIX_SIGNALS (void) sigaction(SIGALRM, &osv, (struct sigaction *)0); #else (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); #endif if (ixfp) { (void) my_fclose(ixfp); (void) unlink (tmpiname); ixfp = NULL; } if (!error) return (XFER_TIMEOUT); if (refused) return (XFER_REFUSED); return (XFER_FAIL); } static SIG_FN term_handler() { cleanup_for_exit(); _exit(XFER_FAIL); /* not safe to call exit() from a signal handler */ } /* * Set flag saying to read was interrupted * used for a read timer */ static SIG_FN read_alarm() { read_interrupted = 1; } static int netread(int fd, char *buf, int len, int timeout) { static const char setitimerStr[] = "setitimer: %m"; struct itimerval ival, zeroival; struct sockaddr_in sa; int n; ISC_SOCKLEN_T salen; #if defined(NETREAD_BROKEN) int retries = 0; #endif memset(&zeroival, 0, sizeof zeroival); ival = zeroival; ival.it_value.tv_sec = timeout; while (len > 0) { #ifndef _WIN32 if (setitimer(ITIMER_REAL, &ival, NULL) < 0) { syslog(LOG_INFO, setitimerStr); return (-1); } #endif errno = 0; salen = sizeof sa; n = recvfrom(fd, buf, len, 0, (struct sockaddr *)&sa, &salen); if (n == 0 && errno == 0) { #if defined(NETREAD_BROKEN) if (++retries < 42) /* doug adams */ continue; #endif syslog(LOG_INFO, "premature EOF, fetching \"%s\"", domain); return (-1); } if (n < 0) { if (errno == 0) { #if defined(NETREAD_BROKEN) if (++retries < 42) /* doug adams */ continue; #endif syslog(LOG_INFO, "recv(len=%d): n=%d && !errno", len, n); return (-1); } if (errno == EINTR) { if (!read_interrupted) { /* It wasn't a timeout; ignore it. */ continue; } errno = ETIMEDOUT; } syslog(LOG_INFO, "recv(len=%d): %m", len); return (-1); } buf += n; len -= n; #if defined(NETREAD_BROKEN) /* Reset the retry counter if we are successfully reading. */ if(n > 0) retries = 0; #endif } #ifndef _WIN32 if (setitimer(ITIMER_REAL, &zeroival, NULL) < 0) { syslog(LOG_INFO, setitimerStr); return (-1); } #endif return (0); } /* * Write a counted buffer to a file descriptor preceded by a length word. */ static int writemsg(int rfd, const u_char *msg, int msglen) { struct iovec iov[2]; u_char len[INT16SZ]; int ret; - __putshort(msglen, len); + ns_put16(msglen, len); iov[0].iov_base = (char *)len; iov[0].iov_len = INT16SZ; DE_CONST(msg, iov[1].iov_base); iov[1].iov_len = msglen; ret = writev(rfd, iov, 2); if (ret != INT16SZ + msglen) { syslog(LOG_DEBUG, "writemsg(%d,%p,%d) failed: %s", rfd, msg, msglen, strerror(errno)); return (-1); } return (ret); } static const char * soa_zinfo(struct zoneinfo *zp, u_char *cp, u_char *eom) { int n, type, class; u_int32_t ttl; u_int16_t dlen; u_char *rdatap; /* Are type, class, and ttl OK? */ if (eom - cp < 3 * INT16SZ + INT32SZ) return ("zinfo too short"); NS_GET16(type, cp); NS_GET16(class, cp); NS_GET32(ttl, cp); NS_GET16(dlen, cp); rdatap = cp; if (type != T_SOA || class != curclass) return ("zinfo wrong typ/cla/ttl"); /* Skip master name and contact name, we can't validate them. */ if ((n = dn_skipname(cp, eom)) == -1) return ("zinfo mname"); cp += n; if ((n = dn_skipname(cp, eom)) == -1) return ("zinfo hname"); cp += n; /* Grab the data fields. */ if (eom - cp < 5 * INT32SZ) return ("zinfo dlen"); NS_GET32(zp->z_serial, cp); NS_GET32(zp->z_refresh, cp); NS_GET32(zp->z_retry, cp); NS_GET32(zp->z_expire, cp); NS_GET32(zp->z_minimum, cp); if (cp != rdatap + dlen) return ("bad soa dlen"); return (NULL); } #define BOUNDS_CHECK(ptr, count) \ do { \ if ((ptr) + (count) > eom) { \ hp->rcode = FORMERR; \ return (-1); \ } \ } while (0) /* * Parse the message, determine if it should be printed, and if so, print it * in .db file form. Does minimal error checking on the message content. * * XXX why aren't we using ns_sprintrr() ? */ static int print_output(struct zoneinfo *zp, u_int32_t serial_no, u_char *msg, int msglen, u_char *rrp, int xfr_detect) { u_char *cp; HEADER *hp = (HEADER *) msg; u_int32_t ttl, tmpnum; int i, j, longname, result, n1, n; u_int class, type, dlen; char data[MAXDATA]; u_char *cp1, *cp2, *temp_ptr, *eom, *rr_type_ptr; u_char *cdata, *rdatap; char *origin, dname[MAXDNAME]; const char *proto; const char *ignore = ""; const char *badsoa_msg; int escaped = 0; eom = msg + msglen; cp = rrp; n = dn_expand(msg, msg + msglen, cp, dname, sizeof dname); if (n < 0) { hp->rcode = FORMERR; return (-1); } cp += n; BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); rr_type_ptr = cp; NS_GET16(type, cp); NS_GET16(class, cp); NS_GET32(ttl, cp); /* * Following the Clarification draft's direction, we treat TTLs with * the MSB set as if they were 0. */ if (ttl > MAXIMUM_TTL) { syslog(LOG_INFO, "%s: TTL > %u, converted to 0", dname, MAXIMUM_TTL); ttl = 0; } NS_GET16(dlen, cp); BOUNDS_CHECK(cp, dlen); rdatap = cp; origin = dname; while (*origin) { if (!escaped && *origin == '.') { origin++; /* skip over '.' */ break; } escaped = (*origin++ == '\\') && !escaped; } - dprintf(3, "print_output: dname %s type %d class %d ttl %u\n", + lprintf(3, "print_output: dname %s type %d class %d ttl %u\n", dname, type, class, ttl); /* * Convert the resource record data into the internal database format. * CP points to the raw resource record. * After this switch: * CP has been updated to point past the RR. * CP1 points to the internal database version. * N is the length of the internal database version. */ switch (type) { case T_A: case T_WKS: case T_HINFO: case T_TXT: case T_X25: case T_ISDN: case T_LOC: case T_NSAP: case T_AAAA: case T_KEY: case ns_t_cert: cp1 = cp; n = dlen; cp += n; break; case T_CNAME: case T_MB: case T_MG: case T_MR: case T_NS: case T_PTR: n = dn_expand(msg, msg + msglen, cp, data, sizeof data); if (n < 0) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 = (u_char *)data; n = strlen(data) + 1; break; case T_MINFO: case T_SOA: case T_RP: n = dn_expand(msg, msg + msglen, cp, data, sizeof data); if (n < 0) { hp->rcode = FORMERR; return (-1); } cp += n; n = strlen(data) + 1; cp1 = (u_char *)data + n; n1 = sizeof data - n; if (type == T_SOA) n1 -= 5 * INT32SZ; n = dn_expand(msg, msg + msglen, cp, (char *)cp1, n1); if (n < 0) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 += strlen((char *) cp1) + 1; if (type == T_SOA) { BOUNDS_CHECK(cp, 5 * INT32SZ); temp_ptr = cp + 4 * INT32SZ; NS_GET32(minimum_ttl, temp_ptr); /* * Following the Clarification draft's direction, * we treat TTLs with the MSB set as if they were 0. */ if (minimum_ttl > MAXIMUM_TTL) { syslog(LOG_INFO, "%s: SOA minimum TTL > %u, converted to 0", dname, MAXIMUM_TTL); minimum_ttl = 0; } n = 5 * INT32SZ; memcpy(cp1, cp, n); cp += n; cp1 += n; } n = cp1 - (u_char *)data; cp1 = (u_char *)data; break; case T_NAPTR: /* Grab weight and port. */ BOUNDS_CHECK(cp, INT16SZ*2); memcpy(data, cp, INT16SZ*2); cp1 = (u_char *)data + INT16SZ*2; cp += INT16SZ*2; /* Flags */ BOUNDS_CHECK(cp, 1); n = *cp++; BOUNDS_CHECK(cp, n); *cp1++ = n; memcpy(cp1, cp, n); cp += n; cp1 += n; /* Service */ BOUNDS_CHECK(cp, 1); n = *cp++; BOUNDS_CHECK(cp, n); *cp1++ = n; memcpy(cp1, cp, n); cp += n; cp1 += n; /* Regexp */ BOUNDS_CHECK(cp, 1); n = *cp++; BOUNDS_CHECK(cp, n); *cp1++ = n; memcpy(cp1, cp, n); cp += n; cp1 += n; /* Replacement */ n = dn_expand(msg, msg + msglen, cp, (char *)cp1, sizeof data - ((char *)cp1 - data)); if (n < 0) return (-1); cp += n; /* compute end of data */ cp1 += strlen((char *)cp1) + 1; /* compute size of data */ n = cp1 - (u_char *)data; cp1 = (u_char *)data; break; case T_MX: case T_AFSDB: case T_RT: case T_SRV: /* grab preference */ BOUNDS_CHECK(cp, INT16SZ); memcpy(data, cp, INT16SZ); cp1 = (u_char *)data + INT16SZ; cp += INT16SZ; if (type == T_SRV) { BOUNDS_CHECK(cp, INT16SZ*2); memcpy(cp1, cp, INT16SZ*2); cp1 += INT16SZ*2; cp += INT16SZ*2; } /* get name */ n = dn_expand(msg, msg + msglen, cp, (char *)cp1, sizeof data - (cp1 - (u_char *)data)); if (n < 0) return (-1); cp += n; /* compute end of data */ cp1 += strlen((char *) cp1) + 1; /* compute size of data */ n = cp1 - (u_char *)data; cp1 = (u_char *)data; break; case T_PX: /* grab preference */ BOUNDS_CHECK(cp, INT16SZ); memcpy(data, cp, INT16SZ); cp1 = (u_char *)data + INT16SZ; cp += INT16SZ; /* get MAP822 name */ n = dn_expand(msg, msg + msglen, cp, (char *)cp1, sizeof data - INT16SZ); if (n < 0) return (-1); cp += n; cp1 += (n = (strlen((char *) cp1) + 1)); n1 = sizeof data - n; /* get MAPX400 name */ n = dn_expand(msg, msg + msglen, cp, (char *)cp1, n1); if (n < 0) return (-1); cp += n; cp1 += strlen((char *) cp1) + 1; n = cp1 - (u_char *)data; cp1 = (u_char *)data; break; case T_SIG: /* CP is the raw resource record as it arrived. * CP1, after this switch, points to the internal database version. */ cp1 = (u_char *)data; /* first just copy over the type_covered, algorithm, */ /* labels, orig ttl, two timestamps, and the footprint */ BOUNDS_CHECK(cp, NS_SIG_SIGNER); memcpy(cp1, cp, NS_SIG_SIGNER); cp += NS_SIG_SIGNER; cp1 += NS_SIG_SIGNER; /* then the signer's name */ n = dn_expand(msg, msg + msglen, cp, (char *)cp1, (sizeof data) - 18); if (n < 0) return (-1); cp += n; cp1 += strlen((char*)cp1)+1; /* finally, we copy over the variable-length signature. Its size is the total data length, minus what we copied. */ n = dlen - (NS_SIG_SIGNER + n); if (n > ((int)(sizeof data) - (int)(cp1 - (u_char *)data))) { hp->rcode = FORMERR; return (-1); /* out of room! */ } memcpy(cp1, cp, n); cp += n; cp1 += n; /* compute size of data */ n = cp1 - (u_char *)data; cp1 = (u_char *)data; break; case T_NXT: n = dn_expand(msg, msg + msglen, cp, (char *)data, sizeof data); if (n < 0) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 = (u_char *)data + strlen(data) + 1; n = dlen - n; if (n > ((int)(sizeof data) - (int)(cp1 - (u_char *)data))) { hp->rcode = FORMERR; return (-1); /* out of room! */ } if (n > 0) { /* Actually, n should never be less than 4 */ memcpy(cp1, cp, n); cp += n; } else { hp->rcode = FORMERR; return (-1); } n += cp1 - (u_char *)data; cp1 = (u_char *)data; break; default: cp1 = cp; n = dlen; cp += n; break; } if (n > MAXDATA) { - dprintf(1, "update type %d: %d bytes is too much data\n", + lprintf(1, "update type %d: %d bytes is too much data\n", type, n); hp->rcode = FORMERR; return (-1); } if (cp != rdatap + dlen) { - dprintf(1, + lprintf(1, "encoded rdata length is %u, but actual length was %u\n", dlen, (u_int)(cp - rdatap)); hp->rcode = FORMERR; return (-1); } cdata = cp1; result = cp - rrp; /* * Special handling for SOA records. */ if (type == T_SOA) { if (ns_samename(dname, zp->z_origin) != 1) { syslog(LOG_INFO, "wrong zone name in XFR (wanted \"%s\", got \"%s\")", zp->z_origin, dname); hp->rcode = FORMERR; return (-1); } if (soa_cnt == 0) { badsoa_msg = soa_zinfo(&zp_start, rr_type_ptr, eom); if (badsoa_msg) { syslog(LOG_INFO, "malformed SOA for zone %s: %s", zp->z_origin, badsoa_msg); hp->rcode = FORMERR; return (-1); } if (SEQ_GT(zp_start.z_serial, serial_no) || !check_serial) { soa_cnt++; } else { syslog(LOG_INFO, "serial went backwards after transfer started"); return (-1); } } else if (soa_cnt == 1) { badsoa_msg = soa_zinfo(&zp_finish, rr_type_ptr, eom); if (badsoa_msg) { syslog(LOG_INFO, "malformed SOA for zone %s: %s", zp->z_origin, badsoa_msg); hp->rcode = FORMERR; return (-1); } if (zp_start.z_serial == zp_finish.z_serial) { methode = ISNOTIXFR; if (xfr_detect == 3) soa_cnt = 0; } else if (zp_finish.z_serial != serial_no) { syslog(LOG_INFO, "Unexpected serial number for zone %s: %u", zp->z_origin, zp_finish.z_serial); } soa_cnt++; if (methode == ISIXFR) return (result); } else { badsoa_msg = soa_zinfo(&zp_finish, rr_type_ptr, eom); if (badsoa_msg) { syslog(LOG_INFO, "malformed SOA for zone %s: %s", zp->z_origin, badsoa_msg); hp->rcode = FORMERR; return (-1); } if (methode == ISIXFR) { if (zp_start.z_serial == zp_finish.z_serial) { if (scdsoa) { soa_cnt = -1; return (result); } else { scdsoa = 1; soa_cnt++; }; } else soa_cnt++; } else { - dprintf(2, "SOA, serial %u\n", + lprintf(2, "SOA, serial %u\n", zp_finish.z_serial); if (zp_start.z_serial != zp_finish.z_serial) { - dprintf(1, "serial changed, restart\n"); + lprintf(1, "serial changed, restart\n"); restarts++; if (restarts > MAX_XFER_RESTARTS) { syslog(LOG_INFO, "too many transfer restarts for zone %s", zp->z_origin); hp->rcode = FORMERR; return (-1); } soa_cnt = 0; ns_cnt = 0; minimum_ttl = 0; strcpy(prev_origin, zp->z_origin); prev_dname[0] = DEF_DNAME; /* * Flush buffer, truncate file * and seek to beginning to restart. */ fflush(dbfp); if (ftruncate(fileno(dbfp), 0) != 0) { if (!quiet) syslog(LOG_INFO, "ftruncate %s: %m\n", tmpname); return (-1); } fseek(dbfp, 0L, 0); return (result); } soa_cnt++; return (result); } } if (soa_cnt == 2) return (result); } if (zp->z_type == Z_STUB) { if (query_type == T_NS && type == T_NS) ns_cnt++; /* * If we're processing a response to an SOA query, we don't * want to print anything from the response except for the SOA. * We do want to check everything in the packet, which is * why we do this check now instead of earlier. */ if (query_type == T_SOA && type != T_SOA) return (result); } if ((!soa_cnt || soa_cnt > 2) && methode == ISNOTIXFR) { const char *gripe; if (!soa_cnt) gripe = "got RR before first SOA"; else gripe = "got RR after second SOA"; syslog(LOG_INFO, "%s in zone %s", gripe, zp->z_origin); hp->rcode = FORMERR; return (-1); } /* * If they are trying to tell us info about something that is * not in the zone that we are transfering, then ignore it! * They don't have the authority to tell us this info. * * We have to do a bit of checking here - the name that we are * checking is is fully qualified & may be in a subdomain of the * zone in question. We also need to ignore any final dots. * * If a domain has both NS records and non-NS records, (for * example, NS and MX records), then we should ignore the non-NS * records (except that we should not ignore glue A records). * XXX: It is difficult to do this properly, so we just compare * the current dname with that in the most recent NS record. * This defends against the most common error case, * where the remote server sends MX records soon after the * NS records for a particular domain. If sent earlier, we lose. XXX */ if (!ns_samedomain(dname, domain)) { (void) fprintf(dbfp, "; Ignoring info about %s, not in zone %s.\n", dname, domain); ignore = "; "; } else if (type != T_NS && type != T_A && ns_samename(zone_top, dname) != 1 && ns_samename(prev_ns_dname, dname) == 1) { (void) fprintf(dbfp, "; Ignoring extra info about %s, invalid after NS delegation.\n", dname); ignore = "; "; } else if (class != (unsigned)zp->z_class) { (void) fprintf(dbfp, "; Ignoring info about %s, not class %s\n", dname, p_class(zp->z_class)); ignore = "; "; } /* * If the current record is not being ignored, but the * previous record was ignored, then we invalidate information * that might have been altered by ignored records. * (This means that we sometimes output unnecessary $ORIGIN * lines, but that is harmless.) * * Also update prev_comment now. */ if (prev_comment && ignore[0] == '\0') { prev_dname[0] = DEF_DNAME; prev_origin[0] = DEF_DNAME; } prev_comment = (ignore[0] != '\0'); /* * set prev_ns_dname if necessary */ if (type == T_NS) { (void) strcpy(prev_ns_dname, dname); } /* * If the origin has changed, print the new origin */ if (ns_samename(prev_origin, origin) != 1) { (void) strcpy(prev_origin, origin); (void) fprintf(dbfp, "%s$ORIGIN %s.\n", ignore, origin); } longname = 0; if (ns_samename(prev_dname, dname) != 1) { /* * set the prev_dname to be the current dname, then cut off all * characters of dname after (and including) the first '.' */ char *cutp; (void) strcpy(prev_dname, dname); escaped = 0; cutp = dname; while (*cutp) { if (!escaped && *cutp == '.') break; escaped = (*cutp++ == '\\') && !escaped; } *cutp = '\0'; if (dname[0] == 0) { if (origin[0] == 0) (void) fprintf(dbfp, "%s.\t", ignore); else (void) fprintf(dbfp, "%s.%s.\t", ignore, origin); /* ??? */ } else { const char *backslash; backslash = (*dname == '@' || *dname == '$') ? "\\" : ""; (void) fprintf(dbfp, "%s%s%s\t", ignore, backslash, dname); } if (strlen(dname) > (size_t)8) longname = 1; } else { (void) fprintf(dbfp, "%s\t", ignore); } (void) fprintf(dbfp, "%d\t", (int) ttl); (void) fprintf(dbfp, "%s\t%s\t", p_class(class), p_type(type)); cp = cdata; /* * Print type specific data */ switch (type) { case T_A: switch (class) { case C_IN: case C_HS: fputs(inet_ntoa(ina_get(cp)), dbfp); break; } (void) fprintf(dbfp, "\n"); break; case T_CNAME: case T_MB: case T_MG: case T_MR: case T_PTR: if (cp[0] == '\0') (void) fprintf(dbfp, ".\n"); else (void) fprintf(dbfp, "%s.\n", cp); break; case T_NS: cp = cdata; if (cp[0] == '\0') (void) fprintf(dbfp, ".\t"); else (void) fprintf(dbfp, "%s.", cp); (void) fprintf(dbfp, "\n"); break; case T_HINFO: case T_ISDN: cp2 = cp + n; for (i = 0; i < 2; i++) { if (i != 0) (void) putc(' ', dbfp); n = *cp++; cp1 = cp + n; if (cp1 > cp2) cp1 = cp2; (void) putc('"', dbfp); j = 0; while (cp < cp1) { if (*cp == '\0') { cp = cp1; break; } if (strchr("\n\"\\", *cp)) (void) putc('\\', dbfp); (void) putc(*cp++, dbfp); j++; } if (j == 0 && (type != T_ISDN || i == 0)) (void) putc('?', dbfp); (void) putc('"', dbfp); } (void) putc('\n', dbfp); break; case T_SOA: (void) fprintf(dbfp, "%s.", cp); cp += strlen((char *) cp) + 1; (void) fprintf(dbfp, " %s. (\n", cp); cp += strlen((char *) cp) + 1; NS_GET32(tmpnum, cp); (void) fprintf(dbfp, "%s\t\t%u", ignore, tmpnum); NS_GET32(tmpnum, cp); (void) fprintf(dbfp, " %u", tmpnum); NS_GET32(tmpnum, cp); (void) fprintf(dbfp, " %u", tmpnum); NS_GET32(tmpnum, cp); (void) fprintf(dbfp, " %u", tmpnum); NS_GET32(tmpnum, cp); (void) fprintf(dbfp, " %u )\n", tmpnum); break; case T_MX: case T_AFSDB: case T_RT: NS_GET16(tmpnum, cp); (void) fprintf(dbfp, "%u", tmpnum); (void) fprintf(dbfp, " %s.\n", cp); break; case T_PX: NS_GET16(tmpnum, cp); (void) fprintf(dbfp, "%u", tmpnum); (void) fprintf(dbfp, " %s.", cp); cp += strlen((char *) cp) + 1; (void) fprintf(dbfp, " %s.\n", cp); break; case T_TXT: case T_X25: cp1 = cp + n; while (cp < cp1) { (void) putc('"', dbfp); if ((i = *cp++) != 0) { for (j = i; j > 0 && cp < cp1; j--) { if (strchr("\n\"\\", *cp)) (void) putc('\\', dbfp); (void) putc(*cp++, dbfp); } } (void) putc('"', dbfp); if (cp < cp1) (void) putc(' ', dbfp); } (void) putc('\n', dbfp); break; case T_NSAP: fprintf(dbfp, "%s\n", inet_nsap_ntoa(n, cp, NULL)); break; case T_AAAA: { char t[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; fprintf(dbfp, "%s\n", inet_ntop(AF_INET6, cp, t, sizeof t)); break; } case T_LOC: { char t[255]; (void) fprintf(dbfp, "%s\n", loc_ntoa(cp, t)); break; } case T_NAPTR: { u_int32_t order, preference; /* Order */ NS_GET16(order, cp); fprintf(dbfp, "%u", order); /* Preference */ NS_GET16(preference, cp); fprintf(dbfp, " %u", preference); /* Flags */ n = *cp++; fprintf(dbfp, " \"%.*s\"", (int)n, cp); cp += n; /* Service */ n = *cp++; fprintf(dbfp, " \"%.*s\"", (int)n, cp); cp += n; /* Regexp */ n = *cp++; fprintf(dbfp, " \"%.*s\"", (int)n, cp); cp += n; /* Replacement */ fprintf(dbfp, " %s.\n", cp); break; } case T_SRV: { u_int priority, weight, port; NS_GET16(priority, cp); NS_GET16(weight, cp); NS_GET16(port, cp); fprintf(dbfp, "\t%u %u %u %s.\n", priority, weight, port, cp); break; } case T_WKS: fputs(inet_ntoa(ina_get(cp)), dbfp); cp += INADDRSZ; fputc(' ', dbfp); proto = protocolname(*cp); cp += sizeof(char); (void) fprintf(dbfp, "%s ", proto); i = 0; while (cp < cdata + n) { j = *cp++; do { if (j & 0200) (void) fprintf(dbfp, " %s", servicename(i, proto)); j <<= 1; } while (++i & 07); } (void) fprintf(dbfp, "\n"); break; case T_MINFO: case T_RP: (void) fprintf(dbfp, "%s.", cp); cp += strlen((char *) cp) + 1; (void) fprintf(dbfp, " %s.\n", cp); break; case T_KEY: { char databuf[16+NS_MD5RSA_MAX_BASE64]; /* 16 for slop */ u_int keyflags; /* get & format key flags */ keyflags = ns_get16(cp); (void) fprintf(dbfp, "0x%04x ", keyflags); cp += INT16SZ; /* protocol id */ (void) fprintf(dbfp, " %u", *cp++); /* algorithm id */ (void) fprintf(dbfp, " %u ", *cp++); /* key itself (which may have zero length) */ n = b64_ntop(cp, (cp1 + n) - cp, databuf, sizeof databuf); if (n < 0) fprintf(dbfp, "; BAD BASE64\n"); else fprintf(dbfp, "%s\n", databuf); break; } case T_SIG: { char databuf[16+NS_MD5RSA_MAX_BASE64]; /* 16 for slop */ /* get & format rr type which signature covers */ (void) fprintf(dbfp,"%s", p_type(ns_get16((u_char*)cp))); cp += INT16SZ; /* algorithm id */ (void) fprintf(dbfp," %d",*cp++); /* labels (# of labels in name) */ (void) fprintf(dbfp," %d",*cp++); /* orig time to live (TTL)) */ (void) fprintf(dbfp," %u", (u_int32_t)ns_get32((u_char*)cp)); cp += INT32SZ; /* expiration time */ (void) fprintf(dbfp," %s", p_secstodate(ns_get32((u_char*)cp))); cp += INT32SZ; /* time signed */ (void) fprintf(dbfp," %s", p_secstodate(ns_get32((u_char*)cp))); cp += INT32SZ; /* Key footprint */ (void) fprintf(dbfp," %d", ns_get16((u_char*)cp)); cp += INT16SZ; /* signer's name */ (void) fprintf(dbfp, " %s. ", cp); cp += strlen((char *) cp) + 1; /* signature itself */ n = b64_ntop(cp, (cdata + n) - cp, databuf, sizeof databuf); if (n < 0) fprintf (dbfp, "; BAD BASE64\n"); else fprintf (dbfp, "%s\n", databuf); break; } case T_NXT: fprintf(dbfp, "%s.", (char *)cp); i = strlen((char *)cp)+1; cp += i; n -= i; for (i=0; i < n*NS_NXT_BITS; i++) if (NS_NXT_BIT_ISSET (i, cp)) fprintf(dbfp, " %s", p_type(i)); fprintf(dbfp,"\n"); break; case ns_t_cert: { int databufsize = n * 4 / 3 + 4; char *databuf = malloc(databufsize); if (databuf == NULL) panic("cert malloc failed", NULL); /* Object id */ (void) fprintf(dbfp,"%d ", ns_get16((u_char*)cp)); cp += INT16SZ; /* Key tag */ (void) fprintf(dbfp,"%d ", ns_get16((u_char*)cp)); cp += INT16SZ; /* Algorithm id */ (void) fprintf(dbfp,"%d ", (u_char)*cp); cp += 1; n = b64_ntop(cp, n - 2 * INT16SZ - 1, databuf, databufsize); if (n < 0) panic ("cert b64_ntop failed", NULL); fprintf (dbfp, "%s\n", databuf); free(databuf); break; } default: fprintf (dbfp, "\\# %u", n); if (n > 0) { fputs(" ( ", dbfp); isc_puthexstring(dbfp, cp1, n, (longname ? 28 : 40), 48, "\n\t\t\t\t"); fputs(" )\n", dbfp); } } if (ferror(dbfp)) { syslog(LOG_ERR, "%s: %m", tmpname); cleanup_for_exit(); exit(XFER_FAIL); } return (result); } #ifdef SHORT_FNAMES /* ** This routine handles creating temporary files with mkstemp ** in the presence of a 14 char filename system. Pathconf() ** does not work over NFS. */ filenamecpy(char *ddtfile, char *optarg) { int namelen, extra, len; char *dirname, *filename; /* determine the length of filename allowed */ if((dirname = strrchr(optarg, '/')) == NULL){ filename = optarg; } else { *dirname++ = '\0'; filename = dirname; } namelen = pathconf(dirname == NULL? "." : optarg, _PC_NAME_MAX); if(namelen <= 0) namelen = 255; /* length could not be determined */ if(dirname != NULL) *--dirname = '/'; /* copy a shorter name if it will be longer than allowed */ extra = (strlen(filename)+strlen(".XXXXXX")) - namelen; if(extra > 0){ len = strlen(optarg) - extra; (void) strncpy(ddtfile, optarg, len); ddtfile[len] = '\0'; } else (void) strcpy(ddtfile, optarg); } #endif /* SHORT_FNAMES */ DST_KEY * tsig_key_from_addr(struct in_addr addr) { tsig_node *n; for (n = HEAD(tsig_list); n != NULL; n = NEXT(n, link)) if (memcpy(&addr, &n->addr, sizeof(struct in_addr))) return n->dst_key; return NULL; } static u_int32_t do_section(ns_msg *handle, ns_sect section, int pflag, FILE *file, int *delete) { int n, sflag, rrnum; char buf[2048]; /* XXX need to malloc */ ns_opcode opcode; ns_rr rr; const unsigned char *cp; const unsigned char *eom; u_int32_t serial = 0; time_t now; time(&now); /* * Print answer records. */ sflag = (_res.pfcode & pflag); if (_res.pfcode && !sflag) return (-1); opcode = (ns_opcode)ns_msg_getflag(*handle, ns_f_opcode); rrnum = 0; for (;;) { if (ns_parserr(handle, section, rrnum, &rr)) { if (errno != ENODEV) { fprintf(file, ";; ns_parserr: %s\n", strerror(errno)); return (-1); } else if (rrnum > 0 && sflag != 0 && (_res.pfcode & RES_PRF_HEAD1)) putc('\n', file); break; } if (rrnum == 0 && sflag != 0 && (_res.pfcode & RES_PRF_HEAD1)) fprintf(file, ";; %s SECTION:\n", p_section(section, opcode)); if (section == ns_s_qd) fprintf(file, ";;\t%s, type = %s, class = %s\n", ns_rr_name(rr), p_type(ns_rr_type(rr)), p_class(ns_rr_class(rr))); else { int print_record = 1; if (rr.type == ns_t_soa) { print_record = 0; *delete = !*delete; cp = ns_rr_rdata(rr); eom = cp + ns_rr_rdlen(rr); if ((n = dn_skipname(cp, eom)) < 0) { rrnum++; continue; } cp += n; if ((n = dn_skipname(cp, eom)) < 0) { rrnum++; continue; } cp += n; NS_GET32(serial, cp); switch (++ixfr_soa) { case 1: final_serial = serial; if (soa_buf == NULL) { if ((soa_buf = (char *)malloc(2 * PACKETSZ)) == NULL) { syslog(LOG_INFO, "malloc(%u) failed", 2 * PACKETSZ); return(-1); } n = ns_sprintrr(handle, &rr, NULL, NULL, soa_buf, 2*PACKETSZ); if (n < 0) { fprintf(file, ";; ns_sprintrr: %s\n", strerror(errno)); return (-1); } } print_record = 0; break; case 2: fprintf(file, "zone:\torigin %s class %s serial %u\n", ns_rr_name(rr), p_class(ns_rr_class(rr)), serial); print_record = 0; break; default: print_record = 0; break; } } if (print_record) { if (rr.type != ns_t_soa) { fprintf(file, "update:\t{%s} ", *delete ? "delete" : "add"); n = ns_sprintrr(handle, &rr, NULL, NULL, buf, sizeof buf); if (n < 0) { fprintf(file, ";; ns_sprintrr: %s\n", strerror(errno)); return(-1); } fputs(buf, file); fputc('\n', file); } } } rrnum++; } return (serial); } static int ixfr_log(const u_char *msg, int len, int *delete, FILE *file, struct sockaddr_in *sin, u_int32_t *serial_no, int *first_rr) { ns_msg handle; ns_type type; ns_class class; ns_opcode opcode; ns_rcode rcode; u_int id; u_int32_t new_serial = 0; char time[25]; ns_rr rr; if ((_res.options & RES_INIT) == 0 && res_init() == -1) { - dprintf(1, "ixfr_log() failed\n"); + lprintf(1, "ixfr_log() failed\n"); return (-1); } if (ns_initparse(msg, len, &handle) < 0) { fprintf(file, ";; ns_initparse: %s\n", strerror(errno)); - dprintf(1, "ixfr_log() failed\n"); + lprintf(1, "ixfr_log() failed\n"); return (-1); } opcode = (ns_opcode) ns_msg_getflag(handle, ns_f_opcode); rcode = (ns_rcode) ns_msg_getflag(handle, ns_f_rcode); id = ns_msg_id(handle); if (ns_parserr(&handle, ns_s_an, 0, &rr)) { (void) fprintf(file,"ns_parserr() failed"); - dprintf(1, "ixfr_log() failed\n"); + lprintf(1, "ixfr_log() failed\n"); return (-1); } type = (ns_type)rr.type; class = (ns_class)rr.rr_class; if (*first_rr == 1) { gettime(&tt); (void) fprintf(file,"%s", LogSignature); sprintf(time, "at %lu", (u_long)tt.tv_sec); fprintf(file, "[IXFR_UPDATE] id %u from [%s].%d %s (named-xfer pid %ld):\n", id, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), time, (long)getpid()); (*first_rr)++; } new_serial = do_section(&handle, ns_s_an, RES_PRF_ANS, file, delete); if (type == T_SOA && SEQ_GT(new_serial, *serial_no) && (*delete)) *serial_no = new_serial; return (1); } static const char * tsig_rcode(int rcode) { static char buffer[64]; switch (rcode) { case ns_r_badkey: case ns_r_badsig: case ns_r_badtime: sprintf(buffer, "message had %s set", p_rcode(rcode)); return (buffer); case -ns_r_badkey: case -ns_r_badsig: case -ns_r_badtime: return (p_rcode(-rcode)); case NS_TSIG_ERROR_NO_TSIG: return ("no TSIG present"); default: break; } return ("FORMERR"); } diff --git a/contrib/bind/bin/named/db_defs.h b/contrib/bind/bin/named/db_defs.h index 37b974cbaab5..05fb19079936 100644 --- a/contrib/bind/bin/named/db_defs.h +++ b/contrib/bind/bin/named/db_defs.h @@ -1,341 +1,341 @@ /* * from db.h 4.16 (Berkeley) 6/1/90 - * $Id: db_defs.h,v 8.47.4.1 2002/11/14 13:22:24 marka Exp $ + * $Id: db_defs.h,v 8.48 2002/11/17 14:51:50 marka Exp $ */ /* * Copyright (c) 1985, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Global definitions for data base routines. */ /* max length of data in RR data field */ #define MAXDATA (3*MAXDNAME + 5*INT32SZ) /* max length of data in a TXT RR segment */ #define MAXCHARSTRING 255 #define DB_ROOT_TIMBUF 3600 #define TIMBUF 300 #define DICT_INDEXBITS 24 #define DICT_MAXLENGTH 127 #define DICT_INSERT_P 0x0001 /* Average hash chain depths. */ #define AVGCH_MARSHAL 5 #define AVGCH_NLOOKUP 3 /* Nonstandard maximum class to force better packing. */ #define ZONE_BITS 24 #define CLASS_BITS 8 #define ZONE_MAX ((1<> ((sizeof(v) * 8) - HASHSHIFT))) #define HASHLOWER(c) ((isascii(c) && isupper(c)) ? tolower(c) : (c)) #define HASHIMILATE(v,c) ((v) = (HASHROTATE(v)) + (HASHLOWER(c) & HASHMASK)) #define TSIG_BUF_SIZE 640 #define TSIG_SIG_SIZE 20 struct tsig_record { u_int8_t sig[TSIG_SIG_SIZE]; struct dst_key *key; int siglen; int tsig_size; }; struct sig_record { u_int16_t sig_type_n; u_int8_t sig_alg_n, sig_labels_n; u_int32_t sig_ottl_n, sig_exp_n, sig_time_n; u_int16_t sig_keyid_n; }; /* This is the wire format size of "struct sig_record", i.e., no padding. */ #define SIG_HDR_SIZE 18 struct dnode { struct databuf *dp; struct dnode *dn_next; int line; const char *file; }; typedef struct dnode * dlist; struct db_rrset { dlist rr_list; dlist rr_sigs; char *rr_name; int16_t rr_class; int16_t rr_type; struct db_rrset *rr_next; }; #define DBHASHSIZE(s) (sizeof(struct hashbuf) + \ (s-1) * sizeof(struct db_rrset *)) #define SIG_COVERS(dp) (ns_get16(dp->d_data)) /* * Flags to updatedb */ #define DB_NODATA 0x01 /* data should not exist */ #define DB_MEXIST 0x02 /* data must exist */ #define DB_DELETE 0x04 /* delete data if it exists */ #define DB_NOTAUTH 0x08 /* must not update authoritative data */ #define DB_NOHINTS 0x10 /* don't reflect update in fcachetab */ #define DB_PRIMING 0x20 /* is this update the result of priming? */ #define DB_MERGE 0x40 /* make no control on rr in db_update (for ixfr) */ #define DB_REPLACE 0x80 /* replace data if it exists */ #define DB_Z_CACHE 0 /* cache-zone-only db_dump() */ #define DB_Z_ALL 65535 /* normal db_dump() */ #define DB_Z_SPECIAL(z) ((z) == DB_Z_CACHE || (z) == DB_Z_ALL) /* * Error return codes */ #define OK 0 #define NONAME (-1) #define NOCLASS (-2) #define NOTYPE (-3) #define NODATA (-4) #define DATAEXISTS (-5) #define NODBFILE (-6) #define TOOMANYZONES (-7) #define GOODDB (-8) #define NEWDB (-9) #define AUTH (-10) #ifdef BIND_UPDATE #define SERIAL (-11) #endif #define CNAMEANDOTHER (-12) #define DNSSECFAIL (-13) /* db_set_update */ #define NONGLUE (-14) /* * getnum() options */ #define GETNUM_NONE 0x00 /* placeholder */ #define GETNUM_SERIAL 0x01 /* treat as serial number */ #define GETNUM_SCALED 0x02 /* permit "k", "m" suffixes, scale result */ /* * db_load() options */ #define ISNOTIXFR 0 #define ISIXFR 1 #define ISAXFRIXFR 2 /* * Database access abstractions. */ #define foreach_rr(dp, np, ty, cl, zn) \ for ((dp) = (np)->n_data; (dp) != NULL; (dp) = (dp)->d_next) \ if (!match(dp, (cl), (ty))) \ continue; \ else if (((zn) == DB_Z_CACHE) \ ? stale(dp) \ : (zn) != (dp)->d_zone) \ continue; \ else if ((dp)->d_rcode) \ continue; \ else \ /* Caller code follows in sequence. */ #define DRCNTINC(x) \ do { \ if (++((x)->d_rcnt) == 0) \ ns_panic(ns_log_db, 1, "++d_rcnt == 0"); \ } while (0) #define DRCNTDEC(x) \ do { \ if (((x)->d_rcnt)-- == 0) \ ns_panic(ns_log_db, 1, "d_rcnt-- == 0"); \ } while (0) #define ISVALIDGLUE(xdp) ((xdp)->d_type == T_NS || (xdp)->d_type == T_A \ || (xdp)->d_type == T_AAAA || (xdp)->d_type == ns_t_a6) diff --git a/contrib/bind/bin/named/db_ixfr.c b/contrib/bind/bin/named/db_ixfr.c index 231db12aa08a..fda89a024cb3 100644 --- a/contrib/bind/bin/named/db_ixfr.c +++ b/contrib/bind/bin/named/db_ixfr.c @@ -1,983 +1,984 @@ #if !defined(lint) && !defined(SABER) -static char rcsid[] = "$Id: db_ixfr.c,v 8.31 2002/01/02 04:47:10 marka Exp $"; +static char rcsid[] = "$Id: db_ixfr.c,v 8.32 2002/07/08 06:26:04 marka Exp $"; #endif /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Manage ixfr transaction log */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" #define DBIXFR_ERROR (-1) #define DBIXFR_FOUND_RR 2 #define DBIXFR_END 3 static int ixfr_getdelta(struct zoneinfo *, FILE *, const char *, char *, ns_updque *, u_int32_t *, u_int32_t *); ns_deltalist * ixfr_get_change_list(struct zoneinfo *zp, u_int32_t from_serial, u_int32_t to_serial) { FILE * fp = NULL; u_int32_t old_serial, new_serial; char origin[MAXDNAME]; ns_deltalist *dlhead = NULL; int ret; ns_updrec *uprec; ns_delta *dl; if (SEQ_GT(from_serial, to_serial)) return (NULL); dlhead = memget(sizeof(*dlhead)); if (dlhead == NULL) return (NULL); INIT_LIST(*dlhead); if ((fp = fopen(zp->z_ixfr_base, "r")) == NULL) { ns_warning(ns_log_db, "%s: %s", zp->z_ixfr_base, strerror(errno)); goto cleanup; } strcpy(origin, zp->z_origin); lineno = 1; old_serial = new_serial = 0; for (;;) { dl = memget(sizeof *dl); if (dl == NULL) { ns_warning(ns_log_db, "ixfr_get_change_list: out of memory"); goto cleanup; } INIT_LINK(dl, d_link); INIT_LIST(dl->d_changes); ret = ixfr_getdelta(zp, fp, zp->z_ixfr_base, origin, &dl->d_changes, &old_serial, &new_serial); switch (ret) { case DBIXFR_ERROR: ns_warning(ns_log_db, "Logical error in %s: unlinking", zp->z_ixfr_base); if (fp != NULL) { (void) my_fclose(fp); fp = NULL; } unlink(zp->z_ixfr_base); goto cleanup; case DBIXFR_FOUND_RR: ns_debug(ns_log_default, 4, "ixfr_getdelta DBIXFR_FOUND_RR (%s)", zp->z_origin); if (EMPTY(*dlhead)) { /* skip updates prior to the one we want */ uprec = HEAD(dl->d_changes); INSIST(uprec != NULL); if (SEQ_LT(uprec->r_zone, from_serial) || SEQ_GT(uprec->r_zone, to_serial)) { while ((uprec = HEAD(dl->d_changes)) != NULL) { UNLINK(dl->d_changes, uprec, r_link); if (uprec->r_dp != NULL) db_detach(&uprec->r_dp); res_freeupdrec(uprec); } memput(dl, sizeof *dl); break; } else if (uprec->r_zone > from_serial) { /* missed the boat */ ns_debug(ns_log_default, 3, "ixfr_getdelta first SOA is %d, asked for %d (%s)", uprec->r_zone, from_serial, zp->z_origin); goto cleanup; } } ns_debug(ns_log_default, 4, "adding to change list (%s)", zp->z_origin); APPEND(*dlhead, dl, d_link); break; case DBIXFR_END: ns_debug(ns_log_default, 4, "ixfr_getdelta DBIXFR_END (%s)", zp->z_origin); (void) my_fclose(fp); memput(dl, sizeof *dl); return (dlhead); default: (void) my_fclose(fp); if (dl != NULL) memput(dl, sizeof *dl); return (NULL); } } cleanup: if (fp != NULL) (void) my_fclose(fp); while ((dl = HEAD(*dlhead)) != NULL) { UNLINK(*dlhead, dl, d_link); while ((uprec = HEAD(dl->d_changes)) != NULL) { UNLINK(dl->d_changes, uprec, r_link); if (uprec->r_dp != NULL) db_detach(&uprec->r_dp); uprec->r_dp = NULL; res_freeupdrec(uprec); } memput(dl, sizeof *dl); } memput(dlhead, sizeof *dlhead); return (NULL); } /* * int ixfr_have_log(struct zoneinfo *zp,u_int32_t from_serial, * u_int32_t to_serial) * * verify that ixfr transaction log contains changes * from from_serial to to_serial * * returns: * 0 = serial number is up to date * 1 = transmission is possible * -1 = error while opening the ixfr transaction log * -2 = error in parameters * -3 = logical error in the history file */ int ixfr_have_log(struct zoneinfo *zp, u_int32_t from_serial, u_int32_t to_serial) { FILE *fp; u_int32_t old_serial = 0, new_serial = 0; u_int32_t last_serial = 0; u_int32_t first_serial = 0; char buf[BUFSIZ]; char *cp; struct stat st; int nonempty_lineno = -1, prev_pktdone = 0, cont = 0, inside_next = 0; int err; int first = 0; int rval = 0; int id, rcode = NOERROR; if (SEQ_GT(from_serial, to_serial)) return (-2); if (from_serial == to_serial) return (0); /* If there is no log file, just return. */ if (zp->z_ixfr_base == NULL || zp->z_updatelog == NULL) return (-1); if (zp->z_serial_ixfr_start > 0) { if (from_serial >= zp->z_serial_ixfr_start) return (1); } if (stat(zp->z_ixfr_base, &st) < 0) { if (errno != ENOENT) ns_error(ns_log_db, "unexpected stat(%s) failure: %s", zp->z_ixfr_base, strerror(errno)); return (-1); } if ((fp = fopen(zp->z_ixfr_base, "r")) == NULL) { ns_warning(ns_log_db, "%s: %s", zp->z_ixfr_base, strerror(errno)); return (-1); } if (fgets(buf, sizeof(buf), fp) == NULL) { ns_error(ns_log_update, "fgets() from %s failed: %s", zp->z_ixfr_base, strerror(errno)); fclose(fp); return (-1); } if (strcmp(buf, LogSignature) != 0) { ns_error(ns_log_update, "invalid log file %s", zp->z_ixfr_base); fclose(fp); return (-3); } lineno = 1; first = 1; for (;;) { if (getword(buf, sizeof buf, fp, 0)) { nonempty_lineno = lineno; } else { if (lineno == (nonempty_lineno + 1)) continue; inside_next = 0; prev_pktdone = 1; cont = 1; } if (!strcasecmp(buf, "[DYNAMIC_UPDATE]") || !strcasecmp(buf, "[IXFR_UPDATE]")) { err = 0; rcode = NOERROR; cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (cp == NULL || !sscanf((char *) cp, "id %d", &id)) id = -1; inside_next = 1; prev_pktdone = 1; cont = 1; } else if (!strcasecmp(buf, "serial")) { cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (sscanf((char *) cp, "%u", &old_serial)) { if (first == 1) { first = 0; first_serial = old_serial; } last_serial = old_serial; if (from_serial >= old_serial) { rval = 1; } } prev_pktdone = 1; cont = 1; } else if (!strcasecmp(buf, "[INCR_SERIAL]")) { /* XXXRTH not enough error checking here */ cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (cp == NULL || sscanf((char *) cp, "from %u to %u", &old_serial, &new_serial) != 2) { rval = -3; break; } else if (from_serial >= old_serial) { if (first == 1) { first = 0; first_serial = old_serial; } last_serial = old_serial; rval = 1; } } if (prev_pktdone) { prev_pktdone = 0; if (feof(fp)) break; } } fclose(fp); if (last_serial +1 < zp->z_serial) { ns_warning(ns_log_db, "%s: File Deleted. Found gap between serial:" " %d and current serial: %d", zp->z_ixfr_base, last_serial, zp->z_serial); (void) unlink(zp->z_ixfr_base); rval = -3; } if (from_serial < first_serial || from_serial > last_serial) rval = -3; if (rval == 1) zp->z_serial_ixfr_start = first_serial; return (rval); } /* from db_load.c */ static struct map m_section[] = { {"zone", S_ZONE}, {"prereq", S_PREREQ}, {"update", S_UPDATE}, {"reserved", S_ADDT}, }; #define M_SECTION_CNT (sizeof(m_section) / sizeof(struct map)) /* from ns_req.c */ static struct map m_opcode[] = { {"nxdomain", NXDOMAIN}, {"yxdomain", YXDOMAIN}, {"nxrrset", NXRRSET}, {"yxrrset", YXRRSET}, {"delete", DELETE}, {"add", ADD}, }; #define M_OPCODE_CNT (sizeof(m_opcode) / sizeof(struct map)) /* XXXRTH workaround map difficulties */ #define M_CLASS_CNT m_class_cnt #define M_TYPE_CNT m_type_cnt /* * read a line from the history of a zone. * * returns: * * DBIXFR_ERROR = an error occured * DBIXFR_FOUND_RR = a rr encountered * DBIXFR_END = end of file */ static int ixfr_getdelta(struct zoneinfo *zp, FILE *fp, const char *filename, char *origin, ns_updque *listuprec, u_int32_t *old_serial, u_int32_t *new_serial) { char data[MAXDATA], dnbuf[MAXDNAME], sclass[3]; char *dname, *cp, *cp1; char buf[MAXDATA]; long unsigned lutmp; u_int32_t serial = 0, ttl; u_int32_t current_serial = 0; int nonempty_lineno = -1, prev_pktdone = 0, cont = 0, inside_next = 0; int id; int i, c, section, opcode, matches, zonenum, err, multiline; int type, class; u_int32_t n; enum transport transport; struct map *mp; int zonelist[MAXDNAME]; struct in_addr ina; int datasize; ns_updrec * rrecp; u_long l; #define ERRTO(msg) if (1) { errtype = msg; goto err; } else (void)NULL err = 0; transport = primary_trans; lineno = 1; zonenum = 0; /* * Look for serial if "first" call othewise use new_serial to * for current_serial. */ if (*old_serial == *new_serial && *old_serial == 0) current_serial = 0; else current_serial = *new_serial; for (;;) { dname = NULL; if (!getword(buf, sizeof buf, fp, 0)) { if (lineno == (nonempty_lineno + 1) && !(feof(fp))) { /* * End of a nonempty line inside an update * packet or not inside an update packet. */ continue; } /* * Empty line or EOF. */ if (feof(fp)) break; inside_next = 0; cont = 1; } else { nonempty_lineno = lineno; } if (!strcasecmp(buf, "[DYNAMIC_UPDATE]") || !strcasecmp(buf, "[IXFR_UPDATE]")) { cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (cp == NULL || !sscanf((char *) cp, "id %d", &id)) id = -1; inside_next = 1; cont = 1; } else if (!strcasecmp(buf, "[INCR_SERIAL]")) { /* XXXRTH not enough error checking here */ cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (cp == NULL || sscanf((char *) cp, "from %u to %u", old_serial, new_serial) != 2) { ns_error(ns_log_update, "incr_serial problem with %s", zp->z_updatelog); } else { serial = get_serial(zp); } cont = 1; } else if (!strcasecmp(buf, "[END_DELTA]")) { prev_pktdone = 1; cont = 1; lineno++; } if (prev_pktdone) { if (!EMPTY(*listuprec)) { n++; return (DBIXFR_FOUND_RR); } prev_pktdone = 0; } if (cont) { cont = 0; continue; } if (!inside_next) continue; /* * inside the same update packet, continue accumulating * records. */ section = -1; n = strlen(buf); if (buf[n - 1] == ':') buf[--n] = '\0'; for (mp = m_section; mp < m_section + M_SECTION_CNT; mp++) if (!strcasecmp(buf, mp->token)) { section = mp->val; break; } ttl = 0; type = -1; class = zp->z_class; n = 0; data[0] = '\0'; opcode = -1; switch (section) { case S_ZONE: cp = fgets(buf, sizeof buf, fp); if (!cp) *buf = '\0'; n = sscanf(cp, "origin %s class %s serial %lu", origin, sclass, &lutmp); serial = lutmp; if (current_serial == 0) current_serial = serial; else if (current_serial != serial) { ns_debug(ns_log_update, 1, "%s:line %d serial # askew %d %d", filename, lineno, serial, current_serial); current_serial = serial; err++; } if (n != 3 || ns_samename(origin, zp->z_origin) != 1) err++; if (cp) lineno++; if (!err && inside_next) { int success; dname = origin; type = T_SOA; class = res_nametoclass(sclass, &success); if (!success) { err++; break; } matches = findzone(dname, class, 0, zonelist, MAXDNAME); if (matches) zonenum = zonelist[0]; else err++; } break; case S_PREREQ: case S_UPDATE: /* Operation code. */ if (!getword(buf, sizeof buf, fp, 0)) { err++; break; } if (buf[0] == '{') { n = strlen(buf); for (i = 0; (u_int32_t) i < n; i++) buf[i] = buf[i + 1]; if (buf[n - 2] == '}') buf[n - 2] = '\0'; } for (mp = m_opcode; mp < m_opcode + M_OPCODE_CNT; mp++) if (!strcasecmp(buf, mp->token)) { opcode = mp->val; break; } if (opcode == -1) { err++; break; } /* Owner's domain name. */ if (!getword((char *) dnbuf, sizeof dnbuf, fp, 0)) { err++; break; } n = strlen((char *) dnbuf) - 1; if (dnbuf[n] == '.') dnbuf[n] = '\0'; dname = dnbuf; ttl = 0; type = -1; class = zp->z_class; n = 0; data[0] = '\0'; (void) getword(buf, sizeof buf, fp, 1); if (isdigit(buf[0])) { /* ttl */ if (ns_parse_ttl(buf, &l) < 0) { err++; break; } ttl = l; (void) getword(buf, sizeof buf, fp, 1); } /* possibly class */ if (buf[0] != '\0') { int success; int maybe_class; maybe_class = res_nametoclass(buf, &success); if (success) { class = maybe_class; (void) getword(buf, sizeof buf, fp, 1); } } /* possibly type */ if (buf[0] != '\0') { int success; int maybe_type; maybe_type = res_nametotype(buf, &success); if (success) { type = maybe_type; (void) getword(buf, sizeof buf, fp, 1); } } if (buf[0] != '\0') /* possibly rdata */ /* * Convert the ascii data 'buf' to the proper * format based on the type and pack into * 'data'. * * XXX - same as in db_load(), consolidation * needed */ switch (type) { case T_A: if (!inet_aton(buf, &ina)) { err++; break; } n = ntohl(ina.s_addr); cp = data; PUTLONG(n, cp); n = INT32SZ; break; case T_HINFO: case T_ISDN: n = strlen(buf); data[0] = n; memcpy(data + 1, buf, n); n++; if (!getword(buf, sizeof buf, fp, 0)) { i = 0; } else { endline(fp); i = strlen(buf); } data[n] = i; n++; memcpy(data + n + 1, buf, i); n += i; break; case T_SOA: case T_MINFO: case T_RP: (void) strcpy(data, buf); cp = data + strlen(data) + 1; if (!getword((char *) cp, sizeof data - (cp - data), fp, 1)) { err++; break; } cp += strlen((char *) cp) + 1; if (type != T_SOA) { n = cp - data; break; } if (class != zp->z_class || ns_samename(dname, zp->z_origin) != 1) { err++; break; } c = getnonblank(fp, zp->z_updatelog, 0); if (c == '(') { multiline = 1; } else { multiline = 0; ungetc(c, fp); } n = getnum(fp, zp->z_updatelog, GETNUM_SERIAL, &multiline); if (getnum_error) { err++; break; } if (opcode == ADD) *new_serial = n; current_serial = n; PUTLONG(n, cp); for (i = 0; i < 4; i++) { if (!getword(buf, sizeof buf, fp, 1)) { err++; break; } if (ns_parse_ttl(buf, &l) < 0) { err++; break; } n = l; PUTLONG(n, cp); } if (multiline) { c = getnonblank(fp, zp->z_updatelog, 1); if (c != ')') { ungetc(c, fp); err++; break; } } endline(fp); n = cp - data; break; case T_WKS: if (!inet_aton(buf, &ina)) { err++; break; } n = ntohl(ina.s_addr); cp = data; PUTLONG(n, cp); *cp = (char) getprotocol(fp, zp->z_updatelog); n = INT32SZ + sizeof(char); n = getservices((int) n, data, fp, zp->z_updatelog); break; case T_NS: case T_CNAME: case T_MB: case T_MG: case T_MR: case T_PTR: (void) strcpy(data, buf); if (makename(data, origin, sizeof(data)) == -1) { err++; break; } n = strlen(data) + 1; break; case T_MX: case T_AFSDB: case T_RT: n = 0; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); /* catch bad values */ cp = data; PUTSHORT((u_int16_t) n, cp); if (!getword(buf, sizeof(buf), fp, 1)) { err++; break; } (void) strcpy((char *) cp, buf); if (makename((char *) cp, origin, sizeof(data) - (cp - data)) == -1) { err++; break; } /* advance pointer to end of data */ cp += strlen((char *) cp) + 1; /* now save length */ n = (cp - data); break; case T_PX: n = 0; data[0] = '\0'; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); cp = data; PUTSHORT((u_int16_t) n, cp); for (i = 0; i < 2; i++) { if (!getword(buf, sizeof(buf), fp, 0)) { err++; break; } (void) strcpy((char *) cp, buf); cp += strlen((char *) cp) + 1; } n = cp - data; break; case T_TXT: case T_X25: i = strlen(buf); cp = data; datasize = sizeof data; cp1 = buf; while (i > MAXCHARSTRING) { if (datasize <= MAXCHARSTRING) { ns_error(ns_log_update, "record too big"); return (-1); } datasize -= MAXCHARSTRING; *cp++ = (char)MAXCHARSTRING; memcpy(cp, cp1, MAXCHARSTRING); cp += MAXCHARSTRING; cp1 += MAXCHARSTRING; i -= MAXCHARSTRING; } if (datasize < i + 1) { ns_error(ns_log_update, "record too big"); return (-1); } *cp++ = i; memcpy(cp, cp1, i); cp += i; n = cp - data; endline(fp); /* XXXVIX: segmented texts 4.9.5 */ break; case T_NSAP: n = inet_nsap_addr(buf, (u_char *) data, sizeof data); endline(fp); break; case T_LOC: cp = buf + (n = strlen(buf)); *cp = ' '; cp++; while ((i = getc(fp), *cp = i, i != EOF) && *cp != '\n' && (n < MAXDATA)) { cp++; n++; } if (*cp == '\n') ungetc(*cp, fp); *cp = '\0'; n = loc_aton(buf, (u_char *) data); if (n == 0) { err++; break; } endline(fp); break; case ns_t_sig: case ns_t_nxt: case ns_t_key: case ns_t_cert:{ const char *errmsg = NULL; n = parse_sec_rdata(buf, sizeof(buf), 1, (u_char *) data, sizeof(data), fp, zp, dname, ttl, type, domain_ctx, transport, &errmsg); if (errmsg) { err++; endline(fp); n = 0; } break; } default: if (strcmp(buf, "\\#") != 0) { err++; break; } if (!getword(buf, sizeof buf, fp, 0) || !isdigit((unsigned char)buf[0])) { err++; break; } - n = strtoul(buf, &cp, 10); - if (n > 0xffff || *cp != '\0') { + errno = 0; + n = strtoul(buf, &cp, 10); + if (errno != 0 || n > 0xffff || *cp != '\0') { err++; break; } multiline = 0; i = isc_gethexstring((u_char *)data, sizeof(data), n, fp, &multiline); if (i == -1) { err++; break; } if (multiline) { c = getnonblank(fp, zp->z_updatelog, 1); if (c != ')') { ungetc(c, fp); err++; break; } multiline = 0; } endline(fp); } if (section == S_PREREQ) { ttl = 0; if (opcode == NXDOMAIN) { class = C_NONE; type = T_ANY; n = 0; } else if (opcode == YXDOMAIN) { class = C_ANY; type = T_ANY; n = 0; } else if (opcode == NXRRSET) { class = C_NONE; n = 0; } else if (opcode == YXRRSET) { if (n == 0) class = C_ANY; } } else {/* section == S_UPDATE */ if (opcode == DELETE) { if (n == 0) { class = C_ANY; if (type == -1) type = T_ANY; } else { class = zp->z_class; } } } break; case S_ADDT: default: ns_debug(ns_log_update, 1, "cannot interpret section: %d", section); inside_next = 0; err++; } if (err) { inside_next = 0; ns_debug(ns_log_update, 1, "merge of update id %d failed due to error at line %d", id, lineno); return (DBIXFR_ERROR); } rrecp = res_mkupdrec(section, dname, class, type, ttl); if (section != S_ZONE) { struct databuf *dp; dp = savedata(class, type, ttl, (u_char *) data, n); dp->d_zone = zonenum; dp->d_cred = DB_C_ZONE; dp->d_clev = nlabels(zp->z_origin); rrecp->r_dp = dp; rrecp->r_opcode = opcode; } else { rrecp->r_zone = zonenum; rrecp->r_opcode = opcode; } /* remove add/delete pairs */ if (section == S_UPDATE) { ns_updrec *arp; int foundmatch; arp = TAIL(*listuprec); foundmatch = 0; while (arp) { if (arp->r_section == S_UPDATE && ((arp->r_opcode == DELETE && opcode == ADD) || (opcode == DELETE && arp->r_opcode == ADD)) && arp->r_dp->d_type == rrecp->r_dp->d_type && arp->r_dp->d_class == rrecp->r_dp->d_class && arp->r_dp->d_ttl == rrecp->r_dp->d_ttl && ns_samename(arp->r_dname, dname) == 1 && db_cmp(arp->r_dp, rrecp->r_dp) == 0) { db_detach(&rrecp->r_dp); db_detach(&arp->r_dp); UNLINK(*listuprec, arp, r_link); res_freeupdrec(arp); res_freeupdrec(rrecp); foundmatch = 1; break; } arp = PREV(arp, r_link); } if (foundmatch) continue; } APPEND(*listuprec, rrecp, r_link); /* Override zone number with current zone serial number */ rrecp->r_zone = serial; } if (err) return (DBIXFR_ERROR); return (DBIXFR_END); } diff --git a/contrib/bind/bin/named/db_load.c b/contrib/bind/bin/named/db_load.c index 12d01979ceff..2b007295bf07 100644 --- a/contrib/bind/bin/named/db_load.c +++ b/contrib/bind/bin/named/db_load.c @@ -1,2749 +1,2747 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)db_load.c 4.38 (Berkeley) 3/2/91"; -static const char rcsid[] = "$Id: db_load.c,v 8.121 2001/11/12 21:22:22 marka Exp $"; +static const char rcsid[] = "$Id: db_load.c,v 8.123 2002/08/20 04:27:23 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1988, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ /* * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Load zone from ASCII file on local host. Format similar to RFC 883. */ /* Import. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" /* Forward. */ static int gettoken(FILE *, const char *); static int getcharstring(char *, char *, int, int, int, FILE *, const char *); static int genname(char *, int, const char *, char *, int); static int getmlword(char *, size_t, FILE *, int); static int getallwords(char *, size_t, FILE *, int); static u_int32_t wordtouint32(char *); static void fixup_soa(const char *fn, struct zoneinfo *zp); static int get_nxt_types(u_char *, FILE *, const char *); static int parse_sig_rr(char *, int, u_char *, int, FILE *, struct zoneinfo *, char *, u_int32_t , enum context, enum transport, const char **); static int parse_key_rr(char *, int, u_char *, int, FILE *, const char **); static int parse_cert_rr(char *, int, u_char *, int, FILE *, const char **); static int parse_nxt_rr(char *, u_char *, int, FILE *, struct zoneinfo *, char *, enum context, enum transport, const char **); static int wordtouint32_error = 0; static int empty_token = 0; static int getmlword_nesting = 0; /* Global. */ static int clev; /* a zone deeper in a hierarchy has more credibility */ /* * Parser token values */ #define CURRENT 1 #define DOT 2 #define AT 3 #define DNAME 4 #define INCLUDE 5 #define ORIGIN 6 #define GENERATE 7 #define DEFAULTTTL 8 #define ERRTOK 9 #define MAKENAME_OK(N) \ do { \ if (!makename_ok(N, origin, class, zp, \ transport, context, \ domain, filename, lineno, \ data_size - ((u_char*)N - data))) { \ errs++; \ sprintf(buf, "bad name \"%s\"", N); \ goto err; \ } \ } while (0) #define MAKENAME_OKZP(N, SI) \ do { \ if (!makename_ok(N, zp->z_origin, zp->z_class, zp, \ transport, context, \ domain, zp->z_source, lineno, \ SI - ((u_char*)N - data))) { \ errs++; \ sprintf(buf, "bad name \"%s\"", N); \ goto err; \ } \ } while (0) #define RANGE(x, min, max) \ (((x) > (max)) ? (max) : (((x) < (min)) ? (min) : (x))) /* Public. */ /* int * db_load(filename, in_origin, zp, def_domain, isixfr) * load a database from `filename' into zone `zp'. append `in_origin' * to all nonterminal domain names in the file. `def_domain' is the * default domain for include files or NULL for zone base files. * returns: * -1 = can't open file * 0 = success * >0 = number of errors encountered */ int db_load(const char *filename, const char *in_origin, struct zoneinfo *zp, const char *def_domain, int isixfr) { static int read_soa, read_ns, rrcount; static u_int32_t default_ttl, default_warn; static struct filenames { struct filenames *next; char *name; } *filenames, *fn; const char *errtype = "Database"; char *cp; char domain[MAXDNAME], origin[MAXDNAME], tmporigin[MAXDNAME]; char buf[MAXDATA]; char genlhs[MAXDNAME], genrhs[MAXDNAME]; u_char data[MAXDATA]; unsigned int data_size = sizeof(data); int c, someclass, class, type, dbflags, dataflags, multiline = 0; int slineno, i, errs, didinclude, ininclude, escape, success; u_int32_t ttl, n, serial; u_long tmplong; struct databuf *dp; FILE *fp; struct stat sb; struct in_addr ina; enum transport transport; enum context context; struct sockaddr_in empty_from; int genstart, genend, genstep; char *thisfile; void *state = NULL; int loggenerr; empty_from.sin_family = AF_INET; empty_from.sin_addr.s_addr = htonl(INADDR_ANY); empty_from.sin_port = htons(0); /* * We use an 'if' inside of the 'do' below because otherwise the Solaris * compiler detects that the 'while' is never executed because of the 'goto' * and complains. */ #define ERRTO(msg) do { if (1) { errtype = msg; goto err; } } while (0) #define ERRTOZ(msg) do { if (1) { errtype = msg; buf[0] = '\0'; goto err; } } while (0) switch (zp->z_type) { case Z_PRIMARY: /* Any updates should be saved before we attempt to reload. */ INSIST((zp->z_flags & (Z_NEED_DUMP|Z_NEED_SOAUPDATE)) == 0); case Z_HINT: if (filename == NULL) { ns_error(ns_log_load, "Required filename not specified for Hint zone"); zp->z_flags |= Z_DB_BAD; zp->z_ftime = 0; return (0); } transport = primary_trans; break; case Z_SECONDARY: case Z_STUB: transport = secondary_trans; break; case Z_CACHE: transport = response_trans; break; default: transport = response_trans; /*guessing*/ break; } errs = 0; didinclude = 0; ininclude = (def_domain != NULL); if (!ininclude) { rrcount = 0; read_soa = 0; read_ns = 0; default_ttl = USE_MINIMUM; default_warn = 1; clev = nlabels(in_origin); filenames = NULL; zp->z_minimum = USE_MINIMUM; } ttl = default_ttl; ns_debug(ns_log_load, 1, "db_load(%s, %s, %d, %s, %s)", filename, in_origin, zp - zones, def_domain ? def_domain : "Nil", isixfr ? "IXFR" : "Normal"); fn = (struct filenames *)memget(sizeof *filenames); if (fn == NULL) ns_panic(ns_log_db, 0, "db_load: memget failed"); thisfile = fn->name = savestr(filename, 1); fn->next = filenames; filenames = fn; strcpy(origin, in_origin); if ((fp = fopen(filename, "r")) == NULL) { ns_warning(ns_log_load, "db_load could not open: %s: %s", filename, strerror(errno)); zp->z_ftime = 0; if (ininclude) return (-1); errs = -1; goto cleanup; } if (zp->z_type == Z_HINT) { dbflags = DB_NODATA | DB_NOHINTS; dataflags = DB_F_HINT; #ifdef STUBS } else if (zp->z_type == Z_STUB && clev == 0) { dbflags = DB_NODATA | DB_NOHINTS; dataflags = DB_F_HINT; #endif } else { dbflags = DB_NODATA; dataflags = 0; } gettime(&tt); if (fstat(fileno(fp), &sb) < 0) { ns_warning(ns_log_load, "fstat failed: %s: %s", filename, strerror(errno)); sb.st_mtime = (int)tt.tv_sec; } slineno = lineno; lineno = 1; if (def_domain) strcpy(domain, def_domain); else domain[0] = '\0'; class = zp->z_class; zp->z_flags &= ~(Z_INCLUDE|Z_DB_BAD); while ((c = gettoken(fp, filename)) != EOF) { switch (c) { case INCLUDE: if (isixfr) { c = ERRTOK; break; } if (!getword(buf, sizeof buf, fp, 0)) /* file name*/ break; if (!getword(tmporigin, sizeof(tmporigin), fp, 1)) strcpy(tmporigin, origin); else { if (makename(tmporigin, origin, sizeof(tmporigin)) == -1) ERRTO("$INCLUDE makename failed"); endline(fp); } didinclude = 1; i = db_load(buf, tmporigin, zp, domain, ISNOTIXFR); errs += (i == -1) ? 1 : i; continue; case ORIGIN: (void) strcpy(buf, origin); if (!getword(origin, sizeof(origin), fp, 1)) break; ns_debug(ns_log_load, 3, "db_load: origin %s, buf %s", origin, buf); if (makename(origin, buf, sizeof(origin)) == -1) ERRTO("$ORIGIN makename failed"); ns_debug(ns_log_load, 3, "db_load: origin now %s", origin); continue; case GENERATE: if (!getword(buf, sizeof(buf), fp, 0)) ERRTOZ("$GENERATE missing RANGE"); n = sscanf(buf, "%d-%d/%d", &genstart, &genend, &genstep); if (n != 2 && n != 3) ERRTO("$GENERATE invalid range"); if (n == 2) genstep = 1; if ((genend < genstart) || (genstart < 0) || (genstep < 0)) ERRTO("$GENERATE invalid range"); if (!getword(genlhs, sizeof(genlhs), fp, 2)) ERRTOZ("$GENERATE missing LHS"); if (!getword(buf, sizeof(buf), fp, 0)) ERRTOZ("GENERATE missing TYPE"); type = res_nametotype(buf, &success); if (success == 0 || type == ns_t_any) { ns_info(ns_log_load, "%s: Line %d: $GENERATE unknown type: %s.", filename, lineno, buf); errs++; endline(fp); continue; } switch (type) { case ns_t_ns: case ns_t_ptr: case ns_t_cname: case ns_t_a: case ns_t_aaaa: break; default: ERRTO("$GENERATE unsupported type"); } if (!getword(genrhs, sizeof(genrhs), fp, 2)) ERRTOZ("$GENERATE missing RHS"); loggenerr = 1; for (i = genstart; i <= genend; i += genstep) { if (genname(genlhs, i, origin, domain, sizeof domain) == -1) ERRTOZ("$GENERATE genname LHS failed"); if (!ns_samedomain(domain, zp->z_origin)) { /* Log first per $GENERATE. */ if (loggenerr) { ns_info(ns_log_load, "%s:%d: $GENERATE LHS out of zone (ignored)", filename, lineno); loggenerr = 0; } continue; } context = ns_ownercontext(type, transport); if (!ns_nameok(NULL, domain, class, zp, transport, context, domain, inaddr_any)) { strcpy(buf, domain); ERRTO("$GENERATE owner name error"); } switch (type) { case ns_t_ns: case ns_t_ptr: case ns_t_cname: if (genname(genrhs, i, origin, (char *)data, sizeof data) == -1) ERRTOZ("$GENERATE genname RHS failed"); switch (type) { case ns_t_ns: context = hostname_ctx; break; case ns_t_ptr: context = ns_ptrcontext(domain); break; case ns_t_cname: context = domain_ctx; break; } if (!ns_nameok(NULL, (char *)data, class, zp, transport, context, domain, inaddr_any)) { strncpy(buf, domain, sizeof(buf)); buf[sizeof(buf)-1] = '\0'; ERRTO("$GENERATE name error"); } n = strlen((char *)data) + 1; break; case ns_t_a: case ns_t_aaaa: if (genname(genrhs, i, NULL, (char *)data, sizeof data) == -1) ERRTOZ("$GENERATE genname RHS failed"); strncpy(buf, (char*)data, sizeof(buf)); buf[sizeof(buf)-1] = '\0'; switch (type) { case ns_t_a: if (!inet_aton(buf, &ina)) ERRTO("IP Address"); (void) ina_put(ina, data); n = NS_INT32SZ; break; case ns_t_aaaa: if (inet_pton(AF_INET6, buf, data) <= 0) ERRTO("IPv6 Address"); n = NS_IN6ADDRSZ; break; } break; default: ERRTOZ("$GENERATE unsupported context"); } dp = savedata(class, type, (u_int32_t)ttl, (u_char *)data, (int)n); dp->d_zone = zp - zones; dp->d_flags = dataflags; dp->d_cred = DB_C_ZONE; dp->d_clev = clev; c = db_set_update(domain, dp, &state, dbflags, (dataflags & DB_F_HINT) != 0 ? &fcachetab : &hashtab, empty_from, &rrcount, lineno, filename); if (c != OK) { if (c == CNAMEANDOTHER || c == NONGLUE) errs++; } db_detach(&dp); } endline(fp); continue; case DNAME: if (!getword(domain, sizeof(domain), fp, 1)) break; if (makename(domain, origin, sizeof(domain)) == -1) ERRTO("ownername makename failed"); goto gotdomain; case DEFAULTTTL: if (getttl(fp, filename, lineno, &n, &multiline) <= 0 || n > MAXIMUM_TTL) { ERRTO("$TTL bad TTL value"); } ttl = default_ttl = n; continue; case AT: (void) strcpy(domain, origin); goto gotdomain; case DOT: domain[0] = '\0'; /* FALLTHROUGH */ case CURRENT: gotdomain: if (!getword(buf, sizeof buf, fp, 0)) { if (c == CURRENT) continue; break; } if (ns_parse_ttl(buf, &tmplong) < 0) { if (zp->z_type == z_master && default_warn && (default_ttl == USE_MINIMUM)) { ns_warning(ns_log_load, "Zone \"%s\" (file %s): %s", zp->z_origin, filename, "No default TTL ($TTL ) set, using SOA minimum instead"); default_warn = 0; } ttl = (u_int32_t)default_ttl; } else { ttl = tmplong; if (ttl > MAXIMUM_TTL) { ns_info(ns_log_load, "%s: Line %d: TTL > %u; converted to 0", filename, lineno, MAXIMUM_TTL); ttl = 0; } if (zp->z_type == Z_CACHE) { /* * This allows the cache entry to age * while sitting on disk (powered off). */ if (ttl > max_cache_ttl) ttl = max_cache_ttl; ttl += sb.st_mtime; } if (!getword(buf, sizeof buf, fp, 0)) break; } /* Parse class (IN, etc) */ someclass = res_nametoclass(buf, &success); if (success && someclass != zp->z_class) { ns_info(ns_log_load, "%s: Line %d: wrong class: %s.", filename, lineno, p_class(someclass)); errs++; break; } if (success && someclass != C_ANY) { class = someclass; (void) getword(buf, sizeof buf, fp, 0); } /* Parse RR type (A, MX, etc) */ type = res_nametotype(buf, &success); if (success == 0 || type == ns_t_any) { ns_info(ns_log_load, "%s: Line %d: Unknown type: %s.", filename, lineno, buf); errs++; break; } if (ttl == USE_MINIMUM) ttl = zp->z_minimum; context = ns_ownercontext(type, transport); if (!ns_nameok(NULL, domain, class, zp, transport, context, domain, inaddr_any)) { errs++; ns_notice(ns_log_load, "%s:%d: owner name error", filename, lineno); break; } context = domain_ctx; switch (type) { case ns_t_key: case ns_t_sig: case ns_t_nxt: case ns_t_cert: /* * Don't do anything here for these types -- * they read their own input separately later. */ goto dont_get_word; case ns_t_soa: case ns_t_minfo: case ns_t_rp: case ns_t_ns: case ns_t_cname: case ns_t_mb: case ns_t_mg: case ns_t_mr: case ns_t_ptr: escape = 1; break; case ns_t_a: case ns_t_md: case ns_t_mf: case ns_t_null: case ns_t_hinfo: case ns_t_mx: case ns_t_txt: case ns_t_afsdb: case ns_t_x25: case ns_t_isdn: case ns_t_rt: case ns_t_nsap: case ns_t_nsap_ptr: case ns_t_px: case ns_t_gpos: case ns_t_aaaa: case ns_t_loc: case ns_t_eid: case ns_t_nimloc: case ns_t_srv: case ns_t_atma: case ns_t_naptr: case ns_t_kx: case ns_t_dname: case ns_t_sink: escape = 0; break; case ns_t_opt: case ns_t_tkey: case ns_t_tsig: case ns_t_ixfr: case ns_t_axfr: case ns_t_mailb: case ns_t_maila: case ns_t_any: case ns_t_zxfr: escape = 0; ns_info(ns_log_load, "%s: Line %d: meta type: %s.", filename, lineno, p_type(type)); errs++; break; case ns_t_a6: /* not implemented */ default: escape = 1; break; } if (!getword(buf, sizeof buf, fp, escape)) break; ns_debug(ns_log_load, 3, "d='%s', c=%d, t=%d, ttl=%u, data='%s'", domain, class, type, ttl, buf); /* * Convert the ascii data 'buf' to the proper format * based on the type and pack into 'data'. */ dont_get_word: switch (type) { case ns_t_a: if (!inet_aton(buf, &ina)) ERRTO("IP Address"); (void) ina_put(ina, data); n = NS_INT32SZ; break; case ns_t_soa: context = hostname_ctx; goto soa_rp_minfo; case ns_t_rp: case ns_t_minfo: context = mailname_ctx; /* FALLTHROUGH */ soa_rp_minfo: (void) strcpy((char *)data, buf); MAKENAME_OK((char *)data); cp = (char *)(data + strlen((char *)data) + 1); if (!getword(cp, (sizeof data) - (cp - (char*)data), fp, 1)) ERRTO("Domain Name"); if (type == ns_t_rp) context = domain_ctx; else context = mailname_ctx; MAKENAME_OK(cp); cp += strlen((char *)cp) + 1; if (type != ns_t_soa) { n = cp - (char *)data; break; } if (ns_samename(zp->z_origin, domain) != 1) { errs++; ns_error(ns_log_load, "%s:%d: SOA for \"%s\" not at zone top \"%s\"", filename, lineno, domain, zp->z_origin); } c = getnonblank(fp, filename, 0); if (c == '(') { multiline = 1; } else { multiline = 0; ungetc(c, fp); } serial = zp->z_serial; zp->z_serial = getnum(fp, filename, GETNUM_SERIAL, &multiline); if (getnum_error) errs++; n = (u_int32_t) zp->z_serial; PUTLONG(n, cp); if (serial != 0 && SEQ_GT(serial, zp->z_serial)) { ns_notice(ns_log_load, "%s:%d: WARNING: new serial number < old (%lu < %lu)", filename , lineno, (unsigned long)zp->z_serial, (unsigned long)serial); } if (getttl(fp, filename, lineno, &n, &multiline) <= 0) { errs++; n = INIT_REFRESH; } PUTLONG(n, cp); zp->z_refresh = RANGE(n, MIN_REFRESH, MAX_REFRESH); if (zp->z_type == Z_SECONDARY #if defined(STUBS) || zp->z_type == Z_STUB #endif ) { ns_refreshtime(zp, MIN(sb.st_mtime, tt.tv_sec)); sched_zone_maint(zp); } #ifdef BIND_UPDATE if ((zp->z_type == Z_PRIMARY) && (zp->z_flags & Z_DYNAMIC)) if ((u_int32_t)zp->z_soaincrintvl > zp->z_refresh/3) { ns_info(ns_log_load, "zone soa update time truncated to 1/3rd of refresh time"); zp->z_soaincrintvl = zp->z_refresh / 3; } #endif if (getttl(fp, filename, lineno, &n, &multiline) <= 0) { errs++; n = INIT_REFRESH; } PUTLONG(n, cp); zp->z_retry = RANGE(n, MIN_RETRY, MAX_RETRY); if (getttl(fp, filename, lineno, &n, &multiline) <= 0) { errs++; n = INIT_REFRESH; } PUTLONG(n, cp); zp->z_expire = RANGE(n, zp->z_refresh, MAX_EXPIRE); if (getttl(fp, filename, lineno, &n, &multiline) <= 0) { errs++; n = 120; } PUTLONG(n, cp); if (n > MAXIMUM_TTL) { ns_info(ns_log_load, "%s: Line %d: SOA minimum TTL > %u; converted to 0", filename, lineno, MAXIMUM_TTL); zp->z_minimum = 0; } else zp->z_minimum = n; if (ttl == USE_MINIMUM) ttl = n; n = cp - (char *)data; if (multiline) { buf[0] = getnonblank(fp, filename, 1); buf[1] = '\0'; if (buf[0] != ')') ERRTO("SOA \")\""); multiline = 0; endline(fp); } read_soa++; if (zp->z_type == Z_PRIMARY) fixup_soa(filename, zp); break; case ns_t_wks: /* Address */ if (!inet_aton(buf, &ina)) ERRTO("WKS IP Address"); (void) ina_put(ina, data); /* Protocol */ data[INADDRSZ] = getprotocol(fp, filename); /* Services */ n = getservices(NS_INT32SZ + sizeof(char), (char *)data, fp, filename); break; case ns_t_ns: if (ns_samename(zp->z_origin, domain) == 1) read_ns++; context = hostname_ctx; goto cname_etc; case ns_t_cname: case ns_t_mb: case ns_t_mg: case ns_t_mr: context = domain_ctx; goto cname_etc; case ns_t_ptr: context = ns_ptrcontext(domain); cname_etc: (void) strcpy((char *)data, buf); MAKENAME_OK((char *)data); n = strlen((char *)data) + 1; break; case ns_t_naptr: /* Order Preference Flags Service Replacement Regexp */ n = 0; cp = buf; /* Order */ while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); /* catch bad values */ if (cp == buf || n > 65535) ERRTO("NAPTR Order"); cp = (char *)data; PUTSHORT((u_int16_t)n, cp); /* Preference */ n = getnum(fp, filename, GETNUM_NONE, &multiline); if (getnum_error || n > 65536) ERRTO("NAPTR Preference"); PUTSHORT((u_int16_t)n, cp); /* Flags */ if (!getword(buf, sizeof buf, fp, 0)) ERRTO("NAPTR Flags"); n = strlen(buf); if (n > 255) ERRTO("NAPTR Flags too big"); *cp++ = n; memcpy(cp, buf, (int)n); cp += n; /* Service Classes */ if (!getword(buf, sizeof buf, fp, 0)) ERRTO("NAPTR Service Classes"); n = strlen(buf); if (n > 255) ERRTO("NAPTR Service Classes too big"); *cp++ = n; memcpy(cp, buf, (int)n); cp += n; /* Pattern */ if (!getword(buf, sizeof buf, fp, 0)) ERRTO("NAPTR Pattern"); n = strlen(buf); if (n > 255) ERRTO("NAPTR Pattern too big"); *cp++ = n; memcpy(cp, buf, (int)n); cp += n; /* Replacement */ if (!getword(buf, sizeof buf, fp, 1)) ERRTO("NAPTR Replacement"); n = strlen(buf); if (n > data_size - ((u_char *)cp - data)) ERRTO("NAPTR Replacement too big"); (void) strcpy((char *)cp, buf); context = domain_ctx; MAKENAME_OK(cp); /* advance pointer to end of data */ cp += strlen((char *)cp) +1; /* now save length */ n = (cp - (char *)data); break; case ns_t_mx: case ns_t_afsdb: case ns_t_rt: case ns_t_srv: n = 0; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); /* catch bad values */ if ((cp == buf) || (n > 65535)) ERRTO("Priority"); cp = (char *)data; PUTSHORT((u_int16_t)n, cp); if (type == ns_t_srv) { n = getnum(fp, filename, GETNUM_NONE, &multiline); if (getnum_error || n > 65536) ERRTO("SRV RR"); PUTSHORT((u_int16_t)n, cp); n = getnum(fp, filename, GETNUM_NONE, &multiline); if (getnum_error || n > 65536) ERRTO("SRV RR"); PUTSHORT((u_int16_t)n, cp); } if (!getword(buf, sizeof buf, fp, 1)) ERRTO("Domain Name"); (void) strcpy((char *)cp, buf); context = hostname_ctx; MAKENAME_OK(cp); /* advance pointer to end of data */ cp += strlen((char *)cp) +1; /* now save length */ n = (cp - (char *)data); break; case ns_t_px: context = domain_ctx; n = 0; data[0] = '\0'; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); /* catch bad values */ if ((cp == buf) || (n > 65535)) ERRTO("PX Priority"); cp = (char *)data; PUTSHORT((u_int16_t)n, cp); if (!getword(buf, sizeof buf, fp, 0)) ERRTO("PX Domain1"); (void) strcpy((char *)cp, buf); MAKENAME_OK(cp); /* advance pointer to next field */ cp += strlen((char *)cp) + 1; if (!getword(buf, sizeof buf, fp, 0)) ERRTO("PX Domain2"); (void) strcpy((char *)cp, buf); MAKENAME_OK(cp); /* advance pointer to end of data */ cp += strlen((char *)cp) + 1; /* now save length */ n = (cp - (char *)data); break; case ns_t_hinfo: n = getcharstring(buf, (char *)data, type, 2, 2, fp, filename); if (n == 0) ERRTO("HINFO RR"); break; case ns_t_isdn: n = getcharstring(buf, (char *)data, type, 1, 2, fp, filename); if (n == 0) ERRTO("ISDN RR"); break; case ns_t_txt: n = getcharstring(buf, (char *)data, type, 1, 0, fp, filename); if (n == 0) ERRTO("TXT RR"); break; case ns_t_x25: n = getcharstring(buf, (char *)data, type, 1, 1, fp, filename); if (n == 0) ERRTO("X25 RR"); break; case ns_t_nsap: if (buf[0] != '0' || (buf[1] != 'x' && buf[1] != 'X')) ERRTO("NSAP RR: no leading 0x"); n = inet_nsap_addr(buf, (u_char *)data, sizeof data); if (n == 0) ERRTO("NSAP RR"); endline(fp); break; case ns_t_aaaa: if (inet_pton(AF_INET6, buf, data) <= 0) ERRTO("IPv4 Address"); n = NS_IN6ADDRSZ; endline(fp); break; case ns_t_nxt: case ns_t_key: case ns_t_cert: case ns_t_sig: { const char *errmsg = NULL; int ret; if (ttl == USE_MINIMUM) /* no ttl set */ ttl = 0; ret = parse_sec_rdata(buf, sizeof(buf), 0, data, sizeof(data), fp, zp, domain, ttl, type, domain_ctx, transport, &errmsg); if (ret < 0) { errtype = errmsg; goto err; } else n = ret; break; } case ns_t_loc: cp = buf + (n = strlen(buf)); *cp = ' '; cp++; n++; while ((i = getc(fp), *cp = i, i != EOF) && *cp != '\n' && (n < MAXDATA)) { cp++; n++; } if (*cp == '\n') /* leave \n for getword */ ungetc(*cp, fp); *cp = '\0'; /* now process the whole line */ n = loc_aton(buf, (u_char *)data); if (n == 0) goto err; endline(fp); break; default: if (strcmp(buf, "\\#") != 0) goto err; if (!getword(buf, sizeof buf, fp, 0) || !isdigit((unsigned char)buf[0])) ERRTO("opaque length"); + errno = 0; n = strtoul(buf, &cp, 10); - if (n > 0xffff || *cp != '\0') + if (errno != 0 || n > 0xffff || *cp != '\0') ERRTO("opaque length"); multiline = 0; i = isc_gethexstring(data, sizeof(data), n, fp, &multiline); if (i == -1) ERRTO("opaque data read failed"); if (multiline) { buf[0] = getnonblank(fp, filename, 1); buf[1] = '\0'; if (buf[0] != ')') ERRTO("\")\" expected"); multiline = 0; } endline(fp); } /* * Ignore data outside the zone. */ if (zp->z_type != Z_CACHE && !ns_samedomain(domain, zp->z_origin)) { ns_info(ns_log_load, "%s:%d: data \"%s\" outside zone \"%s\" (ignored)", filename, lineno, domain, zp->z_origin); continue; } if (ttl == USE_MINIMUM) /* no ttl set */ ttl = 0; dp = savedata(class, type, (u_int32_t)ttl, (u_char *)data, (int)n); dp->d_zone = zp - zones; dp->d_flags = dataflags; dp->d_cred = DB_C_ZONE; dp->d_clev = clev; c = db_set_update(domain, dp, &state, dbflags, (dataflags & DB_F_HINT) != 0 ? &fcachetab : &hashtab, empty_from, &rrcount, lineno, filename); if (c == CNAMEANDOTHER || c == NONGLUE) errs++; db_detach(&dp); continue; case ERRTOK: break; } err: errs++; ns_notice(ns_log_load, "%s:%d: %s error near (%s)", filename, empty_token ? (lineno - 1) : lineno, errtype, buf); if (!empty_token) endline(fp); } c = db_set_update(NULL, NULL, &state, dbflags, (dataflags & DB_F_HINT) ? &fcachetab : &hashtab, empty_from, &rrcount, lineno, filename); if (c != OK) { if (c == CNAMEANDOTHER || c == NONGLUE) errs++; } (void) my_fclose(fp); lineno = slineno; if (!ininclude) { if (didinclude) { zp->z_flags |= Z_INCLUDE; zp->z_ftime = 0; } else zp->z_ftime = sb.st_mtime; zp->z_lastupdate = sb.st_mtime; if (zp->z_type != Z_CACHE && zp->z_type != Z_HINT) { const char *msg = NULL; if (read_soa == 0) msg = "no SOA RR found"; else if (read_soa != 1) msg = "multiple SOA RRs found"; else if (read_ns == 0) msg = "no NS RRs found at zone top"; else if (!rrcount) msg = "no relevant RRs found"; if (msg != NULL) { errs++; ns_warning(ns_log_load, "Zone \"%s\" (file %s): %s", zp->z_origin, filename, msg); } } - errs += purge_nonglue(zp->z_origin, - (dataflags & DB_F_HINT) ? fcachetab : - hashtab, zp->z_class, - zp->z_type == z_master); + errs += purge_nonglue(zp, (dataflags & DB_F_HINT) ? fcachetab : + hashtab, zp->z_type == z_master); cleanup: while (filenames) { fn = filenames; filenames = filenames->next; fn->name = freestr(fn->name); memput(fn, sizeof *fn); } if (errs != 0) { if (errs != -1) ns_error(ns_log_load, "%s zone \"%s\" (%s) rejected due to errors (serial %u)", zoneTypeString(zp->z_type), zp->z_origin, p_class(zp->z_class), zp->z_serial); if ((zp->z_flags & Z_NOTIFY) != 0) ns_stopnotify(zp->z_origin, zp->z_class); - do_reload(zp->z_origin, zp->z_type, zp->z_class, - loading); + do_reload(zp, loading); } else ns_info(ns_log_load, "%s zone \"%s\" (%s) loaded (serial %u)", zoneTypeString(zp->z_type), zp->z_origin, p_class(zp->z_class), zp->z_serial); } if (errs != 0) { zp->z_flags |= Z_DB_BAD; zp->z_ftime = 0; } #ifdef BIND_NOTIFY if (errs == 0 && (!ininclude) && (initial_configuration == 0 || !NS_OPTION_P(OPTION_SUPNOTIFY_INITIAL)) && (zp->z_type == z_master || zp->z_type == z_slave)) ns_notify(zp->z_origin, zp->z_class, ns_t_soa); #endif return (errs); } void db_err(int err, char *domain, int type, const char *filename, int lineno) { if (filename != NULL && err == CNAMEANDOTHER) ns_warning(ns_log_load, "%s:%d:%s: CNAME and OTHER data error", filename, lineno, domain); if (err != DATAEXISTS) ns_debug(ns_log_load, 1, "update failed %s %d", domain, type); } static int gettoken(FILE *fp, const char *src) { int c; char op[32]; for (;;) { c = getc(fp); top: switch (c) { case EOF: return (EOF); case '$': if (getword(op, sizeof op, fp, 0)) { if (!strcasecmp("include", op)) return (INCLUDE); if (!strcasecmp("origin", op)) return (ORIGIN); if (!strcasecmp("generate", op)) return (GENERATE); if (!strcasecmp("ttl", op)) return (DEFAULTTTL); } ns_notice(ns_log_db, "%s:%d: Unknown $ option: $%s", src, lineno, op); return (ERRTOK); case ';': while ((c = getc(fp)) != EOF && c != '\n') ; goto top; case ' ': case '\t': return (CURRENT); case '.': return (DOT); case '@': return (AT); case '\n': lineno++; continue; case '\r': if (NS_OPTION_P(OPTION_TREAT_CR_AS_SPACE) != 0) return (CURRENT); default: (void) ungetc(c, fp); return (DNAME); } } } /* int * getword(buf, size, fp, preserve) * get next word, skipping blanks & comments. * '\' '\n' outside of "quotes" is considered a blank. * parameters: * buf - destination * size - of destination * fp - file to read from * preserve - should we preserve \ before \\ and \.? * if preserve == 2, then keep all \ * return value: * 0 = no word; perhaps EOL or EOF; lineno was incremented. * 1 = word was read */ int getword(char *buf, size_t size, FILE *fp, int preserve) { char *cp = buf; int c, spaceok, once; empty_token = 0; /* XXX global side effect. */ once = 0; while ((c = getc(fp)) != EOF) { once++; if (c == ';') { /* Comment. Skip to end of line. */ while ((c = getc(fp)) != EOF && c != '\n') (void)NULL; c = '\n'; } if (c == '\n') { /* * Unescaped newline. It's a terminator unless we're * already midway into a token. */ if (cp != buf) ungetc(c, fp); else lineno++; break; } if (c == '"') { /* "Quoted string." Gather the whole string here. */ while ((c = getc(fp)) != EOF && c!='"' && c!='\n') { if (c == '\\') { if ((c = getc(fp)) == EOF) c = '\\'; if (preserve) switch (c) { default: if (preserve == 1) break; case '\\': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (cp >= buf+size-1) break; *cp++ = '\\'; } if (c == '\n') lineno++; } if (cp >= buf+size-1) break; *cp++ = c; } /* * Newline string terminators are * not token terminators. */ if (c == '\n') { lineno++; break; } /* Sample following character, check for terminator. */ if ((c = getc(fp)) != EOF) ungetc(c, fp); if (c == EOF || isspace(c)) { *cp = '\0'; return (1); } continue; } spaceok = 0; if (c == '\\') { /* Do escape processing. */ if ((c = getc(fp)) == EOF) c = '\\'; if (preserve) switch (c) { default: if (preserve == 1) break; case '\\': case '#': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (cp >= buf+size-1) break; *cp++ = '\\'; } if (c == ' ' || c == '\t') spaceok++; } if (isspace(c) && !spaceok) { /* Blank of some kind. Skip run. */ while (isspace(c = getc(fp)) && c != '\n') (void)NULL; ungetc(c, fp); /* Blank means terminator if the token is nonempty. */ if (cp != buf) /* Trailing whitespace */ break; continue; /* Leading whitespace */ } if (cp >= buf + size - 1) break; *cp++ = (char)c; } *cp = '\0'; if (cp == buf) empty_token = 1; if (!once) lineno++; return (cp != buf); } /* * int * getttl(fp, fn, ln, ttl, multiline) * read a word from the file and parse it as a TTL. * return: * 1 ttl found * 0 word not read (EOF or EOL?) * -1 word read but it wasn't a ttl * side effects: * *ttl is written if the return value is to be 1. */ int getttl(FILE *fp, const char *fn, int lineno, u_int32_t *ttl, int *multiline) { char buf[MAXDATA]; u_long tmp; int ch; int len; while (!feof(fp) && !getword(buf, sizeof buf, fp, 0) && *multiline) (void)NULL; len = strlen(buf); if (*multiline && len && buf[len-1] == ')') { buf[len-1] = '\0'; *multiline = 0; } if (ns_parse_ttl(buf, &tmp) < 0) { ns_notice(ns_log_db, "%s:%d: expected a TTL, got \"%s\"", fn, lineno, buf); return (-1); } if (*multiline) { ch = getnonblank(fp, fn, 1); if (ch == EOF) return (-1); if (ch == ';') endline(fp); else ungetc(ch, fp); } *ttl = (u_int32_t)tmp; return (1); } /* Get multiline words. Same parameters as getword. Handles any number of leading ('s or )'s in the words it sees. FIXME: We kludge recognition of ( and ) for multiline input. Each paren must appear at the start of a (blank-separated) word, which is particularly counter-intuitive for ). Good enough for now, until Paul rewrites the parser. (gnu@toad.com, oct96) */ static int getmlword(char *buf, size_t size, FILE *fp, int preserve) { char *p; do { while (!getword (buf, size, fp, preserve)) { /* No more words on this line. See if doing the multiline thing. */ if (!getmlword_nesting) { /* Nope... */ ungetc('\n', fp); /* Push back newline */ lineno--; /* Unbump the lineno */ empty_token = 0; /* Undo this botch */ return 0; } if (feof(fp) || ferror(fp)) return 0; /* Error, no terminating ')' */ /* Continue reading til we get a word... */ } while ('(' == *buf) { /* Word starts with paren. Multiline mode. Move the rest of the word down over the paren. */ getmlword_nesting++; p = buf; while (0 != (p[0]=p[1])) p++; } while (')' == *buf) { getmlword_nesting--; p = buf; while (0 != (p[0]=p[1])) p++; } } while (buf[0] == 0); /* loop til we get a non-( non-) word */ return 1; /* Got a word... */ } /* Get all the remaining words on a line, concatenated into one big long (not too long!) string, with the whitespace squeezed out. This routine, like getword(), does not swallow the newline if words seen. This routine, unlike getword(), never swallows the newline if no words. Parameters are the same as getword(). Result is: 0 got no words at all 1 got one or more words -1 got too many words, they don't all fit; or missing close paren */ static int getallwords(char *buf, size_t size, FILE *fp, int preserve) { char *runningbuf = buf; int runningsize = size; int len; while (runningsize > 0) { if (!getmlword (runningbuf, runningsize, fp, preserve)) { return runningbuf!=buf; /* 1 or 0 */ } len = strlen(runningbuf); runningbuf += len; runningsize -= len; } return -1; /* Error, String too long */ } int getnum(FILE *fp, const char *src, int opt, int *multiline) { int c, n; int seendigit = 0; int seendecimal = 0; int m = 0; int allow_dots = 0; getnum_error = 0; #ifdef DOTTED_SERIAL if (opt & GETNUM_SERIAL) allow_dots++; #endif for (n = 0; (c = getc(fp)) != EOF; ) { if (isspace(c)) { if (c == '\n') { if (*multiline) lineno++; else if (!seendigit) goto eol; } if (seendigit) break; continue; } if (c == ';') { while ((c = getc(fp)) != EOF && c != '\n') ; if (c == '\n') { if (*multiline) lineno++; else if (!seendigit) goto eol; } if (seendigit) break; continue; } if (getnum_error) continue; if (!isdigit(c)) { if (c == ')' && seendigit) { (void) ungetc(c, fp); break; } if (seendigit && (opt & GETNUM_SCALED) && strchr("KkMmGg", c) != NULL) { switch (c) { case 'K': case 'k': n *= 1024; break; case 'M': case 'm': n *= (1024 * 1024); break; case 'G': case 'g': n *= (1024 * 1024 * 1024); break; } break; } if (seendecimal || c != '.' || !allow_dots) { ns_notice(ns_log_db, "%s:%d: expected a number", src, lineno); getnum_error = 1; } else { if (!seendigit) n = 1; #ifdef SENSIBLE_DOTS n *= 10000; #else n *= 1000; #endif seendigit = 1; seendecimal = 1; } continue; } #ifdef SENSIBLE_DOTS if (seendecimal) m = m * 10 + (c - '0'); else n = n * 10 + (c - '0'); #else n = n * 10 + (c - '0'); #endif seendigit = 1; } if (getnum_error) return (0); if (m > 9999) { ns_info(ns_log_db, "%s:%d: number after the decimal point exceeds 9999", src, lineno); getnum_error = 1; return (0); } if (seendecimal) { ns_info(ns_log_db, "%s:%d: decimal serial number interpreted as %d", src, lineno, n+m); } return (n + m); eol: ns_error(ns_log_db, "%s:%d: unexpected end of line", src, lineno); getnum_error = 1; (void) ungetc(c, fp); return (0); } #ifndef BIND_UPDATE static #endif int getnonblank(FILE *fp, const char *src, int multiline) { int c; while ((c = getc(fp)) != EOF) { if (isspace(c)) { if (c == '\n') { if (multiline) lineno++; else goto eol; } continue; } if (c == ';') { while ((c = getc(fp)) != EOF && c != '\n') ; if (c == '\n') { if (multiline) lineno++; else goto eol; } continue; } return (c); } ns_info(ns_log_db, "%s:%d: unexpected EOF", src, lineno); return (EOF); eol: ns_error(ns_log_db, "%s:%d: unexpected end of line", src, lineno); /* don't ungetc(c, fp); as the caller will do this. */ return(c); } /* * Replace all single "$"'s in "name" with "it". * ${delta} will add delta to "it" before printing. * ${delta,width} will change print width as well, zero fill is implied * ${delta,width,radix} will change radix as well, can be d, o, x, X. * i.e. ${0,2,X} will produce a two digit hex (upper case) with zero fill. * Append "origin" to name if required and validate result with makename. * To get a "$" or "{" in the output use \ before it. * Return 0 on no error or -1 on error. * Resulting name stored in "buf". */ static int genname(char *name, int it, const char *origin, char *buf, int size) { char *bp = buf; char *eom = buf + size; char *cp; char numbuf[32]; char fmt[32]; int delta = 0; int width; while (*name) { if (*name == '$') { if (*(++name) == '$') { /* should be deprecated. how? */ if (bp >= eom) return (-1); *bp++ = *name++; } else { strcpy(fmt, "%d"); if (*name == '{') { switch (sscanf(name, "{%d,%d,%1[doxX]}", &delta, &width, numbuf)) { case 1: break; case 2: sprintf(fmt, "%%0%dd", width); break; case 3: sprintf(fmt, "%%0%d%c", width, numbuf[0]); break; default: return (-1); } while (*name && *name++ != '}') { continue; } } sprintf(numbuf, fmt, it + delta); cp = numbuf; while (*cp) { if (bp >= eom) return (-1); *bp++ = *cp++; } } } else if (*name == '\\') { if (*(++name) == '\0') { if (bp >= eom) return (-1); *bp++ = '\\'; } else { switch (*name) { case '\\': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (bp >= eom) return (-1); *bp++ = '\\'; default: if (bp >= eom) return (-1); *bp++ = *name++; } } } else { if (bp >= eom) return (-1); *bp++ = *name++; } } if (bp >= eom) return (-1); *bp = '\0'; return (origin == NULL ? 0 : makename(buf, origin, size)); } /* * Take name and fix it according to following rules: * "." means root. * "@" means current origin. * "name." means no changes. * "name" means append origin. */ int makename(char *name, const char *origin, int size) { int n; u_char domain[MAXCDNAME]; switch (ns_name_pton(name, domain, sizeof(domain))) { case -1: return (-1); case 1: /* FULLY QUALIFIED */ break; case 0: /* UNQUALIFIED */ if (strcmp(name, "@") == 0) /* must test raw name */ domain[0] = 0; if ((n = dn_skipname(domain, domain+sizeof(domain))) == -1) return (-1); /* step back over root, append origin */ switch (ns_name_pton(origin, domain+n-1, sizeof(domain)-n+1)) { case -1: return (-1); case 0: case 1: break; } break; } if (ns_name_ntop(domain, name, size) == -1) return (-1); if (name[0] == '.') /* root */ name[0] = '\0'; return (0); } int makename_ok(char *name, const char *origin, int class, struct zoneinfo *zp, enum transport transport, enum context context, const char *owner, const char *filename, int lineno, int size) { int ret = 1; if (makename(name, origin, size) == -1) { ns_info(ns_log_db, "%s:%d: makename failed", filename, lineno); return (0); } if (!ns_nameok(NULL, name, class, zp, transport, context, owner, inaddr_any)) { ns_info(ns_log_db, "%s:%d: database naming error", filename, lineno); ret = 0; } return (ret); } void endline(FILE *fp) { int c; while ((c = getc(fp)) != '\0') { if (c == '\n') { (void) ungetc(c,fp); break; } else if (c == EOF) { break; } } } #define MAXPORT 1024 #define MAXLEN 24 #ifndef BIND_UPDATE static #endif char getprotocol(FILE *fp, const char *src) { int k; char b[MAXLEN]; (void) getword(b, sizeof(b), fp, 0); k = protocolnumber(b); if (k == -1) ns_info(ns_log_db, "%s:%d: unknown protocol: %s.", src, lineno, b); return ((char) k); } #ifndef BIND_UPDATE static #endif int getservices(int offset, char *data, FILE *fp, const char *src) { int j, ch, k, maxl, bracket; char bm[MAXPORT/8]; char b[MAXLEN]; for (j = 0; j < MAXPORT/8; j++) bm[j] = 0; maxl = 0; bracket = 0; while (getword(b, sizeof(b), fp, 0) || bracket) { if (feof(fp) || ferror(fp)) break; if (strlen(b) == 0) continue; if (b[0] == '(') { bracket++; continue; } if (b[0] == ')') { bracket = 0; while ((ch = getc(fp)) != EOF && ch != '\n') (void)NULL; if (ch == '\n') lineno++; break; } k = servicenumber(b); if (k == -1) { ns_info(ns_log_db, "%s:%d: Unknown service '%s'", src, lineno, b); continue; } if ((k < MAXPORT) && (k)) { bm[k/8] |= (0x80>>(k%8)); if (k > maxl) maxl = k; } else { ns_info(ns_log_db, "%s:%d: port no. (%d) too big", src, lineno, k); } } if (bracket) ns_info(ns_log_db, "%s:%d: missing close paren", src, lineno); maxl = maxl/8+1; memcpy(data+offset, bm, maxl); return (maxl+offset); } /* * Converts a word to a u_int32_t. Error if any non-numeric * characters in the word, except leading or trailing white space. */ static u_int32_t wordtouint32(buf) char *buf; { u_long result; u_int32_t res2; char *bufend; wordtouint32_error = 0; result = strtoul(buf, &bufend, 0); if (bufend == buf) wordtouint32_error = 1; else while ('\0' != *bufend) { if (isspace(*bufend)) bufend++; else { wordtouint32_error = 1; break; } } /* Check for truncation between u_long and u_int32_t */ res2 = result; if (res2 != result) wordtouint32_error = 1; return (res2); } static int getcharstring(char *buf, char *data, int type, int minfields, int maxfields, FILE *fp, const char *src) { int nfield = 0, done = 0, n = 0, i; char *b = buf; do { nfield++; i = strlen(buf); #ifdef ALLOW_LONG_TXT_RDATA b = buf; if (type == ns_t_txt || type == ns_t_x25) { while (i > MAXCHARSTRING && n + MAXCHARSTRING + 1 < MAXDATA) { data[n] = (char)MAXCHARSTRING; memmove(data + n + 1, b, MAXCHARSTRING); n += MAXCHARSTRING + 1; b += MAXCHARSTRING; i -= MAXCHARSTRING; } } #endif /* ALLOW_LONG_TXT_RDATA */ if (i > MAXCHARSTRING) { ns_info(ns_log_db, "%s:%d: RDATA field %d too long", src, lineno -1, nfield); return (0); } if (n + i + 1 > MAXDATA) { ns_info(ns_log_db, "%s:%d: total RDATA too long", src, lineno -1); return (0); } data[n] = i; memmove(data + n + 1, b, (int)i); n += i + 1; done = (maxfields && nfield >= maxfields); } while (!done && getword(buf, MAXDATA, fp, 0)); if (nfield < minfields) { ns_info(ns_log_db, "%s:%d: expected %d RDATA fields, only saw %d", src, lineno -1, minfields, nfield); return (0); } if (done) endline(fp); return (n); } /* * get_nxt_types(): Read the list of types in the NXT record. * * Data is the array where the bit flags are stored; it must * contain at least ns_t_any/NS_NXT_BITS bytes. * FP is the input FILE *. * Filename is the sourcefile * * The result is how many bytes are significant in the result. * ogud@tis.com 1995 */ static int get_nxt_types(u_char *data, FILE *fp, const char *filename) { char b[MAXLABEL]; /* Not quite the right size, but good enough */ int maxtype=0; int success; int type; int errs = 0; memset(data, 0, NS_NXT_MAX/NS_NXT_BITS+1); while (getmlword(b, sizeof(b), fp, 0)) { if (feof(fp) || ferror(fp)) break; if (strlen(b) == 0 || b[0] == '\n') continue; /* Parse RR type (A, MX, etc) */ type = res_nametotype((char *)b, &success); if ((!success) || type == ns_t_any) { errs++; ns_info(ns_log_db, "%s: Line %d: Unknown type: %s in NXT record.", filename, lineno, b); continue; } NS_NXT_BIT_SET(type, data); if (type > maxtype) maxtype = type; } if (errs) return (0); else return (maxtype/NS_NXT_BITS+1); } /* sanity checks PRIMARY ONLY */ static void fixup_soa(const char *fn, struct zoneinfo *zp) { /* Sanity: give enough time for the zone to transfer (retry). */ if (zp->z_expire < (zp->z_refresh + zp->z_retry)) ns_notice(ns_log_db, "%s: WARNING SOA expire value is less than SOA refresh+retry (%u < %u+%u)", fn, zp->z_expire, zp->z_refresh, zp->z_retry); /* Sanity. */ if (zp->z_expire < (zp->z_refresh + 10 * zp->z_retry)) ns_warning(ns_log_db, "%s: WARNING SOA expire value is less than refresh + 10 * retry \ (%u < (%u + 10 * %u))", fn, zp->z_expire, zp->z_refresh, zp->z_retry); /* * Sanity: most hardware/telco faults are detected and fixed within * a week, secondaries should continue to operate for this time. * (minimum of 4 days for long weekends) */ if (zp->z_expire < (7 * 24 * 3600)) ns_warning(ns_log_db, "%s: WARNING SOA expire value is less than 7 days (%u)", fn, zp->z_expire); /* * Sanity: maximum down time if we havn't talked for six months * war must have broken out. */ if (zp->z_expire > ( 183 * 24 * 3600)) ns_warning(ns_log_db, "%s: WARNING SOA expire value is greater than 6 months (%u)", fn, zp->z_expire); /* Sanity. */ if (zp->z_refresh < (zp->z_retry * 2)) ns_warning(ns_log_db, "%s: WARNING SOA refresh value is less than 2 * retry (%u < %u * 2)", fn, zp->z_refresh, zp->z_retry); } /* this function reads in the sig record rdata from the input file and * returns the following codes * > 0 length of the recrod * ERR_EOF end of file * */ static int parse_sig_rr(char *buf, int buf_len, u_char *data, int data_size, FILE *fp, struct zoneinfo *zp, char *domain, u_int32_t ttl, enum context domain_ctx, enum transport transport, const char **errmsg) { /* The SIG record looks like this in the db file: Name Cl SIG RRtype Algid [OTTL] Texp Tsig Kfoot Signer Sig where: Name and Cl are as usual SIG is a keyword RRtype is a char string ALGid is 8 bit u_int Labels is 8 bit u_int OTTL is 32 bit u_int (optionally present) Texp is YYYYMMDDHHMMSS Tsig is YYYYMMDDHHMMSS Kfoot is 16-bit unsigned decimal integer Signer is a char string Sig is 64 to 319 base-64 digits A missing OTTL is detected by the magnitude of the Texp value that follows it, which is larger than any u_int. The Labels field in the binary RR does not appear in the text RR. It's too crazy to run these pages of SIG code at the right margin. I'm exdenting them for readability. */ u_int32_t sig_type; int dateerror; int siglen, success; u_char *cp; u_int32_t al, la, n; u_int32_t signtime, exptime, timetilexp; u_int32_t origTTL; enum context context; time_t now; const char *errtype = "SIG error"; int i, my_buf_size = MAXDATA, errs = 0; /* The TTL gets checked against the Original TTL, and bounded by the signature expiration time, which are both under the signature. We can't let TTL drift based on the SOA record. If defaulted, fix it now. (It's not clear to me why USE_MINIMUM isn't eliminated before putting ALL RR's into the database. -gnu@toad.com) */ if (ttl == USE_MINIMUM) ttl = zp->z_minimum; i = 0; data[i] = '\0'; getmlword_nesting = 0; /* KLUDGE err recovery */ /* RRtype (char *) * if old style inp will contain the next token *copy that into buffer, otherwise read from file */ if (buf && buf_len == 0) if (!getmlword((char*)buf, my_buf_size, fp, 0)) ERRTO("SIG record doesn't specify type"); sig_type = res_nametotype(buf, &success); if (!success || sig_type == ns_t_any) { /* * We'll also accept a numeric RR type, * for signing RR types that this version * of named doesn't yet understand. * In the ns_t_any case, we rely on wordtouint32 * to fail when scanning the string "ANY". */ sig_type = wordtouint32 (buf); if (wordtouint32_error || sig_type > 0xFFFF) ERRTO("Unknown RR type in SIG record"); } cp = &data[i]; PUTSHORT((u_int16_t)sig_type, cp); i += 2; /* Algorithm id (8-bit decimal) */ if (!getmlword(buf, my_buf_size, fp, 0)) ERRTO("Missing algorithm ID"); al = wordtouint32(buf); if (0 == al || wordtouint32_error || 255 <= al) ERRTO("Bad algorithm number"); data[i] = (u_char) al; i++; /* * Labels (8-bit decimal) */ if (!getmlword(buf, my_buf_size, fp, 0)) ERRTO("Missing label count"); la = wordtouint32(buf); if (wordtouint32_error || 255 <= la || (0 == la && *domain != '\0')) ERRTO("Bad label count number"); data[i] = (u_char) la; i++; /* * OTTL (optional u_int32_t) and * Texp (u_int32_t date) */ if (!getmlword(buf, my_buf_size, fp, 0)) ERRTO("OTTL and expiration time missing"); /* * See if OTTL is missing and this is a date. * This relies on good, silent error checking * in ns_datetosecs. */ exptime = ns_datetosecs(buf, &dateerror); if (!dateerror) { /* Output TTL as OTTL */ origTTL = ttl; cp = &data[i]; PUTLONG (origTTL, cp); i += 4; } else { /* Parse and output OTTL; scan TEXP */ origTTL = wordtouint32(buf); if (wordtouint32_error || (origTTL > 0x7fffffffU)) ERRTO("Original TTL value bad"); cp = &data[i]; PUTLONG(origTTL, cp); i += 4; if (!getmlword(buf, my_buf_size, fp, 0)) ERRTO("Expiration time missing"); exptime = ns_datetosecs(buf, &dateerror); } if (dateerror || exptime > 0x7fffffff || exptime <= 0) ERRTO("Invalid expiration time"); cp = &data[i]; PUTLONG(exptime, cp); i += 4; /* Tsig (u_int32_t) */ if (!getmlword(buf, my_buf_size, fp, 0)) ERRTO("Missing signature time"); signtime = ns_datetosecs(buf, &dateerror); if (0 == signtime || dateerror) ERRTO("Invalid signature time"); cp = &data[i]; PUTLONG(signtime, cp); i += 4; /* Kfootprint (unsigned_16) */ if (!getmlword(buf, my_buf_size, fp, 0)) ERRTO("Missing key footprint"); n = wordtouint32(buf); if (wordtouint32_error || n >= 0x0ffff) ERRTO("Invalid key footprint"); cp = &data[i]; PUTSHORT((u_int16_t)n, cp); i += 2; /* Signer's Name */ if (!getmlword((char*)buf, my_buf_size, fp, 0)) ERRTO("Missing signer's name"); cp = &data[i]; strcpy((char *)cp, buf); context = domain_ctx; MAKENAME_OKZP((char *)cp, data_size); i += strlen((char *)cp) + 1; /* * Signature (base64 of any length) * We don't care what algorithm it uses or what * the internal structure of the BASE64 data is. */ if (!getallwords(buf, my_buf_size, fp, 0)) { siglen = 0; } else { cp = &data[i]; siglen = b64_pton(buf, (u_char*)cp, data_size - i); if (siglen < 0) ERRTO("Signature block bad"); } /* set total length and we're done! */ n = i + siglen; /* * Check signature time, expiration, and adjust TTL. Note * that all time values are in GMT (UTC), *not* local time. */ now = time (0); /* need to find a better place for this XXX ogud */ /* Don't let bogus name servers increase the signed TTL */ if (ttl > origTTL) ERRTO("TTL is greater than signed original TTL"); /* Don't let bogus signers "sign" in the future. */ if (signtime > (u_int32_t)now) ERRTO("signature time is in the future"); /* Ignore received SIG RR's that are already expired. */ if (exptime <= (u_int32_t)now) ERRTO("expiration time is in the past"); /* Lop off the TTL at the expiration time. */ timetilexp = exptime - now; if (timetilexp < ttl) { ns_debug(ns_log_load, 1, "shrinking expiring %s SIG TTL from %d to %d", p_secstodate(exptime), ttl, timetilexp); ttl = timetilexp; } /* * Check algorithm-ID and key structure, for * the algorithm-ID's that we know about. */ switch (al) { case NS_ALG_MD5RSA: if (siglen == 0) ERRTO("No key for RSA algorithm"); if (siglen < 1) ERRTO("Signature too short"); if (siglen > (NS_MD5RSA_MAX_BITS + 7) / 8) ERRTO("Signature too long"); break; case NS_ALG_DH: if (siglen < 1) ERRTO("DH Signature too short"); break; /* need more tests here */ case NS_ALG_DSA: if (siglen < NS_DSA_SIG_SIZE) ERRTO("DSS Signature too short"); else if (siglen > NS_DSA_SIG_SIZE) ERRTO("DSS Signature too long "); break; /* need more tests here */ case NS_ALG_EXPIRE_ONLY: if (siglen != 0) ERRTO( "Signature supplied to expire-only algorithm"); break; case NS_ALG_PRIVATE_OID: if (siglen == 0) ERRTO("No ObjectID in key"); break; default: ERRTO("UNKOWN SIG algorithm"); } /* Should we complain about algorithm-ID's that we don't understand? It may help debug some obscure cases, but in general we should accept any RR whether we could cryptographically process it or not; it may be being published for some newer DNS clients to validate themselves. */ endline(fp); /* flush the rest of the line */ return (n); err: *errmsg = errtype; return (-1); } static int parse_nxt_rr(char *buf, u_char *data, int data_size, FILE *fp, struct zoneinfo *zp, char *domain, enum context context, enum transport transport, const char **errmsg) { /* The NXT record looks like: Name Cl NXT nextname RRT1 RRT2 MX A SOA ... where: Name and Cl are as usual NXT is a keyword nextname is the next valid name in the zone after "Name". All names between the two are known to be nonexistent. RRT's... are a series of RR type names, which indicate that RR's of these types are published for "Name", and that no RR's of any other types are published for "Name". When a NXT record is cryptographically signed, it proves the nonexistence of an RR (actually a whole set of RR's). */ int n, errs = 0, i; u_char *cp; /* char *origin = zp->z_origin; int class = zp->z_class; */ *errmsg = "NXT name error"; (void) strcpy((char *)data, buf); MAKENAME_OKZP((char *)data, data_size); n = strlen((char *)data) + 1; cp = n + data; i = get_nxt_types(cp, fp, zp->z_source); if( i > 0) return (n + i); *errmsg = "NXT type error"; err: return (-1); } static int parse_cert_rr(char *buf, int buf_len, u_char *data, int data_size, FILE *fp, const char **errmsg) { /* Cert record looks like: * Type Key_tag Alg Cert * Type: certification type number (16) * Key_tag: tag of corresponding KEY RR (16) * Alg: algorithm of the KEY RR (8) * Cert: base64 enocded block */ u_char *cp; u_int32_t cert_type, key_tag, alg; const char *errtype = "CERT parse error"; int certlen, i, n, success; i = 0; cp = &data[i]; cert_type = sym_ston(__p_cert_syms, buf, &success); if (!success) { cert_type = wordtouint32(buf); if (wordtouint32_error || cert_type > 0xFFFF) ERRTO("CERT type out of range"); } if (i + INT16SZ > data_size) ERRTO("CERT no space"); PUTSHORT((u_int16_t)cert_type, cp); i += INT16SZ; if (!getmlword((char*)buf, buf_len, fp, 0)) ERRTO("CERT doesn't specify type"); key_tag = wordtouint32(buf); if (wordtouint32_error || key_tag > 0xFFFF) ERRTO("CERT KEY tag out of range"); if (i + INT16SZ > data_size) ERRTO("CERT no space"); PUTSHORT((u_int16_t)key_tag, cp); i += INT16SZ; if (!getmlword(buf, buf_len, fp, 0)) ERRTO("CERT missing algorithm ID"); alg = sym_ston(__p_key_syms, buf, &success); if (!success) { alg = wordtouint32(buf); if (wordtouint32_error || alg > 0xFF) ERRTO("CERT KEY alg out of range"); } if (i + 1 > data_size) ERRTO("CERT no space"); data[i++] = (u_char)alg; if (!getallwords(buf, buf_len, fp, 0)) { certlen = 0; } else { cp = &data[i]; certlen = b64_pton(buf, (u_char*)cp, data_size - i); if (certlen < 0) ERRTO("CERT blob has encoding error"); } /* set total length */ n = i + certlen; return (n); err: *errmsg = errtype; return (-1); } static int parse_key_rr(char *buf, int buf_len, u_char *data, int data_size, FILE *fp, const char **errmsg) { /* The KEY record looks like this in the db file: * Name Cl KEY Flags Proto Algid PublicKeyData * where: * Name,Cl per usual * KEY RR type * Flags 4 digit hex value (unsigned_16) * Proto 8 bit u_int * Algid 8 bit u_int * PublicKeyData * a string of base64 digits, * skipping any embedded whitespace. */ u_int32_t al, pr; int nk, klen,i, n; u_int32_t keyflags; const char *errtype = "KEY error"; u_char *cp, *expstart; u_int expbytes, modbytes; i = n = 0; data[i] = '\0'; cp = data; getmlword_nesting = 0; /* KLUDGE err recov. */ /*>>> Flags (unsigned_16) */ keyflags = wordtouint32(buf); if (wordtouint32_error || 0xFFFF < keyflags) ERRTO("KEY flags error"); if (keyflags & NS_KEY_RESERVED_BITMASK) ERRTO("KEY Reserved Flag Bit"); PUTSHORT(keyflags, cp); /*>>> Protocol (8-bit decimal) */ if (!getmlword((char*)buf, buf_len, fp, 0)) ERRTO("KEY Protocol Field"); pr = wordtouint32(buf); if (wordtouint32_error || 255 < pr) ERRTO("KEY Protocol Field"); *cp++ = (u_char) pr; /*>>> Algorithm id (8-bit decimal) */ if (!getmlword((char*)buf, buf_len, fp, 0)) ERRTO("KEY Algorithm ID"); al = wordtouint32(buf); if (wordtouint32_error || 0 == al || 255 == al || 255 < al) ERRTO("KEY Algorithm ID"); *cp++ = (u_char) al; /*>>> Extended KEY flag field in bytes 5 and 6 */ if (NS_KEY_EXTENDED_FLAGS & keyflags) { u_int32_t keyflags2; if (!getmlword((char*)buf, buf_len, fp, 0)) ERRTO("KEY Flags Field"); keyflags2 = wordtouint32(buf); if (wordtouint32_error || 0xFFFF < keyflags2) ERRTO("Extended key flags error"); if (keyflags2 & NS_KEY_RESERVED_BITMASK2) ERRTO("KEY Reserved Flag2 Bit"); PUTSHORT(keyflags2, cp); } /*>>> Public Key data is in BASE64. * We don't care what algorithm it uses or what * the internal structure of the BASE64 data is. */ if (!getallwords(buf, MAXDATA, fp, 0)) klen = 0; else { /* Convert from BASE64 to binary. */ klen = b64_pton(buf, (u_char*)cp, data_size - (cp - data)); if (klen < 0) ERRTO("KEY Public Key"); } /* set total length */ n = klen + (cp - data); /* * Now check for valid key flags & algs & etc, from the RFC. */ if (NS_KEY_TYPE_NO_KEY == (keyflags & NS_KEY_TYPEMASK)) nk = 1; /* No-key */ else nk = 0; /* have a key */ if ((keyflags & (NS_KEY_NAME_TYPE | NS_KEY_TYPEMASK)) == (NS_KEY_NAME_ZONE | NS_KEY_TYPE_CONF_ONLY)) /* Zone key must have Auth bit set. */ ERRTO("KEY Zone Key Auth. bit"); if (al == 0 && nk == 0) ERRTO("KEY Algorithm"); if (al != 0 && pr == 0) ERRTO("KEY Protocols"); if (nk == 1 && klen != 0) ERRTO("KEY No-Key Flags Set"); if (nk == 0 && klen == 0) ERRTO("KEY Type Spec'd"); /* * Check algorithm-ID and key structure, for the algorithm-ID's * that we know about. */ switch (al) { case NS_ALG_MD5RSA: if (klen == 0) break; expstart = cp; expbytes = *expstart++; if (expbytes == 0) GETSHORT(expbytes, expstart); if (expbytes < 1) ERRTO("Exponent too short"); if (expbytes > (NS_MD5RSA_MAX_BITS + 7) / 8) ERRTO("Exponent too long"); if (*expstart == 0) ERRTO("Exponent w/ 0"); modbytes = klen - (expbytes + (expstart - cp)); if (modbytes < (NS_MD5RSA_MIN_BITS + 7) / 8) ERRTO("Modulus too short"); if (modbytes > (NS_MD5RSA_MAX_BITS + 7) / 8) ERRTO("Modulus too long"); if (*(expstart+expbytes) == 0) ERRTO("Modulus starts w/ 0"); break; case NS_ALG_DH: { u_char *dh_cp; u_int16_t dh_len, plen, glen, ulen; dh_cp = (u_char *)cp; GETSHORT(plen, dh_cp); if(plen < 16) ERRTO("DH short plen"); dh_len = 2 + plen; if(dh_len > klen) ERRTO("DH plen > klen"); GETSHORT(glen, dh_cp); if(glen <= 0 || glen > plen) ERRTO("DH glen bad"); dh_len = 2 + glen; if(dh_len > klen) ERRTO("DH glen > klen"); GETSHORT(ulen, dh_cp); if(ulen <= 0 || ulen > plen) ERRTO("DH ulen bad"); dh_len = 2 + ulen; if(dh_len > klen) ERRTO("DH ulen > klen"); else if (dh_len < klen) ERRTO("DH *len < klen"); break; } case NS_ALG_DSA: { u_int8_t t; if ( klen == 0) break; t = *cp; if (t > 8) ERRTO("DSA T value"); if (klen != (1 + 20 + 3 *(64+8*t))) ERRTO("DSA length"); break; } case NS_ALG_PRIVATE_OID: if (klen == 0) ERRTO("No ObjectID in key"); break; default: ERRTO("Unknown Key algorithm"); } endline(fp); /* flush the rest of the line */ return (n); err: *errmsg = errtype; return (-1); } /*T_KEY*/ /* * function to invoke DNSSEC specific parsing routines. * this is simpler than copying these complicated blocks into the * multiple souce files that read files (ixfr, nsupdate etc..). * this code should be in a library rather than in this file but * what the heck for now (ogud@tislabs.com) */ int parse_sec_rdata(char *buf, int buf_len, int buf_full, u_char *data, int data_size, FILE *fp, struct zoneinfo *zp, char *domain, u_int32_t ttl, int type, enum context context, enum transport transport, const char **errmsg) { int ret = -1; getmlword_nesting = 0; /* KLUDGE err recov. */ if (!buf_full && buf && buf_len != 0) /* check if any data in buf */ if (!getmlword(buf, buf_len, fp, 1)) { *errmsg = "unexpected end of input"; goto err; } switch (type) { case ns_t_sig: ret = parse_sig_rr(buf, buf_len, data, data_size, fp, zp, domain, ttl, context, transport, errmsg); break; case ns_t_key: ret = parse_key_rr(buf, buf_len, data, data_size, fp, errmsg); break; case ns_t_nxt: ret = parse_nxt_rr(buf, data, data_size, fp, zp, domain, context, transport, errmsg); break; case ns_t_cert: ret = parse_cert_rr(buf, buf_len, data, data_size, fp, errmsg); break; default: ret = -1; *errmsg = "parse_sec_rdata():Unsupported SEC type type"; goto err; } return (ret); err: endline(fp); return (ret); } diff --git a/contrib/bind/bin/named/db_sec.c b/contrib/bind/bin/named/db_sec.c index 9c5bad285687..5e03fb9a342e 100644 --- a/contrib/bind/bin/named/db_sec.c +++ b/contrib/bind/bin/named/db_sec.c @@ -1,1081 +1,1081 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: db_sec.c,v 8.35.4.2 2002/11/14 13:24:44 marka Exp $"; +static const char rcsid[] = "$Id: db_sec.c,v 8.36 2002/11/17 14:51:50 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" struct zpubkey { struct dst_key *zpk_key; /* Should be DST_KEY */ char *zpk_name; struct zpubkey *zpk_next; }; typedef struct zpubkey *zpubkey_list; static int nxt_match_rrset(struct databuf *dp, struct db_rrset *rrset); /* * A converted databuf is a stripped down databuf after converting the * data to wire format. */ struct converted_databuf { struct converted_databuf *cd_next; u_char *cd_data; int cd_size, cd_alloc; }; /* All of the trusted keys and zone keys */ static tree *trusted_keys = NULL; static int compare_pubkey (struct zpubkey *zpk1, struct zpubkey *zpk2) { char ta[NS_MAXDNAME], tb[NS_MAXDNAME]; if (ns_makecanon(zpk1->zpk_name, ta, sizeof ta) < 0 || ns_makecanon(zpk2->zpk_name, tb, sizeof tb) < 0) return (-1); return (strcasecmp(ta, tb)); } static struct zpubkey * tree_srch_pubkey (const char *name) { struct zpubkey tkey, *key; DE_CONST(name, tkey.zpk_name); if (trusted_keys == NULL) { tree_init(&trusted_keys); return (NULL); } key = (struct zpubkey *)tree_srch(&trusted_keys, compare_pubkey, &tkey); return (key); } static DST_KEY * find_public_key (const char *name, u_int16_t key_id) { struct namebuf *knp; struct hashbuf *htp; struct databuf *dp; const char *fname; DST_KEY *key; ns_debug(ns_log_default, 5, "find_public_key(%s, %d)", name, key_id); htp = hashtab; knp = nlookup (name, &htp, &fname, 0); if (fname != name) /* The name doesn't exist, so there's no key */ return (NULL); for (dp = knp->n_data; dp != NULL; dp = dp->d_next) { if (dp->d_type != ns_t_key || dp->d_secure < DB_S_SECURE) continue; key = dst_dnskey_to_key(name, dp->d_data, dp->d_size); /* XXX what about multiple keys with same footprint? */ if (key) { if (key->dk_id == ntohs(key_id)) return (key); else dst_free_key(key); } } return (NULL); } static DST_KEY * find_trusted_key (const char *name, u_int16_t key_id) { struct zpubkey *zpk; zpubkey_list keylist = tree_srch_pubkey (name); ns_debug(ns_log_default, 5, "find_trusted_key(%s, %d)", name, key_id); for (zpk = keylist; zpk; zpk = zpk->zpk_next) if (zpk->zpk_key->dk_id == ntohs(key_id)) return (zpk->zpk_key); return (NULL); } int add_trusted_key (const char *name, const int flags, const int proto, const int alg, const char *str) { zpubkey_list keylist; struct zpubkey *zpk; u_char buf[1024]; int n; keylist = tree_srch_pubkey (name); zpk = (struct zpubkey *) memget (sizeof (struct zpubkey)); if (zpk == NULL) ns_panic(ns_log_default, 1, "add_trusted_key: memget failed(%s)", name); n = b64_pton(str, buf, sizeof(buf)); if (n < 0) goto failure; zpk->zpk_key = dst_buffer_to_key(name, alg, flags, proto, buf, n); if (zpk->zpk_key == NULL) { ns_warning(ns_log_default, "add_trusted_key: dst_buffer_to_key(%s) failed", name); goto failure; } zpk->zpk_name = zpk->zpk_key->dk_key_name; zpk->zpk_next = NULL; if (keylist == NULL) { if (tree_add (&trusted_keys, compare_pubkey, zpk, NULL) == NULL) goto failure; } else { struct zpubkey *tkey = keylist; while (tkey->zpk_next) tkey = tkey->zpk_next; tkey->zpk_next = zpk; } return (1); failure: memput(zpk, sizeof (struct zpubkey)); return (0); } /* Can the signer sign records for this name? This is a heuristic. */ static int can_sign(const char *name, const char *signer) { return (ns_samedomain(name, signer) && dn_count_labels(name) - dn_count_labels(signer) <= 2); } static int rrset_set_security(struct db_rrset *rrset, int slev) { struct dnode *dnp; for (dnp = rrset->rr_list; dnp != NULL; dnp = dnp->dn_next) dnp->dp->d_secure = slev; for (dnp = rrset->rr_sigs; dnp != NULL; dnp = dnp->dn_next) dnp->dp->d_secure = slev; return (slev); } static int convert_databuf(struct databuf *dp, struct converted_databuf *cdp) { u_char *bp = cdp->cd_data; u_char *cp = dp->d_data; u_char *eob = cdp->cd_data + cdp->cd_alloc; int len; u_char buf[MAXDNAME]; switch (dp->d_type) { case ns_t_soa: case ns_t_minfo: case ns_t_rp: if (eob - bp < (int)strlen((char *)cp) + 1) return (-1); if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) return (-1); len = ns_name_ntol(buf, bp, eob - bp); if (len < 0) return (-1); bp += len; cp += strlen((char *)cp) + 1; if (eob - bp < (int)strlen((char *)cp) + 1) return (-1); if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) return (-1); len = ns_name_ntol(buf, bp, eob - bp); if (len < 0) return (-1); bp += len; cp += strlen((char *)cp) + 1; if (dp->d_type == ns_t_soa) { if (eob - bp < 5 * INT32SZ) return (-1); memcpy(bp, cp, 5 * INT32SZ); bp += (5 * INT32SZ); cp += (5 * INT32SZ); } break; case ns_t_ns: case ns_t_cname: case ns_t_mb: case ns_t_mg: case ns_t_mr: case ns_t_ptr: case ns_t_nxt: if (eob - bp < (int)strlen((char *)cp) + 1) return (-1); if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) return (-1); len = ns_name_ntol(buf, bp, eob - bp); if (len < 0) return (-1); bp += len; cp += (len = strlen((char *)cp) + 1); if (dp->d_type == ns_t_nxt) { if (eob - bp < dp->d_size - len) return (-1); memcpy(bp, cp, dp->d_size - len); bp += (dp->d_size - len); cp += (dp->d_size - len); } break; case ns_t_srv: if (eob - bp < 2 * INT16SZ) return (-1); memcpy(bp, cp, 2 * INT16SZ); bp += (2 * INT16SZ); cp += (2 * INT16SZ); /* no break */ case ns_t_rt: case ns_t_mx: case ns_t_afsdb: case ns_t_px: if (eob - bp < INT16SZ) return (-1); memcpy (bp, cp, INT16SZ); bp += INT16SZ; cp += INT16SZ; if (eob - bp < (int)strlen((char *)cp) + 1) return (-1); if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) return (-1); len = ns_name_ntol(buf, bp, eob - bp); if (len < 0) return (-1); bp += len; cp += strlen((char *)cp) + 1; if (dp->d_type == ns_t_px) { if (eob - bp < (int)strlen((char *)cp) + 1) return (-1); if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) return (-1); len = ns_name_ntol(buf, bp, eob - bp); if (len < 0) return (-1); bp += len; cp += strlen((char *)cp) + 1; } break; default: if (eob - bp < dp->d_size) return (-1); memcpy(bp, cp, dp->d_size); bp += dp->d_size; } cdp->cd_size = bp - cdp->cd_data; return (cdp->cd_size); } static int digest_rr(char *envelope, int elen, struct converted_databuf *cdp, char *buffer, int blen) { char *bp = buffer, *eob = buffer + blen; if (eob - bp < elen) return (-1); memcpy (bp, envelope, elen); bp += elen; if (eob - bp < INT16SZ) return (-1); PUTSHORT(cdp->cd_size, bp); if (eob - bp < cdp->cd_size) return (-1); memcpy (bp, cdp->cd_data, cdp->cd_size); bp += cdp->cd_size; return (bp - buffer); } /* Sorts the converted databuf in the list */ static void insert_converted_databuf(struct converted_databuf *cdp, struct converted_databuf **clist) { struct converted_databuf *tcdp, *next; int t; #define compare_cdatabuf(c1, c2, t) \ (t = memcmp(c1->cd_data, c2->cd_data, MIN(c1->cd_size, c2->cd_size)), \ t == 0 ? c1->cd_size - c2->cd_size : t) if (*clist == NULL) { *clist = cdp; return; } tcdp = *clist; if (compare_cdatabuf(cdp, tcdp, t) < 0) { cdp->cd_next = tcdp; *clist = cdp; return; } next = tcdp->cd_next; while (next) { if (compare_cdatabuf(cdp, next, t) < 0) { cdp->cd_next = next; tcdp->cd_next = cdp; return; } tcdp = next; next = next->cd_next; } tcdp->cd_next = cdp; #undef compare_cdatabuf } static void free_clist(struct converted_databuf *clist) { struct converted_databuf *cdp; while (clist != NULL) { cdp = clist; clist = clist->cd_next; memput(cdp->cd_data, cdp->cd_alloc); memput(cdp, sizeof(struct converted_databuf)); } } /* Removes all empty nodes from an rrset's SIG list. */ static void rrset_trim_sigs(struct db_rrset *rrset) { struct dnode *dnp, *odnp, *ndnp; odnp = NULL; dnp = rrset->rr_sigs; while (dnp != NULL) { if (dnp->dp != NULL) { odnp = dnp; dnp = dnp->dn_next; } else { if (odnp != NULL) odnp->dn_next = dnp->dn_next; else rrset->rr_sigs = dnp->dn_next; ndnp = dnp->dn_next; memput(dnp, sizeof(struct dnode)); dnp = ndnp; } } } static int verify_set(struct db_rrset *rrset) { DST_KEY *key = NULL; struct sig_record *sigdata; struct dnode *sigdn; struct databuf *sigdp; u_int32_t now; u_int32_t exptime; u_int32_t signtime; char *signer; u_char name_n[MAXDNAME]; u_char *sig, *eom; int trustedkey = 0, siglen, labels, len = 0, ret; u_char *buffer = NULL, *bp; u_char envelope[MAXDNAME+32], *ep; struct dnode *dnp; int bufsize = 2048; /* Large enough for MAXDNAME + SIG_HDR_SIZE */ struct converted_databuf *clist = NULL, *cdp; int dnssec_failed = 0, dnssec_succeeded = 0; int return_value; int i; int expired = 0; if (rrset == NULL || rrset->rr_name == NULL) { ns_warning (ns_log_default, "verify_set: missing rrset/name"); return (rrset_set_security(rrset, DB_S_FAILED)); } if (rrset->rr_sigs == NULL) return (rrset_set_security(rrset, DB_S_INSECURE)); ns_debug(ns_log_default, 5, "verify_set(%s, %s, %s)", rrset->rr_name, p_type(rrset->rr_type), p_class(rrset->rr_class)); now = time(NULL); for (sigdn = rrset->rr_sigs; sigdn != NULL; sigdn = sigdn->dn_next) { u_int32_t namefield; struct sig_record sigrec; sigdp = sigdn->dp; eom = sigdp->d_data + sigdp->d_size; if (sigdp->d_size < SIG_HDR_SIZE) { return_value = DB_S_FAILED; goto end; } memcpy(&sigrec, sigdp->d_data, SIG_HDR_SIZE); sigdata = &sigrec; signer = (char *)sigdp->d_data + SIG_HDR_SIZE; sig = (u_char *)signer + strlen(signer) + 1; siglen = eom - sig; /* * Don't verify a set if the SIG inception time is in * the future. This should be fixed before 2038 (BEW) */ signtime = ntohl(sigdata->sig_time_n); if (SEQ_GT(signtime, now)) continue; /* An expired set is dropped, but the data is not. */ exptime = ntohl(sigdata->sig_exp_n); if (SEQ_GT(now, exptime)) { expired++; db_detach(&sigdn->dp); sigdp = NULL; continue; } /* Cleanup from the last iteration if we continue'd */ if (trustedkey == 0 && key != NULL) dst_free_key(key); key = find_trusted_key(signer, sigdata->sig_keyid_n); if (key == NULL) { trustedkey = 0; key = find_public_key(signer, sigdata->sig_keyid_n); } else trustedkey = 1; /* if we don't have the key, either * - the data should be considered insecure * - the sig is not a dnssec signature */ if (key == NULL) continue; /* Can a key with this name sign the data? */ if (!can_sign(rrset->rr_name, signer)) continue; /* Check the protocol and flags of the key */ if (key->dk_proto != NS_KEY_PROT_DNSSEC && key->dk_proto != NS_KEY_PROT_ANY) continue; if (key->dk_flags & NS_KEY_NO_AUTH) continue; namefield = key->dk_flags & NS_KEY_NAME_TYPE; if (namefield == NS_KEY_NAME_USER || namefield == NS_KEY_NAME_RESERVED) continue; if (namefield == NS_KEY_NAME_ENTITY && (key->dk_flags & NS_KEY_SIGNATORYMASK) == 0) continue; /* * If we're still here, we have a non-null key that's either * a zone key or an entity key with signing authority. */ if (buffer == NULL) { bp = buffer = memget(bufsize); if (bp == NULL) { return_value = DB_S_FAILED; goto end; } } else bp = buffer; /* Digest the fixed portion of the SIG record */ memcpy(bp, (char *) sigdata, SIG_HDR_SIZE); bp += SIG_HDR_SIZE; /* Digest the signer's name, canonicalized */ if (ns_name_pton(signer, name_n, sizeof name_n) < 0) { return_value = DB_S_FAILED; goto end; } i = ns_name_ntol(name_n, (u_char *)bp, bufsize - SIG_HDR_SIZE); if (i < 0) { return_value = DB_S_FAILED; goto end; } bp += i; /* create the dns record envelope: * */ if (ns_name_pton(rrset->rr_name, name_n, sizeof name_n) < 0 || ns_name_ntol(name_n, (u_char *)envelope, sizeof envelope) < 0) { return_value = DB_S_FAILED; goto end; } labels = dn_count_labels(rrset->rr_name); if (labels > sigdata->sig_labels_n) { ep = envelope; for (i=0; i < (labels - 1 - sigdata->sig_labels_n); i++) ep += (*ep+1); i = dn_skipname(ep, envelope + sizeof envelope); if (i < 0) { return_value = DB_S_FAILED; goto end; } envelope[0] = '\001'; envelope[1] = '*'; memmove(envelope + 2, ep, i); } i = dn_skipname(envelope, envelope + sizeof envelope); if (i < 0) { return_value = DB_S_FAILED; goto end; } ep = envelope + i; PUTSHORT (rrset->rr_type, ep); PUTSHORT (rrset->rr_class, ep); if (envelope + sizeof(envelope) - ep < INT32SZ) { return_value = DB_S_FAILED; goto end; } memcpy (ep, &sigdata->sig_ottl_n, INT32SZ); ep += INT32SZ; if (clist == NULL) { for (dnp = rrset->rr_list; dnp != NULL; dnp = dnp->dn_next) { struct databuf *dp = dnp->dp; cdp = memget(sizeof(struct converted_databuf)); if (cdp == NULL) { return_value = DB_S_FAILED; goto end; } memset(cdp, 0, sizeof(*cdp)); /* Should be large enough... */ cdp->cd_alloc = dp->d_size + 8; cdp->cd_data = memget(cdp->cd_alloc); if (cdp->cd_data == NULL) { memput(cdp, sizeof(*cdp)); return_value = DB_S_FAILED; goto end; } while (convert_databuf(dp, cdp) < 0) { memput(cdp->cd_data, cdp->cd_alloc); cdp->cd_alloc *= 2; cdp->cd_data = memget(cdp->cd_alloc); if (cdp->cd_data == NULL) { memput(cdp, sizeof(*cdp)); return_value = DB_S_FAILED; goto end; } } insert_converted_databuf(cdp, &clist); } } for (cdp = clist; cdp != NULL; cdp = cdp->cd_next) { len = digest_rr((char *)envelope, ep-envelope, cdp, (char *)bp, bufsize - (bp - buffer)); while (len < 0) { u_char *newbuf; /* Double the buffer size */ newbuf = memget(bufsize*2); if (newbuf == NULL) { return_value = DB_S_FAILED; goto end; } memcpy(newbuf, buffer, bp - buffer); bp = (bp - buffer) + newbuf; memput(buffer, bufsize); buffer = newbuf; bufsize *= 2; len = digest_rr((char *)envelope, ep-envelope, cdp, (char *)bp, bufsize - (bp - buffer)); } bp += len; } if (len < 0) { return_value = DB_S_FAILED; goto end; } ret = dst_verify_data(SIG_MODE_ALL, key, NULL, buffer, bp - buffer, sig, siglen); if (ret < 0) { dnssec_failed++; db_detach(&sigdn->dp); sigdp = NULL; } else dnssec_succeeded++; } end: if (dnssec_failed > 0 || expired > 0) rrset_trim_sigs(rrset); if (trustedkey == 0 && key != NULL) dst_free_key(key); if (dnssec_failed > 0 && dnssec_succeeded == 0) { ns_warning (ns_log_default, "verify_set(%s, %s, %s) failed", rrset->rr_name, p_type(rrset->rr_type), p_class(rrset->rr_class)); return_value = DB_S_FAILED; } else if (dnssec_succeeded > 0) return_value = DB_S_SECURE; else return_value = DB_S_INSECURE; free_clist(clist); if (buffer != NULL) memput(buffer, bufsize); return (rrset_set_security(rrset, return_value)); } static void rrset_free(struct db_rrset *rrset) { struct dnode *dnp; ns_debug(ns_log_default, 5, "rrset_free(%s)", rrset->rr_name); while (rrset->rr_list) { dnp = rrset->rr_list; rrset->rr_list = rrset->rr_list->dn_next; if (dnp->dp != NULL) db_detach(&dnp->dp); memput(dnp, sizeof(struct dnode)); } while (rrset->rr_sigs) { dnp = rrset->rr_sigs; rrset->rr_sigs = rrset->rr_sigs->dn_next; if (dnp->dp != NULL) db_detach(&dnp->dp); memput(dnp, sizeof(struct dnode)); } } /* * This is called when we have an rrset with SIGs and no other data. * Returns 1 if we either found the necessary data or if the SIG can be added * with no other data. 0 indicates that the SIG cannot be added. */ static int attach_data(struct db_rrset *rrset) { int type, class; struct databuf *dp, *newdp, *sigdp; struct dnode *dnp; struct namebuf *np; struct hashbuf *htp; char *signer; const char *fname; char *name = rrset->rr_name; sigdp = rrset->rr_sigs->dp; type = SIG_COVERS(sigdp); class = sigdp->d_class; signer = (char *)(sigdp + SIG_HDR_SIZE); /* First, see if the signer can sign data for the name. If not, * it's not a DNSSEC signature, so we can insert it with no * corresponding data. */ if (!can_sign(name, signer)) return (1); htp = hashtab; np = nlookup (name, &htp, &fname, 0); if (fname != name) return (0); for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (dp->d_type == type && dp->d_class == class) { newdp = savedata(class, type, dp->d_ttl, dp->d_data, dp->d_size); dnp = (struct dnode *) memget (sizeof (struct dnode)); if (dnp == NULL) ns_panic(ns_log_default, 1, "attach_data: memget failed"); dnp->dp = newdp; dnp->dn_next = rrset->rr_list; rrset->rr_list = dnp; } } if (rrset->rr_list != NULL) return (1); else return (0); } static int rrset_db_update(struct db_rrset *rrset, int flags, struct hashbuf **htpp, struct sockaddr_in from, int *rrcount) { struct dnode *dnp; int ret; /* If we have any unattached SIG records that are DNSSEC signatures, * don't cache them unless we already have the corresponding data. * If we do cache unattached SIGs, we run into problems later if we * have a SIG X and get a query for type X. */ if (rrset->rr_list == NULL) { if (attach_data(rrset) == 0) { rrset_free(rrset); return (OK); } if (rrset->rr_list != NULL && verify_set(rrset) == DB_S_FAILED) { rrset_free(rrset); return (OK); } } for (dnp = rrset->rr_list; dnp != NULL; dnp = dnp->dn_next) { ret = db_update(rrset->rr_name, dnp->dp, dnp->dp, NULL, flags, (*htpp), from); if (ret != OK) { /* XXX Probably should do rollback. */ db_err(ret, rrset->rr_name, dnp->dp->d_type, dnp->file, dnp->line); if (ret != DATAEXISTS) { rrset_free(rrset); return (ret); } } if (rrcount != NULL) (*rrcount)++; } for (dnp = rrset->rr_sigs; dnp != NULL; dnp = dnp->dn_next) { if (dnp->dp == NULL) /* verifyset() can remove sigs */ continue; ret = db_update(rrset->rr_name, dnp->dp, dnp->dp, NULL, flags, (*htpp), from); if (ret != OK) { /* XXX Probably should do rollback. */ db_err(ret, rrset->rr_name, dnp->dp->d_type, dnp->file, dnp->line); if (ret != DATAEXISTS) { rrset_free(rrset); return (ret); } } if (rrcount != NULL) (*rrcount)++; } rrset_free(rrset); return (OK); } static int rr_in_set(struct databuf *rr, struct dnode *set) { struct dnode *dnp; if (set == NULL) return (0); for(dnp = set; dnp != NULL; dnp = dnp->dn_next) { if (dnp->dp->d_size == rr->d_size && memcmp(dnp->dp->d_data, rr->d_data, dnp->dp->d_size) == 0) return (1); } return (0); } static int add_to_rrset_list(struct db_rrset **rrsets, char *name, struct databuf *dp, int line, const char *file) { struct db_rrset *rrset = *rrsets; struct dnode *dnp; while (rrset != NULL) { if (rrset->rr_type != ns_t_nxt || dp->d_type != ns_t_nxt) { if (dp->d_type == ns_t_sig) { if ((int)SIG_COVERS(dp) == rrset->rr_type) break; } else { if (dp->d_type == rrset->rr_type) break; } } else if (nxt_match_rrset(dp, rrset)) break; rrset = rrset->rr_next; } if (rrset != NULL) { if ((dp->d_type == ns_t_sig && rr_in_set(dp, rrset->rr_sigs)) || (dp->d_type != ns_t_sig && rr_in_set(dp, rrset->rr_list))) return (DATAEXISTS); } else { rrset = (struct db_rrset *) memget(sizeof(struct db_rrset)); if (rrset == NULL) ns_panic(ns_log_default, 1, "add_to_rrset_list: memget failed(%s)", name); memset(rrset, 0, sizeof(struct db_rrset)); rrset->rr_name = savestr(name, 1); rrset->rr_class = dp->d_class; if (dp->d_type == ns_t_sig) rrset->rr_type = SIG_COVERS(dp); else rrset->rr_type = dp->d_type; rrset->rr_next = *rrsets; *rrsets = rrset; } dnp = (struct dnode *) memget(sizeof(struct dnode)); if (dnp == NULL) ns_panic(ns_log_default, 1, "add_to_rrset_list: memget failed(%s)", name); memset(dnp, 0, sizeof(struct dnode)); dnp->dp = dp; DRCNTINC(dnp->dp); if (dp->d_type == ns_t_sig) { if (rrset->rr_sigs != NULL) { struct dnode *fdnp; /* Preserve the order of the RRs */ /* Add this one to the end of the list */ for (fdnp = rrset->rr_sigs; fdnp->dn_next != NULL; fdnp = fdnp->dn_next) /* NULL */ ; fdnp->dn_next = dnp; } else rrset->rr_sigs = dnp; } else { if (rrset->rr_list != NULL) { struct dnode *fdnp; /* Preserve the order of the RRs */ /* Add this one to the end of the list */ for (fdnp = rrset->rr_list; fdnp->dn_next != NULL; fdnp = fdnp->dn_next) /* NULL */ ; fdnp->dn_next = dnp; } else rrset->rr_list = dnp; } dnp->file = file; dnp->line = line; return (0); } static int update_rrset_list(struct db_rrset **rrsets, int flags, struct hashbuf **htpp, struct sockaddr_in from, int *rrcount) { struct db_rrset *rrset = *rrsets, *next = NULL, *last = NULL; int result = 0, tresult, cnameandother = 0; while (rrset != NULL) { if (rrset->rr_type == ns_t_key) break; last = rrset; rrset = rrset->rr_next; } if (rrset != NULL && last != NULL) { last->rr_next = rrset->rr_next; rrset->rr_next = *rrsets; *rrsets = rrset; } rrset = *rrsets; while (rrset != NULL) { if (verify_set(rrset) > DB_S_FAILED) { ns_debug(ns_log_default, 10, "update_rrset_list(%s, %s): set verified", rrset->rr_name, p_type(rrset->rr_type)); tresult = rrset_db_update(rrset, flags, htpp, from, rrcount); if (tresult == CNAMEANDOTHER) cnameandother++; if (tresult != OK) result = tresult; } else { rrset_free(rrset); result = DNSSECFAIL; } rrset->rr_name = freestr(rrset->rr_name); next = rrset->rr_next; memput(rrset, sizeof(struct db_rrset)); rrset = next; } *rrsets = NULL; if (cnameandother != 0) return (CNAMEANDOTHER); return (result); } int db_set_update(char *name, struct databuf *dp, void **state, int flags, struct hashbuf **htpp, struct sockaddr_in from, int *rrcount, int line, const char *file) { struct db_rrset **rrsets; struct db_rrset *rrset; int result = 0; ns_debug(ns_log_default, 5, "db_set_update(%s)", (name == NULL) ? "" : (*name == 0) ? "." : name); if (state == NULL) ns_panic(ns_log_default, 1, "Called db_set_update with state == NULL"); rrsets = (struct db_rrset **) state; if (*rrsets != NULL) { rrset = *rrsets; if (rrset->rr_name != NULL && dp != NULL && name != NULL && ns_samename(name, rrset->rr_name) == 1 && dp->d_class == rrset->rr_class) return (add_to_rrset_list(rrsets, name, dp, line, file)); } if (*rrsets != NULL) result = update_rrset_list(rrsets, flags, htpp, from, rrcount); if (dp != NULL) { ns_debug(ns_log_default, 10, "db_set_update(%s), creating new list", name); (void) add_to_rrset_list(rrsets, name, dp, line, file); } return (result); } static int nxt_match_rrset(struct databuf *dp, struct db_rrset *rrset) { if (rrset->rr_list != NULL) return (nxtmatch(rrset->rr_name, dp, rrset->rr_list->dp)); else return (nxtmatch(rrset->rr_name, dp, rrset->rr_sigs->dp)); } diff --git a/contrib/bind/bin/named/ns_config.c b/contrib/bind/bin/named/ns_config.c index ad4de85d92f1..84973ceb9132 100644 --- a/contrib/bind/bin/named/ns_config.c +++ b/contrib/bind/bin/named/ns_config.c @@ -1,3194 +1,3194 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_config.c,v 8.135 2002/05/24 03:04:59 marka Exp $"; +static const char rcsid[] = "$Id: ns_config.c,v 8.136.8.1 2003/06/02 09:56:34 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #ifdef HAVE_GETRUSAGE /* XXX */ #include #endif #include "named.h" #include "ns_parseutil.h" /* Private. */ static int tmpnum = 0; static int config_initialized = 0; static int need_logging_free = 0; static int default_logging_installed; static int options_installed = 0; static int logging_installed = 0; static int default_options_installed; static char **logging_categories; static char *current_pid_filename = NULL; #define ZONE_SYM_TABLE_SIZE 4973 static symbol_table zone_symbol_table; /* Zones */ void free_zone_timerinfo(struct zoneinfo *zp) { if (zp->z_timerinfo != NULL) { zp->z_timerinfo->name = freestr(zp->z_timerinfo->name); memput(zp->z_timerinfo, sizeof *zp->z_timerinfo); zp->z_timerinfo = NULL; } else ns_error(ns_log_config, "timer for zone '%s' had no timerinfo", zp->z_origin); } void free_zone_contents(struct zoneinfo *zp, int undefine_sym) { INSIST(zp != NULL); if (undefine_sym) undefine_symbol(zone_symbol_table, zp->z_origin, zp->z_class); if (zp->z_flags & Z_TIMER_SET) { free_zone_timerinfo(zp); if (evClearTimer(ev, zp->z_timer) < 0) ns_error(ns_log_config, "evClearTimer for zone '%s' failed in ns_init: %s", zp->z_origin, strerror(errno)); } if (zp->z_origin != NULL) zp->z_origin = freestr(zp->z_origin); if (zp->z_source != NULL) zp->z_source = freestr(zp->z_source); if (zp->z_ixfr_base != NULL) zp->z_ixfr_base = freestr(zp->z_ixfr_base); if (zp->z_ixfr_tmp != NULL) zp->z_ixfr_tmp = freestr(zp->z_ixfr_tmp); if (zp->z_update_acl != NULL) free_ip_match_list(zp->z_update_acl); zp->z_update_acl = NULL; if (zp->z_query_acl != NULL) free_ip_match_list(zp->z_query_acl); zp->z_query_acl = NULL; if (zp->z_transfer_acl != NULL) free_ip_match_list(zp->z_transfer_acl); zp->z_transfer_acl = NULL; #ifdef BIND_UPDATE if (zp->z_updatelog != NULL) zp->z_updatelog = freestr(zp->z_updatelog); #endif /* BIND_UPDATE */ #ifdef BIND_NOTIFY if (zp->z_also_notify != NULL) memput(zp->z_also_notify, zp->z_notify_count * sizeof *zp->z_also_notify); zp->z_also_notify = NULL; #endif if (zp->z_fwdtab != NULL) free_forwarders(zp->z_fwdtab); zp->z_fwdtab = NULL; block_signals(); if (LINKED(zp, z_reloadlink)) UNLINK(reloadingzones, zp, z_reloadlink); unblock_signals(); } static void release_zone(struct zoneinfo *zp) { INSIST(zp != NULL); free_zone_contents(zp, 0); memput(zp, sizeof *zp); } struct zoneinfo * find_zone(const char *name, int class) { struct zoneinfo *zp; symbol_value value; ns_debug(ns_log_config, 3, "find_zone(%s, %d)", *name ? name : ".", class); if (lookup_symbol(zone_symbol_table, name, class, &value)) { INSIST(value.integer >= 0 && value.integer < nzones); ns_debug(ns_log_config, 3, "find_zone: existing zone %d", value.integer); zp = &zones[value.integer]; return (zp); } ns_debug(ns_log_config, 3, "find_zone: unknown zone"); return (NULL); } static struct zoneinfo * new_zone(void) { struct zoneinfo *zp; if (EMPTY(freezones)) make_new_zones(); zp = HEAD(freezones); UNLINK(freezones, zp, z_freelink); return (zp); } /* * Check out a zoneinfo structure and return non-zero if it's OK. */ static int validate_zone(struct zoneinfo *zp) { char filename[MAXPATHLEN+1]; /* Check name */ if (!res_dnok(zp->z_origin)) { ns_error(ns_log_config, "invalid zone name '%s'", zp->z_origin); return (0); } /* Check class */ if (zp->z_class == C_ANY || zp->z_class == C_NONE) { ns_error(ns_log_config, "invalid class %d for zone '%s'", zp->z_class, zp->z_origin); return (0); } /* Check type. */ if (zp->z_type == 0) { ns_error(ns_log_config, "no type specified for zone '%s'", zp->z_origin); return (0); } if (zp->z_type == z_cache && ns_samename(zp->z_origin, "") != 1) { ns_error(ns_log_config, "only the root zone may be a cache zone (zone '%s')", zp->z_origin); return (0); } if (zp->z_type == z_hint && ns_samename(zp->z_origin, "") != 1) { ns_error(ns_log_config, "only the root zone may be a hint zone (zone '%s')", zp->z_origin); return (0); } /* Check filename. */ if (zp->z_type == z_master && zp->z_source == NULL) { ns_error(ns_log_config, "'file' statement missing for master zone %s", zp->z_origin); return (0); } /* * XXX We should run filename through an OS-specific * validator here. */ if (zp->z_source != NULL && strlen(zp->z_source) > MAXPATHLEN) { ns_error(ns_log_config, "filename too long for zone '%s'", zp->z_origin); return (0); } if (zp->z_ixfr_base != NULL && strlen(zp->z_ixfr_base) > MAXPATHLEN) { ns_error(ns_log_config, "ixfr filename too long for zone '%s'", zp->z_origin); return (0); } if (zp->z_ixfr_tmp != NULL && strlen(zp->z_ixfr_tmp) > MAXPATHLEN) { ns_error(ns_log_config, "tmp ixfr filename too long for zone '%s'", zp->z_origin); return (0); } /* Check masters */ if (zp->z_addrcnt != 0) { if (zp->z_type == z_master || zp->z_type == z_hint || zp->z_type == z_cache) { ns_error(ns_log_config, "'masters' statement present for %s zone '%s'", (zp->z_type == z_master) ? "master" : (zp->z_type == z_hint) ? "hint" : "cache", zp->z_origin); return (0); } } else { if (zp->z_type == z_slave || zp->z_type == z_stub) { ns_error(ns_log_config, "no 'masters' statement for non-master zone '%s'", zp->z_origin); return (0); } } /* Check allow-update and allow-transfer. */ if (zp->z_update_acl || zp->z_transfer_acl) { if (zp->z_type != z_master && zp->z_type != z_slave) { ns_error(ns_log_config, "'allow-{update,transfer}' option for non-{master,slave} zone '%s'", zp->z_origin); return (0); } } /* Check allow-query. */ if (zp->z_query_acl) { if (zp->z_type != z_master && zp->z_type != z_slave && #ifdef FORWARD_ALLOWS zp->z_type != z_forward && #endif zp->z_type != z_stub) { ns_error(ns_log_config, #ifdef FORWARD_ALLOWS "'allow-query' option for hint zone '%s'", #else "'allow-query' option for non-{master,slave,stub} zone '%s'", #endif zp->z_origin); return (0); } } #ifdef BIND_NOTIFY /* Check notify */ if (zp->z_notify != notify_use_default) { if (zp->z_type != z_master && zp->z_type != z_slave) { ns_error(ns_log_config, "'notify' given for non-master, non-slave zone '%s'", zp->z_origin); return (0); } } /* Check also-notify */ if (zp->z_notify_count != 0) { if (zp->z_type != z_master && zp->z_type != z_slave) { ns_error(ns_log_config, "'also-notify' given for non-master, non-slave zone '%s'", zp->z_origin); return (0); } } #endif #ifdef BIND_UPDATE /* XXX need more checking here */ if (!zp->z_updatelog && zp->z_source) { /* XXX OS-specific filename validation here */ if ((strlen(zp->z_source) + (sizeof ".log" - 1)) > MAXPATHLEN) { ns_error(ns_log_config, "filename too long for dynamic zone '%s'", zp->z_origin); return (0); } /* this sprintf() is now safe */ sprintf(filename, "%s.log", zp->z_source); zp->z_updatelog = savestr(filename, 1); } /* Check forward */ if (zp->z_optset & OPTION_FORWARD_ONLY) { if (zp->z_type == z_hint) { ns_error(ns_log_config, "'forward' given for hint zone '%s'", zp->z_origin); return (0); } } /* Check forwarders */ if (zp->z_fwdtab) { if (zp->z_type == z_hint) { ns_error(ns_log_config, "'forwarders' given for hint zone '%s'", zp->z_origin); return (0); } } if (zp->z_type == z_master) { if (!zp->z_soaincrintvl) zp->z_soaincrintvl = SOAINCRINTVL; if (!zp->z_dumpintvl) zp->z_dumpintvl = DUMPINTVL; if (!zp->z_deferupdcnt) zp->z_deferupdcnt = DEFERUPDCNT; } #endif /* BIND_UPDATE */ if (!zp->z_ixfr_base && zp->z_source) { /* XXX OS-specific filename validation here */ if ((strlen(zp->z_source) + (sizeof ".ixfr" - 1)) > MAXPATHLEN) { ns_error(ns_log_config, "filename too long for dynamic zone '%s'", zp->z_origin); return (0); } /* this sprintf() is now safe */ sprintf(filename, "%s.ixfr", zp->z_source); zp->z_ixfr_base = savestr(filename, 1); } if (!zp->z_ixfr_tmp && zp->z_source) { /* XXX OS-specific filename validation here */ if ((strlen(zp->z_source) + (sizeof ".ixfr.tmp" - 1)) > MAXPATHLEN) { ns_error(ns_log_config, "filename too long for dynamic zone '%s'", zp->z_origin); return (0); } /* this sprintf() is now safe */ sprintf(filename, "%s.ixfr.tmp", zp->z_source); zp->z_ixfr_tmp = savestr(filename, 1); } return (1); } /* * Start building a new zoneinfo structure. Returns an opaque * zone_config suitable for use by the parser. */ zone_config begin_zone(char *name, int class) { zone_config zh; struct zoneinfo *zp; /* * require: name is canonical, class is a valid class */ ns_debug(ns_log_config, 3, "begin_zone('%s', %d)", (*name == '\0') ? "." : name, class); zp = (struct zoneinfo *)memget(sizeof (struct zoneinfo)); if (zp == NULL) panic("memget failed in begin_zone", NULL); memset(zp, 0, sizeof (struct zoneinfo)); zp->z_origin = name; zp->z_class = class; zp->z_checknames = not_set; if (server_options->flags & OPTION_MAINTAIN_IXFR_BASE) zp->z_maintain_ixfr_base = 1; else zp->z_maintain_ixfr_base = 0; zp->z_max_log_size_ixfr = server_options->max_log_size_ixfr; zh.opaque = zp; return (zh); } /* * Merge new configuration information into an existing zone. The * new zoneinfo must be valid. */ static void update_zone_info(struct zoneinfo *zp, struct zoneinfo *new_zp) { char buf[MAXPATHLEN+1]; int i; INSIST(zp != NULL); INSIST(new_zp != NULL); ns_debug(ns_log_config, 1, "update_zone_info('%s', %d)", (*new_zp->z_origin == '\0') ? "." : new_zp->z_origin, new_zp->z_type); #ifdef BIND_UPDATE /* * A dynamic master zone that's becoming non-dynamic may need to be * dumped before we start the update. */ if ((zp->z_flags & Z_DYNAMIC) && !(new_zp->z_flags & Z_DYNAMIC) && ((zp->z_flags & Z_NEED_SOAUPDATE) || (zp->z_flags & Z_NEED_DUMP))) (void) zonedump(zp, ISNOTIXFR); #endif /* * First do the simple stuff, making sure to free * any data that was dynamically allocated. */ if (zp->z_origin != NULL) (void)freestr(zp->z_origin); zp->z_origin = new_zp->z_origin; new_zp->z_origin = NULL; zp->z_maintain_ixfr_base = new_zp->z_maintain_ixfr_base; zp->z_max_log_size_ixfr = new_zp->z_max_log_size_ixfr; zp->z_class = new_zp->z_class; zp->z_type = new_zp->z_type; zp->z_checknames = new_zp->z_checknames; for (i = 0; i < new_zp->z_addrcnt; i++) { zp->z_addr[i] = new_zp->z_addr[i]; zp->z_keys[i] = new_zp->z_keys[i]; } zp->z_addrcnt = new_zp->z_addrcnt; if (zp->z_update_acl) free_ip_match_list(zp->z_update_acl); zp->z_update_acl = new_zp->z_update_acl; new_zp->z_update_acl = NULL; if (zp->z_query_acl) free_ip_match_list(zp->z_query_acl); zp->z_query_acl = new_zp->z_query_acl; new_zp->z_query_acl = NULL; zp->z_axfr_src = new_zp->z_axfr_src; if (zp->z_transfer_acl) free_ip_match_list(zp->z_transfer_acl); zp->z_transfer_acl = new_zp->z_transfer_acl; new_zp->z_transfer_acl = NULL; zp->z_max_transfer_time_in = new_zp->z_max_transfer_time_in; #ifdef BIND_NOTIFY zp->z_notify = new_zp->z_notify; if (zp->z_also_notify) memput(zp->z_also_notify, zp->z_notify_count * sizeof *zp->z_also_notify); zp->z_also_notify = new_zp->z_also_notify; zp->z_notify_count = new_zp->z_notify_count; new_zp->z_also_notify = NULL; new_zp->z_notify_count = 0; #endif if ((new_zp->z_flags & Z_FORWARD_SET) != 0) zp->z_flags |= Z_FORWARD_SET; else zp->z_flags &= ~Z_FORWARD_SET; if (zp->z_fwdtab != NULL) free_forwarders(zp->z_fwdtab); zp->z_fwdtab = new_zp->z_fwdtab; new_zp->z_fwdtab = NULL; zp->z_dialup = new_zp->z_dialup; zp->z_options = new_zp->z_options; zp->z_optset = new_zp->z_optset; #ifdef BIND_UPDATE if (new_zp->z_flags & Z_DYNAMIC) zp->z_flags |= Z_DYNAMIC; else zp->z_flags &= ~Z_DYNAMIC; zp->z_soaincrintvl = new_zp->z_soaincrintvl; zp->z_dumpintvl = new_zp->z_dumpintvl; zp->z_deferupdcnt = new_zp->z_deferupdcnt; if (zp->z_updatelog) (void)freestr(zp->z_updatelog); zp->z_updatelog = new_zp->z_updatelog; new_zp->z_updatelog = NULL; #endif /* BIND_UPDATE */ zp->z_port = new_zp->z_port; /* * Now deal with files. */ switch (zp->z_type) { case z_cache: ns_panic(ns_log_config, 1, "impossible condition"); break; case z_hint: ns_debug(ns_log_config, 1, "source = %s", new_zp->z_source); zp->z_refresh = 0; /* No dumping. */ if (zp->z_source != NULL && strcmp(new_zp->z_source, zp->z_source) == 0 && (reconfiging || !zonefile_changed_p(zp))) { ns_debug(ns_log_config, 1, "cache is up to date"); break; } /* File has changed, or hasn't been loaded yet. */ if (zp->z_source) { zp->z_source = freestr(zp->z_source); ns_stopxfrs(zp); - purge_zone(zp->z_origin, fcachetab, zp->z_class); + purge_zone(zp, fcachetab); } zp->z_source = new_zp->z_source; new_zp->z_source = NULL; if (zp->z_ixfr_base) (void)freestr(zp->z_ixfr_base); zp->z_ixfr_base = new_zp->z_ixfr_base; new_zp->z_ixfr_base = NULL; if (zp->z_ixfr_tmp) (void)freestr(zp->z_ixfr_tmp); zp->z_ixfr_tmp = new_zp->z_ixfr_tmp; new_zp->z_ixfr_tmp = NULL; ns_debug(ns_log_config, 1, "reloading hint zone"); (void) db_load(zp->z_source, zp->z_origin, zp, NULL, ISNOTIXFR); break; case z_master: ns_debug(ns_log_config, 1, "source = %s", new_zp->z_source); /* * If we've loaded this file, and the file hasn't changed * then there's no need to reload. */ if (zp->z_source != NULL && strcmp(new_zp->z_source, zp->z_source) == 0 && (reconfiging || !zonefile_changed_p(zp))) { ns_debug(ns_log_config, 1, "zone is up to date"); break; } #ifdef BIND_UPDATE if (zp->z_source && (zp->z_flags & Z_DYNAMIC)) ns_warning(ns_log_config, "source file of dynamic zone '%s' has changed", zp->z_origin); primary_reload: #endif /* BIND_UPDATE */ if (zp->z_source != NULL) (void)freestr(zp->z_source); zp->z_source = new_zp->z_source; new_zp->z_source = NULL; if (zp->z_ixfr_base != NULL) (void)freestr(zp->z_ixfr_base); zp->z_ixfr_base = new_zp->z_ixfr_base; new_zp->z_ixfr_base = NULL; if (zp->z_ixfr_tmp != NULL) (void)freestr(zp->z_ixfr_tmp); zp->z_ixfr_tmp = new_zp->z_ixfr_tmp; new_zp->z_ixfr_tmp = NULL; if (reload_master(zp) == 1) { /* * Note that going to primary_reload * unconditionally reloads the zone. */ new_zp->z_source = savestr(zp->z_source, 1); new_zp->z_ixfr_base = savestr(zp->z_ixfr_base, 1); new_zp->z_ixfr_tmp = savestr(zp->z_ixfr_tmp, 1); goto primary_reload; } break; case z_slave: #ifdef STUBS case z_stub: #endif ns_debug(ns_log_config, 1, "addrcnt = %d", zp->z_addrcnt); if (!new_zp->z_source) { /* * We will always transfer this zone again * after a reload. */ sprintf(buf, "NsTmp%ld.%d", (long)getpid(), tmpnum++); new_zp->z_source = savestr(buf, 1); zp->z_flags |= Z_TMP_FILE; } else zp->z_flags &= ~Z_TMP_FILE; /* * If we had a backup file name, and it was changed, * free old zone and start over. If we don't have * current zone contents, try again now in case * we have a new server on the list. */ if (zp->z_source != NULL && (strcmp(new_zp->z_source, zp->z_source) != 0 || ((!reconfiging) && zonefile_changed_p(zp)))) { ns_debug(ns_log_config, 1, "backup file changed or missing"); zp->z_source = freestr(zp->z_source); zp->z_serial = 0; /* force xfer */ ns_stopxfrs(zp); /* * We only need to reload if we have ever * successfully transferred the zone. */ if ((zp->z_flags & Z_AUTH) != 0) { zp->z_flags &= ~Z_AUTH; /* * Purge old data and mark the parent for * reloading so that NS records are present * during the zone transfer. */ - do_reload(zp->z_origin, zp->z_type, - zp->z_class, 1); + do_reload(zp, 1); } } if (zp->z_source == NULL) { zp->z_source = new_zp->z_source; new_zp->z_source = NULL; } if (zp->z_ixfr_base != NULL) (void)freestr(zp->z_ixfr_base); zp->z_ixfr_base = new_zp->z_ixfr_base; new_zp->z_ixfr_base = NULL; if (zp->z_ixfr_tmp != NULL) freestr(zp->z_ixfr_tmp); zp->z_ixfr_tmp = new_zp->z_ixfr_tmp; new_zp->z_ixfr_tmp = NULL; if ((!noexpired || ((zp->z_flags & Z_EXPIRED) == 0)) && ((zp->z_flags & Z_AUTH) == 0)) zoneinit(zp); else { /* ** Force slave to try transfer soon after SIGHUP. */ if ((zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING)) == 0 && reloading && !reconfiging) { qserial_retrytime(zp, tt.tv_sec); sched_zone_maint(zp); } } break; case z_forward: /* * We don't know if the forwarder's list has changed * so just purge the cache. In the future we may want * see if the forwarders list has changed and only * do this then. */ clean_cache_from(zp->z_origin, hashtab); break; } if ((zp->z_flags & Z_FOUND) != 0 && /* already found? */ (zp - zones) != DB_Z_CACHE) /* cache never sets Z_FOUND */ ns_error(ns_log_config, "Zone \"%s\" declared more than once", zp->z_origin); zp->z_flags |= Z_FOUND; ns_debug(ns_log_config, 1, "zone[%d] type %d: '%s' z_time %lu, z_refresh %u", zp-zones, zp->z_type, *(zp->z_origin) == '\0' ? "." : zp->z_origin, (u_long)zp->z_time, zp->z_refresh); } /* * Finish constructing a new zone. If valid, the constructed zone is * merged into the zone database. The zone_config used is invalid after * end_zone() completes. */ void end_zone(zone_config zh, int should_install) { struct zoneinfo *zp, *new_zp; const char *zname; symbol_value value; new_zp = zh.opaque; INSIST(new_zp != NULL); zname = (new_zp->z_origin[0] == '\0') ? "." : new_zp->z_origin; ns_debug(ns_log_config, 3, "end_zone('%s', %d)", zname, should_install); if (!should_install) { release_zone(new_zp); return; } if (!validate_zone(new_zp)) { ns_error(ns_log_config, "zone '%s' did not validate, skipping", zname); release_zone(new_zp); return; } zp = find_zone(new_zp->z_origin, new_zp->z_class); if (zp != NULL && zp->z_type != new_zp->z_type) { remove_zone(zp, "redefined"); zp = NULL; } if (zp == NULL) { zp = new_zone(); INSIST(zp != NULL); value.integer = (zp - zones); define_symbol(zone_symbol_table, new_zp->z_origin, new_zp->z_class, value, 0); } ns_debug(ns_log_config, 5, "zone '%s', type = %d, class = %d", zname, new_zp->z_type, new_zp->z_class); if (new_zp->z_source != NULL) ns_debug(ns_log_config, 5, " file = %s", new_zp->z_source); ns_debug(ns_log_config, 5, " checknames = %d", new_zp->z_checknames); if (new_zp->z_addrcnt != 0) { int i; ns_debug(ns_log_config, 5, " masters:"); for (i = 0; i < new_zp->z_addrcnt; i++) ns_debug(ns_log_config, 5, " %s", inet_ntoa(new_zp->z_addr[i])); } update_zone_info(zp, new_zp); release_zone(new_zp); zh.opaque = NULL; } int set_zone_type(zone_config zh, int type) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if type already set for this zone */ if (zp->z_type != 0) return (0); zp->z_type = type; return (1); } int set_zone_filename(zone_config zh, char *filename) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if filename already set for this zone */ if (zp->z_source != NULL) return (0); zp->z_source = filename; return (1); } int set_zone_checknames(zone_config zh, enum severity s) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if checknames already set for this zone */ if (zp->z_checknames != not_set) return (0); zp->z_checknames = s; return (1); } int set_zone_ixfr_file(zone_config zh, char *filename) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if filename already set for this zone */ if (zp->z_ixfr_base != NULL) return (0); zp->z_ixfr_base = filename; if (zp->z_ixfr_tmp == NULL) { int len = strlen(zp->z_ixfr_base) + (sizeof ".tmp"); char *str = (char *) memget(len); sprintf(str, "%s.tmp", zp->z_ixfr_base); zp->z_ixfr_tmp = savestr(str, 1); memput(str, len); } return (1); } int set_zone_ixfr_tmp(zone_config zh, char *filename) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if filename already set for this zone */ if (zp->z_ixfr_tmp != NULL) return (0); zp->z_ixfr_tmp = filename; return (1); } int set_zone_dialup(zone_config zh, int value) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); if (value) { zp->z_dialup = zdialup_yes; #ifdef BIND_NOTIFY zp->z_notify = notify_yes; #endif } else zp->z_dialup = zdialup_no; return (1); } int set_zone_notify(zone_config zh, enum notify value) { #ifdef BIND_NOTIFY struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); zp->z_notify = value; #endif return (1); } int set_zone_maintain_ixfr_base(zone_config zh, int value) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); zp->z_maintain_ixfr_base = value; return (1); } int set_zone_update_acl(zone_config zh, ip_match_list iml) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if update_acl already set for this zone */ if (zp->z_update_acl != NULL) return (0); zp->z_update_acl = iml; #ifdef BIND_UPDATE if (!ip_match_is_none(iml)) zp->z_flags |= Z_DYNAMIC; else ns_debug(ns_log_config, 3, "update acl is none for '%s'", zp->z_origin); #endif return (1); } int set_zone_query_acl(zone_config zh, ip_match_list iml) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if allow-query acl already set for this zone */ if (zp->z_query_acl != NULL) return (0); zp->z_query_acl = iml; return (1); } int set_zone_master_port(zone_config zh, u_short port) { struct zoneinfo *zp = zh.opaque; zp->z_port = port; return (1); } int set_zone_transfer_source(zone_config zh, struct in_addr ina) { struct zoneinfo *zp = zh.opaque; zp->z_axfr_src = ina; return (1); } int set_zone_transfer_acl(zone_config zh, ip_match_list iml) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if allow-transfer acl already set for this zone */ if (zp->z_transfer_acl != NULL) return (0); zp->z_transfer_acl = iml; return (1); } int set_zone_transfer_time_in(zone_config zh, long max_time) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); /* Fail if max-transfer-time-in already set for this zone */ if (zp->z_max_transfer_time_in) return (0); zp->z_max_transfer_time_in = max_time; return (1); } int set_zone_max_log_size_ixfr(zone_config zh, int size) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); zp->z_max_log_size_ixfr = size; return (0); } int set_zone_pubkey(zone_config zh, const int flags, const int proto, const int alg, const char *str) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); INSIST(zp != NULL && zp->z_origin != NULL); return (add_trusted_key(zp->z_origin, flags, proto, alg, str)); } int set_trusted_key(const char *name, const int flags, const int proto, const int alg, const char *str) { INSIST(name != NULL); return (add_trusted_key(name, flags, proto, alg, str)); } int add_zone_master(zone_config zh, struct in_addr address, struct dst_key * key) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); zp->z_addr[zp->z_addrcnt] = address; zp->z_keys[zp->z_addrcnt] = key; zp->z_addrcnt++; if (zp->z_addrcnt >= NSMAX) { ns_warning(ns_log_config, "NSMAX reached for zone '%s'", zp->z_origin); zp->z_addrcnt = NSMAX - 1; } return (1); } int add_zone_notify(zone_config zh, struct in_addr address) { #ifdef BIND_NOTIFY struct zoneinfo *zp; int i; zp = zh.opaque; INSIST(zp != NULL); /* Check for duplicates. */ for (i = 0; i < zp->z_notify_count; i++) { if (memcmp(zp->z_also_notify + i, &address, sizeof address) == 0) { ns_warning(ns_log_config, "duplicate also-notify address ignored [%s] for zone '%s'", inet_ntoa(address), zp->z_origin); return (1); } } i = 0; if (zp->z_also_notify == NULL) { zp->z_also_notify = memget(sizeof *zp->z_also_notify); if (zp->z_also_notify == NULL) i = 1; } else { register size_t size; register struct in_addr *an_tmp; size = zp->z_notify_count * sizeof *zp->z_also_notify; an_tmp = memget(size + sizeof *zp->z_also_notify); if (an_tmp == NULL) { i = 1; } else { memcpy(an_tmp, zp->z_also_notify, size); memput(zp->z_also_notify, size); zp->z_also_notify = an_tmp; } } if (i == 0) { zp->z_also_notify[zp->z_notify_count] = address; zp->z_notify_count++; } else { ns_warning(ns_log_config, "also-notify add failed (memget) [%s] for zone '%s'", inet_ntoa(address), zp->z_origin); } #endif return (1); } /* Options */ options new_options() { options op; char hostname[256]; op = (options)memget(sizeof (struct options)); if (op == NULL) panic("memget failed in new_options()", NULL); op->version = savestr(ShortVersion, 1); if (gethostname(hostname, sizeof(hostname)) == 0) op->hostname = savestr(hostname, 1); else op->hostname = NULL; op->directory = savestr(".", 1); op->pid_filename = savestr(_PATH_PIDFILE, 1); op->named_xfer = savestr(_PATH_XFER, 1); op->dump_filename = savestr(_PATH_DUMPFILE, 1); op->stats_filename = savestr(_PATH_STATS, 1); op->memstats_filename = savestr(_PATH_MEMSTATS, 1); op->flags = DEFAULT_OPTION_FLAGS; op->transfers_in = DEFAULT_XFERS_RUNNING; op->transfers_per_ns = DEFAULT_XFERS_PER_NS; op->transfers_out = 0; op->serial_queries = MAXQSERIAL; op->transfer_format = axfr_one_answer; op->max_transfer_time_in = MAX_XFER_TIME; memset(&op->query_source, 0, sizeof op->query_source); op->query_source.sin_family = AF_INET; op->query_source.sin_addr.s_addr = htonl(INADDR_ANY); op->query_source.sin_port = htons(0); /* INPORT_ANY */ op->axfr_src.s_addr = 0; #ifdef BIND_NOTIFY op->notify_count = 0; op->also_notify = NULL; #endif op->blackhole_acl = NULL; op->query_acl = NULL; op->transfer_acl = NULL; op->recursion_acl = NULL; op->sortlist = NULL; op->topology = NULL; op->data_size = 0UL; /* use system default */ op->stack_size = 0UL; /* use system default */ op->core_size = 0UL; /* use system default */ op->files = ULONG_MAX; /* unlimited */ op->check_names[primary_trans] = fail; op->check_names[secondary_trans] = warn; op->check_names[response_trans] = ignore; op->listen_list = NULL; op->fwdtab = NULL; /* XXX init forwarding */ op->clean_interval = 3600; op->interface_interval = 3600; op->stats_interval = 3600; op->ordering = NULL; op->max_ncache_ttl = DEFAULT_MAX_NCACHE_TTL; op->max_host_stats = 0; op->lame_ttl = NTTL; op->heartbeat_interval = 3600; op->max_log_size_ixfr = 0; op->minroots = MINROOTS; op->preferred_glue = 0; #ifdef BIND_NOTIFY op->notify = notify_yes; #endif + op->edns_udp_size = EDNS_MESSAGE_SZ; return (op); } void free_options(options op) { INSIST(op != NULL); if (op->hostname) op->hostname = freestr(op->hostname); if (op->version) op->version = freestr(op->version); if (op->directory) op->directory = freestr(op->directory); if (op->pid_filename) op->pid_filename = freestr(op->pid_filename); if (op->named_xfer) op->named_xfer = freestr(op->named_xfer); if (op->dump_filename) op->dump_filename = freestr(op->dump_filename); if (op->stats_filename) op->stats_filename = freestr(op->stats_filename); if (op->memstats_filename) op->memstats_filename = freestr(op->memstats_filename); #ifdef BIND_NOTIFY if (op->also_notify) free_also_notify(op); #endif if (op->blackhole_acl) free_ip_match_list(op->blackhole_acl); if (op->query_acl) free_ip_match_list(op->query_acl); if (op->recursion_acl) free_ip_match_list(op->recursion_acl); if (op->transfer_acl) free_ip_match_list(op->transfer_acl); if (op->sortlist) free_ip_match_list(op->sortlist); if (op->ordering) free_rrset_order_list(op->ordering); if (op->topology) free_ip_match_list(op->topology); if (op->listen_list) free_listen_info_list(op->listen_list); if (op->fwdtab) free_forwarders(op->fwdtab); memput(op, sizeof *op); } static void set_boolean_option(u_int *op_flags, int bool_opt, int value) { INSIST(op_flags != NULL); switch (bool_opt) { #ifdef HITCOUNTS case OPTION_HITCOUNT: #endif /* HITCOUNTS */ case OPTION_NORECURSE: case OPTION_NOFETCHGLUE: case OPTION_FORWARD_ONLY: case OPTION_FAKE_IQUERY: case OPTION_SUPNOTIFY_INITIAL: case OPTION_NONAUTH_NXDOMAIN: case OPTION_MULTIPLE_CNAMES: case OPTION_USE_IXFR: case OPTION_MAINTAIN_IXFR_BASE: case OPTION_HOSTSTATS: case OPTION_DEALLOC_ON_EXIT: case OPTION_USE_ID_POOL: case OPTION_NORFC2308_TYPE1: case OPTION_NODIALUP: case OPTION_TREAT_CR_AS_SPACE: if (value) *op_flags |= bool_opt; else *op_flags &= ~bool_opt; break; default: panic("unexpected option in set_boolean_option", NULL); } } void set_global_boolean_option(options op, int bool_opt, int value) { INSIST(op != NULL); set_boolean_option(&op->flags, bool_opt, value); } void set_zone_boolean_option(zone_config zh, int bool_opt, int value) { struct zoneinfo *zp; zp = zh.opaque; INSIST(zp != NULL); set_boolean_option(&zp->z_options, bool_opt, value); /* Flag that zone option overrides corresponding global option */ zp->z_optset |= bool_opt; } #ifdef HAVE_GETRUSAGE enum limit { Datasize, Stacksize, Coresize, Files }; static struct rlimit initial_data_size; static struct rlimit initial_stack_size; static struct rlimit initial_core_size; static struct rlimit initial_num_files; static void get_initial_limits() { int fdlimit = evHighestFD(ev) + 1; # ifdef RLIMIT_DATA if (getrlimit(RLIMIT_DATA, &initial_data_size) < 0) ns_warning(ns_log_config, "getrlimit(DATA): %s", strerror(errno)); # endif # ifdef RLIMIT_STACK if (getrlimit(RLIMIT_STACK, &initial_stack_size) < 0) ns_warning(ns_log_config, "getrlimit(STACK): %s", strerror(errno)); # endif # ifdef RLIMIT_CORE if (getrlimit(RLIMIT_CORE, &initial_core_size) < 0) ns_warning(ns_log_config, "getrlimit(CORE): %s", strerror(errno)); # endif # ifdef RLIMIT_NOFILE if (getrlimit(RLIMIT_NOFILE, &initial_num_files) < 0) ns_warning(ns_log_config, "getrlimit(NOFILE): %s", strerror(errno)); else if (initial_num_files.rlim_cur > fdlimit) { initial_num_files.rlim_cur = fdlimit; if (initial_num_files.rlim_cur > initial_num_files.rlim_max) initial_num_files.rlim_max = fdlimit; if (setrlimit(RLIMIT_NOFILE, &initial_num_files) < 0) { ns_warning(ns_log_config, "setrlimit(files): %s", strerror(errno)); } else { ns_warning(ns_log_config, "limit files set to fdlimit (%d)", fdlimit); } } # endif } static void ns_rlimit(enum limit limit, u_long limit_value) { struct rlimit limits, old_limits; int rlimit = -1; int fdlimit = evHighestFD(ev) + 1; const char *name; rlimit_type value; if (limit_value == ULONG_MAX) { #ifndef RLIMIT_FILE_INFINITY if (limit == Files) value = MIN((rlimit_type)evHighestFD(ev) + 1, initial_num_files.rlim_max); else #endif value = (rlimit_type)RLIM_INFINITY; } else value = (rlimit_type)limit_value; limits.rlim_cur = limits.rlim_max = value; switch (limit) { case Datasize: #ifdef RLIMIT_DATA rlimit = RLIMIT_DATA; #endif name = "max data size"; if (value == 0) limits = initial_data_size; break; case Stacksize: #ifdef RLIMIT_STACK rlimit = RLIMIT_STACK; #endif name = "max stack size"; if (value == 0) limits = initial_stack_size; break; case Coresize: #ifdef RLIMIT_CORE rlimit = RLIMIT_CORE; #endif name = "max core size"; if (value == 0) limits = initial_core_size; break; case Files: #ifdef RLIMIT_NOFILE rlimit = RLIMIT_NOFILE; #endif name = "max number of open files"; if (value == 0) limits = initial_num_files; if ((int)value > fdlimit) limits.rlim_cur = limits.rlim_max = value = fdlimit; break; default: name = NULL; /* Make gcc happy. */ panic("impossible condition in ns_rlimit()", NULL); } if (rlimit == -1) { ns_warning(ns_log_config, "limit \"%s\" not supported on this system - ignored", name); return; } if (getrlimit(rlimit, &old_limits) < 0) { ns_warning(ns_log_config, "getrlimit(%s): %s", name, strerror(errno)); } if (user_id != 0 && limits.rlim_max == RLIM_INFINITY) limits.rlim_cur = limits.rlim_max = old_limits.rlim_max; if (setrlimit(rlimit, &limits) < 0) { ns_warning(ns_log_config, "setrlimit(%s): %s", name, strerror(errno)); return; } else { if (value == 0) ns_debug(ns_log_config, 3, "%s is default", name); else if (value == RLIM_INFINITY) ns_debug(ns_log_config, 3, "%s is unlimited", name); else #ifdef RLIMIT_LONGLONG ns_debug(ns_log_config, 3, "%s is %llu", name, (unsigned long long)value); #else ns_debug(ns_log_config, 3, "%s is %lu", name, value); #endif } } #endif /* HAVE_GETRUSAGE */ listen_info_list new_listen_info_list() { listen_info_list ll; ll = (listen_info_list)memget(sizeof (struct listen_info_list)); if (ll == NULL) panic("memget failed in new_listen_info_list()", NULL); ll->first = NULL; ll->last = NULL; return (ll); } void free_listen_info_list(listen_info_list ll) { listen_info li, next_li; INSIST(ll != NULL); for (li = ll->first; li != NULL; li = next_li) { next_li = li->next; free_ip_match_list(li->list); memput(li, sizeof *li); } memput(ll, sizeof *ll); } void add_listen_on(options op, u_short port, ip_match_list iml) { listen_info_list ll; listen_info ni; INSIST(op != NULL); if (op->listen_list == NULL) op->listen_list = new_listen_info_list(); ll = op->listen_list; ni = (listen_info)memget(sizeof (struct listen_info)); if (ni == NULL) panic("memget failed in add_listen_on", NULL); ni->port = port; ni->list = iml; ni->next = NULL; if (ll->last != NULL) ll->last->next = ni; ll->last = ni; if (ll->first == NULL) ll->first = ni; } FILE * write_open(char *filename) { FILE *stream; int fd; struct stat sb; int regular; if (stat(filename, &sb) < 0) { if (errno != ENOENT) { ns_error(ns_log_os, "write_open: stat of %s failed: %s", filename, strerror(errno)); return (NULL); } regular = 1; } else regular = (sb.st_mode & S_IFREG); if (!regular) { ns_error(ns_log_os, "write_open: %s isn't a regular file", filename); return (NULL); } (void)unlink(filename); fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if (fd < 0) return (NULL); (void) fchown(fd, user_id, group_id); stream = fdopen(fd, "w"); if (stream == NULL) { (void)unlink(filename); (void)close(fd); } return (stream); } void update_pid_file() { FILE *fp; REQUIRE(server_options != NULL); REQUIRE(server_options->pid_filename != NULL); /* XXX */ ns_debug(ns_log_default, 1, "update_pid_file()"); if (current_pid_filename != NULL) { (void)unlink(current_pid_filename); current_pid_filename = freestr(current_pid_filename); } current_pid_filename = savestr(server_options->pid_filename, 0); if (current_pid_filename == NULL) { ns_error(ns_log_config, "savestr() failed in update_pid_file()"); return; } fp = write_open(current_pid_filename); if (fp != NULL) { (void) fprintf(fp, "%ld\n", (long)getpid()); (void) fclose(fp); } else ns_error(ns_log_config, "couldn't create pid file '%s'", server_options->pid_filename); } /* * XXX This function will eventually be public and will be relocated to * the UNIX OS support library. */ static int os_change_directory(const char *name) { struct stat sb; if (name == NULL || *name == '\0') { errno = EINVAL; return (0); } if (chdir(name) < 0) return (0); if (stat(name, &sb) < 0) { ns_error(ns_log_os, "stat(%s) failed: %s", name, strerror(errno)); return (1); } if (sb.st_mode & S_IWOTH) ns_warning(ns_log_os, "directory %s is world-writable", name); return (1); } static void periodic_getnetconf(evContext ctx, void *uap, struct timespec due, struct timespec inter) { UNUSED(ctx); UNUSED(uap); UNUSED(due); UNUSED(inter); getnetconf(1); } static int clean_interval = 0; static int interface_interval = 0; static int stats_interval = 0; static int heartbeat_interval = 0; static void set_interval_timer(int which_timer, int interval) { evTimerID *tid = NULL; evTimerFunc func = NULL; int changed = 0; switch (which_timer) { case CLEAN_TIMER: if (clean_interval != interval) changed = 1; clean_interval = interval; tid = &clean_timer; func = ns_cleancache; break; case INTERFACE_TIMER: if (interface_interval != interval) changed = 1; interface_interval = interval; tid = &interface_timer; func = periodic_getnetconf; break; case STATS_TIMER: if (stats_interval != interval) changed = 1; stats_interval = interval; tid = &stats_timer; func = ns_logstats; break; case HEARTBEAT_TIMER: if (heartbeat_interval != interval) changed = 1; heartbeat_interval = interval; tid = &heartbeat_timer; func = ns_heartbeat; break; default: ns_panic(ns_log_config, 1, "set_interval_timer: unknown timer %d", which_timer); } if ((active_timers & which_timer) != 0) { if (interval > 0) { if (changed && evResetTimer(ev, *tid, func, NULL, evAddTime(evNowTime(), evConsTime(interval, 0)), evConsTime(interval, 0)) < 0) ns_error(ns_log_config, "evResetTimer %d interval %d failed: %s", which_timer, interval, strerror(errno)); } else { if (evClearTimer(ev, *tid) < 0) ns_error(ns_log_config, "evClearTimer %d failed: %s", which_timer, strerror(errno)); else active_timers &= ~which_timer; } } else if (interval > 0) { if (evSetTimer(ev, func, NULL, evAddTime(evNowTime(), evConsTime(interval, 0)), evConsTime(interval, 0), tid) < 0) ns_error(ns_log_config, "evSetTimer %d interval %d failed: %s", which_timer, interval, strerror(errno)); else active_timers |= which_timer; } } /* * Set all named global options based on the global options structure * generated by the parser. */ void set_options(options op, int is_default) { INSIST(op != NULL); if (op->listen_list == NULL) { ip_match_list iml; ip_match_element ime; struct in_addr address; op->listen_list = new_listen_info_list(); address.s_addr = htonl(INADDR_ANY); iml = new_ip_match_list(); ime = new_ip_match_pattern(address, 0); add_to_ip_match_list(iml, ime); add_listen_on(op, htons(NS_DEFAULTPORT), iml); } if (op->topology == NULL) { ip_match_list iml; ip_match_element ime; /* default topology is { localhost; localnets; } */ iml = new_ip_match_list(); ime = new_ip_match_localhost(); add_to_ip_match_list(iml, ime); ime = new_ip_match_localnets(); add_to_ip_match_list(iml, ime); op->topology = iml; } if (server_options != NULL) free_options(server_options); server_options = op; /* XXX should validate pid filename */ INSIST(op->pid_filename != NULL); if (op->directory && !os_change_directory(op->directory)) ns_panic(ns_log_config, 0, "can't change directory to %s: %s", op->directory, strerror(errno)); /* XXX currently a value of 0 means "use default"; it would be better if the options block had a "attributes updated" vector (like the way X deals with GC updates) */ if (!op->transfers_in) op->transfers_in = DEFAULT_XFERS_RUNNING; else if (op->transfers_in > MAX_XFERS_RUNNING) { ns_warning(ns_log_config, "the maximum number of concurrent inbound transfers is %d", MAX_XFERS_RUNNING); op->transfers_in = MAX_XFERS_RUNNING; } if (!op->transfers_per_ns) op->transfers_per_ns = DEFAULT_XFERS_PER_NS; if (!op->max_transfer_time_in) op->max_transfer_time_in = MAX_XFER_TIME; /* XXX currently transfers_out is not used */ if (!op->max_ncache_ttl) op->max_ncache_ttl = DEFAULT_MAX_NCACHE_TTL; else if (op->max_ncache_ttl > max_cache_ttl) op->max_ncache_ttl = max_cache_ttl; if (op->lame_ttl > (3 * NTTL)) op->lame_ttl = 3 * NTTL; /* * Limits */ #ifdef HAVE_GETRUSAGE ns_rlimit(Datasize, op->data_size); ns_rlimit(Stacksize, op->stack_size); ns_rlimit(Coresize, op->core_size); ns_rlimit(Files, op->files); #else ns_info(ns_log_config, "cannot set resource limits on this system"); #endif /* * Timers */ set_interval_timer(CLEAN_TIMER, server_options->clean_interval); set_interval_timer(INTERFACE_TIMER, server_options->interface_interval); set_interval_timer(STATS_TIMER, server_options->stats_interval); set_interval_timer(HEARTBEAT_TIMER, server_options->heartbeat_interval); options_installed = 1; default_options_installed = is_default; } void use_default_options() { set_options(new_options(), 1); } /* * rrset order types */ static struct res_sym order_table [] = { { unknown_order, " unknown ", NULL }, /* can't match */ { fixed_order, "fixed", NULL }, { cyclic_order, "cyclic", NULL }, { random_order, "random", NULL }, { unknown_order, NULL, NULL } }; /* * Return the print name of the ordering value. */ const char * p_order(int order) { return (__sym_ntos(order_table, order, (int *)0)); } /* * Lookup the ordering by name and return the matching enum value. */ enum ordering lookup_ordering(const char *name) { int i; for (i = 0; order_table[i].name != NULL; i++) if (strcasecmp(name,order_table[i].name) == 0) return ((enum ordering)order_table[i].number); return (unknown_order); } /* * rrset-order Lists */ rrset_order_list new_rrset_order_list() { rrset_order_list rol ; rol = (rrset_order_list)memget(sizeof (struct rrset_order_list)); if (rol == NULL) panic("memget failed in new_rrset_order_list", NULL); rol->first = NULL; rol->last = NULL; return (rol); } void free_rrset_order_list(rrset_order_list rol) { rrset_order_element roe, next_element; for (roe = rol->first; roe != NULL; roe = next_element) { next_element = roe->next; roe->name = freestr(roe->name); memput(roe, sizeof (*roe)); } memput(rol, sizeof (*rol)); } void add_to_rrset_order_list(rrset_order_list rol, rrset_order_element roe) { INSIST(rol != NULL); INSIST(roe != NULL); if (rol->last != NULL) rol->last->next = roe; roe->next = NULL; rol->last = roe; if (rol->first == NULL) rol->first = roe; } #ifdef notyet /* XXX this isn't being used yet, but it probably should be. Where? */ void dprint_rrset_order_list(int category, rrset_order_list rol, int indent, char *allow, char *deny) { rrset_order_element roe ; char spaces[40+1]; INSIST(rol != NULL); if (indent > 40) indent = 40; if (indent) memset(spaces, ' ', indent); spaces[indent] = '\0'; for (roe = rol->first; roe != NULL; roe = roe->next) { ns_debug(category, 7, "%sclass %s type %s name %s order %s", spaces, p_class(roe->class), p_type(roe->type), roe->name, p_order(roe->order)); } } #endif rrset_order_element new_rrset_order_element(int class, int type, char *name, enum ordering order) { rrset_order_element roe; int i ; roe = (rrset_order_element)memget(sizeof (struct rrset_order_element)); if (roe == NULL) panic("memget failed in new_rrset_order_element", NULL); roe->class = class ; roe->type = type ; roe->name = name; roe->order = order; i = strlen(roe->name) - 1; INSIST (i >= 0); if (roe->name[i - 1] == '.') { /* We compare from right to left so we don't need a dot on the end. */ roe->name[i - 1] = '\0' ; } return roe ; } /* * IP Matching Lists */ ip_match_list new_ip_match_list() { ip_match_list iml; iml = (ip_match_list)memget(sizeof (struct ip_match_list)); if (iml == NULL) panic("memget failed in new_ip_match_list", NULL); iml->first = NULL; iml->last = NULL; return (iml); } void free_ip_match_list(ip_match_list iml) { ip_match_element ime, next_element; for (ime = iml->first; ime != NULL; ime = next_element) { next_element = ime->next; memput(ime, sizeof *ime); } memput(iml, sizeof *iml); } ip_match_element new_ip_match_pattern(struct in_addr address, u_int mask_bits) { ip_match_element ime; u_int32_t mask; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_pattern", NULL); ime->type = ip_match_pattern; ime->flags = 0; ime->u.direct.address = address; if (mask_bits == 0) /* can't shift >= the size of a type in bits, so we deal with an empty mask here */ mask = 0; else { /* set the 'mask_bits' most significant bits */ mask = 0xffffffffU; mask >>= (32 - mask_bits); mask <<= (32 - mask_bits); } mask = ntohl(mask); ime->u.direct.mask.s_addr = mask; ime->next = NULL; if (!ina_onnet(ime->u.direct.address, ime->u.direct.address, ime->u.direct.mask)) { memput(ime, sizeof *ime); ime = NULL; } return (ime); } ip_match_element new_ip_match_mask(struct in_addr address, struct in_addr mask) { ip_match_element ime; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_pattern", NULL); ime->type = ip_match_pattern; ime->flags = 0; ime->u.direct.address = address; ime->u.direct.mask = mask; ime->next = NULL; if (!ina_onnet(ime->u.direct.address, ime->u.direct.address, ime->u.direct.mask)) { memput(ime, sizeof *ime); ime = NULL; } return (ime); } ip_match_element new_ip_match_indirect(ip_match_list iml) { ip_match_element ime; INSIST(iml != NULL); ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_indirect", NULL); ime->type = ip_match_indirect; ime->flags = 0; ime->u.indirect.list = iml; ime->next = NULL; return (ime); } ip_match_element new_ip_match_key(DST_KEY *dst_key) { ip_match_element ime; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_key", NULL); ime->type = ip_match_key; ime->flags = 0; ime->u.key.key = dst_key; return (ime); } ip_match_element new_ip_match_localhost() { ip_match_element ime; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_localhost", NULL); ime->type = ip_match_localhost; ime->flags = 0; ime->u.indirect.list = NULL; ime->next = NULL; return (ime); } ip_match_element new_ip_match_localnets() { ip_match_element ime; ime = (ip_match_element)memget(sizeof (struct ip_match_element)); if (ime == NULL) panic("memget failed in new_ip_match_localnets", NULL); ime->type = ip_match_localnets; ime->flags = 0; ime->u.indirect.list = NULL; ime->next = NULL; return (ime); } void ip_match_negate(ip_match_element ime) { if (ime->flags & IP_MATCH_NEGATE) ime->flags &= ~IP_MATCH_NEGATE; else ime->flags |= IP_MATCH_NEGATE; } void add_to_ip_match_list(ip_match_list iml, ip_match_element ime) { INSIST(iml != NULL); INSIST(ime != NULL); if (iml->last != NULL) iml->last->next = ime; ime->next = NULL; iml->last = ime; if (iml->first == NULL) iml->first = ime; } void dprint_ip_match_list(int category, ip_match_list iml, int indent, const char *allow, const char *deny) { ip_match_element ime; char spaces[40+1]; char addr_text[sizeof "255.255.255.255"]; char mask_text[sizeof "255.255.255.255"]; INSIST(iml != NULL); if (indent > 40) indent = 40; if (indent) memset(spaces, ' ', indent); spaces[indent] = '\0'; for (ime = iml->first; ime != NULL; ime = ime->next) { switch (ime->type) { case ip_match_pattern: memset(addr_text, 0, sizeof addr_text); strncpy(addr_text, inet_ntoa(ime->u.direct.address), ((sizeof addr_text) - 1)); memset(mask_text, 0, sizeof mask_text); strncpy(mask_text, inet_ntoa(ime->u.direct.mask), ((sizeof mask_text) - 1)); ns_debug(category, 7, "%s%saddr: %s, mask: %s", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow, addr_text, mask_text); break; case ip_match_localhost: ns_debug(category, 7, "%s%slocalhost", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow); break; case ip_match_localnets: ns_debug(category, 7, "%s%slocalnets", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow); break; case ip_match_indirect: ns_debug(category, 7, "%s%sindirect list %p", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow, ime->u.indirect.list); if (ime->u.indirect.list != NULL) dprint_ip_match_list(category, ime->u.indirect.list, indent+2, allow, deny); break; case ip_match_key: ns_debug(category, 7, "%s%skey %s", spaces, (ime->flags & IP_MATCH_NEGATE) ? deny : allow, ime->u.key.key->dk_key_name); break; default: panic("unexpected ime type in dprint_ip_match_list()", NULL); } } } int ip_match_addr_or_key(ip_match_list iml, struct in_addr address, DST_KEY *key) { ip_match_element ime; int ret; int indirect; INSIST(iml != NULL); for (ime = iml->first; ime != NULL; ime = ime->next) { switch (ime->type) { case ip_match_pattern: indirect = 0; break; case ip_match_indirect: indirect = 1; break; case ip_match_localhost: ime->u.indirect.list = local_addresses; indirect = 1; break; case ip_match_localnets: ime->u.indirect.list = local_networks; indirect = 1; break; case ip_match_key: if (key == NULL) { indirect = 0; break; } else { if (ns_samename(ime->u.key.key->dk_key_name, key->dk_key_name) == 1) return (1); else continue; } default: indirect = 0; panic("unexpected ime type in ip_match_addr_or_key()", NULL); } if (indirect) { ret = ip_match_addr_or_key(ime->u.indirect.list, address, key); if (ret > 0) { if (ime->flags & IP_MATCH_NEGATE) ret = (ret) ? 0 : 1; return (ret); } } else { if (ina_onnet(address, ime->u.direct.address, ime->u.direct.mask)) { if (ime->flags & IP_MATCH_NEGATE) return (0); else return (1); } } } return (-1); } int ip_match_address(ip_match_list iml, struct in_addr address) { return ip_match_addr_or_key(iml, address, NULL); } int ip_addr_or_key_allowed(ip_match_list iml, struct in_addr address, DST_KEY *key) { int ret; if (iml == NULL) return (0); ret = ip_match_addr_or_key(iml, address, key); if (ret < 0) ret = 0; return (ret); } int ip_address_allowed(ip_match_list iml, struct in_addr address) { return(ip_addr_or_key_allowed(iml, address, NULL)); } int ip_match_network(ip_match_list iml, struct in_addr address, struct in_addr mask) { ip_match_element ime; int ret; int indirect; INSIST(iml != NULL); for (ime = iml->first; ime != NULL; ime = ime->next) { switch (ime->type) { case ip_match_pattern: indirect = 0; break; case ip_match_indirect: indirect = 1; break; case ip_match_localhost: ime->u.indirect.list = local_addresses; indirect = 1; break; case ip_match_localnets: ime->u.indirect.list = local_networks; indirect = 1; break; case ip_match_key: indirect = 0; break; default: indirect = 0; /* Make gcc happy. */ panic("unexpected ime type in ip_match_network()", NULL); } if (indirect) { ret = ip_match_network(ime->u.indirect.list, address, mask); if (ret >= 0) { if (ime->flags & IP_MATCH_NEGATE) ret = (ret) ? 0 : 1; return (ret); } } else { if (address.s_addr == ime->u.direct.address.s_addr && mask.s_addr == ime->u.direct.mask.s_addr) { if (ime->flags & IP_MATCH_NEGATE) return (0); else return (1); } } } return (-1); } int distance_of_address(ip_match_list iml, struct in_addr address) { ip_match_element ime; int ret; int indirect; int distance; INSIST(iml != NULL); for (distance = 1, ime = iml->first; ime != NULL; ime = ime->next, distance++) { switch (ime->type) { case ip_match_pattern: indirect = 0; break; case ip_match_indirect: indirect = 1; break; case ip_match_localhost: ime->u.indirect.list = local_addresses; indirect = 1; break; case ip_match_localnets: ime->u.indirect.list = local_networks; indirect = 1; break; case ip_match_key: indirect = 0; return (-1); default: indirect = 0; /* Make gcc happy. */ panic("unexpected ime type in distance_of_address()", NULL); } if (indirect) { ret = ip_match_address(ime->u.indirect.list, address); if (ret >= 0) { if (ime->flags & IP_MATCH_NEGATE) ret = (ret) ? 0 : 1; if (distance > MAX_TOPOLOGY_DISTANCE) distance = MAX_TOPOLOGY_DISTANCE; if (ret) return (distance); else return (MAX_TOPOLOGY_DISTANCE); } } else { if (ina_onnet(address, ime->u.direct.address, ime->u.direct.mask)) { if (distance > MAX_TOPOLOGY_DISTANCE) distance = MAX_TOPOLOGY_DISTANCE; if (ime->flags & IP_MATCH_NEGATE) return (MAX_TOPOLOGY_DISTANCE); else return (distance); } } } return (UNKNOWN_TOPOLOGY_DISTANCE); } int ip_match_is_none(ip_match_list iml) { ip_match_element ime; if ((iml == NULL) || (iml->first == NULL)) return (1); ime = iml->first; if (ime->type == ip_match_indirect) { if (ime->flags & IP_MATCH_NEGATE) return (0); iml = ime->u.indirect.list; if ((iml == NULL) || (iml->first == NULL)) return (0); ime = iml->first; } if (ime->type == ip_match_pattern) { if ((ime->flags & IP_MATCH_NEGATE) && ime->u.direct.address.s_addr == 0 && ime->u.direct.mask.s_addr == 0) return (1); } return (0); } /* * find_forwarder finds the fwddata structure for an address, * allocating one if we can't find one already existing. */ static struct fwddata * find_forwarder(struct in_addr address) { struct fwddata *fdp; struct fwddata **fdpp = NULL; register int i; for (i = 0; i < fwddata_count; i++) { fdp = fwddata[i]; if (fdp == NULL) { if (fdpp == NULL) fdpp = &fwddata[i]; continue; } if (memcmp(&fdp->fwdaddr.sin_addr, &address, sizeof(address)) == 0) { fdp->ref_count++; return (fdp); } } fdp = (struct fwddata *)memget(sizeof(struct fwddata)); if (!fdp) panic("memget failed in find_forwarder", NULL); memset(&fdp->fwdaddr, 0, sizeof(fdp->fwdaddr)); fdp->fwdaddr.sin_family = AF_INET; fdp->fwdaddr.sin_addr = address; fdp->fwdaddr.sin_port = ns_port; fdp->ns = savedata(C_IN, T_NS, 0, NULL, 0); if (!fdp->ns) panic("memget failed in find_forwarder", NULL); fdp->nsdata = savedata(C_IN, T_A, 0, NULL, 0); if (!fdp->nsdata) panic("memget failed in find_forwarder", NULL); fdp->nsdata->d_nstime = 1 + (int)(25.0*rand()/(RAND_MAX + 1.0)); fdp->ref_count = 1; if (fdpp != NULL) { *fdpp = fdp; return (fdp); } i = 0; if (fwddata == NULL) { fwddata = memget(sizeof *fwddata); if (fwddata == NULL) i = 1; } else { register size_t size; register struct fwddata **an_tmp; size = fwddata_count * sizeof *fwddata; an_tmp = memget(size + sizeof *fwddata); if (an_tmp == NULL) { i = 1; } else { memcpy(an_tmp, fwddata, size); memput(fwddata, size); fwddata = an_tmp; } } if (i == 0) { fwddata[fwddata_count] = fdp; fwddata_count++; } else { ns_warning(ns_log_config, "forwarder add failed (memget) [%s]", inet_ntoa(address)); } return (fdp); } /* * Forwarder glue * * XXX This will go away when the rest of bind understands * forward zones. */ static void add_forwarder(struct fwdinfo **fipp, struct in_addr address) { struct fwdinfo *fip = *fipp, *ftp = NULL; struct fwddata *fdp; #ifdef FWD_LOOP if (aIsUs(address)) { ns_error(ns_log_config, "forwarder '%s' ignored, my address", inet_ntoa(address)); return; } #endif /* FWD_LOOP */ /* On multiple forwarder lines, move to end of the list. */ while (fip != NULL && fip->next != NULL) fip = fip->next; fdp = find_forwarder(address); ftp = (struct fwdinfo *)memget(sizeof(struct fwdinfo)); if (!ftp) panic("memget failed in add_forwarder", NULL); ftp->fwddata = fdp; ftp->next = NULL; if (fip == NULL) *fipp = ftp; /* First time only */ else fip->next = ftp; } void free_also_notify(options op) { #ifdef BIND_NOTIFY memput(op->also_notify, op->notify_count * sizeof *op->also_notify); op->also_notify = NULL; op->notify_count = 0; #endif } int add_global_also_notify(options op, struct in_addr address) { #ifdef BIND_NOTIFY int i; INSIST(op != NULL); ns_debug(ns_log_config, 2, "adding global notify %s", inet_ntoa(address)); /* Check for duplicates. */ for (i = 0; i < op->notify_count; i++) { if (memcmp(op->also_notify + i, &address, sizeof address) == 0) { ns_warning(ns_log_config, "duplicate global also-notify address ignored [%s]", inet_ntoa(address)); return (1); } } i = 0; if (op->also_notify == NULL) { op->also_notify = memget(sizeof *op->also_notify); if (op->also_notify == NULL) i = 1; } else { register size_t size; register struct in_addr *an_tmp; size = op->notify_count * sizeof *op->also_notify; an_tmp = memget(size + sizeof *op->also_notify); if (an_tmp == NULL) { i = 1; } else { memcpy(an_tmp, op->also_notify, size); memput(op->also_notify, size); op->also_notify = an_tmp; } } if (i == 0) { op->also_notify[op->notify_count] = address; op->notify_count++; } else { ns_warning(ns_log_config, "global also-notify add failed (memget) [%s]", inet_ntoa(address)); } #endif return (1); } void add_global_forwarder(options op, struct in_addr address) { INSIST(op != NULL); ns_debug(ns_log_config, 2, "adding default forwarder %s", inet_ntoa(address)); add_forwarder(&op->fwdtab, address); } void set_zone_forward(zone_config zh) { struct zoneinfo *zp; zp = zh.opaque; zp->z_flags |= Z_FORWARD_SET; set_zone_boolean_option(zh, OPTION_FORWARD_ONLY, 0); } void add_zone_forwarder(zone_config zh, struct in_addr address) { struct zoneinfo *zp; const char *zname; zp = zh.opaque; INSIST(zp != NULL); zname = (zp->z_origin[0] == '\0') ? "." : zp->z_origin; ns_debug(ns_log_config, 2, "adding forwarder %s for zone zone '%s'", inet_ntoa(address), zname); zp->z_flags |= Z_FORWARD_SET; add_forwarder(&zp->z_fwdtab, address); } void free_forwarders(struct fwdinfo *fwdtab) { struct fwdinfo *ftp, *fnext; int i; for (ftp = fwdtab; ftp != NULL; ftp = fnext) { fnext = ftp->next; if (--ftp->fwddata->ref_count == 0) { for (i = 0 ; i < fwddata_count; i++) if (fwddata[i] == ftp->fwddata) { fwddata[i] = NULL; break; } db_detach(&ftp->fwddata->ns); db_detach(&ftp->fwddata->nsdata); memput(ftp->fwddata, sizeof *ftp->fwddata); } memput(ftp, sizeof *ftp); } fwdtab = NULL; } /* * Servers */ static server_info new_server(struct in_addr address) { server_info si; si = (server_info)memget(sizeof (struct server_info)); if (si == NULL) panic("memget failed in new_server()", NULL); si->address = address; si->flags = 0U; si->transfers = 0; si->transfer_format = axfr_use_default; si->key_list = NULL; si->next = NULL; if (server_options->flags & OPTION_MAINTAIN_IXFR_BASE) si->flags |= SERVER_INFO_SUPPORT_IXFR; else si->flags &= ~SERVER_INFO_SUPPORT_IXFR; si->flags |= SERVER_INFO_EDNS; return (si); } static void free_server(server_info si) { if (si->key_list) free_key_info_list(si->key_list); memput(si, sizeof *si); } server_info find_server(struct in_addr address) { server_info si; for (si = nameserver_info; si != NULL; si = si->next) if (si->address.s_addr == address.s_addr) break; return (si); } static void add_server(server_info si) { ip_match_element ime; si->next = nameserver_info; nameserver_info = si; /* * To ease transition, we'll add bogus nameservers to an * ip matching list. This will probably be redone when the * merging of nameserver data structures occurs. */ if (si->flags & SERVER_INFO_BOGUS) { ime = new_ip_match_pattern(si->address, 32); INSIST(ime != NULL); add_to_ip_match_list(bogus_nameservers, ime); } ns_debug(ns_log_config, 3, "server %s: flags %08x transfers %d", inet_ntoa(si->address), si->flags, si->transfers); if (si->key_list != NULL) dprint_key_info_list(si->key_list); } static void free_nameserver_info() { server_info si_next, si; for (si = nameserver_info; si != NULL; si = si_next) { si_next = si->next; free_server(si); } nameserver_info = NULL; if (bogus_nameservers != NULL) { free_ip_match_list(bogus_nameservers); bogus_nameservers = NULL; } } static void free_secretkey_info() { if (secretkey_info != NULL) { free_key_info_list(secretkey_info); secretkey_info = NULL; } } server_config begin_server(struct in_addr address) { server_config sc; sc.opaque = new_server(address); return (sc); } void end_server(server_config sc, int should_install) { server_info si; si = sc.opaque; INSIST(si != NULL); if (should_install) add_server(si); else free_server(si); sc.opaque = NULL; } void set_server_option(server_config sc, int bool_opt, int value) { server_info si; si = sc.opaque; INSIST(si != NULL); switch (bool_opt) { case SERVER_INFO_BOGUS: case SERVER_INFO_SUPPORT_IXFR: case SERVER_INFO_EDNS: if (value) si->flags |= bool_opt; else si->flags &= ~bool_opt; break; default: panic("unexpected option in set_server_option", NULL); } } void set_server_transfers(server_config sc, int transfers) { server_info si; si = sc.opaque; INSIST(si != NULL); if (transfers < 0) transfers = 0; si->transfers = transfers; } void set_server_transfer_format(server_config sc, enum axfr_format transfer_format) { server_info si; si = sc.opaque; INSIST(si != NULL); si->transfer_format = transfer_format; } void add_server_key_info(server_config sc, DST_KEY *dst_key) { server_info si; si = sc.opaque; INSIST(si != NULL); if (si->key_list == NULL) si->key_list = new_key_info_list(); add_to_key_info_list(si->key_list, dst_key); } /* * Keys */ DST_KEY * new_key_info(char *name, char *algorithm, char *secret) { DST_KEY *dst_key; int alg, blen; u_char buffer[1024]; INSIST(name != NULL); INSIST(algorithm != NULL); INSIST(secret != NULL); alg = tsig_alg_value(algorithm); if (alg == -1) { ns_warning(ns_log_config, "Unsupported TSIG algorithm %s", algorithm); return (NULL); } blen = b64_pton(secret, buffer, sizeof(buffer)); if (blen < 0) { ns_warning(ns_log_config, "Invalid TSIG secret \"%s\"", secret); return (NULL); } dst_key = dst_buffer_to_key(name, alg, NS_KEY_TYPE_AUTH_ONLY|NS_KEY_NAME_ENTITY, NS_KEY_PROT_ANY, buffer, blen); if (dst_key == NULL) ns_warning(ns_log_config, "dst_buffer_to_key failed in new_key_info"); return (dst_key); } void free_key_info(DST_KEY *dst_key) { INSIST(dst_key != NULL); dst_free_key(dst_key); } DST_KEY * find_key(char *name, char *algorithm) { key_list_element ke; if (secretkey_info == NULL) return (NULL); for (ke = secretkey_info->first; ke != NULL; ke = ke->next) { DST_KEY *dst_key = ke->key; if (ns_samename(name, dst_key->dk_key_name) != 1) continue; if (algorithm == NULL || dst_key->dk_alg == tsig_alg_value(algorithm)) break; } if (ke == NULL) return (NULL); return (ke->key); } void dprint_key_info(DST_KEY *dst_key) { INSIST(dst_key != NULL); ns_debug(ns_log_config, 7, "key %s", dst_key->dk_key_name); ns_debug(ns_log_config, 7, " algorithm %d", dst_key->dk_alg); } key_info_list new_key_info_list() { key_info_list kil; kil = (key_info_list)memget(sizeof (struct key_info_list)); if (kil == NULL) panic("memget failed in new_key_info_list()", NULL); kil->first = NULL; kil->last = NULL; return (kil); } void free_key_info_list(key_info_list kil) { key_list_element kle, kle_next; INSIST(kil != NULL); for (kle = kil->first; kle != NULL; kle = kle_next) { kle_next = kle->next; /* note we do NOT free kle->info */ memput(kle, sizeof *kle); } memput(kil, sizeof *kil); } void add_to_key_info_list(key_info_list kil, DST_KEY *dst_key) { key_list_element kle; INSIST(kil != NULL); INSIST(dst_key != NULL); kle = (key_list_element)memget(sizeof (struct key_list_element)); if (kle == NULL) panic("memget failed in add_to_key_info_list()", NULL); kle->key = dst_key; if (kil->last != NULL) kil->last->next = kle; kle->next = NULL; kil->last = kle; if (kil->first == NULL) kil->first = kle; } void dprint_key_info_list(key_info_list kil) { key_list_element kle; INSIST(kil != NULL); for (kle = kil->first; kle != NULL; kle = kle->next) dprint_key_info(kle->key); } /* * Logging. */ log_config begin_logging() { log_config log_cfg; log_context lc; log_cfg = (log_config)memget(sizeof (struct log_config)); if (log_cfg == NULL) ns_panic(ns_log_config, 0, "memget failed creating log_config"); if (log_new_context(ns_log_max_category, logging_categories, &lc) < 0) ns_panic(ns_log_config, 0, "log_new_context() failed: %s", strerror(errno)); log_cfg->log_ctx = lc; log_cfg->eventlib_channel = NULL; log_cfg->packet_channel = NULL; log_cfg->default_debug_active = 0; return (log_cfg); } void add_log_channel(log_config log_cfg, int category, log_channel chan) { log_channel_type type; INSIST(log_cfg != NULL); type = log_get_channel_type(chan); if (category == ns_log_eventlib) { if (type != log_file && type != log_null) { ns_error(ns_log_config, "must specify a file or null channel for the eventlib category"); return; } if (log_cfg->eventlib_channel != NULL) { ns_error(ns_log_config, "only one channel allowed for the eventlib category"); return; } log_cfg->eventlib_channel = chan; } if (category == ns_log_packet) { if (type != log_file && type != log_null) { ns_error(ns_log_config, "must specify a file or null channel for the packet category"); return; } if (log_cfg->packet_channel != NULL) { ns_error(ns_log_config, "only one channel allowed for the packet category"); return; } log_cfg->packet_channel = chan; } if (log_add_channel(log_cfg->log_ctx, category, chan) < 0) { ns_error(ns_log_config, "log_add_channel() failed"); return; } if (chan == debug_channel) log_cfg->default_debug_active = 1; } void open_special_channels() { int using_null = 0; if (log_open_stream(eventlib_channel) == NULL) { eventlib_channel = null_channel; using_null = 1; } if (log_open_stream(packet_channel) == NULL) { packet_channel = null_channel; using_null = 1; } if (using_null && log_open_stream(null_channel) == NULL) ns_panic(ns_log_config, 1, "couldn't open null channel"); } void set_logging(log_config log_cfg, int is_default) { log_context lc; INSIST(log_cfg != NULL); lc = log_cfg->log_ctx; /* * Add the default category if it's not in the context already. */ if (!log_category_is_active(lc, ns_log_default)) { add_log_channel(log_cfg, ns_log_default, debug_channel); add_log_channel(log_cfg, ns_log_default, syslog_channel); } /* * Add the panic category if it's not in the context already. */ if (!log_category_is_active(lc, ns_log_panic)) { add_log_channel(log_cfg, ns_log_panic, stderr_channel); add_log_channel(log_cfg, ns_log_panic, syslog_channel); } /* * Add the eventlib category if it's not in the context already. */ if (!log_category_is_active(lc, ns_log_eventlib)) add_log_channel(log_cfg, ns_log_eventlib, debug_channel); /* * Add the packet category if it's not in the context already. */ if (!log_category_is_active(lc, ns_log_packet)) add_log_channel(log_cfg, ns_log_packet, debug_channel); #ifdef DEBUG /* * Preserve debugging state. */ log_option(lc, LOG_OPTION_DEBUG, debug); log_option(lc, LOG_OPTION_LEVEL, debug); #endif /* * Special case for query-log, so we can co-exist with the command * line option and SIGWINCH. */ if (log_category_is_active(lc, ns_log_queries)) qrylog = 1; /* * Cleanup the old context. */ if (need_logging_free) log_free_context(log_ctx); /* * The default file channels will never have their reference counts * drop to zero, and so they will not be closed by the logging system * when log_free_context() is called. We don't want to keep files * open unnecessarily, and we want them to behave like user-created * channels, so we close them here. */ if (log_get_stream(debug_channel) != stderr) (void)log_close_stream(debug_channel); (void)log_close_stream(null_channel); /* * Install the new context. */ log_ctx = lc; eventlib_channel = log_cfg->eventlib_channel; packet_channel = log_cfg->packet_channel; #ifdef DEBUG if (debug) { open_special_channels(); evSetDebug(ev, debug, log_get_stream(eventlib_channel)); } #endif log_ctx_valid = 1; need_logging_free = 1; logging_installed = 1; default_logging_installed = is_default; } void end_logging(log_config log_cfg, int should_install) { if (should_install) set_logging(log_cfg, 0); else log_free_context(log_cfg->log_ctx); memput(log_cfg, sizeof (struct log_config)); } void use_default_logging() { log_config log_cfg; log_cfg = begin_logging(); set_logging(log_cfg, 1); memput(log_cfg, sizeof (struct log_config)); } static void init_default_log_channels() { u_int flags; const char *name; FILE *stream; syslog_channel = log_new_syslog_channel(0, log_info, ISC_FACILITY); if (syslog_channel == NULL || log_inc_references(syslog_channel) < 0) ns_panic(ns_log_config, 0, "couldn't create syslog_channel"); flags = LOG_USE_CONTEXT_LEVEL|LOG_REQUIRE_DEBUG; if (foreground) { name = NULL; stream = stderr; } else { name = _PATH_DEBUG; stream = NULL; } debug_channel = log_new_file_channel(flags, log_info, name, stream, 0, ULONG_MAX); if (debug_channel == NULL || log_inc_references(debug_channel) < 0) ns_panic(ns_log_config, 0, "couldn't create debug_channel"); log_set_file_owner(debug_channel, user_id, group_id); stderr_channel = log_new_file_channel(0, log_info, NULL, stderr, 0, ULONG_MAX); if (stderr_channel == NULL || log_inc_references(stderr_channel) < 0) ns_panic(ns_log_config, 0, "couldn't create stderr_channel"); log_set_file_owner(stderr_channel, user_id, group_id); null_channel = log_new_file_channel(LOG_CHANNEL_OFF, log_info, _PATH_DEVNULL, NULL, 0, ULONG_MAX); if (null_channel == NULL || log_inc_references(null_channel) < 0) ns_panic(ns_log_config, 0, "couldn't create null_channel"); log_set_file_owner(null_channel, user_id, group_id); } static void shutdown_default_log_channels() { log_free_channel(syslog_channel); log_free_channel(debug_channel); log_free_channel(stderr_channel); log_free_channel(null_channel); } void init_logging() { int size; const struct ns_sym *s; char category_name[256]; size = ns_log_max_category * (sizeof (char *)); logging_categories = (char **)memget(size); if (logging_categories == NULL) ns_panic(ns_log_config, 0, "memget failed in init_logging"); memset(logging_categories, 0, size); for (s = category_constants; s != NULL && s->name != NULL; s++) { sprintf(category_name, "%s: ", s->name); logging_categories[s->number] = savestr(category_name, 1); } init_default_log_channels(); use_default_logging(); } void shutdown_logging() { int size; const struct ns_sym *s; evSetDebug(ev, 0, NULL); shutdown_default_log_channels(); log_free_context(log_ctx); for (s = category_constants; s != NULL && s->name != NULL; s++) logging_categories[s->number] = freestr(logging_categories[s->number]); size = ns_log_max_category * (sizeof (char *)); memput(logging_categories, size); logging_categories = NULL; } /* * Main Loader */ void init_configuration() { /* * Remember initial limits for use if "default" is specified in * a config file. */ #ifdef HAVE_GETRUSAGE get_initial_limits(); #endif zone_symbol_table = new_symbol_table(ZONE_SYM_TABLE_SIZE, NULL); use_default_options(); parser_initialize(); ns_ctl_initialize(); config_initialized = 1; } void shutdown_configuration() { REQUIRE(config_initialized); ns_ctl_shutdown(); if (server_options != NULL) { free_options(server_options); server_options = NULL; } if (current_pid_filename != NULL) current_pid_filename = freestr(current_pid_filename); free_nameserver_info(); free_secretkey_info(); free_symbol_table(zone_symbol_table); parser_shutdown(); if (fwddata != NULL) memput(fwddata, fwddata_count * sizeof *fwddata); fwddata = NULL; fwddata_count = 0; config_initialized = 0; } time_t load_configuration(const char *filename) { time_t mtime; REQUIRE(config_initialized); ns_debug(ns_log_config, 3, "load configuration %s", filename); loading = 1; /* * Clean up any previous configuration and initialize * global data structures we'll be updating. */ free_nameserver_info(); free_secretkey_info(); bogus_nameservers = new_ip_match_list(); options_installed = 0; logging_installed = 0; mtime = parse_configuration(filename); /* * If the user didn't specify logging or options, but they previously * had specified one or both of them, then we need to * re-establish the default environment. We have to be careful * about when we install default options because the parser * must respect limits (e.g. data-size, number of open files) * specified in the options file. In the ordinary case where the * options section isn't changing on a zone reload, it would be bad * to lower these limits temporarily, because we might not survive * to the point where they get raised back again. The logging case * has similar motivation -- we don't want to override the existing * logging scheme (perhaps causing log messages to go somewhere * unexpected) when the user hasn't expressed a desire for a new * scheme. */ if (!logging_installed) use_default_logging(); if (!options_installed && !default_options_installed) { use_default_options(); ns_warning(ns_log_config, "re-establishing default options"); } update_pid_file(); /* Init or reinit the interface/port list and associated sockets. */ getnetconf(0); opensocket_f(); initial_configuration = 0; loading = 0; /* release queued notifies */ notify_afterload(); return (mtime); } diff --git a/contrib/bind/bin/named/ns_ctl.c b/contrib/bind/bin/named/ns_ctl.c index c8fc907153d7..ced69d5468fc 100644 --- a/contrib/bind/bin/named/ns_ctl.c +++ b/contrib/bind/bin/named/ns_ctl.c @@ -1,1147 +1,1147 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_ctl.c,v 8.47 2002/06/24 07:11:07 marka Exp $"; +static const char rcsid[] = "$Id: ns_ctl.c,v 8.48 2002/07/29 02:06:56 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1997-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Extern. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" /* Defs. */ #define CONTROL_FOUND 0x0001 /* for mark and sweep. */ #define MAX_STR_LEN 500 struct control { LINK(struct control) link; enum { t_dead, t_inet, t_unix } type; struct ctl_sctx *sctx; u_int flags; union { struct { struct sockaddr_in in; ip_match_list allow; } v_inet; #ifndef NO_SOCKADDR_UN struct { struct sockaddr_un un; mode_t mode; uid_t owner; gid_t group; } v_unix; #endif } var; }; /* Forward. */ static struct ctl_sctx *mksrvr(control, const struct sockaddr *, size_t); static control new_control(void); static void free_control(controls *, control); static void free_controls(controls *); static int match_control(control, control); static control find_control(controls, control); static void propagate_changes(const control, control); static void install(control); static void install_inet(control); static void install_unix(control); static void logger(enum ctl_severity, const char *fmt, ...) ISC_FORMAT_PRINTF(2,3); static void verb_connect(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void verb_getpid(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void getpid_closure(struct ctl_sctx *, struct ctl_sess *, void *); static void verb_status(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void status_closure(struct ctl_sctx *, struct ctl_sess *, void *); static void verb_stop(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void verb_exec(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void exec_closure(struct ctl_sctx *, struct ctl_sess *, void *); static void verb_reload(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void verb_reconfig(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void verb_dumpdb(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void verb_stats(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void verb_trace(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void trace_closure(struct ctl_sctx *, struct ctl_sess *, void *); static void verb_notrace(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void verb_querylog(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void verb_help(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void verb_quit(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void verb_args(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); /* Private data. */ static controls server_controls; static struct ctl_verb verbs[] = { { "", verb_connect, ""}, { "getpid", verb_getpid, "getpid"}, { "status", verb_status, "status"}, { "stop", verb_stop, "stop"}, { "exec", verb_exec, "exec"}, { "reload", verb_reload, "reload [zone] ..."}, { "reconfig", verb_reconfig, "reconfig [-noexpired] (just sees new/gone zones)"}, { "dumpdb", verb_dumpdb, "dumpdb"}, { "stats", verb_stats, "stats [clear]"}, { "trace", verb_trace, "trace [level]"}, { "notrace", verb_notrace, "notrace"}, { "querylog", verb_querylog, "querylog"}, { "qrylog", verb_querylog, "qrylog"}, { "help", verb_help, "help"}, { "quit", verb_quit, "quit"}, { "args", verb_args, "args"}, { NULL, NULL, NULL} }; /* Public functions. */ void ns_ctl_initialize(void) { INIT_LIST(server_controls); } void ns_ctl_shutdown(void) { if (!EMPTY(server_controls)) free_controls(&server_controls); } void ns_ctl_defaults(controls *list) { #ifdef NO_SOCKADDR_UN struct in_addr saddr; ip_match_list iml; ip_match_element ime; /* * If the operating system does not support local domain sockets, * connect with ndc on 127.0.0.1, port 101, and only allow * connections from 127.0.0.1. */ saddr.s_addr = htonl (INADDR_LOOPBACK); iml = new_ip_match_list(); ime = new_ip_match_pattern(saddr, 32); add_to_ip_match_list(iml, ime); ns_ctl_add(list, ns_ctl_new_inet(saddr, htons (101), iml)); #else #ifdef NEED_SECURE_DIRECTORY ns_ctl_add(list, ns_ctl_new_unix(_PATH_NDCSOCK, 0700, 0, 0)); #else ns_ctl_add(list, ns_ctl_new_unix(_PATH_NDCSOCK, 0600, 0, 0)); #endif #endif /*NO_SOCKADDR_UN*/ } void ns_ctl_add(controls *list, control new) { if (!find_control(*list, new)) APPEND(*list, new, link); } control ns_ctl_new_inet(struct in_addr saddr, u_int sport, ip_match_list allow) { control new = new_control(); INIT_LINK(new, link); new->type = t_inet; memset(&new->var.v_inet.in, 0, sizeof new->var.v_inet.in); new->var.v_inet.in.sin_family = AF_INET; new->var.v_inet.in.sin_addr = saddr; new->var.v_inet.in.sin_port = sport; new->var.v_inet.allow = allow; return (new); } #ifndef NO_SOCKADDR_UN control ns_ctl_new_unix(const char *path, mode_t mode, uid_t owner, gid_t group) { control new = new_control(); INIT_LINK(new, link); new->type = t_unix; memset(&new->var.v_unix.un, 0, sizeof new->var.v_unix.un); new->var.v_unix.un.sun_family = AF_UNIX; strncpy(new->var.v_unix.un.sun_path, path, sizeof new->var.v_unix.un.sun_path - 1); new->var.v_unix.mode = mode; new->var.v_unix.owner = owner; new->var.v_unix.group = group; return (new); } #endif void ns_ctl_install(controls *new) { control ctl, old, next; /* Find all the controls which aren't new or deleted. */ for (ctl = HEAD(server_controls); ctl != NULL; ctl = NEXT(ctl, link)) ctl->flags &= ~CONTROL_FOUND; for (ctl = HEAD(*new); ctl != NULL; ctl = next) { next = NEXT(ctl, link); old = find_control(server_controls, ctl); if (old != NULL) { old->flags |= CONTROL_FOUND; propagate_changes(ctl, old); if (old->sctx == NULL) free_control(&server_controls, old); free_control(new, ctl); } } /* Destroy any old controls which weren't found. */ for (ctl = HEAD(server_controls); ctl != NULL; ctl = next) { next = NEXT(ctl, link); if ((ctl->flags & CONTROL_FOUND) == 0) free_control(&server_controls, ctl); } /* Add any new controls which were found. */ for (ctl = HEAD(*new); ctl != NULL; ctl = next) { next = NEXT(ctl, link); UNLINK(*new, ctl, link); APPEND(server_controls, ctl, link); install(ctl); if (ctl->sctx == NULL) free_control(&server_controls, ctl); } } /* Private functions. */ static struct ctl_sctx * mksrvr(control ctl, const struct sockaddr *sa, size_t salen) { return (ctl_server(ev, sa, salen, verbs, 500, 222, 600, 5, 10, logger, ctl)); } static control new_control(void) { control new = memget(sizeof *new); if (new == NULL) panic("memget failed in new_control()", NULL); new->type = t_dead; new->sctx = NULL; return (new); } static void free_control(controls *list, control this) { int was_live = 0; struct stat sb; if (this->sctx != NULL) { ctl_endserver(this->sctx); this->sctx = NULL; was_live = 1; } switch (this->type) { case t_inet: if (this->var.v_inet.allow != NULL) { free_ip_match_list(this->var.v_inet.allow); this->var.v_inet.allow = NULL; } break; #ifndef NO_SOCKADDR_UN case t_unix: /* XXX Race condition. */ if (was_live && stat(this->var.v_unix.un.sun_path, &sb) == 0 && (S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode))) { /* XXX Race condition. */ unlink(this->var.v_unix.un.sun_path); } break; #endif default: panic("impossible type in free_control", NULL); /* NOTREACHED */ } UNLINK(*list, this, link); memput(this, sizeof *this); } static void free_controls(controls *list) { control ctl, next; for (ctl = HEAD(*list); ctl != NULL; ctl = next) { next = NEXT(ctl, link); free_control(list, ctl); } INIT_LIST(*list); } static int match_control(control l, control r) { int match = 1; if (l->type != r->type) match = 0; else switch (l->type) { case t_inet: if (l->var.v_inet.in.sin_family != r->var.v_inet.in.sin_family || l->var.v_inet.in.sin_port != r->var.v_inet.in.sin_port || l->var.v_inet.in.sin_addr.s_addr != r->var.v_inet.in.sin_addr.s_addr) match = 0; break; #ifndef NO_SOCKADDR_UN case t_unix: if (l->var.v_unix.un.sun_family != r->var.v_unix.un.sun_family || strcmp(l->var.v_unix.un.sun_path, r->var.v_unix.un.sun_path) != 0) match = 0; break; #endif default: panic("impossible type in match_control", NULL); /* NOTREACHED */ } ns_debug(ns_log_config, 20, "match_control(): %d", match); return (match); } static control find_control(controls list, control new) { control ctl; for (ctl = HEAD(list); ctl != NULL; ctl = NEXT(ctl, link)) if (match_control(ctl, new)) return (ctl); return (NULL); } static void propagate_changes(const control diff, control base) { int need_install = 0; switch (base->type) { case t_inet: if (base->var.v_inet.allow != NULL) free_ip_match_list(base->var.v_inet.allow); base->var.v_inet.allow = diff->var.v_inet.allow; diff->var.v_inet.allow = NULL; need_install++; break; #ifndef NO_SOCKADDR_UN case t_unix: if (base->var.v_unix.mode != diff->var.v_unix.mode) { base->var.v_unix.mode = diff->var.v_unix.mode; need_install++; } if (base->var.v_unix.owner != diff->var.v_unix.owner) { base->var.v_unix.owner = diff->var.v_unix.owner; need_install++; } if (base->var.v_unix.group != diff->var.v_unix.group) { base->var.v_unix.group = diff->var.v_unix.group; need_install++; } break; #endif default: panic("impossible type in ns_ctl::propagate_changes", NULL); /* NOTREACHED */ } if (need_install) install(base); } static void install(control ctl) { switch (ctl->type) { case t_inet: install_inet(ctl); break; #ifndef NO_SOCKADDR_UN case t_unix: install_unix(ctl); break; #endif default: panic("impossible type in ns_ctl::install", NULL); /* NOTREACHED */ } } static void install_inet(control ctl) { if (ctl->sctx == NULL) { ctl->sctx = mksrvr(ctl, (struct sockaddr *)&ctl->var.v_inet.in, sizeof ctl->var.v_inet.in); } } #ifndef NO_SOCKADDR_UN /* * Unattach an old unix domain socket if it exists. */ static void unattach(control ctl) { int s; struct stat sb; s = socket(AF_UNIX, SOCK_STREAM, 0); if (s < 0) { ns_warning(ns_log_config, "unix control \"%s\" socket failed: %s", ctl->var.v_unix.un.sun_path, strerror(errno)); return; } if (stat(ctl->var.v_unix.un.sun_path, &sb) < 0) { switch (errno) { case ENOENT: /* We exited cleanly last time */ break; default: ns_warning(ns_log_config, "unix control \"%s\" stat failed: %s", ctl->var.v_unix.un.sun_path, strerror(errno)); break; } goto cleanup; } if (!(S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode))) { ns_warning(ns_log_config, "unix control \"%s\" not socket", ctl->var.v_unix.un.sun_path); goto cleanup; } if (connect(s, (struct sockaddr *)&ctl->var.v_unix.un, sizeof ctl->var.v_unix.un) < 0) { switch (errno) { case ECONNREFUSED: case ECONNRESET: if (unlink(ctl->var.v_unix.un.sun_path) < 0) ns_warning(ns_log_config, "unix control \"%s\" unlink failed: %s", ctl->var.v_unix.un.sun_path, strerror(errno)); break; default: ns_warning(ns_log_config, "unix control \"%s\" connect failed: %s", ctl->var.v_unix.un.sun_path, strerror(errno)); break; } } cleanup: close(s); } static void install_unix(control ctl) { char *path; #ifdef NEED_SECURE_DIRECTORY char *slash; path = savestr(ctl->var.v_unix.un.sun_path, 1); slash = strrchr(path, '/'); if (slash != NULL) { if (slash != path) *slash = '\0'; else { (void)freestr(path); path = savestr("/", 1); } } else { (void)freestr(path); path = savestr(".", 1); } if (mkdir(path, ctl->var.v_unix.mode) < 0) { if (errno != EEXIST) { ns_warning(ns_log_config, "unix control \"%s\" mkdir failed: %s", path, strerror(errno)); } } #else path = ctl->var.v_unix.un.sun_path; #endif if (ctl->sctx == NULL) { unattach(ctl); ctl->sctx = mksrvr(ctl, (struct sockaddr *)&ctl->var.v_unix.un, sizeof ctl->var.v_unix.un); } if (ctl->sctx != NULL) { /* XXX Race condition. */ if (chmod(path, ctl->var.v_unix.mode) < 0) { ns_warning(ns_log_config, "chmod(\"%s\", 0%03o): %s", ctl->var.v_unix.un.sun_path, ctl->var.v_unix.mode, strerror(errno)); } if (chown(path, ctl->var.v_unix.owner, ctl->var.v_unix.group) < 0) { ns_warning(ns_log_config, "chown(\"%s\", %d, %d): %s", ctl->var.v_unix.un.sun_path, ctl->var.v_unix.owner, ctl->var.v_unix.group, strerror(errno)); } } #ifdef NEED_SECURE_DIRECTORY (void)freestr(path); #endif } #endif static void logger(enum ctl_severity ctlsev, const char *format, ...) { va_list args; int logsev; switch (ctlsev) { case ctl_debug: logsev = log_debug(5); break; case ctl_warning: logsev = log_warning; break; case ctl_error: logsev = log_error; break; default: logsev = 0; panic("invalid ctlsev in logger", NULL); } if (!log_ctx_valid) return; va_start(args, format); log_vwrite(log_ctx, ns_log_control, logsev, format, args); va_end(args); } static void verb_connect(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { const struct sockaddr *sa = (const struct sockaddr *)respctx; control nsctl = (control)uctx; UNUSED(ctl); UNUSED(verb); UNUSED(rest); UNUSED(respflags); if (sa->sa_family == AF_INET) { const struct sockaddr_in *in = (const struct sockaddr_in *)sa; const ip_match_list acl = nsctl->var.v_inet.allow; if (!ip_address_allowed(acl, in->sin_addr)) { ctl_response(sess, 502, "Permission denied.", CTL_EXIT, NULL, NULL, NULL, NULL, 0); return; } } ctl_response(sess, 220, server_options->version, 0, NULL, NULL, NULL, NULL, 0); } static void verb_getpid(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { char *msg = memget(MAX_STR_LEN); UNUSED(ctl); UNUSED(verb); UNUSED(rest); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); if (msg == NULL) { ctl_response(sess, 503, "(out of memory)", 0, NULL, NULL, NULL, NULL, 0); return; } sprintf(msg, "my pid is <%ld>", (long)getpid()); ctl_response(sess, 250, msg, 0, NULL, getpid_closure, msg, NULL, 0); } static void getpid_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) { char *msg = uap; UNUSED(sctx); UNUSED(sess); memput(msg, MAX_STR_LEN); } enum state { e_version = 0, e_config, e_nzones, e_debug, e_xfersrun, e_xfersdfr, e_qserials, e_qrylog, e_priming, e_finito }; struct pvt_status { enum state state; char text[MAX_STR_LEN]; }; static void verb_status(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { struct pvt_status *pvt = ctl_getcsctx(sess); UNUSED(ctl); UNUSED(verb); UNUSED(rest); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); if (pvt == NULL) { pvt = memget(sizeof *pvt); if (pvt == NULL) { ctl_response(sess, 505, "(out of memory)", 0, NULL, NULL, NULL, NULL, 0); return; } pvt->state = (enum state)0; (void)ctl_setcsctx(sess, pvt); } switch (pvt->state++) { case e_version: strncpy(pvt->text, Version, sizeof pvt->text); pvt->text[sizeof pvt->text - 1] = '\0'; break; case e_config: sprintf(pvt->text, "config (%s) last loaded at age: %24s", conffile, ctime(&confmtime)); break; case e_nzones: sprintf(pvt->text, "number of zones allocated: %d", nzones); break; case e_debug: sprintf(pvt->text, "debug level: %d", debug); break; case e_xfersrun: sprintf(pvt->text, "xfers running: %d", xfers_running); break; case e_xfersdfr: sprintf(pvt->text, "xfers deferred: %d", xfers_deferred); break; case e_qserials: sprintf(pvt->text, "soa queries in progress: %d", qserials_running); break; case e_qrylog: sprintf(pvt->text, "query logging is %s", qrylog ? "ON" : "OFF"); break; case e_priming: if (priming) sprintf(pvt->text, "server is initialising itself"); else sprintf(pvt->text, "server is up and running"); break; case e_finito: return; } ctl_response(sess, 250, pvt->text, (pvt->state == e_finito) ? 0 : CTL_MORE, NULL, status_closure, NULL, NULL, 0); } static void status_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) { struct pvt_status *pvt = ctl_getcsctx(sess); UNUSED(sctx); UNUSED(uap); memput(pvt, sizeof *pvt); ctl_setcsctx(sess, NULL); } static void verb_stop(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { UNUSED(ctl); UNUSED(verb); UNUSED(rest); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); ns_need(main_need_exit); ctl_response(sess, 250, "Shutdown initiated.", 0, NULL, NULL, NULL, NULL, 0); } static void verb_exec(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { struct stat sb; UNUSED(ctl); UNUSED(verb); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); if (rest != NULL && *rest != '\0') { if (stat(rest, &sb) < 0) { ctl_response(sess, 503, strerror(errno), 0, NULL, NULL, NULL, NULL, 0); return; } saved_argv[0] = savestr(rest, 1); /* Never strfreed. */ } if (stat(saved_argv[0], &sb) < 0) { const char *save = strerror(errno); ns_warning(ns_log_default, "can't exec, %s: %s", saved_argv[0], save); ctl_response(sess, 502, save, 0, NULL, NULL, NULL, NULL, 0); } else if (user_name != NULL || group_name != NULL) { ctl_response(sess, 502, "can't exec as user or group was specified", 0, NULL, NULL, NULL, NULL, 0); } else { ctl_response(sess, 250, "Restart initiated.", 0, NULL, exec_closure, NULL, NULL, 0); } } static void exec_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) { UNUSED(sctx); UNUSED(sess); UNUSED(uap); ns_need(main_need_restart); } static void verb_reload(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { static const char spaces[] = " \t"; struct zoneinfo *zp; char *tmp = NULL, *x; const char *cl; const char *msg; int class, code, success; UNUSED(ctl); UNUSED(verb); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); /* If there are no args, this is a classic reload of the config. */ if (rest == NULL || *rest == '\0') { ns_need(main_need_reload); code = 250; msg = "Reload initiated."; goto respond; } /* Look for optional zclass argument. Default is "in". */ tmp = savestr(rest, 1); x = tmp + strcspn(tmp, spaces); if (*x != '\0') { *x++ = '\0'; x += strspn(x, spaces); } cl = (x == NULL || *x == '\0') ? "in" : x; class = res_nametoclass(cl, &success); if (!success) { code = 507; msg = "unrecognized class"; goto respond; } /* Look for the zone, and do the right thing to it. */ zp = find_zone(tmp, class); if (zp == NULL) { code = 506; msg = "Zone not found."; goto respond; } switch (zp->z_type) { case z_master: ns_stopxfrs(zp); /*FALLTHROUGH*/ case z_hint: block_signals(); code = 251; msg = deferred_reload_unsafe(zp); unblock_signals(); break; case z_slave: case z_stub: ns_stopxfrs(zp); if (zonefile_changed_p(zp)) zp->z_serial = 0; /* force xfer */ addxfer(zp); code = 251; msg = "Slave transfer queued."; goto respond; case z_forward: case z_cache: default: msg = "Non reloadable zone."; code = 507; break; } respond: ctl_response(sess, code, msg, 0, NULL, NULL, NULL, NULL, 0); if (tmp != NULL) (void)freestr(tmp); } static void verb_reconfig(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { UNUSED(ctl); UNUSED(verb); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); if (strcmp(rest, "-noexpired") != 0) ns_need(main_need_reconfig); else ns_need(main_need_noexpired); ctl_response(sess, 250, "Reconfig initiated.", 0, NULL, NULL, NULL, NULL, 0); } static void verb_dumpdb(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { UNUSED(ctl); UNUSED(verb); UNUSED(rest); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); ns_need(main_need_dump); ctl_response(sess, 250, "Database dump initiated.", 0, NULL, NULL, NULL, NULL, 0); } static void verb_stats(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { UNUSED(ctl); UNUSED(verb); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); if (rest != NULL && strcmp(rest, "clear") == 0) { ns_need(main_need_statsdumpandclear); ctl_response(sess, 250, "Statistics dump and clear initiated.", 0, NULL, NULL, NULL, NULL, 0); } else { ns_need(main_need_statsdump); ctl_response(sess, 250, "Statistics dump initiated.", 0, NULL, NULL, NULL, NULL, 0); } } static void verb_trace(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { int i = atoi(rest); char *msg = memget(MAX_STR_LEN); UNUSED(ctl); UNUSED(verb); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); if (msg == NULL) { ctl_response(sess, 503, "(out of memory)", 0, NULL, NULL, NULL, NULL, 0); return; } if (isdigit(*(const unsigned char *)rest) && i >= 0) desired_debug = i; else desired_debug++; ns_need(main_need_debug); if (desired_debug == 0) sprintf(msg, "Debugging turned off."); else sprintf(msg, "Debug level: %d", desired_debug); ctl_response(sess, 250, msg, 0, NULL, trace_closure, msg, NULL, 0); } static void trace_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) { char *msg = uap; UNUSED(sctx); UNUSED(sess); memput(msg, MAX_STR_LEN); } static void verb_notrace(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { UNUSED(ctl); UNUSED(verb); UNUSED(rest); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); desired_debug = 0; ns_need(main_need_debug); ctl_response(sess, 250, "Debugging turned off.", 0, NULL, NULL, NULL, NULL, 0); } static void verb_querylog(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { static const char on[] = "Query logging is now on.", off[] = "Query logging is now off."; UNUSED(ctl); UNUSED(verb); UNUSED(rest); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); toggle_qrylog(); ctl_response(sess, 250, qrylog ? on : off, 0, NULL, NULL, NULL, NULL, 0); } static void verb_help(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { UNUSED(ctl); UNUSED(verb); UNUSED(rest); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); ctl_sendhelp(sess, 214); } static void verb_quit(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { UNUSED(ctl); UNUSED(verb); UNUSED(rest); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); ctl_response(sess, 221, "End of control session.", CTL_EXIT, NULL, NULL, NULL, NULL, 0); } static char hex[] = "0123456789abcdef"; struct pvt_args { int argc; char text[MAX_STR_LEN]; }; static void args_closure(struct ctl_sctx *sctx, struct ctl_sess *sess, void *uap) { struct pvt_args *pvt = ctl_getcsctx(sess); UNUSED(sctx); UNUSED(uap); memput(pvt, sizeof *pvt); ctl_setcsctx(sess, NULL); } static void verb_args(struct ctl_sctx *ctl, struct ctl_sess *sess, const struct ctl_verb *verb, const char *rest, u_int respflags, const void *respctx, void *uctx) { struct pvt_args *pvt = ctl_getcsctx(sess); char *cp, *tp; UNUSED(ctl); UNUSED(verb); UNUSED(rest); UNUSED(respflags); UNUSED(respctx); UNUSED(uctx); if (pvt == NULL) { unsigned int i = 0; pvt = memget(sizeof *pvt); if (pvt == NULL) { ctl_response(sess, 505, "(out of memory)", 0, NULL, NULL, NULL, NULL, 0); return; } pvt->argc = 0; ctl_setcsctx(sess, pvt); /* Send the arguement count. */ while (saved_argv[i] != NULL) i++; sprintf(pvt->text, "%u", i); ctl_response(sess, 250, pvt->text, CTL_MORE, NULL, args_closure, NULL, NULL, 0); return; } /* * Percent escape arguement. */ cp = saved_argv[pvt->argc++]; tp = pvt->text; - while (cp && *cp != NULL) + while (cp && *cp != '\0') if (*cp == '%' || *cp == ' ' || !isprint((unsigned char)*cp)) { if (tp >= pvt->text + sizeof(pvt->text) - 4) break; *tp++ = '%'; *tp++ = hex[(*cp>>4)&0xf]; *tp++ = hex[(*cp++)&0xf]; } else { if (tp >= pvt->text + sizeof(pvt->text) - 2) break; *tp++ = *cp++; } *tp = '\0'; ctl_response(sess, 250, pvt->text, saved_argv[pvt->argc] == NULL ? 0 : CTL_MORE, NULL, args_closure, NULL, NULL, 0); } diff --git a/contrib/bind/bin/named/ns_defs.h b/contrib/bind/bin/named/ns_defs.h index 79b8196fc140..7c00a1faaa53 100644 --- a/contrib/bind/bin/named/ns_defs.h +++ b/contrib/bind/bin/named/ns_defs.h @@ -1,932 +1,935 @@ /* * from ns.h 4.33 (Berkeley) 8/23/90 - * $Id: ns_defs.h,v 8.121.2.1 2002/11/14 13:28:12 marka Exp $ + * $Id: ns_defs.h,v 8.124.6.1 2003/06/02 09:56:34 marka Exp $ */ /* * Copyright (c) 1986 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Global definitions for the name server. */ /* * Effort has been expended here to make all structure members 32 bits or * larger land on 32-bit boundaries; smaller structure members have been * deliberately shuffled and smaller integer sizes chosen where possible * to make sure this happens. This is all meant to avoid structure member * padding which can cost a _lot_ of memory when you have hundreds of * thousands of entries in your cache. */ /* * Timeout time should be around 1 minute or so. Using the * the current simplistic backoff strategy, the sequence * retrys after 4, 8, and 16 seconds. With 3 servers, this * dies out in a little more than a minute. * (sequence RETRYBASE, 2*RETRYBASE, 4*RETRYBASE... for MAXRETRY) */ #define NEWZONES 64 /* how many zones to grow the zone table by */ #define INITIALZONES NEWZONES /* how many zones are allocated initially */ #define MINROOTS 2 /* min number of root hints */ #define NSMAX 16 /* max number of NS addrs to try ([0..255]) */ #define RETRYBASE 4 /* base time between retries */ #define MAXCLASS 255 /* XXX - may belong elsewhere */ #define MAXRETRY 3 /* max number of retries per addr */ #define MAXCNAMES 8 /* max # of CNAMES tried per addr */ #define MAXQUERIES 20 /* max # of queries to be made */ #define MAXQSERIAL 4 /* max # of outstanding QSERIAL's */ /* (prevent "recursive" loops) */ #define INIT_REFRESH 600 /* retry time for initial slave */ /* contact (10 minutes) */ #define MIN_REFRESH 2 /* never refresh more frequently than once */ /* every MIN_REFRESH seconds */ #define MIN_RETRY 1 /* never retry more frequently than once */ /* every MIN_RETRY seconds */ #define MAX_REFRESH 2419200 /* perform a refresh query at least */ /* every 4 weeks*/ #define MAX_RETRY 1209600 /* perform a retry after no more than 2 weeks */ #define MAX_EXPIRE 31536000 /* expire a zone if we have not talked to */ /* the primary in 1 year */ #define NADDRECS 20 /* max addt'l rr's per resp */ #define XFER_TIMER 120 /* named-xfer's connect timeout */ #define MAX_XFER_TIME 60*60*2 /* default max seconds for an xfer */ #define XFER_TIME_FUDGE 10 /* MAX_XFER_TIME fudge */ #define MAX_XFERS_RUNNING 20 /* max value of transfers_in */ #define DEFAULT_XFERS_RUNNING 10 /* default value of transfers_in */ #define DEFAULT_XFERS_PER_NS 2 /* default # of xfers per peer nameserver */ #define XFER_BUFSIZE (16*1024) /* arbitrary but bigger than most MTU's */ #define MAX_SYNCDELAY 3 /* Presumed timeout in use by our clients. */ #define MAX_SYNCDRAIN 100000 /* How long we'll spin in drain_all_rcvbuf. */ #define MAX_SYNCSTORE 500 #define NS_MAX_DISTANCE 3 /* maximum nameserver chaining before failure */ /* maximum time to cache negative answers */ #define DEFAULT_MAX_NCACHE_TTL (3*60*60) #define ALPHA 0.7 /* How much to preserve of old response time */ #define BETA 1.2 /* How much to penalize response time on failure */ #define GAMMA 0.98 /* How much to decay unused response times */ /* What maintainance operations need to be performed sometime soon? */ typedef enum need { main_need_zreload = 0, /* ns_zreload() needed. */ main_need_reload, /* ns_reload() needed. */ main_need_reconfig, /* ns_reconfig() needed. */ main_need_endxfer, /* endxfer() needed. */ main_need_zoneload, /* loadxfer() needed. */ main_need_dump, /* doadump() needed. */ main_need_statsdump, /* ns_stats() needed. */ main_need_statsdumpandclear, /* ns_stats() needed. */ main_need_exit, /* exit() needed. */ main_need_qrylog, /* toggle_qrylog() needed. */ main_need_debug, /* use_desired_debug() needed. */ main_need_restart, /* exec() needed. */ main_need_reap, /* need to reap dead children. */ main_need_noexpired, /* ns_reconfig() needed w/ noexpired set. */ main_need_tryxfer, /* attemt to start a zone transfer. */ main_need_num /* MUST BE LAST. */ } main_need; /* What global options are set? */ #define OPTION_NORECURSE 0x00000001 /* Don't recurse even if asked. */ #define OPTION_NOFETCHGLUE 0x00000002 /* Don't fetch missing glue. */ #define OPTION_FORWARD_ONLY 0x00000004 /* Don't use NS RR's, just forward. */ #define OPTION_FAKE_IQUERY 0x00000008 /* Fake up bogus response to IQUERY. */ #ifdef BIND_NOTIFY /* #define OPTION_NONOTIFY 0x00000010 */ /* Turn off notify */ #define OPTION_SUPNOTIFY_INITIAL 0x00000020 /* Supress initial notify */ #endif #define OPTION_NONAUTH_NXDOMAIN 0x00000040 /* Generate non-auth NXDOMAINs? */ #define OPTION_MULTIPLE_CNAMES 0x00000080 /* Allow a name to have multiple * CNAME RRs */ #define OPTION_HOSTSTATS 0x00000100 /* Maintain per-host statistics? */ #define OPTION_DEALLOC_ON_EXIT 0x00000200 /* Deallocate everything on exit? */ #define OPTION_NODIALUP 0x00000400 /* Turn off dialup support */ #define OPTION_NORFC2308_TYPE1 0x00000800 /* Prevent type1 respones (RFC 2308) * to cached negative respones */ #define OPTION_USE_ID_POOL 0x00001000 /* Use the memory hogging query ID */ #define OPTION_TREAT_CR_AS_SPACE 0x00002000 /* Treat CR in zone files as * space */ #define OPTION_USE_IXFR 0x00004000 /* Use by default ixfr in zone * transfer */ #define OPTION_MAINTAIN_IXFR_BASE 0x00008000 /* Part of IXFR file name logic. */ #define OPTION_HITCOUNT 0x00010000 /* Keep track of each time an * RR gets hit in the database */ #define DEFAULT_OPTION_FLAGS (OPTION_NODIALUP|OPTION_NONAUTH_NXDOMAIN|\ OPTION_USE_ID_POOL|OPTION_NORFC2308_TYPE1) #ifdef BIND_UPDATE #define SOAINCRINTVL 300 /* default value for the time after which * the zone serial number must be incremented * after a successful update has occurred */ #define DUMPINTVL 3600 /* default interval at which to dump changed zones * randomized, not exact */ #define DEFERUPDCNT 100 /* default number of updates that can happen * before the zone serial number will be * incremented */ #define UPDATE_TIMER XFER_TIMER #endif /* BIND_UPDATE */ #define USE_MINIMUM 0xffffffff #define MAXIMUM_TTL 0x7fffffff #define CLEAN_TIMER 0x01 #define INTERFACE_TIMER 0x02 #define STATS_TIMER 0x04 #define HEARTBEAT_TIMER 0x08 /* IP address accessor, network byte order. */ #define ina_ulong(ina) (ina.s_addr) /* IP address accessor, host byte order, read only. */ #define ina_hlong(ina) ntohl(ina.s_addr) /* IP address equality. */ /* XXX: assumes that network byte order won't affect equality. */ #define ina_equal(a, b) (ina_ulong(a) == ina_ulong(b)) /* IP address equality with a mask. */ #define ina_onnet(h, n, m) ((ina_ulong(h) & ina_ulong(m)) == ina_ulong(n)) /* Sequence space arithmetic. */ #define SEQ_GT(a,b) ((int32_t)((a)-(b)) > 0) #define SEQ_LT(a,b) ((int32_t)((a)-(b)) < 0) #define NS_OPTION_P(option) ((server_options == NULL) ? \ (panic(panic_msg_no_options, NULL), 0) : \ ((server_options->flags & option) != 0)) #define NS_ZOPTION_P(zp, option) \ (((zp) != NULL && (((zp)->z_optset & option) != 0)) ? \ (((zp)->z_options & option) != 0) : NS_OPTION_P(option)) #define NS_ZFWDTAB(zp) (((zp) == NULL) ? \ server_options->fwdtab : (zp)->z_fwdtab) #define NS_INCRSTAT(addr, which) \ do { \ if ((int)which >= (int)nssLast) \ ns_panic(ns_log_insist, 1, panic_msg_bad_which, \ __FILE__, __LINE__, #which); \ else { \ if (NS_OPTION_P(OPTION_HOSTSTATS)) { \ struct nameser *ns = \ nameserFind(addr, NS_F_INSERT); \ if (ns != NULL) \ ns->stats[(int)which]++; \ } \ globalStats[(int)which]++; \ } \ } while (0) enum severity { ignore, warn, fail, not_set }; #ifdef BIND_NOTIFY enum notify { notify_use_default=0, notify_yes, notify_no, notify_explicit }; #endif enum zdialup { zdialup_use_default=0, zdialup_yes, zdialup_no }; enum axfr_format { axfr_use_default=0, axfr_one_answer, axfr_many_answers }; struct ip_match_direct { struct in_addr address; struct in_addr mask; }; struct ip_match_indirect { struct ip_match_list *list; }; struct ip_match_key { struct dst_key *key; }; typedef enum { ip_match_pattern, ip_match_indirect, ip_match_localhost, ip_match_localnets, ip_match_key } ip_match_type; typedef struct ip_match_element { ip_match_type type; u_int flags; union { struct ip_match_direct direct; struct ip_match_indirect indirect; struct ip_match_key key; } u; struct ip_match_element *next; } *ip_match_element; /* Flags for ip_match_element */ #define IP_MATCH_NEGATE 0x01 /* match means deny access */ typedef struct ip_match_list { ip_match_element first; ip_match_element last; } *ip_match_list; typedef struct ztimer_info { char *name; int class; int type; } *ztimer_info; /* * These fields are ordered to maintain word-alignment; * be careful about changing them. */ struct zoneinfo { char *z_origin; /* root domain name of zone */ time_t z_time; /* time for next refresh */ time_t z_lastupdate; /* time of last soa serial increment */ u_int32_t z_refresh; /* refresh interval */ u_int32_t z_retry; /* refresh retry interval */ u_int32_t z_expire; /* expiration time for cached info */ u_int32_t z_minimum; /* minimum TTL value */ u_int32_t z_serial; /* changes if zone modified */ char *z_source; /* source location of data */ time_t z_ftime; /* modification time of source file */ struct in_addr z_axfr_src; /* bind() the axfr socket to this */ struct in_addr z_addr[NSMAX]; /* list of master servers for zone */ struct dst_key * z_keys[NSMAX]; /* tsig key associated with master */ u_char z_addrcnt; /* number of entries in z_addr[] */ struct in_addr z_xaddr[NSMAX]; /* list of master servers for xfer */ u_char z_xaddrcnt; /* number of entries in z_xaddr[] */ u_char z_type; /* type of zone; see below */ u_int32_t z_flags; /* state bits; see below */ pid_t z_xferpid; /* xfer child pid */ u_int z_options; /* options set specific to this zone */ u_int z_optset; /* which opts override global opts */ int z_class; /* class of zone */ int z_numxfrs; /* Ref count of concurrent xfrs. */ enum severity z_checknames; /* How to handle non-RFC-compliant names */ #ifdef BIND_UPDATE time_t z_dumptime; /* randomized time for next zone dump * if Z_NEED_DUMP is set */ u_int32_t z_dumpintvl; /* time interval between zone dumps */ time_t z_soaincrintvl; /* interval for updating soa serial */ time_t z_soaincrtime; /* time for soa increment */ u_int32_t z_deferupdcnt; /* max number of updates before SOA * serial number incremented */ u_int32_t z_updatecnt; /* number of update requests processed * since the last SOA serial update */ char *z_updatelog; /* log file for updates */ #endif ip_match_list z_update_acl; /* list of who can issue dynamic updates */ ip_match_list z_query_acl; /* sites we'll answer questions for */ ip_match_list z_transfer_acl; /* sites that may get a zone transfer from us */ long z_max_transfer_time_in; /* max num seconds for AXFR */ #ifdef BIND_NOTIFY enum notify z_notify; /* Notify mode */ struct in_addr *z_also_notify; /* More nameservers to notify */ int z_notify_count; #endif enum zdialup z_dialup; /* secondaries over a dialup link */ char *z_ixfr_base; /* where to find the history of the zone */ char *z_ixfr_tmp; /* tmp file for the ixfr */ int z_maintain_ixfr_base; long z_max_log_size_ixfr; u_int32_t z_serial_ixfr_start; evTimerID z_timer; /* maintenance timer */ ztimer_info z_timerinfo; /* UAP associated with timer */ time_t z_nextmaint; /* time of next maintenance */ u_int16_t z_port; /* perform AXFR to this port */ struct fwdinfo *z_fwdtab; /* zone-specific forwarders */ LINK(struct zoneinfo) z_freelink; /* if it's on the free list. */ LINK(struct zoneinfo) z_reloadlink; /* if it's on the reload list. */ }; /* zone types (z_type) */ enum zonetype { z_nil, z_master, z_slave, z_hint, z_stub, z_forward, z_cache, z_any }; #define Z_NIL z_nil /* XXX */ #define Z_MASTER z_master /* XXX */ #define Z_PRIMARY z_master /* XXX */ #define Z_SLAVE z_slave /* XXX */ #define Z_SECONDARY z_slave /* XXX */ #define Z_HINT z_hint /* XXX */ #define Z_CACHE z_cache /* XXX */ #define Z_STUB z_stub /* XXX */ #define Z_FORWARD z_forward /* XXX */ #define Z_ANY z_any /* XXX*2 */ /* zone state bits (32 bits) */ #define Z_AUTH 0x00000001 /* zone is authoritative */ #define Z_NEED_XFER 0x00000002 /* waiting to do xfer */ #define Z_XFER_RUNNING 0x00000004 /* asynch. xfer is running */ #define Z_NEED_RELOAD 0x00000008 /* waiting to do reload */ #define Z_SYSLOGGED 0x00000010 /* have logged timeout */ #define Z_QSERIAL 0x00000020 /* sysquery()'ing for serial number */ #define Z_FOUND 0x00000040 /* found in boot file when reloading */ #define Z_INCLUDE 0x00000080 /* set if include used in file */ #define Z_DB_BAD 0x00000100 /* errors when loading file */ #define Z_TMP_FILE 0x00000200 /* backup file for xfer is temporary */ #ifdef BIND_UPDATE #define Z_DYNAMIC 0x00000400 /* allow dynamic updates */ #define Z_NEED_DUMP 0x00000800 /* zone has changed, needs a dump */ #define Z_NEED_SOAUPDATE 0x00001000 /* soa serial number needs increment */ #endif /* BIND_UPDATE */ #define Z_XFER_ABORTED 0x00002000 /* zone transfer has been aborted */ #define Z_XFER_GONE 0x00004000 /* zone transfer process is gone */ #define Z_TIMER_SET 0x00008000 /* z_timer contains a valid id */ #ifdef BIND_NOTIFY #define Z_NOTIFY 0x00010000 /* has an outbound notify executing */ #endif #define Z_NEED_QSERIAL 0x00020000 /* we need to re-call qserial() */ #define Z_PARENT_RELOAD 0x00040000 /* we need to reload this as parent */ #define Z_FORWARD_SET 0x00080000 /* has forwarders been set */ #define Z_EXPIRED 0x00100000 /* expire timer has gone off */ #define Z_NEEDREFRESH 0x00200000 /* need to perform a refresh check */ /* named_xfer exit codes */ #define XFER_UPTODATE 0 /* zone is up-to-date */ #define XFER_SUCCESS 1 /* performed transfer successfully */ #define XFER_TIMEOUT 2 /* no server reachable/xfer timeout */ #define XFER_FAIL 3 /* other failure, has been logged */ #define XFER_SUCCESSAXFR 4 /* named-xfr recived a xfr */ #define XFER_SUCCESSIXFR 5 /* named-xfr recived a ixfr */ #define XFER_SUCCESSAXFRIXFRFILE 6 /* named-xfr received AXFR for IXFR */ #define XFER_REFUSED 7 /* one master returned REFUSED */ #define XFER_ISAXFR -1 /* the last XFR is AXFR */ #define XFER_ISIXFR -2 /* the last XFR is IXFR */ #define XFER_ISAXFRIXFR -3 /* the last XFR is AXFR but we must create IXFR base */ struct qserv { struct sockaddr_in ns_addr; /* address of NS */ struct databuf *ns; /* databuf for NS record */ struct databuf *nsdata; /* databuf for server address */ struct timeval stime; /* time first query started */ unsigned int forwarder:1; /* this entry is for a forwarder */ unsigned int noedns:1; /* don't try edns */ - unsigned int nretry:30; /* # of times addr retried */ + unsigned int lame:1; /* this server was lame, try it last */ + unsigned int nretry:29; /* # of times addr retried */ u_int32_t serial; /* valid if Q_ZSERIAL */ }; /* * Structure for recording info on forwarded or generated queries. */ struct qinfo { u_int16_t q_id; /* id of query */ u_int16_t q_nsid; /* id of forwarded query */ struct sockaddr_in q_from; /* requestor's address */ u_char *q_msg, /* the message */ *q_cmsg; /* the cname message */ int16_t q_msglen, /* len of message */ q_msgsize, /* allocated size of message */ q_cmsglen, /* len of cname message */ q_cmsgsize; /* allocated size of cname message */ int16_t q_dfd; /* UDP file descriptor */ u_int16_t q_udpsize; /* UDP message size */ int q_distance; /* distance this query is from the * original query that the server * received. */ time_t q_time; /* time to retry */ time_t q_expire; /* time to expire */ struct qinfo *q_next; /* rexmit list (sorted by time) */ struct qinfo *q_link; /* storage list (random order) */ struct databuf *q_usedns[NSMAX]; /* databuf for NS that we've tried */ struct qserv q_addr[NSMAX]; /* addresses of NS's */ #ifdef notyet struct nameser *q_ns[NSMAX]; /* name servers */ #endif struct dst_key *q_keys[NSMAX]; /* keys to use with this address */ u_char q_naddr; /* number of addr's in q_addr */ u_char q_curaddr; /* last addr sent to */ u_char q_nusedns; /* number of elements in q_usedns[] */ u_int8_t q_flags; /* see below */ int16_t q_cname; /* # of cnames found */ int16_t q_nqueries; /* # of queries required */ struct qstream *q_stream; /* TCP stream, null if UDP */ struct zoneinfo *q_zquery; /* Zone query is about (Q_ZSERIAL) */ struct zoneinfo *q_fzone; /* Forwarding zone, if any */ char *q_domain; /* domain of most enclosing zone cut */ char *q_name; /* domain of query */ u_int16_t q_class; /* class of query */ u_int16_t q_type; /* type of query */ #ifdef BIND_NOTIFY int q_notifyzone; /* zone which needs another notify() * when the reply to this comes in. */ #endif struct tsig_record *q_tsig; /* forwarded query's TSIG record */ struct tsig_record *q_nstsig; /* forwarded query's TSIG record */ }; /* q_flags bits (8 bits) */ #define Q_SYSTEM 0x01 /* is a system query */ #define Q_PRIMING 0x02 /* generated during priming phase */ #define Q_ZSERIAL 0x04 /* getting zone serial for xfer test */ #define Q_USEVC 0x08 /* forward using tcp not udp */ #define Q_EDNS 0x10 /* add edns opt record to answer */ #define Q_NEXTADDR(qp,n) (&(qp)->q_addr[n].ns_addr) #define RETRY_TIMEOUT 45 /* * Return codes from ns_forw: */ #define FW_OK 0 #define FW_DUP 1 #define FW_NOSERVER 2 #define FW_SERVFAIL 3 typedef void (*sq_closure)(struct qstream *qs); #ifdef BIND_UPDATE struct fdlist { int fd; struct fdlist *next; }; #endif typedef struct ns_delta { LINK(struct ns_delta) d_link; ns_updque d_changes; } ns_delta; typedef LIST(ns_delta) ns_deltalist; typedef struct _interface { int dfd, /* Datagram file descriptor */ sfd; /* Stream file descriptor. */ time_t gen; /* Generation number. */ struct in_addr addr; /* Interface address. */ u_int16_t port; /* Interface port. */ u_int16_t flags; /* Valid bits for evXXXXID. */ evFileID evID_d; /* Datagram read-event. */ evConnID evID_s; /* Stream listen-event. */ LINK(struct _interface) link; } interface; #define INTERFACE_FILE_VALID 0x01 #define INTERFACE_CONN_VALID 0x02 #define INTERFACE_FORWARDING 0x04 struct qstream { int s_rfd; /* stream file descriptor */ int s_size; /* expected amount of data to rcv */ int s_bufsize; /* amount of data received in s_buf */ u_char *s_buf; /* buffer of received data */ u_char *s_wbuf; /* send buffer */ u_char *s_wbuf_send; /* next sendable byte of send buffer */ u_char *s_wbuf_free; /* next free byte of send buffer */ u_char *s_wbuf_end; /* byte after end of send buffer */ sq_closure s_wbuf_closure; /* callback for writable descriptor */ struct qstream *s_next; /* next stream */ struct sockaddr_in s_from; /* address query came from */ interface *s_ifp; /* interface query came from */ time_t s_time; /* time stamp of last transaction */ int s_refcnt; /* number of outstanding queries */ u_char s_temp[HFIXEDSZ]; #ifdef BIND_UPDATE int s_opcode; /* type of request */ int s_linkcnt; /* number of client connections using * this connection to forward updates * to the primary */ struct fdlist *s_fds; /* linked list of connections to the * primaries that have been used by * the server to forward this client's * update requests */ #endif evStreamID evID_r; /* read event. */ evFileID evID_w; /* writable event handle. */ evConnID evID_c; /* connect event handle */ u_int flags; /* see below */ struct qstream_xfr { enum { s_x_base, s_x_firstsoa, s_x_zone, s_x_lastsoa, s_x_done, s_x_adding, s_x_deleting, s_x_addsoa, s_x_deletesoa } state; /* state of transfer. */ u_char *msg, /* current assembly message. */ *cp, /* where are we in msg? */ *eom, /* end of msg. */ *ptrs[128]; /* ptrs for dn_comp(). */ int class, /* class of an XFR. */ type, /* type of XFR. */ id, /* id of an XFR. */ opcode; /* opcode of an XFR. */ u_int zone; /* zone being XFR'd. */ union { struct namebuf *axfr; /* top np of an AXFR. */ ns_deltalist *ixfr; /* top udp of an IXFR. */ } top; int ixfr_zone; u_int32_t serial; /* serial number requested in IXFR */ ns_tcp_tsig_state *tsig_state; /* used by ns_sign_tcp */ int tsig_skip; /* skip calling ns_sign_tcp * during the next flush */ int tsig_size; /* need to reserve this space * for the tsig. */ struct qs_x_lev { /* decompose the recursion. */ enum {sxl_ns, sxl_all, sxl_sub} state; /* what's this level doing? */ int flags; /* see below (SXL_*). */ char dname[MAXDNAME]; struct namebuf *np, /* this node. */ *nnp, /* next node to process. */ **npp, /* subs. */ **npe; /* end of subs. */ struct databuf *dp; /* current rr. */ struct qs_x_lev *next; /* link. */ } *lev; /* LIFO. */ enum axfr_format transfer_format; } xfr; }; #define SXL_GLUING 0x01 #define SXL_ZONECUT 0x02 /* flags */ #define STREAM_MALLOC 0x01 #define STREAM_WRITE_EV 0x02 #define STREAM_READ_EV 0x04 #define STREAM_CONNECT_EV 0x08 #define STREAM_DONE_CLOSE 0x10 #define STREAM_AXFR 0x20 #define STREAM_AXFRIXFR 0x40 #define ALLOW_NETS 0x0001 #define ALLOW_HOSTS 0x0002 #define ALLOW_ALL (ALLOW_NETS | ALLOW_HOSTS) struct fwddata { struct sockaddr_in fwdaddr; /* address of NS */ struct databuf *ns; /* databuf for NS record */ struct databuf *nsdata; /* databuf for server address */ int ref_count; /* how many users of this */ }; struct fwdinfo { struct fwdinfo *next; struct fwddata *fwddata; }; enum nameserStats { nssRcvdR, /* sent us an answer */ nssRcvdNXD, /* sent us a negative response */ nssRcvdFwdR, /* sent us a response we had to fwd */ nssRcvdDupR, /* sent us an extra answer */ nssRcvdFail, /* sent us a SERVFAIL */ nssRcvdFErr, /* sent us a FORMERR */ nssRcvdErr, /* sent us some other error */ nssRcvdAXFR, /* sent us an AXFR */ nssRcvdLDel, /* sent us a lame delegation */ nssRcvdOpts, /* sent us some IP options */ nssSentSysQ, /* sent them a sysquery */ nssSentAns, /* sent them an answer */ nssSentFwdQ, /* fwdd a query to them */ nssSentDupQ, /* sent them a retry */ nssSendtoErr, /* error in sendto */ nssRcvdQ, /* sent us a query */ nssRcvdIQ, /* sent us an inverse query */ nssRcvdFwdQ, /* sent us a query we had to fwd */ nssRcvdDupQ, /* sent us a retry */ nssRcvdTCP, /* sent us a query using TCP */ nssSentFwdR, /* fwdd a response to them */ nssSentFail, /* sent them a SERVFAIL */ nssSentFErr, /* sent them a FORMERR */ nssSentNaAns, /* sent them a non autoritative answer */ nssSentNXD, /* sent them a negative response */ nssRcvdUQ, /* sent us an unapproved query */ nssRcvdURQ, /* sent us an unapproved recursive query */ nssRcvdUXFR, /* sent us an unapproved AXFR or IXFR */ nssRcvdUUpd, /* sent us an unapproved update */ nssLast }; struct nameser { struct in_addr addr; /* key */ u_long stats[nssLast]; /* statistics */ #ifdef notyet u_int32_t rtt; /* round trip time */ /* XXX - need to add more stuff from "struct qserv", and use our rtt */ u_int16_t flags; /* see below */ u_int8_t xfers; /* #/xfers running right now */ #endif }; enum transport { primary_trans, secondary_trans, response_trans, update_trans, num_trans }; /* types used by the parser or config routines */ typedef struct zone_config { void *opaque; } zone_config; typedef struct listen_info { u_short port; ip_match_list list; struct listen_info *next; } *listen_info; typedef struct listen_info_list { listen_info first; listen_info last; } *listen_info_list; #ifndef RLIMIT_TYPE #define RLIMIT_TYPE u_long #endif typedef RLIMIT_TYPE rlimit_type; struct control; typedef struct control *control; typedef LIST(struct control) controls; enum ordering { unknown_order, fixed_order, cyclic_order, random_order }; #define DEFAULT_ORDERING cyclic_order typedef struct rrset_order_element { int class; int type; char *name; enum ordering order; struct rrset_order_element *next; } *rrset_order_element ; typedef struct rrset_order_list { rrset_order_element first; rrset_order_element last; } *rrset_order_list; typedef struct options { u_int32_t flags; char *hostname; char *version; char *directory; char *dump_filename; char *pid_filename; char *stats_filename; char *memstats_filename; char *named_xfer; int transfers_in; int transfers_per_ns; int transfers_out; int serial_queries; int max_log_size_ixfr; enum axfr_format transfer_format; long max_transfer_time_in; struct sockaddr_in query_source; struct in_addr axfr_src; #ifdef BIND_NOTIFY int notify_count; struct in_addr *also_notify; #endif ip_match_list query_acl; ip_match_list recursion_acl; ip_match_list transfer_acl; ip_match_list blackhole_acl; ip_match_list topology; ip_match_list sortlist; enum severity check_names[num_trans]; u_long data_size; u_long stack_size; u_long core_size; u_long files; listen_info_list listen_list; struct fwdinfo *fwdtab; /* XXX need to add forward option */ int clean_interval; int interface_interval; int stats_interval; rrset_order_list ordering; int heartbeat_interval; u_int max_ncache_ttl; u_int max_host_stats; u_int lame_ttl; int minroots; u_int16_t preferred_glue; + u_int16_t edns_udp_size; enum notify notify; } *options; typedef struct key_list_element { struct dst_key *key; struct key_list_element *next; } *key_list_element; typedef struct key_info_list { key_list_element first; key_list_element last; } *key_info_list; typedef struct topology_config { void *opaque; } topology_config; #define UNKNOWN_TOPOLOGY_DISTANCE 9998 #define MAX_TOPOLOGY_DISTANCE 9999 typedef struct topology_distance { ip_match_list patterns; struct topology_distance *next; } *topology_distance; typedef struct topology_context { topology_distance first; topology_distance last; } *topology_context; typedef struct acl_table_entry { char *name; ip_match_list list; struct acl_table_entry *next; } *acl_table_entry; typedef struct server_config { void *opaque; } server_config; #define SERVER_INFO_BOGUS 0x01 #define SERVER_INFO_SUPPORT_IXFR 0x02 #define SERVER_INFO_EDNS 0x04 typedef struct server_info { struct in_addr address; u_int flags; int transfers; enum axfr_format transfer_format; key_info_list key_list; /* could move statistics to here, too */ struct server_info *next; } *server_info; /* * enum <--> name translation */ struct ns_sym { int number; /* Identifying number, like ns_log_default */ const char * name; /* Its symbolic name, like "default" */ }; /* * Logging options */ typedef enum ns_logging_categories { ns_log_default = 0, ns_log_config, ns_log_parser, ns_log_queries, ns_log_lame_servers, ns_log_statistics, ns_log_panic, ns_log_update, ns_log_ncache, ns_log_xfer_in, ns_log_xfer_out, ns_log_db, ns_log_eventlib, ns_log_packet, #ifdef BIND_NOTIFY ns_log_notify, #endif ns_log_cname, ns_log_security, ns_log_os, ns_log_insist, ns_log_maint, ns_log_load, ns_log_resp_checks, ns_log_control, + ns_log_update_security, ns_log_max_category } ns_logging_categories; typedef struct log_config { log_context log_ctx; log_channel eventlib_channel; log_channel packet_channel; int default_debug_active; } *log_config; struct map { const char * token; int val; }; #define NOERROR_NODATA 15 /* only used internally by the server, used for * -ve $ing non-existence of records. 15 is not * a code used as yet anyway. */ #define NTTL 600 /* ttl for negative data: 10 minutes? */ #define VQEXPIRY 900 /* a VQ entry expires in 15*60 = 900 seconds */ #ifdef BIND_UPDATE enum req_action { Finish, Refuse, Return }; #endif #ifdef INIT error "INIT already defined, check system include files" #endif #ifdef DECL error "DECL already defined, check system include files" #endif #ifdef MAIN_PROGRAM #define INIT(x) = x #define DECL #else #define INIT(x) #define DECL extern #endif #define EDNS_MESSAGE_SZ 4096 diff --git a/contrib/bind/bin/named/ns_forw.c b/contrib/bind/bin/named/ns_forw.c index 746257b0f33a..c527a80e69ce 100644 --- a/contrib/bind/bin/named/ns_forw.c +++ b/contrib/bind/bin/named/ns_forw.c @@ -1,1314 +1,1331 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_forw.c 4.32 (Berkeley) 3/3/91"; -static const char rcsid[] = "$Id: ns_forw.c,v 8.91 2002/05/24 03:04:57 marka Exp $"; +static const char rcsid[] = "$Id: ns_forw.c,v 8.92.6.1 2003/06/02 09:56:34 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1986 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" struct complaint { u_long tag1, tag2; time_t expire; struct complaint *next; }; static struct complaint *complaints = NULL; static int retry_timer_set = 0; /* * Forward the query to get the answer since its not in the database. * Returns FW_OK if a request struct is allocated and the query sent. * Returns FW_DUP if this is a duplicate of a pending request. * Returns FW_NOSERVER if there were no addresses for the nameservers. * Returns FW_SERVFAIL on memory allocation error or if asked to do something * dangerous, such as fwd to ourselves or fwd to the host that asked us. * * (no action is taken on errors and qpp is not filled in.) */ int ns_forw(struct databuf *nsp[], u_char *msg, int msglen, struct sockaddr_in from, struct qstream *qsp, int dfd, struct qinfo **qpp, const char *dname, int class, int type, struct namebuf *np, int use_tcp, struct tsig_record *in_tsig) { struct qinfo *qp; char tmpdomain[MAXDNAME]; struct sockaddr_in *nsa; HEADER *hp; u_int16_t id; int sendto_errno = 0; int n, has_tsig, oldqlen = 0; u_char *oldqbuf = NULL; u_char *smsg = NULL; int smsglen, smsgsize = 0, siglen; u_char sig[TSIG_SIG_SIZE]; DST_KEY *key; ns_debug(ns_log_default, 3, "ns_forw()"); hp = (HEADER *) msg; id = hp->id; /* Look at them all */ for (qp = nsqhead; qp != NULL; qp = qp->q_link) { if (qp->q_id == id && memcmp(&qp->q_from, &from, sizeof qp->q_from) == 0 && ((qp->q_cmsglen == 0 && qp->q_msglen == msglen && memcmp(qp->q_msg + 2, msg + 2, msglen - 2) == 0) || (qp->q_cmsglen == msglen && memcmp(qp->q_cmsg + 2, msg + 2, msglen - 2) == 0) )) { ns_debug(ns_log_default, 3, "forw: dropped DUP id=%d", ntohs(id)); nameserIncr(from.sin_addr, nssRcvdDupQ); return (FW_DUP); } } qp = qnew(dname, class, type, 1); getname(np, tmpdomain, sizeof tmpdomain); qp->q_domain = savestr(tmpdomain, 1); qp->q_from = from; /* nslookup wants to know this */ if (NS_ZFWDTAB(qp->q_fzone)) nsfwdadd(qp, NS_ZFWDTAB(qp->q_fzone)); if (NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) n = 0; else n = nslookup(nsp, qp, dname, "ns_forw"); if (n < 0) { if (n == -1) ns_debug(ns_log_default, 2, "forw: nslookup reports danger"); ns_freeqry(qp); return (FW_SERVFAIL); } if (n == 0 && !NS_ZFWDTAB(qp->q_fzone)) { ns_debug(ns_log_default, 2, "forw: no nameservers found"); ns_freeqry(qp); return (FW_NOSERVER); } qp->q_stream = qsp; qp->q_curaddr = 0; qp->q_dfd = dfd; qp->q_id = id; qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2; if (in_tsig != NULL) qp->q_tsig = new_tsig(in_tsig->key, in_tsig->sig, in_tsig->siglen); if (use_tcp) qp->q_flags |= Q_USEVC; hp->id = qp->q_nsid = htons(nsid_next()); hp->ancount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); if ((qp->q_msg = (u_char *)memget((unsigned)msglen)) == NULL) { ns_notice(ns_log_default, "forw: memget: %s", strerror(errno)); ns_freeqry(qp); return (FW_SERVFAIL); } qp->q_msgsize = msglen; memcpy(qp->q_msg, msg, qp->q_msglen = msglen); hp = (HEADER *) qp->q_msg; hp->rd = (qp->q_addr[0].forwarder ? 1 : 0); qp->q_addr[0].stime = tt; schedretry(qp, retrytime(qp)); nsa = Q_NEXTADDR(qp, 0); ns_debug(ns_log_default, 1, "forw: forw -> [%s].%d ds=%d nsid=%d id=%d %dms retry %dsec", inet_ntoa(nsa->sin_addr), ntohs(nsa->sin_port), ds, ntohs(qp->q_nsid), ntohs(qp->q_id), (qp->q_addr[0].nsdata != NULL) ? qp->q_addr[0].nsdata->d_nstime : -1, (int)(qp->q_time - tt.tv_sec)); #ifdef DEBUG if (debug >= 10) res_pquery(&res, msg, msglen, log_get_stream(packet_channel)); #endif key = qp->q_keys[0]; if (key == NULL) key = qp->q_keys[0] = tsig_key_from_addr(nsa->sin_addr); if (key != NULL || !qp->q_addr[0].noedns) { smsgsize = qp->q_msglen + TSIG_BUF_SIZE + 11; smsg = memget(smsgsize); if (smsg == NULL) ns_panic(ns_log_default, 1, "ns_forw: memget failed"); smsglen = qp->q_msglen; siglen = sizeof(sig); memcpy(smsg, qp->q_msg, qp->q_msglen); } if (!qp->q_addr[0].noedns) smsglen += ns_add_opt(smsg, smsg + smsglen, smsgsize, 0, 0, - EDNS_MESSAGE_SZ, 0, NULL, 0); + server_options->edns_udp_size, + 0, NULL, 0); if (key != NULL) { n = ns_sign(smsg, &smsglen, smsgsize, NOERROR, key, NULL, 0, sig, &siglen, 0); if (n == 0) { has_tsig = 1; free_tsig(qp->q_nstsig); qp->q_nstsig = new_tsig(key, sig, siglen); } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; INSIST(0); } } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; } if (smsg != NULL) { oldqbuf = qp->q_msg; oldqlen = qp->q_msglen; qp->q_msglen = smsglen; qp->q_msg = smsg; hp = (HEADER *) qp->q_msg; } if (qp->q_flags & Q_USEVC) { if (tcp_send(qp) != NOERROR) { if (!haveComplained(ina_ulong(nsa->sin_addr), (u_long)tcpsendStr)) ns_info(ns_log_default, "ns_forw: tcp_send(%s) failed: %s", sin_ntoa(*nsa), strerror(errno)); } } else if (sendto(ds, (char *)qp->q_msg, qp->q_msglen, 0, (struct sockaddr *)nsa, sizeof(struct sockaddr_in)) < 0) { sendto_errno = errno; if (!haveComplained(ina_ulong(nsa->sin_addr), (u_long)sendtoStr)) ns_info(ns_log_default, "ns_forw: sendto(%s): %s", sin_ntoa(*nsa), strerror(errno)); nameserIncr(nsa->sin_addr, nssSendtoErr); } if (smsgsize != 0) { memput(smsg, smsgsize); qp->q_msg = oldqbuf; qp->q_msglen = oldqlen; hp = (HEADER *) qp->q_msg; } nameserIncr(from.sin_addr, nssRcvdFwdQ); nameserIncr(nsa->sin_addr, nssSentFwdQ); if (qpp) *qpp = qp; hp->rd = 1; switch (sendto_errno) { case ENETDOWN: case ENETUNREACH: case EHOSTDOWN: case EHOSTUNREACH: unsched(qp); schedretry(qp, (time_t) 0); } return (0); } /* haveComplained(tag1, tag2) * check to see if we have complained about (tag1,tag2) recently * returns: * boolean: have we complained recently? * side-effects: * outdated complaint records removed from our static list * author: * Paul Vixie (DECWRL) April 1991 */ int haveComplained(u_long tag1, u_long tag2) { struct complaint *cur, *next, *prev; int r = 0; for (cur = complaints, prev = NULL; cur != NULL; prev = cur, cur = next) { next = cur->next; if (tt.tv_sec > cur->expire) { if (prev) prev->next = next; else complaints = next; memput(cur, sizeof *cur); cur = prev; } else if (tag1 == cur->tag1 && tag2 == cur->tag2) r++; } if (!r) { cur = (struct complaint *)memget(sizeof(struct complaint)); if (cur) { cur->tag1 = tag1; cur->tag2 = tag2; cur->expire = tt.tv_sec + INIT_REFRESH; /* "10:00" */ cur->next = NULL; if (prev) prev->next = cur; else complaints = cur; } } return (r); } void freeComplaints(void) { struct complaint *cur, *next; for (cur = complaints; cur != NULL; cur = next) { next = cur->next; memput(cur, sizeof *cur); } complaints = NULL; } /* void * nslookupComplain(sysloginfo, queryname, complaint, dname, a_rr) * Issue a complaint about a dangerous situation found by nslookup(). * params: * sysloginfo is a string identifying the complainant. * queryname is the domain name associated with the problem. * complaint is a string describing what is wrong. * dname and a_rr are the problematic other name server. */ static void nslookupComplain(const char *sysloginfo, const char *queryname, const char *complaint, const char *dname, const struct databuf *a_rr, const struct databuf *nsdp) { char *a, *ns; const char *a_type; int print_a; ns_debug(ns_log_default, 2, "NS '%s' %s", dname, complaint); if (sysloginfo && queryname && !haveComplained((u_long)queryname, (u_long)complaint)) { char nsbuf[20], abuf[20]; a = ns = (char *)NULL; print_a = (a_rr->d_type == T_A); a_type = p_type(a_rr->d_type); if (a_rr->d_rcode) { print_a = 0; switch(a_rr->d_rcode) { case NXDOMAIN: a_type = "NXDOMAIN"; break; case NOERROR_NODATA: a_type = "NODATA"; break; } } if (nsdp != NULL) { if (nsdp->d_addr.s_addr != htonl(0)) { strcpy(nsbuf, inet_ntoa(nsdp->d_addr)); ns = nsbuf; } else { ns = zones[nsdp->d_zone].z_origin; } } if (a_rr->d_addr.s_addr != htonl(0)) { strcpy(abuf, inet_ntoa(a_rr->d_addr)); a = abuf; } else { a = zones[a_rr->d_zone].z_origin; } if (a != NULL || ns != NULL) ns_info(ns_log_default, "%s: query(%s) %s (%s:%s) learnt (%s=%s:NS=%s)", sysloginfo, queryname, complaint, dname, print_a ? inet_ntoa(ina_get(a_rr->d_data)) : "", a_type, a ? a : "", ns ? ns : "" ); else ns_info(ns_log_default, "%s: query(%s) %s (%s:%s)", sysloginfo, queryname, complaint, dname, print_a ? inet_ntoa(ina_get(a_rr->d_data)) : ""); } } /* * nslookup(nsp, qp, syslogdname, sysloginfo) * Lookup the address for each nameserver in `nsp' and add it to * the list saved in the qinfo structure pointed to by `qp'. * Omits information about nameservers that we shouldn't ask. * Detects the following dangerous operations: * One of the A records for one of the nameservers in nsp * refers to the address of one of our own interfaces; * One of the A records refers to the nameserver port on * the host that asked us this question. * returns: the number of addresses added, or -1 if a dangerous operation * is detected. * side effects: * logs if a dangerous situation is detected and * (syslogdname && sysloginfo) */ int nslookup(struct databuf *nsp[], struct qinfo *qp, const char *syslogdname, const char *sysloginfo) { struct namebuf *np; struct databuf *dp, *nsdp; struct qserv *qs; int n; u_int i; struct hashbuf *tmphtp; char *dname; const char *fname; - int oldn, naddr, class, found_arr, potential_ns, lame_ns; + int oldn, naddr, class, found_arr, potential_ns; time_t curtime; int found_auth6; ns_debug(ns_log_default, 3, "nslookup(nsp=%p, qp=%p, \"%s\", d=%d)", nsp, qp, syslogdname, qp->q_distance); - lame_ns = potential_ns = 0; + potential_ns = 0; naddr = n = qp->q_naddr; curtime = (u_long) tt.tv_sec; while ((nsdp = *nsp++) != NULL && n < NSMAX) { class = nsdp->d_class; dname = (char *)nsdp->d_data; ns_debug(ns_log_default, 3, "nslookup: NS \"%s\" c=%d t=%d (flags 0x%lu)", dname, class, nsdp->d_type, (u_long)nsdp->d_flags); /* don't put in servers we have tried */ for (i = 0; i < qp->q_nusedns; i++) { if (qp->q_usedns[i] == nsdp) { ns_debug(ns_log_default, 2, "skipping used NS w/name %s", nsdp->d_data); goto skipserver; } } - /* skip lame servers */ - if ((nsdp->d_flags & DB_F_LAME) != 0) { - time_t when; - when = db_lame_find(qp->q_domain, nsdp); - if (when != 0 && when > tt.tv_sec) { - ns_debug(ns_log_default, 3, - "skipping lame NS"); - lame_ns++; - goto skipserver; - } - } - found_arr = 0; found_auth6 = 0; tmphtp = ((nsdp->d_flags & DB_F_HINT) ?fcachetab :hashtab); np = nlookup(dname, &tmphtp, &fname, 0); if (np == NULL) { ns_debug(ns_log_default, 3, "%s: not found %s %p", dname, fname, np); goto need_sysquery; } if (fname != dname) goto need_sysquery; oldn = n; /* look for name server addresses */ (void)delete_stale(np); for (dp = np->n_data; dp != NULL; dp = dp->d_next) { struct in_addr nsa; if (dp->d_type == T_CNAME && dp->d_class == class) { static const char *complaint = "NS points to CNAME"; if (dp->d_rcode) continue; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); goto skipserver; } if (dp->d_rcode == NXDOMAIN && dp->d_class == class) goto skipserver; if (dp->d_class == class && (dp->d_type == T_AAAA || dp->d_type == ns_t_a6) && (zones[dp->d_zone].z_type == z_master || zones[dp->d_zone].z_type == z_slave)) { found_auth6++; continue; } if (dp->d_type != T_A || dp->d_class != class) continue; if (dp->d_rcode) { /* Negative caching element. */ goto skipserver; } if (ina_hlong(ina_get(dp->d_data)) == INADDR_ANY) { static const char *complaint = "Bogus (0.0.0.0) A RR"; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); continue; } #ifdef INADDR_LOOPBACK if (ina_hlong(ina_get(dp->d_data))==INADDR_LOOPBACK) { static const char *complaint = "Bogus LOOPBACK A RR"; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); continue; } #endif #ifdef INADDR_BROADCAST if (ina_hlong(ina_get(dp->d_data))==INADDR_BROADCAST){ static const char *complaint = "Bogus BROADCAST A RR"; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); continue; } #endif #ifdef IN_MULTICAST if (IN_MULTICAST(ina_hlong(ina_get(dp->d_data)))) { static const char *complaint = "Bogus MULTICAST A RR"; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); continue; } #endif /* * Don't use records that may become invalid to * reference later when we do the rtt computation. * Never delete our safety-belt information! */ if ((dp->d_zone == DB_Z_CACHE) && (dp->d_ttl < (u_int32_t)curtime) && !(dp->d_flags & DB_F_HINT) ) { ns_debug(ns_log_default, 1, "nslookup: stale '%s'", NAME(*np)); n = oldn; found_arr = 0; goto need_sysquery; } found_arr++; nsa = ina_get(dp->d_data); /* don't put in duplicates */ qs = qp->q_addr; for (i = 0; i < (u_int)n; i++, qs++) if (ina_equal(qs->ns_addr.sin_addr, nsa)) goto skipaddr; qs->ns_addr.sin_family = AF_INET; qs->ns_addr.sin_port = ns_port; qs->ns_addr.sin_addr = nsa; qp->q_keys[n] = NULL; qs->ns = nsdp; qs->nsdata = dp; qs->forwarder = 0; qs->noedns = dp->d_noedns; if (!qs->noedns) { server_info si = find_server(nsa); if (si && (si->flags & SERVER_INFO_EDNS) == 0) qs->noedns = 1; } + qs->lame = 0; + if ((nsdp->d_flags & DB_F_LAME) != 0) { + time_t when; + when = db_lame_find(qp->q_domain, nsdp); + if (when != 0 && when > tt.tv_sec) + qs->lame = 1; + } qs->nretry = 0; /* * If this A RR has no RTT, initialize its RTT to a * small random value. */ if (dp->d_nstime == 0) dp->d_nstime = 1 + (int)(25.0*rand()/(RAND_MAX + 1.0)); /* * if we are being asked to fwd a query whose * nameserver list includes our own name/address(es), * then we have detected a lame delegation and rather * than melt down the network and hose down the other * servers (who will hose us in return), we'll return * -1 here which will cause SERVFAIL to be sent to * the client's resolver which will hopefully then * shut up. * * (originally done in nsContainsUs by vix@dec mar92; * moved into nslookup by apb@und jan1993) * * try to limp along instead of denying service * gdonl mar96 */ if (aIsUs(nsa)) { static const char *complaint = "contains our address"; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); continue; } /* * If we want to forward to a host that asked us * this question then either we or they are sick * (unless they asked from some port other than * their nameserver port). (apb@und jan1993) * * try to limp along instead of denying service * gdonl mar96 */ if (memcmp(&qp->q_from, &qs->ns_addr, sizeof(qp->q_from)) == 0) { static const char *complaint = "forwarding loop"; nslookupComplain(sysloginfo, syslogdname, complaint, dname, dp, nsdp); continue; } #ifdef BOGUSNS /* * Don't forward queries to bogus servers. Note * that this is unlike the previous tests, which * are fatal to the query. Here we just skip the * server, which is only fatal if it's the last * server. Note also that we antialias here -- all * A RR's of a server are considered the same server, * and if any of them is bogus we skip the whole * server. Those of you using multiple A RR's to * load-balance your servers will (rightfully) lose * here. But (unfortunately) only if they are bogus. */ if (ip_match_address(bogus_nameservers, nsa) > 0) goto skipserver; #endif if (server_options->blackhole_acl != NULL && ip_match_address(server_options->blackhole_acl, nsa) == 1) continue; n++; if (n >= NSMAX) break; skipaddr: (void)NULL; } ns_debug(ns_log_default, 8, "nslookup: %d ns addrs", n); need_sysquery: if (found_arr == 0 && found_auth6 == 0) { potential_ns++; if (qp->q_distance < NS_MAX_DISTANCE) (void) sysquery(dname, class, T_A, NULL, NULL, 0, ns_port, QUERY, qp->q_distance + 1); } skipserver: (void)NULL; } ns_debug(ns_log_default, 3, "nslookup: %d ns addrs total", n); qp->q_naddr = n; if (n == 0 && potential_ns == 0 && !NS_ZFWDTAB(qp->q_fzone)) { static const char *complaint = "No possible A RRs"; - if (lame_ns != 0) - complaint = "All possible A RR's lame"; if (sysloginfo && syslogdname && !haveComplained((u_long)syslogdname, (u_long)complaint)) { ns_info(ns_log_default, "%s: query(%s) %s", sysloginfo, syslogdname, complaint); } - return ((lame_ns == 0) ? -1 : -2); + return (-1); } /* Update the refcounts before the sort. */ for (i = naddr; i < (u_int)n; i++) { DRCNTINC(qp->q_addr[i].nsdata); DRCNTINC(qp->q_addr[i].ns); } /* Just sort the NS RR's we added, since the forwarders may * be ahead of us (naddr > 0) */ if (n > naddr) { qsort((char *)(qp->q_addr+naddr), n-naddr, sizeof(struct qserv), (int (*)(const void *, const void *))qcomp); } return (n - naddr); } /* * qcomp - compare two NS addresses, and return a negative, zero, or * positive value depending on whether the first NS address is * "better than", "equally good as", or "inferior to" the second * NS address. * * How "goodness" is defined (for the purposes of this routine): * - If the estimated round trip times differ by an amount deemed significant * then the one with the smaller estimate is preferred; else * - If we can determine which one is topologically closer then the * closer one is preferred; else * - The one with the smaller estimated round trip time is preferred * (zero is returned if the two estimates are identical). * * How "topological closeness" is defined (for the purposes of this routine): * Ideally, named could consult some magic map of the Internet and * determine the length of the path to an arbitrary destination. Sadly, * no such magic map exists. However, named does have a little bit of * topological information in the form of the sortlist (which includes * the directly connected subnet(s), the directly connected net(s), and * any additional nets that the administrator has added using the "sortlist" * directive in the bootfile. Thus, if only one of the addresses matches * something in the sortlist then it is considered to be topologically * closer. If both match, but match different entries in the sortlist, * then the one that matches the entry closer to the beginning of the * sorlist is considered to be topologically closer. In all other cases, * topological closeness is ignored because it's either indeterminate or * equal. * * How times are compared: * Both times are rounded to the closest multiple of the NOISE constant * defined below and then compared. If the rounded values are equal * then the difference in the times is deemed insignificant. Rounding * is used instead of merely taking the absolute value of the difference * because doing the latter would make the ordering defined by this * routine be incomplete in the mathematical sense (e.g. A > B and * B > C would not imply A > C). The mathematics are important in * practice to avoid core dumps in qsort(). * * XXX: this doesn't solve the European root nameserver problem very well. * XXX: we should detect and mark as inferior nameservers that give bogus * answers * * (this was originally vixie's stuff but almquist fixed fatal bugs in it * and wrote the above documentation) */ /* * RTT delta deemed to be significant, in milliseconds. With the current * definition of RTTROUND it must be a power of 2. */ #define NOISE 64 #define RTTROUND(rtt) (((rtt) + (NOISE >> 1)) & ~(NOISE - 1)) int qcomp(struct qserv *qs1, struct qserv *qs2) { u_int rtt1, rtt2, rttr1, rttr2; + /* sort lame servers to last */ + if (qs1->lame != qs2->lame) + return (qs1->lame - qs2->lame); + + /* sort by rtt */ if (qs1->nsdata == NULL) { rtt1 = 0; rttr1 = 0; } else { rtt1 = qs1->nsdata->d_nstime; rttr1 = RTTROUND(rtt1); } if (qs2->nsdata == NULL) { rtt2 = 0; rttr2 = 0; } else { rtt2 = qs2->nsdata->d_nstime; rttr2 = RTTROUND(rtt2); } #ifdef DEBUG if (debug >= 10) { char t[sizeof "255.255.255.255"]; strcpy(t, inet_ntoa(qs1->ns_addr.sin_addr)); ns_debug(ns_log_default, 10, "qcomp(%s, %s) %u (%u) - %u (%u) = %u", t, inet_ntoa(qs2->ns_addr.sin_addr), rtt1, rttr1, rtt2, rttr2, rtt1 - rtt2); } #endif if (rttr1 == rttr2) { int pos1, pos2, pdiff; pos1 = distance_of_address(server_options->topology, qs1->ns_addr.sin_addr); pos2 = distance_of_address(server_options->topology, qs2->ns_addr.sin_addr); pdiff = pos1 - pos2; ns_debug(ns_log_default, 10, "\tpos1=%d, pos2=%d", pos1, pos2); if (pdiff != 0) return (pdiff); } return (rtt1 - rtt2); } #undef RTTROUND /* * Arrange that forwarded query (qp) is retried after t seconds. * Query list will be sorted after z_time is updated. */ void schedretry(struct qinfo *qp, time_t t) { struct qinfo *qp1, *qp2; ns_debug(ns_log_default, 4, "schedretry(%p, %ld sec)", qp, (long)t); if (qp->q_time) ns_debug(ns_log_default, 4, "WARNING: schedretry(%#lx, %ld) q_time already %ld", (u_long)qp, (long)t, (long)qp->q_time); gettime(&tt); t += (u_long) tt.tv_sec; qp->q_time = t; if ((qp1 = retryqp) == NULL) { retryqp = qp; qp->q_next = NULL; goto done; } if (t < qp1->q_time) { qp->q_next = qp1; retryqp = qp; goto done; } while ((qp2 = qp1->q_next) != NULL && qp2->q_time < t) qp1 = qp2; qp1->q_next = qp; qp->q_next = qp2; done: reset_retrytimer(); } /* * Unsched is called to remove a forwarded query entry. */ void unsched(struct qinfo *qp) { struct qinfo *np; ns_debug(ns_log_default, 3, "unsched(%#lx, %d)", (u_long)qp, ntohs(qp->q_id)); if (retryqp == qp) { retryqp = qp->q_next; } else { for (np = retryqp; np->q_next != NULL; np = np->q_next) { if (np->q_next != qp) continue; np->q_next = qp->q_next; /* dequeue */ break; } } qp->q_next = NULL; /* sanity check */ qp->q_time = 0; reset_retrytimer(); } void reset_retrytimer() { static evTimerID id; if (retry_timer_set) { (void) evClearTimer(ev, id); retry_timer_set = 0; } if (retryqp) { evSetTimer(ev, retrytimer, NULL, evConsTime(retryqp->q_time, 0), evConsTime(0, 0), &id); retry_timer_set = 1; } else memset(&id, 0, sizeof id); } void retrytimer(evContext ctx, void *uap, struct timespec due, struct timespec ival) { UNUSED(ctx); UNUSED(uap); UNUSED(due); UNUSED(ival); retry_timer_set = 0; retry(retryqp, 0); } /* * Retry is called to retransmit query 'qp'. */ void retry(struct qinfo *qp, int samehost) { int n, has_tsig, oldqlen = 0; HEADER *hp; struct sockaddr_in *nsa; int sendto_errno = 0; u_char *oldqbuf = NULL; u_char *smsg = NULL; int smsglen, smsgsize = 0, siglen; u_char sig[TSIG_SIG_SIZE]; DST_KEY *key; ns_debug(ns_log_default, 3, "retry(%#lx) id=%d", (u_long)qp, ntohs(qp->q_id)); if (qp->q_msg == NULL) { qremove(qp); return; } if (qp->q_expire < tt.tv_sec) { ns_debug(ns_log_default, 1, "retry(%#lx): expired @ %lu (%d secs before now (%lu))", (u_long)qp, (u_long)qp->q_expire, (int)(tt.tv_sec - qp->q_expire), (u_long)tt.tv_sec); goto fail; } /* Try next address. */ n = qp->q_curaddr; if (samehost) { qp->q_addr[n].nretry++; if (qp->q_addr[n].nretry < MAXRETRY) goto found; qp->q_addr[n].nretry--; } if (qp->q_naddr > 0) { qp->q_addr[n].noedns = 1; ++qp->q_addr[n].nretry; + /* + * Look for a non-lame server. + */ + do { + if (++n >= (int)qp->q_naddr) + n = 0; + if ((qp->q_flags & Q_ZSERIAL) != 0 && + qp->q_addr[n].serial != 0) + continue; + if (qp->q_addr[n].lame) + continue; + if (qp->q_addr[n].nretry < MAXRETRY) + goto found; + } while (n != qp->q_curaddr); + /* + * Look for any server including lame servers. + */ do { if (++n >= (int)qp->q_naddr) n = 0; if ((qp->q_flags & Q_ZSERIAL) != 0 && qp->q_addr[n].serial != 0) continue; if (qp->q_addr[n].nretry < MAXRETRY) goto found; } while (n != qp->q_curaddr); if ((qp->q_flags & Q_ZSERIAL) != 0) { qremove(qp); return; } } fail: /* * Give up. Can't reach destination. */ hp = (HEADER *)(qp->q_cmsg ? qp->q_cmsg : qp->q_msg); if ((qp->q_flags & Q_PRIMING) != 0) { /* Can't give up priming */ if (qp->q_expire < tt.tv_sec) { /* * The query has expired. Reset it and retry from * the beginning. */ hp->rcode = NOERROR; hp->qr = hp->aa = 0; for (n = 0; n < (int)qp->q_naddr; n++) qp->q_addr[n].nretry = 0; n = 0; qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2; goto found; } /* * The query hasn't expired yet; it probably ran out * of servers or forwarders. Wait up to 60 seconds * past the expire time. */ unsched(qp); schedretry(qp, (time_t)(qp->q_expire - tt.tv_sec + 60)); return; } ns_debug(ns_log_default, 5, "give up"); if ((qp->q_flags & Q_SYSTEM) == 0) { n = (qp->q_cmsg ? qp->q_cmsglen : qp->q_msglen); hp->id = qp->q_id; hp->qr = 1; hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); hp->rd = 1; hp->rcode = SERVFAIL; #ifdef DEBUG if (debug >= 10) res_pquery(&res, qp->q_msg, n, log_get_stream(packet_channel)); #endif if (send_msg((u_char *)hp, n, qp)) { ns_debug(ns_log_default, 1, "gave up retry(%#lx) nsid=%d id=%d", (u_long)qp, ntohs(qp->q_nsid), ntohs(qp->q_id)); } if (NS_OPTION_P(OPTION_HOSTSTATS)) nameserIncr(qp->q_from.sin_addr, nssSentFail); } qremove(qp); return; found: if (qp->q_addr[n].nretry == 0) qp->q_addr[n].stime = tt; qp->q_curaddr = n; hp = (HEADER *)qp->q_msg; hp->rd = (qp->q_addr[n].forwarder ? 1 : 0); nsa = Q_NEXTADDR(qp, n); ns_debug(ns_log_default, 1, "%s(addr=%d n=%d) -> [%s].%d ds=%d nsid=%d id=%d %dms", (qp->q_addr[n].forwarder ? "reforw" : "resend"), n, qp->q_addr[n].nretry, inet_ntoa(nsa->sin_addr), ntohs(nsa->sin_port), ds, ntohs(qp->q_nsid), ntohs(qp->q_id), (qp->q_addr[n].nsdata != 0) ? qp->q_addr[n].nsdata->d_nstime : (-1)); #ifdef DEBUG if (debug >= 10) res_pquery(&res, qp->q_msg, qp->q_msglen, log_get_stream(packet_channel)); #endif key = qp->q_keys[n]; if (key == NULL) key = qp->q_keys[n] = tsig_key_from_addr(nsa->sin_addr); if (key != NULL || !qp->q_addr[n].noedns) { smsgsize = qp->q_msglen + TSIG_BUF_SIZE + 11; smsg = memget(smsgsize); smsglen = qp->q_msglen; siglen = sizeof(sig); memcpy(smsg, qp->q_msg, qp->q_msglen); } if (!qp->q_addr[n].noedns) smsglen += ns_add_opt(smsg, smsg + smsglen, smsgsize, 0, 0, - EDNS_MESSAGE_SZ, 0, NULL, 0); + server_options->edns_udp_size, + 0, NULL, 0); if (key != NULL) { n = ns_sign(smsg, &smsglen, smsgsize, NOERROR, key, NULL, 0, sig, &siglen, 0); if (n == 0) { has_tsig = 1; free_tsig(qp->q_nstsig); qp->q_nstsig = new_tsig(key, sig, siglen); } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; INSIST(0); } } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; } if (smsg != NULL) { oldqbuf = qp->q_msg; oldqlen = qp->q_msglen; qp->q_msglen = smsglen; qp->q_msg = smsg; } if (qp->q_flags & Q_USEVC) { if (tcp_send(qp) != NOERROR) ns_debug(ns_log_default, 3, "error resending tcp msg: %s", strerror(errno)); } else if (sendto(ds, (char*)qp->q_msg, qp->q_msglen, 0, (struct sockaddr *)nsa, sizeof(struct sockaddr_in)) < 0) { sendto_errno = errno; ns_debug(ns_log_default, 3, "error resending msg: %s", strerror(errno)); } if (smsgsize != 0) { memput(smsg, smsgsize); qp->q_msg = oldqbuf; qp->q_msglen = oldqlen; } hp->rd = 1; /* leave set to 1 for dup detection */ nameserIncr(nsa->sin_addr, nssSentDupQ); unsched(qp); switch (sendto_errno) { case ENETDOWN: case ENETUNREACH: case EHOSTDOWN: case EHOSTUNREACH: schedretry(qp, (time_t) 0); return; } schedretry(qp, retrytime(qp)); } /* * Compute retry time for the next server for a query. * Use a minimum time of RETRYBASE (4 sec.) or twice the estimated * service time; * back off exponentially on retries, but place a 45-sec. * ceiling on retry times for now. (This is because we don't hold a reference * on servers or their addresses, and we have to finish before they time out.) */ time_t retrytime(struct qinfo *qp) { time_t t, u, v; struct qserv *ns = &qp->q_addr[qp->q_curaddr]; if (ns->nsdata != NULL) t = (time_t) MAX(RETRYBASE, 2 * ns->nsdata->d_nstime / 1000); else t = (time_t) RETRYBASE; u = t << ns->nretry; v = MIN(u, RETRY_TIMEOUT); /* max. retry timeout for now */ ns_debug(ns_log_default, 3, "retrytime: nstime%ldms t%ld nretry%ld u%ld : v%ld", ns->nsdata ? (long)(ns->nsdata->d_nstime / 1000) : (long)-1, (long)t, (long)ns->nretry, (long)u, (long)v); return (v); } void qflush() { while (nsqhead) qremove(nsqhead); nsqhead = NULL; priming = 0; } void qremove(struct qinfo *qp) { ns_debug(ns_log_default, 3, "qremove(%#lx)", (u_long)qp); if ((qp->q_flags & Q_ZSERIAL) != 0) qserial_answer(qp); unsched(qp); ns_freeqry(qp); } struct qinfo * qfindid(u_int16_t id) { struct qinfo *qp; for (qp = nsqhead; qp != NULL; qp = qp->q_link) if (qp->q_nsid == id) break; ns_debug(ns_log_default, 3, "qfindid(%d) -> %#lx", ntohs(id), (u_long)qp); return (qp); } struct qinfo * qnew(const char *name, int class, int type, int forward) { struct qinfo *qp; const char *s; int escape = 0; qp = (struct qinfo *)memget(sizeof *qp); if (qp == NULL) ns_panic(ns_log_default, 1, "qnew: memget failed"); memset(qp, 0, sizeof *qp); ns_debug(ns_log_default, 5, "qnew(%#lx)", (u_long)qp); #ifdef BIND_NOTIFY qp->q_notifyzone = DB_Z_CACHE; #endif qp->q_link = nsqhead; nsqhead = qp; qp->q_name = savestr(name, 1); qp->q_class = (u_int16_t)class; qp->q_type = (u_int16_t)type; qp->q_flags = 0; s = name; qp->q_fzone = NULL; for (;forward;) { /* find forwarding zone, if any */ if ((qp->q_fzone = find_zone(s, class)) != NULL && (qp->q_fzone->z_flags & Z_FORWARD_SET) != 0) break; qp->q_fzone = NULL; if (*s == '\0') break; while (*s != '\0' && (escape || *s != '.')) { escape = escape ? 0 : (*s == '\\'); s++; } if (*s != '\0') s++; } return (qp); } void ns_freeqns(struct qinfo *qp) { unsigned int i; for (i = 0 ; i < qp->q_naddr ; i++) { if (qp->q_addr[i].ns != NULL) db_detach(&qp->q_addr[i].ns); if (qp->q_addr[i].nsdata != NULL) db_detach(&qp->q_addr[i].nsdata); } } void ns_freeqry(struct qinfo *qp) { struct qinfo *np; ns_debug(ns_log_default, 3, "ns_freeqry(%#lx)", (u_long)qp); if (qp->q_next) ns_debug(ns_log_default, 1, "WARNING: ns_freeqry of linked ptr %#lx", (u_long)qp); if (qp->q_msg != NULL) memput(qp->q_msg, qp->q_msgsize); if (qp->q_cmsg != NULL) memput(qp->q_cmsg, qp->q_cmsgsize); if (qp->q_domain != NULL) qp->q_domain = freestr(qp->q_domain); if (qp->q_name != NULL) qp->q_name = freestr(qp->q_name); if (qp->q_tsig != NULL) memput(qp->q_tsig, sizeof(struct tsig_record)); if (qp->q_nstsig != NULL) memput(qp->q_nstsig, sizeof(struct tsig_record)); ns_freeqns(qp); if (nsqhead == qp) nsqhead = qp->q_link; else { for(np = nsqhead; np->q_link != NULL; np = np->q_link) { if (np->q_link != qp) continue; np->q_link = qp->q_link; /* dequeue */ break; } } memput(qp, sizeof *qp); } void nsfwdadd(struct qinfo *qp, struct fwdinfo *fwd) { int i, n; struct qserv *qs; n = qp->q_naddr; while (fwd != NULL && n < NSMAX) { qs = qp->q_addr; for (i = 0; i < n; i++, qs++) if (ina_equal(qs->ns_addr.sin_addr, fwd->fwddata->fwdaddr.sin_addr)) goto nextfwd; qs->ns_addr = fwd->fwddata->fwdaddr; qs->ns = fwd->fwddata->ns; qs->nsdata = fwd->fwddata->nsdata; qs->forwarder = 1; qs->noedns = fwd->fwddata->nsdata->d_noedns; if (!qs->noedns) { server_info si = find_server(qs->ns_addr.sin_addr); if (si && (si->flags & SERVER_INFO_EDNS) == 0) qs->noedns = 1; } qs->nretry = 0; n++; nextfwd: fwd = fwd->next; } /* Update the refcounts before the sort. */ for (i = qp->q_naddr; i < n; i++) { DRCNTINC(qp->q_addr[i].nsdata); DRCNTINC(qp->q_addr[i].ns); } qp->q_naddr = n; if (n > 1) { qsort((char *)qp->q_addr, n, sizeof(struct qserv), (int (*)(const void *, const void *))qcomp); } } diff --git a/contrib/bind/bin/named/ns_func.h b/contrib/bind/bin/named/ns_func.h index e035d93ba8cd..8b77283776bc 100644 --- a/contrib/bind/bin/named/ns_func.h +++ b/contrib/bind/bin/named/ns_func.h @@ -1,530 +1,528 @@ /* * Copyright (c) 1985, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* ns_func.h - declarations for ns_*.c's externally visible functions * - * $Id: ns_func.h,v 8.117 2002/04/25 05:27:07 marka Exp $ + * $Id: ns_func.h,v 8.120.8.1 2003/06/02 05:19:56 marka Exp $ */ /* ++from ns_glue.c++ */ struct in_addr ina_get(const u_char *data); const char * sin_ntoa(struct sockaddr_in); int ns_wouldlog(int category, int level); void ns_debug(int, int, const char *, ...) ISC_FORMAT_PRINTF(3, 4); void ns_info(int, const char *, ...) ISC_FORMAT_PRINTF(2, 3); void ns_notice(int, const char *, ...) ISC_FORMAT_PRINTF(2, 3); void ns_warning(int, const char *, ...) ISC_FORMAT_PRINTF(2, 3); void ns_error(int, const char *, ...) ISC_FORMAT_PRINTF(2, 3); void ns_critical(int, const char *, ...) ISC_FORMAT_PRINTF(2, 3); void ns_panic(int, int, const char *, ...) ISC_FORMAT_PRINTF(3, 4); void ns_assertion_failed(const char *file, int line, assertion_type type, const char *cond, int print_errno); void panic(const char *, const void *); void gettime(struct timeval *); int nlabels(const char *); int my_close(int); int my_fclose(FILE *); void * __freestr(char *); char * __newstr(size_t, int); char * __savestr(const char *, int); const char * checked_ctime(const time_t *t); const char * ctimel(long); void * __freestr_record(char *, const char *, int); char * __newstr_record(size_t, int, const char *, int); char * __savestr_record(const char *, int, const char *, int); u_char * ina_put(struct in_addr ina, u_char *data); u_char * savebuf(const u_char *, size_t, int); -void dprintf(int level, const char *format, ...) ISC_FORMAT_PRINTF(2, 3); #ifdef DEBUG_STRINGS char * debug_newstr(size_t, int, const char *, int); char * debug_savestr(const char *, int, const char *, int); void * debug_freestr(char *, const char *, int); #define newstr(l, n) debug_newstr((l), (n), __FILE__, __LINE__) #define savestr(s, n) debug_savestr((s), (n), __FILE__, __LINE__) #define freestr(s) debug_freestr((s), __FILE__, __LINE__) #else #ifdef RECORD_STRINGS #define newstr(l, n) __newstr_record((l), (n), __FILE__, __LINE__) #define savestr(s, n) __savestr_record((s), (n), __FILE__, __LINE__) #define freestr(s) __freestr_record((s), __FILE__, __LINE__) #else #define newstr(l, n) __newstr((l), (n)) #define savestr(s, n) __savestr((s), (n)) #define freestr(s) __freestr((s)) #endif #endif /* DEBUG_STRINGS */ /* --from ns_glue.c-- */ /* ++from ns_notify.c++ */ #ifdef BIND_NOTIFY void ns_notify(const char *, ns_class, ns_type); void notify_afterload(void); void ns_unnotify(void); void ns_stopnotify(const char *, ns_class); #endif /* --from ns_notify.c-- */ /* ++from ns_resp.c++ */ void ns_resp(u_char *, int, struct sockaddr_in, struct qstream *); void prime_cache(void); void delete_all(struct namebuf *, int, int); int delete_stale(struct namebuf *); struct qinfo * sysquery(const char *, int, int, struct in_addr *, struct dst_key **keys, int, u_int16_t, int, int); int doupdate(u_char *, u_char *, struct databuf **, int, int, int, u_int, struct sockaddr_in); int send_msg(u_char *, int, struct qinfo *); int findns(struct namebuf **, int, struct databuf **, int *, int); int finddata(struct namebuf *, int, int, HEADER *, - char **, int *, int *); + char **, int *, int *, int, int); int add_data(struct namebuf *, struct databuf **, u_char *, int, int *); int trunc_adjust(u_char *, int, int); /* --from ns_resp.c-- */ /* ++from ns_req.c++ */ int ns_get_opt(u_char *msg, u_char *eom, u_int8_t *versionp, u_int16_t *rcodep, u_int16_t *flagp, u_int16_t *bufsizep, u_char **optionsp, size_t *optsizep); int ns_add_opt(u_char *msg, u_char *cp, size_t buflen, u_int8_t version, u_int16_t rcode, u_int16_t size, u_int16_t flags, u_char *options, size_t optlen); void ns_req(u_char *, int, int, struct qstream *, struct sockaddr_in, int); void free_addinfo(void); void free_nsp(struct databuf **); int stale(struct databuf *); int make_rr(const char *, struct databuf *, u_char *, int, int, u_char **, u_char **, int); int doaddinfo(HEADER *, u_char *, int); int doaddauth(HEADER *, u_char *, int, struct namebuf *, struct databuf *); #ifdef BIND_NOTIFY int findZonePri(const struct zoneinfo *, const struct sockaddr_in); #endif int drop_port(u_int16_t); /* --from ns_req.c-- */ /* ++from ns_xfr.c++ */ void ns_xfr(struct qstream *qsp, struct namebuf *znp, int zone, int class, int type, int id, int opcode, u_int32_t serial_ixfr, struct tsig_record *in_tsig); void ns_stopxfrs(struct zoneinfo *); void ns_freexfr(struct qstream *); void sx_newmsg(struct qstream *qsp); void sx_sendlev(struct qstream *qsp); void sx_sendsoa(struct qstream *qsp); /* --from ns_xfr.c-- */ /* ++from ns_ctl.c++ */ void ns_ctl_initialize(void); void ns_ctl_shutdown(void); void ns_ctl_defaults(controls *); void ns_ctl_add(controls *, control); control ns_ctl_new_inet(struct in_addr, u_int, ip_match_list); #ifndef NO_SOCKADDR_UN control ns_ctl_new_unix(const char *, mode_t, uid_t, gid_t); #endif void ns_ctl_install(controls *); /* --from ns_ctl.c-- */ /* ++from ns_ixfr.c++ */ void sx_send_ixfr(struct qstream *); int ixfr_log_maint(struct zoneinfo *); /* --from ns_ixfr.c-- */ /* ++from ns_forw.c++ */ time_t retrytime(struct qinfo *); int ns_forw(struct databuf *nsp[], u_char *msg, int msglen, struct sockaddr_in from, struct qstream *qsp, int dfd, struct qinfo **qpp, const char *dname, int class, int type, struct namebuf *np, int use_tcp, struct tsig_record *in_tsig); int haveComplained(u_long, u_long); int nslookup(struct databuf *nsp[], struct qinfo *qp, const char *syslogdname, const char *sysloginfo); int qcomp(struct qserv *, struct qserv *); void schedretry(struct qinfo *, time_t); void unsched(struct qinfo *); void reset_retrytimer(void); void retrytimer(evContext ctx, void *uap, struct timespec due, struct timespec ival); void retry(struct qinfo *, int); void qflush(void); void qremove(struct qinfo *); void ns_freeqns(struct qinfo *); void ns_freeqry(struct qinfo *); void freeComplaints(void); void nsfwdadd(struct qinfo *, struct fwdinfo *); struct qinfo * qfindid(u_int16_t); struct qinfo * qnew(const char *, int, int, int); /* --from ns_forw.c-- */ /* ++from ns_main.c++ */ void toggle_qrylog(void); struct in_addr net_mask(struct in_addr); void sq_remove(struct qstream *); void sq_flushw(struct qstream *); void sq_flush(struct qstream *allbut); void dq_remove_gen(time_t gen); void dq_remove_all(void); void sq_done(struct qstream *); void ns_setproctitle(char *, int); void getnetconf(int); void nsid_init(void); void ns_setoption(int option); void writestream(struct qstream *, const u_char *, int); void ns_need_unsafe(enum need); void ns_need(enum need); void opensocket_f(void); void nsid_hash(u_char *, size_t); u_int16_t nsid_next(void); int sq_openw(struct qstream *, int); int sq_writeh(struct qstream *, sq_closure); int sq_write(struct qstream *, const u_char *, int); int tcp_send(struct qinfo *); int aIsUs(struct in_addr); /* --from ns_main.c-- */ /* ++from ns_maint.c++ */ void zone_maint(struct zoneinfo *); void sched_zone_maint(struct zoneinfo *); void ns_cleancache(evContext ctx, void *uap, struct timespec due, struct timespec inter); void clean_cache_from(char *dname, struct hashbuf *htp); void remove_zone(struct zoneinfo *, const char *); -void purge_zone(const char *, struct hashbuf *, int); +void purge_zone(struct zoneinfo *, struct hashbuf *); void loadxfer(void); void qserial_retrytime(struct zoneinfo *, time_t); void qserial_query(struct zoneinfo *); void qserial_answer(struct qinfo *); #ifdef DEBUG void printzoneinfo(int, int, int); #endif void endxfer(void); void tryxfer(void); void addxfer(struct zoneinfo *); void ns_zreload(void); void ns_reload(void); void ns_reconfig(void); void ns_noexpired(void); #if 0 int reload_all_unsafe(void); #endif int zonefile_changed_p(struct zoneinfo *); int reload_master(struct zoneinfo *); const char * deferred_reload_unsafe(struct zoneinfo *); struct namebuf * purge_node(struct hashbuf *htp, struct namebuf *np); int clean_cache(struct hashbuf *, int); void reapchild(void); const char * zoneTypeString(unsigned int); void ns_heartbeat(evContext ctx, void *uap, struct timespec, struct timespec); void make_new_zones(void); void free_zone(struct zoneinfo *); struct zoneinfo * find_auth_zone(const char *, ns_class); -int purge_nonglue(const char *dname, struct hashbuf *htp, - int class, int log); +int purge_nonglue(struct zoneinfo *, struct hashbuf *htp, + int log); /* --from ns_maint.c-- */ /* ++from ns_sort.c++ */ void sort_response(u_char *, u_char *, int, struct sockaddr_in *); /* --from ns_sort.c-- */ /* ++from ns_init.c++ */ void ns_refreshtime(struct zoneinfo *, time_t); void ns_retrytime(struct zoneinfo *, time_t); time_t ns_init(const char *); -void purgeandload(struct zoneinfo *zp); enum context ns_ptrcontext(const char *owner); enum context ns_ownercontext(int type, enum transport); int ns_nameok(const struct qinfo *qry, const char *name, int class, struct zoneinfo *zp, enum transport, enum context, const char *owner, struct in_addr source); int ns_wildcard(const char *name); void zoneinit(struct zoneinfo *); -void do_reload(const char *, int, int, int); +void do_reload(struct zoneinfo *, int); void ns_shutdown(void); /* --from ns_init.c-- */ /* ++from ns_ncache.c++ */ void cache_n_resp(u_char *, int, struct sockaddr_in, const char *, int, int); /* --from ns_ncache.c-- */ /* ++from ns_udp.c++ */ void ns_udp(void); /* --from ns_udp.c-- */ /* ++from ns_stats.c++ */ void ns_stats(void); void ns_stats_dumpandclear(void); void ns_freestats(void); void ns_logstats(evContext ctx, void *uap, struct timespec, struct timespec); void qtypeIncr(int qtype); struct nameser * nameserFind(struct in_addr addr, int flags); #define NS_F_INSERT 0x0001 #define nameserIncr(a,w) NS_INCRSTAT(a,w) /* XXX should change name. */ /* --from ns_stats.c-- */ /* ++from ns_update.c++ */ struct databuf * findzonesoa(struct zoneinfo *); void free_rrecp(ns_updque *, int rcode, struct sockaddr_in); int findzone(const char *, int, int, int *, int); u_char * findsoaserial(u_char *data); u_int32_t get_serial_unchecked(struct zoneinfo *zp); u_int32_t get_serial(struct zoneinfo *zp); void set_serial(struct zoneinfo *zp, u_int32_t serial); int schedule_soa_update(struct zoneinfo *, int); int schedule_dump(struct zoneinfo *); int incr_serial(struct zoneinfo *zp); int merge_logs(struct zoneinfo *zp, char *logname); int zonedump(struct zoneinfo *zp, int isixfr); void dynamic_about_to_exit(void); enum req_action req_update(HEADER *hp, u_char *cp, u_char *eom, u_char *msg, struct sockaddr_in from, struct tsig_record *in_tsig); void rdata_dump(struct databuf *dp, FILE *fp); /* --from ns_update.c-- */ /* ++from ns_config.c++ */ void add_to_rrset_order_list(rrset_order_list, rrset_order_element); const char * p_order(int); int set_zone_ixfr_file(zone_config, char *); int set_zone_master_port(zone_config, u_short); int set_zone_max_log_size_ixfr(zone_config, int); int set_zone_dialup(zone_config, int); int set_trusted_key(const char *, const int, const int, const int, const char *); int set_zone_ixfr_tmp(zone_config, char *); void free_zone_timerinfo(struct zoneinfo *); void free_zone_contents(struct zoneinfo *, int); struct zoneinfo * find_zone(const char *, int); zone_config begin_zone(char *, int); void end_zone(zone_config, int); int set_zone_type(zone_config, int); int set_zone_filename(zone_config, char *); int set_zone_checknames(zone_config, enum severity); #ifdef BIND_NOTIFY int set_zone_notify(zone_config, enum notify value); #endif int set_zone_maintain_ixfr_base(zone_config, int value); int set_zone_update_acl(zone_config, ip_match_list); int set_zone_query_acl(zone_config, ip_match_list); int set_zone_transfer_acl(zone_config, ip_match_list); int set_zone_transfer_source(zone_config, struct in_addr); int set_zone_pubkey(zone_config, const int, const int, const int, const char *); int set_zone_transfer_time_in(zone_config, long); int add_zone_master(zone_config, struct in_addr, struct dst_key *); #ifdef BIND_NOTIFY int add_zone_notify(zone_config, struct in_addr); #endif void set_zone_forward(zone_config); void add_zone_forwarder(zone_config, struct in_addr); void set_zone_boolean_option(zone_config, int, int); options new_options(void); void free_options(options); void free_rrset_order_list(rrset_order_list); void set_global_boolean_option(options, int, int); listen_info_list new_listen_info_list(void); void free_listen_info_list(listen_info_list); void add_listen_on(options, u_short, ip_match_list); FILE * write_open(char *filename); void update_pid_file(void); void set_options(options, int); void use_default_options(void); enum ordering lookup_ordering(const char *); rrset_order_list new_rrset_order_list(void); rrset_order_element new_rrset_order_element(int, int, char *, enum ordering); ip_match_list new_ip_match_list(void); void free_ip_match_list(ip_match_list); ip_match_element new_ip_match_pattern(struct in_addr, u_int); ip_match_element new_ip_match_mask(struct in_addr, struct in_addr); ip_match_element new_ip_match_indirect(ip_match_list); ip_match_element new_ip_match_key(struct dst_key *dst_key); ip_match_element new_ip_match_localhost(void); ip_match_element new_ip_match_localnets(void); void ip_match_negate(ip_match_element); void add_to_ip_match_list(ip_match_list, ip_match_element); void dprint_ip_match_list(int, ip_match_list, int, const char *, const char *); int ip_match_address(ip_match_list, struct in_addr); int ip_match_addr_or_key(ip_match_list, struct in_addr, struct dst_key *key); int ip_address_allowed(ip_match_list, struct in_addr); int ip_addr_or_key_allowed(ip_match_list iml, struct in_addr, struct dst_key *key); int ip_match_network(ip_match_list, struct in_addr, struct in_addr); int ip_match_key_name(ip_match_list iml, char *name); int distance_of_address(ip_match_list, struct in_addr); int ip_match_is_none(ip_match_list); #ifdef BIND_NOTIFY void free_also_notify(options); int add_global_also_notify(options, struct in_addr); #endif void add_global_forwarder(options, struct in_addr); void free_forwarders(struct fwdinfo *); server_info find_server(struct in_addr); server_config begin_server(struct in_addr); void end_server(server_config, int); void set_server_option(server_config, int, int); void set_server_transfers(server_config, int); void set_server_transfer_format(server_config, enum axfr_format); void add_server_key_info(server_config, struct dst_key *); struct dst_key *new_key_info(char *, char *, char *); void free_key_info(struct dst_key *); struct dst_key *find_key(char *name, char *algorithm); void dprint_key_info(struct dst_key *); key_info_list new_key_info_list(void); void free_key_info_list(key_info_list); void add_to_key_info_list(key_info_list, struct dst_key *); void dprint_key_info_list(key_info_list); log_config begin_logging(void); void add_log_channel(log_config, int, log_channel); void open_special_channels(void); void set_logging(log_config, int); void end_logging(log_config, int); void use_default_logging(void); void init_logging(void); void shutdown_logging(void); void init_configuration(void); void shutdown_configuration(void); time_t load_configuration(const char *); /* --from ns_config.c-- */ /* ++from parser.y++ */ ip_match_list lookup_acl(const char *); void define_acl(const char *, ip_match_list); struct dst_key *lookup_key(char *); void define_key(const char *, struct dst_key *); time_t parse_configuration(const char *); void parser_initialize(void); void parser_shutdown(void); /* --from parser.y-- */ /* ++from ns_signal.c++ */ void init_signals(void); void block_signals(void); void unblock_signals(void); /* --from ns_signal.c-- */ diff --git a/contrib/bind/bin/named/ns_glob.h b/contrib/bind/bin/named/ns_glob.h index 8f052f5d83a3..35fcb6eacff9 100644 --- a/contrib/bind/bin/named/ns_glob.h +++ b/contrib/bind/bin/named/ns_glob.h @@ -1,346 +1,347 @@ /* * from ns.h 4.33 (Berkeley) 8/23/90 - * $Id: ns_glob.h,v 8.58 2002/06/05 04:53:50 marka Exp $ + * $Id: ns_glob.h,v 8.59 2002/07/19 22:44:08 marka Exp $ */ /* * Copyright (c) 1986 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Global variables for the name server. */ /* original argv[] from main() */ DECL char **saved_argv; #ifdef DEBUG DECL int debug INIT(0); DECL int desired_debug INIT(0); #endif /* global event context */ DECL evContext ev; /* global resolver context. */ DECL struct __res_state res; /* list of open streams */ DECL struct qstream *streamq; /* often set to the current time */ DECL struct timeval tt; /* head of allocated queries */ DECL struct qinfo *nsqhead; /* datagram socket for sysquery() and ns_forw(). */ DECL int ds INIT(-1); /* event ID for reads of "ds". */ DECL evFileID ds_evID; #ifdef QRYLOG /* is query logging turned on? */ DECL int qrylog; #endif /*QRYLOG*/ /* port to which we send queries */ DECL u_int16_t ns_port; /* Source addr of our internal resolver. */ DECL struct sockaddr_in source_addr; /* INITs to . */ /* Used by ns_stats */ DECL time_t boottime; DECL time_t resettime; /* next query to retry */ DECL struct qinfo *retryqp; /* configuration file name */ DECL char *conffile; /* configuration file mtime */ DECL time_t confmtime; /* default debug output file */ DECL char *debugfile; /* zone information */ DECL struct zoneinfo *zones; /* number of zones allocated */ DECL int nzones; /* free list of unused zones[] elements. */ DECL LIST(struct zoneinfo) freezones; /* list of zones that have a reload pending. */ DECL LIST(struct zoneinfo) reloadingzones; /* set if we need a priming */ DECL int needs_prime_cache; /* is cache being primed */ DECL int priming; /* ptrs to dnames in msg for dn_comp */ DECL u_char *dnptrs[40]; /* end pointer for dnptrs */ DECL u_char **dnptrs_end INIT(dnptrs + sizeof dnptrs / sizeof(u_char*)); /* data about all forwarders */ DECL struct fwddata **fwddata; /* how many forwarders are there in fwddata? */ DECL int fwddata_count; /* number of names in addinfo */ DECL int addcount; /* name of cache file */ DECL const char *cache_file; #ifdef BIND_UPDATE DECL const char * LogSignature INIT(";BIND LOG V8\n"); DECL const char * DumpSignature INIT(";BIND DUMP V8\n"); DECL const char * DumpSuffix INIT(".dumptmp"); #endif DECL const char sendtoStr[] INIT("sendto"); DECL const char tcpsendStr[] INIT("tcp_send"); /* defined in version.c, can't use DECL/INIT */ extern char Version[]; extern char ShortVersion[]; /* If getnum() has an error, here will be the result. */ DECL int getnum_error INIT(0); enum context { domain_ctx, owner_ctx, mailname_ctx, hostname_ctx }; DECL const char *context_strings[] #ifdef MAIN_PROGRAM = { "domain", "owner", "mail", "host", NULL } #endif ; DECL const char *transport_strings[] #ifdef MAIN_PROGRAM = { "primary", "secondary", "response", NULL } #endif ; DECL const char *severity_strings[] #ifdef MAIN_PROGRAM = { "ignore", "warn", "fail", "not_set", NULL } #endif ; DECL struct in_addr inaddr_any; /* Inits to 0.0.0.0 */ DECL options server_options INIT(NULL); DECL server_info nameserver_info INIT(NULL); DECL key_info_list secretkey_info INIT(NULL); DECL ip_match_list bogus_nameservers INIT(NULL); DECL log_context log_ctx; DECL int log_ctx_valid INIT(0); DECL log_channel syslog_channel INIT(NULL); DECL log_channel debug_channel INIT(NULL); DECL log_channel stderr_channel INIT(NULL); DECL log_channel eventlib_channel INIT(NULL); DECL log_channel packet_channel INIT(NULL); DECL log_channel null_channel INIT(NULL); DECL ip_match_list local_addresses INIT(NULL); DECL ip_match_list local_networks INIT(NULL); /* are we running in no-fork mode? */ DECL int foreground INIT(0); DECL const struct ns_sym logging_constants[] #ifdef MAIN_PROGRAM = { { log_info, "info" }, { log_notice, "notice" }, { log_warning, "warning" }, { log_error, "error" }, { log_critical, "critical" }, { 0, NULL } } #endif ; DECL const struct ns_sym syslog_constants[] #ifdef MAIN_PROGRAM = { { LOG_KERN, "kern" }, { LOG_USER, "user" }, { LOG_MAIL, "mail" }, { LOG_DAEMON, "daemon" }, { LOG_AUTH, "auth" }, { LOG_SYSLOG, "syslog" }, { LOG_LPR, "lpr" }, #ifdef LOG_NEWS { LOG_NEWS, "news" }, #endif #ifdef LOG_UUCP { LOG_UUCP, "uucp" }, #endif #ifdef LOG_CRON { LOG_CRON, "cron" }, #endif #ifdef LOG_AUTHPRIV { LOG_AUTHPRIV, "authpriv" }, #endif #ifdef LOG_FTP { LOG_FTP, "ftp" }, #endif { LOG_LOCAL0, "local0"}, { LOG_LOCAL1, "local1"}, { LOG_LOCAL2, "local2"}, { LOG_LOCAL3, "local3"}, { LOG_LOCAL4, "local4"}, { LOG_LOCAL5, "local5"}, { LOG_LOCAL6, "local6"}, { LOG_LOCAL7, "local7"}, { 0, NULL } } #endif ; DECL const struct ns_sym category_constants[] #ifdef MAIN_PROGRAM = { { ns_log_default, "default" }, { ns_log_config, "config" }, { ns_log_parser, "parser" }, { ns_log_queries, "queries" }, { ns_log_lame_servers, "lame-servers" }, { ns_log_statistics, "statistics" }, { ns_log_panic, "panic" }, { ns_log_update, "update" }, { ns_log_ncache, "ncache" }, { ns_log_xfer_in, "xfer-in" }, { ns_log_xfer_out, "xfer-out" }, { ns_log_db, "db" }, { ns_log_eventlib, "eventlib" }, { ns_log_packet, "packet" }, #ifdef BIND_NOTIFY { ns_log_notify, "notify" }, #endif { ns_log_cname, "cname" }, { ns_log_security, "security" }, { ns_log_os, "os" }, { ns_log_insist, "insist" }, { ns_log_maint, "maintenance" }, { ns_log_load, "load" }, { ns_log_resp_checks, "response-checks" }, { ns_log_control, "control" }, + { ns_log_update_security, "update-security" }, { 0, NULL } } #endif ; DECL const char panic_msg_no_options[] INIT("no server_options in NS_OPTION_P"); DECL const char panic_msg_insist_failed[] INIT("%s:%d: insist '%s' failed: %s"); DECL const char panic_msg_bad_which[] INIT("%s:%d: INCRSTATS(%s): bad \"which\""); DECL u_long globalStats[nssLast]; DECL evTimerID clean_timer; DECL evTimerID interface_timer; DECL evTimerID stats_timer; DECL evTimerID heartbeat_timer; DECL int active_timers INIT(0); DECL uid_t user_id; DECL char * user_name INIT(NULL); DECL gid_t group_id; DECL char * group_name INIT(NULL); DECL char * chroot_dir INIT(NULL); DECL char * working_dir INIT(NULL); DECL int loading INIT(0); DECL int xfers_running INIT(0); DECL int xfers_deferred INIT(0); DECL int qserials_running INIT(0); DECL int initial_configuration INIT(1); diff --git a/contrib/bind/bin/named/ns_init.c b/contrib/bind/bin/named/ns_init.c index 773192b99ae4..a0fce64ad7d0 100644 --- a/contrib/bind/bin/named/ns_init.c +++ b/contrib/bind/bin/named/ns_init.c @@ -1,590 +1,593 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_init.c 4.38 (Berkeley) 3/21/91"; -static const char rcsid[] = "$Id: ns_init.c,v 8.76 2001/12/19 01:41:51 marka Exp $"; +static const char rcsid[] = "$Id: ns_init.c,v 8.77 2002/08/20 04:27:23 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" #ifdef DEBUG static void content_zone(int, int); #endif +static void purgeandload(struct zoneinfo *zp); /* * Set new refresh time for zone. Use a random number in the last half of * the refresh limit; we want it to be substantially correct while still * preventing slave synchronization. */ void ns_refreshtime(struct zoneinfo *zp, time_t timebase) { u_long refresh = (zp->z_refresh > 0) ? zp->z_refresh : INIT_REFRESH; time_t half = (refresh + 1) / 2; if (zp->z_flags & Z_NEEDREFRESH) { zp->z_flags &= ~Z_NEEDREFRESH; zp->z_time = timebase; } else zp->z_time = timebase + half + (rand() % half); } /* * Set new retry time for zone. */ void ns_retrytime(struct zoneinfo *zp, time_t timebase) { zp->z_flags &= ~Z_NEEDREFRESH; zp->z_time = timebase + zp->z_retry; } /* * Read configuration file and save it as internal state. */ time_t ns_init(const char *conffile) { struct zoneinfo *zp; static int loads = 0; /* number of times loaded */ time_t mtime; ns_debug(ns_log_config, 1, "ns_init(%s)", conffile); gettime(&tt); if (loads == 0) { /* Init zone data. */ zones = NULL; INIT_LIST(freezones); INIT_LIST(reloadingzones); nzones = 0; make_new_zones(); /* Init cache. */ zones[0].z_type = z_cache; zones[0].z_origin = savestr("", 1); /* Allocate cache hash table, formerly the root hash table. */ hashtab = savehash((struct hashbuf *)NULL); /* Allocate root-hints/file-cache hash table. */ fcachetab = savehash((struct hashbuf *)NULL); /* Init other misc stuff. */ dst_init(); init_configuration(); } else { /* Mark previous zones as not yet found in boot file. */ block_signals(); for (zp = &zones[1]; zp < &zones[nzones]; zp++) if (zp->z_type != z_nil) { zp->z_flags &= ~Z_FOUND; if (LINKED(zp, z_reloadlink)) UNLINK(reloadingzones, zp, z_reloadlink); } unblock_signals(); } #ifdef DEBUG if (debug >= 3) { ns_debug(ns_log_config, 3, "content of zones before loading"); content_zone(nzones - 1, 3); } #endif mtime = load_configuration(conffile); /* Erase all old zones that were not found. */ for (zp = &zones[0]; zp < &zones[nzones]; zp++) { if (zp->z_type == z_cache) continue; if (zp->z_type != z_nil && (zp->z_flags & Z_FOUND) == 0) remove_zone(zp, "removed"); } /* Reload parent zones of zones removed */ for (zp = &zones[0]; zp < &zones[nzones]; zp++) { if (zp->z_type == z_cache) continue; if (zp->z_type != z_nil && (zp->z_flags & Z_PARENT_RELOAD) != 0) { zp->z_flags &= ~Z_PARENT_RELOAD; purgeandload(zp); } } #ifdef DEBUG if (debug >= 2) { ns_debug(ns_log_config, 2, "content of zones after loading"); content_zone(nzones-1, 2); } #endif ns_debug(ns_log_config, 1, "exit ns_init()"); loads++; return (mtime); } void zoneinit(struct zoneinfo *zp) { struct stat sb; int result; /* * Try to load zone from backup file, * if one was specified and it exists. * If not, or if the data are out of date, * we will refresh the zone from a primary * immediately. */ if (zp->z_source == NULL) return; result = stat(zp->z_source, &sb); if (result != -1) { ns_stopxfrs(zp); - purge_zone(zp->z_origin, hashtab, zp->z_class); + purge_zone(zp, hashtab); } if (result == -1 || db_load(zp->z_source, zp->z_origin, zp, NULL, ISNOTIXFR)) { /* * Set zone to be refreshed immediately. */ zp->z_refresh = INIT_REFRESH; zp->z_retry = INIT_REFRESH; if ((zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING)) == 0) { zp->z_time = tt.tv_sec; sched_zone_maint(zp); } } else { zp->z_flags |= Z_AUTH; zp->z_flags &= ~(Z_NEED_RELOAD|Z_EXPIRED); ns_refreshtime(zp, tt.tv_sec); sched_zone_maint(zp); } } /* * Purge the zone and reload all parent zones. This needs to be done when * we unload a zone, since the child zone will have stomped the parent's * delegation to that child when it was first loaded. */ void -do_reload(const char *domain, int type, int class, int mark) { +do_reload(struct zoneinfo *ozp, int mark) { struct zoneinfo *zp; + const char *domain = ozp->z_origin; + int type = ozp->z_type; + int class = ozp->z_class; ns_debug(ns_log_config, 1, "do_reload: %s %d %d %d", *domain ? domain : ".", type, class, mark); /* * Check if the zone has changed type. If so, we might not need to * do any purging or parent reloading. * * If the new zone is a master zone, then it will have purged the * old data and loaded, so we don't need to do anything. * * If the new zone is a slave or stub zone and has successfully loaded, * then we don't need to do anything either. * * NOTE: we take care not to match ourselves. */ zp = find_zone(domain, class); if (zp != NULL && ((type != z_master && zp->z_type == z_master) || (type != z_slave && zp->z_type == z_slave && zp->z_serial != 0) || (type != z_stub && zp->z_type == z_stub && zp->z_serial != 0))) return; /* * Clean up any leftover data. */ ns_stopxfrs(zp); if (type == z_hint || (type == z_stub && *domain == 0)) - purge_zone(domain, fcachetab, class); + purge_zone(ozp, fcachetab); else - purge_zone(domain, hashtab, class); + purge_zone(ozp, hashtab); /* * Reload */ while (*domain) { const char *s; int escaped; /* * XXX this is presentation level hair and belongs elsewhere. */ escaped = 0; for (s = domain; *s != '\0'; s++) { if (!escaped) { if (*s == '.') break; else if (*s == '\\') escaped = 1; } else escaped = 0; } if (*s != '\0') domain = s + 1; /* skip label and its separator */ else domain = ""; /* root zone */ zp = find_zone(domain, class); - if (zp != NULL) { + if (zp != NULL && zp->z_type != Z_HINT) { ns_debug(ns_log_config, 1, "do_reload: matched %s", *domain ? domain : "."); if (mark) zp->z_flags |= Z_PARENT_RELOAD; else purgeandload(zp); break; } } } -void +static void purgeandload(struct zoneinfo *zp) { #ifdef BIND_UPDATE /* * A dynamic zone might have changed, so we * need to dump it before removing it. */ if (zp->z_type == Z_PRIMARY && (zp->z_flags & Z_DYNAMIC) != 0 && ((zp->z_flags & Z_NEED_SOAUPDATE) != 0 || (zp->z_flags & Z_NEED_DUMP) != 0)) (void) zonedump(zp, ISNOTIXFR); #endif ns_stopxfrs(zp); if (zp->z_type == Z_HINT) - purge_zone(zp->z_origin, fcachetab, zp->z_class); + purge_zone(zp, fcachetab); else - purge_zone(zp->z_origin, hashtab, zp->z_class); + purge_zone(zp, hashtab); zp->z_flags &= ~Z_AUTH; switch (zp->z_type) { case Z_SECONDARY: case Z_STUB: zoneinit(zp); break; case Z_PRIMARY: if (db_load(zp->z_source, zp->z_origin, zp, 0, ISNOTIXFR) == 0) zp->z_flags |= Z_AUTH; break; case Z_HINT: case Z_CACHE: (void)db_load(zp->z_source, zp->z_origin, zp, 0, ISNOTIXFR); break; } } #ifdef DEBUG /* prints out the content of zones */ static void content_zone(int end, int level) { int i; for (i = 0; i <= end; i++) { printzoneinfo(i, ns_log_config, level); } } #endif enum context ns_ptrcontext(owner) const char *owner; { if (ns_samedomain(owner, "in-addr.arpa") || ns_samedomain(owner, "ip6.int")) return (hostname_ctx); return (domain_ctx); } enum context ns_ownercontext(type, transport) int type; enum transport transport; { enum context context = domain_ctx; switch (type) { case T_A: case T_WKS: case T_MX: switch (transport) { case update_trans: case primary_trans: case secondary_trans: context = owner_ctx; break; case response_trans: context = hostname_ctx; break; default: panic("impossible condition in ns_ownercontext()", NULL); } break; case T_MB: case T_MG: context = mailname_ctx; break; default: /* Nothing to do. */ break; } return (context); } int ns_nameok(const struct qinfo *qry, const char *name, int class, struct zoneinfo *zp, enum transport transport, enum context context, const char *owner, struct in_addr source) { enum severity severity = not_set; int ok = 1; if (zp != NULL) severity = zp->z_checknames; if (severity == not_set) severity = server_options->check_names[transport]; if (severity == ignore) return (1); switch (context) { case domain_ctx: ok = (class != C_IN) || res_dnok(name); break; case owner_ctx: ok = (class != C_IN) || res_ownok(name); break; case mailname_ctx: ok = res_mailok(name); break; case hostname_ctx: ok = res_hnok(name); break; default: ns_panic(ns_log_default, 1, "unexpected context %d in ns_nameok", (int)context); } if (!ok) { char *q, *s, *o; if (source.s_addr == INADDR_ANY) s = savestr(transport_strings[transport], 0); else { s = newstr(strlen(transport_strings[transport]) + sizeof " from [000.000.000.000] for [000.000.000.000]", 0); if (s != NULL) { if (transport == response_trans && qry != NULL) { if ((qry->q_flags & Q_PRIMING) != 0) { sprintf(s, "%s from [%s] for priming", transport_strings[transport], inet_ntoa(source)); } else if ((qry->q_flags & Q_ZSERIAL) != 0) { sprintf(s, "%s from [%s] for soacheck", transport_strings[transport], inet_ntoa(source)); } else if ((qry->q_flags & Q_SYSTEM) != 0) { sprintf(s, "%s from [%s] for sysquery", transport_strings[transport], inet_ntoa(source)); } else { q=strdup(inet_ntoa(qry->q_from.sin_addr)); sprintf(s, "%s from [%s] for [%s]", transport_strings[transport], inet_ntoa(source), q != NULL ? q : "memget failed"); free(q); } } else { sprintf(s, "%s from [%s]", (transport == response_trans) ? "query" : transport_strings[transport], inet_ntoa(source)); } } } if (ns_samename(owner, name) == 1) o = savestr("", 0); else { const char *t = (*owner == '\0') ? "." : owner; o = newstr(strlen(t) + sizeof " (owner \"\")", 0); if (o) sprintf(o, " (owner \"%s\")", t); } /* * We use log_write directly here to avoid duplicating * the message formatting and arguments. */ log_write(log_ctx, ns_log_default, (transport != response_trans) || (o == NULL) || (s == NULL) || ( (qry != NULL) && (qry->q_flags & (Q_PRIMING|Q_ZSERIAL)) ) ? log_warning : log_info, "%s name \"%s\"%s %s (%s) is invalid - %s", context_strings[context], name, o != NULL ? o : "[memget failed]", p_class(class), s != NULL ? s : "[memget failed]", (severity == fail) ? "rejecting" : "proceeding anyway"); if (severity == warn) ok = 1; if (s != NULL) (void)freestr(s); if (o != NULL) (void)freestr(o); } return (ok); } int ns_wildcard(const char *name) { if (*name != '*') return (0); return (*++name == '\0'); } void ns_shutdown() { struct zoneinfo *zp; #ifdef BIND_NOTIFY ns_unnotify(); #endif /* Erase zones. */ for (zp = &zones[0]; zp < &zones[nzones]; zp++) { if (zp->z_type) { if (zp->z_type != z_hint && zp->z_type != z_cache) { ns_stopxfrs(zp); - purge_zone(zp->z_origin, hashtab, zp->z_class); + purge_zone(zp, hashtab); } else if (zp->z_type == z_hint) - purge_zone(zp->z_origin, fcachetab, - zp->z_class); + purge_zone(zp, fcachetab); free_zone_contents(zp, 1); } } /* Erase the cache. */ clean_cache(hashtab, 1); hashtab->h_cnt = 0; /* ??? */ rm_hash(hashtab); hashtab = NULL; clean_cache(fcachetab, 1); fcachetab->h_cnt = 0; /* ??? */ rm_hash(fcachetab); fcachetab = NULL; if (zones != NULL) memput(zones, nzones * sizeof *zones); zones = NULL; freeComplaints(); shutdown_configuration(); } diff --git a/contrib/bind/bin/named/ns_ixfr.c b/contrib/bind/bin/named/ns_ixfr.c index 0492bc0e20bf..b6d632dfedb6 100644 --- a/contrib/bind/bin/named/ns_ixfr.c +++ b/contrib/bind/bin/named/ns_ixfr.c @@ -1,590 +1,584 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_ixfr.c,v 8.32 2002/05/18 01:02:57 marka Exp $"; +static const char rcsid[] = "$Id: ns_ixfr.c,v 8.33 2003/02/24 23:36:01 marka Exp $"; #endif /* not lint */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" static void sx_new_ixfrmsg(struct qstream * qsp); void sx_send_ixfr(struct qstream * qsp); static int sx_flush(struct qstream * qsp), sx_addrr(struct qstream * qsp, const char *dname, struct databuf * dp); /* * u_char * sx_new_ixfrmsg(msg) init the header of a message, reset the * compression pointers, and reset the write pointer to the first byte * following the header. */ static void sx_new_ixfrmsg(struct qstream *qsp) { HEADER * hp = (HEADER *) qsp->xfr.msg; memset(hp, 0, HFIXEDSZ); hp->id = htons(qsp->xfr.id); hp->opcode = qsp->xfr.opcode; hp->qr = 1; hp->aa = 1; hp->rcode = NOERROR; qsp->xfr.ptrs[0] = qsp->xfr.msg; qsp->xfr.ptrs[1] = NULL; qsp->xfr.cp = qsp->xfr.msg + HFIXEDSZ; if (qsp->xfr.ixfr_zone == 0) { int count, n; int buflen; struct namebuf *np; struct hashbuf *htp; struct zoneinfo *zp; const char * fname; qsp->xfr.ixfr_zone = qsp->xfr.zone; zp = &zones[qsp->xfr.zone]; n = dn_comp(zp->z_origin, qsp->xfr.cp, XFER_BUFSIZE - (qsp->xfr.cp - qsp->xfr.msg), NULL, NULL); qsp->xfr.cp += n; PUTSHORT((u_int16_t) T_IXFR, qsp->xfr.cp); PUTSHORT((u_int16_t) zp->z_class, qsp->xfr.cp); hp->qdcount = htons(ntohs(hp->qdcount) + 1); count = qsp->xfr.cp - qsp->xfr.msg; htp = hashtab; np = nlookup(zp->z_origin, &htp, &fname, 0); buflen = XFER_BUFSIZE; } } /* * int * sx_flush(qsp) * flush the intermediate buffer out to the stream IO system. * return: * passed through from sq_write(). */ static int sx_flush(struct qstream *qsp) { int ret; #ifdef DEBUG if (debug >= 10) fp_nquery(qsp->xfr.msg, qsp->xfr.cp - qsp->xfr.msg, log_get_stream(packet_channel)); #endif if (qsp->xfr.tsig_state != NULL && qsp->xfr.tsig_skip == 0) { int msglen = qsp->xfr.cp - qsp->xfr.msg; ns_sign_tcp(qsp->xfr.msg, &msglen, qsp->xfr.eom - qsp->xfr.msg, NOERROR, qsp->xfr.tsig_state, qsp->xfr.state == s_x_done); if (qsp->xfr.state == s_x_done) { memput(qsp->xfr.tsig_state, sizeof(ns_tcp_tsig_state)); qsp->xfr.tsig_state = NULL; } qsp->xfr.cp = qsp->xfr.msg + msglen; } if (qsp->xfr.cp - qsp->xfr.msg > 0) ret = sq_write(qsp, qsp->xfr.msg, qsp->xfr.cp - qsp->xfr.msg); else { ns_debug(ns_log_default, 3, " Flush negative number *********"); ret = -1; } if (ret >= 0) { qsp->xfr.cp = NULL; qsp->xfr.tsig_skip = 0; } else qsp->xfr.tsig_skip = 1; return (ret); } /* * int sx_addrr(qsp, name, dp) add name/dp's RR to the current assembly * message. if it won't fit, write current message out, renew the message, * and then RR should fit. return: -1 = the sq_write() failed so we could not * queue the full message. 0 = one way or another, everything is fine. side * effects: on success, the ANCOUNT is incremented and the pointers are * advanced. */ static int sx_addrr(struct qstream *qsp, const char *dname, struct databuf *dp) { HEADER *hp = (HEADER *) qsp->xfr.msg; u_char **edp = qsp->xfr.ptrs + sizeof qsp->xfr.ptrs / sizeof(u_char *); int n; if (qsp->xfr.cp != NULL) { if (qsp->xfr.transfer_format == axfr_one_answer && sx_flush(qsp) < 0) return (-1); } if (qsp->xfr.cp == NULL) sx_new_ixfrmsg(qsp); n = make_rr(dname, dp, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, 0, qsp->xfr.ptrs, edp, 0); if (n < 0) { if (sx_flush(qsp) < 0) return (-1); if (qsp->xfr.cp == NULL) sx_new_ixfrmsg(qsp); n = make_rr(dname, dp, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, 0, qsp->xfr.ptrs, edp, 0); INSIST(n >= 0); } hp->ancount = htons(ntohs(hp->ancount) + 1); qsp->xfr.cp += n; return (0); } void sx_send_ixfr(struct qstream *qsp) { char * cp; struct zoneinfo *zp = NULL; struct databuf *soa_dp; struct databuf *old_soadp; ns_delta *dp; ns_updrec *rp; int foundsoa; zp = &zones[qsp->xfr.zone]; soa_dp = (struct databuf *) findzonesoa(zp); if (soa_dp == NULL) { /* XXX should be more graceful */ ns_panic(ns_log_update, 1, "sx_send_ixfr: unable to locate soa"); } old_soadp = memget(BIND_DATASIZE(soa_dp->d_size)); if (old_soadp == NULL) ns_panic(ns_log_update, 1, "sx_send_ixfr: out of memory"); memcpy(old_soadp, soa_dp, BIND_DATASIZE(soa_dp->d_size)); again: switch (qsp->xfr.state) { case s_x_firstsoa: ns_debug(ns_log_default, 3, "IXFR: s_x_firstsoa (%s)", zp->z_origin); if (sx_addrr(qsp, zp->z_origin, soa_dp) < 0) goto cleanup; qsp->xfr.state = s_x_deletesoa; /* FALLTHROUGH */ case s_x_deletesoa: ns_debug(ns_log_default, 3, "IXFR: s_x_deletesoa (%s)", zp->z_origin); dp = NULL; if (qsp->xfr.top.ixfr != NULL && !EMPTY(*qsp->xfr.top.ixfr)) dp = HEAD(*qsp->xfr.top.ixfr); if (dp != NULL) { foundsoa = 0; rp = HEAD(dp->d_changes); while (rp != NULL) { if (rp->r_opcode == DELETE && rp->r_dp != NULL && rp->r_dp->d_type == T_SOA) { if (sx_addrr(qsp, rp->r_dname, rp->r_dp) < 0) goto cleanup; db_detach(&rp->r_dp); foundsoa = 1; break; } rp = NEXT(rp, r_link); } if (!foundsoa) { cp = (char *)findsoaserial(old_soadp->d_data); PUTLONG(HEAD(dp->d_changes)->r_zone, cp); if (sx_addrr(qsp, zp->z_origin, old_soadp) < 0) goto cleanup; } } qsp->xfr.state = s_x_deleting; /* FALLTHROUGH */ case s_x_deleting: ns_debug(ns_log_default, 3, "IXFR: s_x_deleting (%s)", zp->z_origin); dp = NULL; if (qsp->xfr.top.ixfr != NULL && !EMPTY(*qsp->xfr.top.ixfr)) dp = HEAD(*qsp->xfr.top.ixfr); if (dp != NULL) { rp = HEAD(dp->d_changes); while (rp != NULL) { if (rp->r_opcode == DELETE && rp->r_dp != NULL) { /* * Drop any SOA deletes */ if (rp->r_dp->d_type != T_SOA && sx_addrr(qsp, rp->r_dname, rp->r_dp) < 0) goto cleanup; db_detach(&rp->r_dp); } rp = NEXT(rp, r_link); } } qsp->xfr.state = s_x_addsoa; /* FALLTHROUGH */ case s_x_addsoa: ns_debug(ns_log_default, 3, "IXFR: s_x_addsoa (%s)", zp->z_origin); dp = NULL; if (qsp->xfr.top.ixfr != NULL && !EMPTY(*qsp->xfr.top.ixfr)) dp = HEAD(*qsp->xfr.top.ixfr); if (dp != NULL) { foundsoa = 0; rp = HEAD(dp->d_changes); while (rp != NULL) { if (rp->r_opcode == ADD && rp->r_dp != NULL && rp->r_dp->d_type == T_SOA) { if (sx_addrr(qsp, rp->r_dname, rp->r_dp) < 0) goto cleanup; db_detach(&rp->r_dp); foundsoa = 1; break; } rp = NEXT(rp, r_link); } if (!foundsoa) { cp = (char *)findsoaserial(old_soadp->d_data); if (NEXT(dp, d_link) != NULL) { PUTLONG(HEAD(dp->d_changes)->r_zone, cp); if (sx_addrr(qsp, zp->z_origin, old_soadp) < 0) goto cleanup; } else { if (sx_addrr(qsp, zp->z_origin, soa_dp) < 0) goto cleanup; } } } qsp->xfr.state = s_x_adding; /* FALLTHROUGH */ case s_x_adding: ns_debug(ns_log_default, 3, "IXFR: s_x_adding (%s)", zp->z_origin); dp = NULL; if (qsp->xfr.top.ixfr != NULL && !EMPTY(*qsp->xfr.top.ixfr)) { dp = HEAD(*qsp->xfr.top.ixfr); if (dp != NULL) { /* see s_x_deleting */ rp = HEAD(dp->d_changes); while (rp != NULL) { if (rp->r_opcode == ADD && rp->r_dp != NULL && rp->r_dp->d_type != T_SOA) { if (sx_addrr(qsp, rp->r_dname, rp->r_dp) < 0) goto cleanup; db_detach(&rp->r_dp); } rp = NEXT(rp, r_link); } /* move to next update */ UNLINK(*qsp->xfr.top.ixfr, dp, d_link); /* clean up old update */ while ((rp = HEAD(dp->d_changes)) != NULL) { UNLINK(dp->d_changes, rp, r_link); if (rp->r_dp != NULL) db_detach(&rp->r_dp); res_freeupdrec(rp); } memput(dp, sizeof (*dp)); if (HEAD(*qsp->xfr.top.ixfr) != NULL) { qsp->xfr.state = s_x_deletesoa; goto again; } } } qsp->xfr.state = s_x_lastsoa; /* FALLTHROUGH */ case s_x_lastsoa: ns_debug(ns_log_default, 3, "IXFR: s_x_lastsoa (%s)", zp->z_origin); if (qsp->xfr.ixfr_zone != 0) sx_addrr(qsp, zp->z_origin, soa_dp); break; default: break; } ns_debug(ns_log_default, 3, "IXFR: flushing %s", zp->z_origin); qsp->xfr.state = s_x_done; sx_flush(qsp); sq_writeh(qsp, sq_flushw); if (qsp->xfr.top.ixfr != NULL) { if(!EMPTY(*qsp->xfr.top.ixfr)) { while ((dp = HEAD(*qsp->xfr.top.ixfr)) != NULL) { UNLINK(*qsp->xfr.top.ixfr, dp, d_link); while ((rp = HEAD(dp->d_changes)) != NULL) { UNLINK(dp->d_changes, rp, r_link); if (rp->r_dp != NULL) db_detach(&rp->r_dp); res_freeupdrec(rp); } memput(dp, sizeof *dp); } } memput(qsp->xfr.top.ixfr, sizeof *qsp->xfr.top.ixfr); qsp->xfr.top.ixfr = NULL; } cleanup: memput(old_soadp, BIND_DATASIZE(old_soadp->d_size)); } #ifndef MAXBSIZE #define MAXBSIZE 8192 #endif /* * int ixfr_log_maint(struct zoneinfo *zp, int fast_trim) * * zp - pointer to the zone information */ int ixfr_log_maint(struct zoneinfo *zp) { int fd, rcount, wcount; int found = 0; int error = 0; long seek = 0; FILE *to_fp, *from_fp, *db_fp; char *tmpname; int len; struct stat db_sb; struct stat sb; + size_t check_size; static char buf[MAXBSIZE]; ns_debug(ns_log_default, 3, "ixfr_log_maint(%s)", zp->z_origin); /* find out how big the zone db file is */ if ((db_fp = fopen(zp->z_source, "r")) == NULL) { ns_warning(ns_log_db, "%s: %s", zp->z_source, strerror(errno)); return (-1); } if (fstat(fileno(db_fp), &db_sb) < 0) { ns_warning(ns_log_db, "%s: %s", zp->z_source, strerror(errno)); (void) my_fclose(db_fp); return (-1); } (void) my_fclose(db_fp); ns_debug(ns_log_default, 3, "%s, size %ld", zp->z_source, (long)db_sb.st_size); /* open up the zone ixfr log */ if ((from_fp = fopen(zp->z_ixfr_base, "r")) == NULL) { ns_warning(ns_log_db, "%s: %s", zp->z_ixfr_base, strerror(errno)); return (-1); } if (fstat(fileno(from_fp), &sb) < 0) { ns_warning(ns_log_db, "%s: %s", zp->z_ixfr_base, strerror(errno)); (void) my_fclose(from_fp); return (-1); } ns_debug(ns_log_default, 3, "%s, size %ld max %ld\n", zp->z_ixfr_base, (long)sb.st_size, (long)zp->z_max_log_size_ixfr); - if (zp->z_max_log_size_ixfr) { - if (sb.st_size > zp->z_max_log_size_ixfr) - seek = sb.st_size - - (size_t)(zp->z_max_log_size_ixfr + - (zp->z_max_log_size_ixfr * 0.10) ); - else - seek = 0; - } else { - if (sb.st_size > (db_sb.st_size * 0.50)) - seek = sb.st_size - (size_t)((db_sb.st_size * 0.50) - + ((db_sb.st_size * zp->z_max_log_size_ixfr) * 0.10)); - else - seek = 0; - } + check_size = zp->z_max_log_size_ixfr; + if (!check_size) + check_size = db_sb.st_size / 2; + if (sb.st_size > check_size) + seek = (sb.st_size - check_size) + (check_size / 10); + else + seek = 0; ns_debug(ns_log_default, 3, "seek: %ld", (long)seek); if (seek < 1) { ns_debug(ns_log_default, 3, "%s does not need to be reduced", zp->z_ixfr_base); (void) my_fclose(from_fp); return (-1); } len = strlen(zp->z_ixfr_base) + sizeof(".XXXXXX") + 1; tmpname = memget(len); if (!tmpname) { ns_warning(ns_log_default, "memget failed"); return (-1); } #ifdef SHORT_FNAMES filenamecpy(tmpname, zp->z_ixfr_base); #else (void) strcpy(tmpname, zp->z_ixfr_base); #endif /* SHORT_FNAMES */ (void) strcat(tmpname, ".XXXXXX"); if ((fd = mkstemp(tmpname)) == -1) { ns_warning(ns_log_db, "can't make tmpfile (%s): %s", tmpname, strerror(errno)); memput(tmpname, len); (void) my_fclose(from_fp); return (-1); } if ((to_fp = fdopen(fd, "r+")) == NULL) { ns_warning(ns_log_db, "%s: %s", tmpname, strerror(errno)); (void) unlink(tmpname); memput(tmpname, len); (void) my_fclose(from_fp); (void) close(fd); return (-1); } if (fgets(buf, sizeof(buf), from_fp) == NULL) { ns_error(ns_log_update, "fgets() from %s failed: %s", zp->z_ixfr_base, strerror(errno)); error++; goto clean_up; } if (strcmp(buf, LogSignature) != 0) { ns_error(ns_log_update, "invalid log file %s", zp->z_ixfr_base); error++; goto clean_up; } if (fseek( from_fp, seek, 0) < 0) { error++; goto clean_up; } found = 0; for (;;) { if (getword(buf, sizeof buf, from_fp, 0)) { if (strcasecmp(buf, "[END_DELTA]") == 0) { if (!(fgets(buf, 2, from_fp) == NULL)) /* eat */ found = 1; break; } } if (feof(from_fp)) break; } if (found) { ns_debug(ns_log_default, 1, "ixfr_log_maint(): found [END_DELTA]"); fprintf(to_fp, "%s", LogSignature); while ((rcount = fread(buf, sizeof(char), MAXBSIZE, from_fp)) > 0) { wcount = fwrite(buf, sizeof(char), rcount, to_fp); if (rcount != wcount || wcount == -1) { ns_warning(ns_log_default, "ixfr_log_maint: error in writting copy"); break; } } if (rcount < 0) ns_warning(ns_log_default, "ixfr_log_maint: error in reading copy"); } clean_up: (void) my_fclose(to_fp); (void) my_fclose(from_fp); if (error == 0) { if (isc_movefile(tmpname, zp->z_ixfr_base) == -1) { ns_warning(ns_log_default, "can not rename %s to %s :%s", tmpname, zp->z_ixfr_base, strerror(errno)); } if ((from_fp = fopen(zp->z_ixfr_base, "r")) == NULL) { ns_warning(ns_log_db, "%s: %s", zp->z_ixfr_base, strerror(errno)); memput(tmpname, len); return (-1); } if (fstat(fileno(from_fp), &sb) < 0) { ns_warning(ns_log_db, "%s: %s", zp->z_ixfr_base, strerror(errno)); memput(tmpname, len); (void) my_fclose(from_fp); return (-1); } if (sb.st_size <= 0) (void) unlink(zp->z_ixfr_base); else if (chmod(zp->z_ixfr_base, 0644) < 0) ns_error(ns_log_update, "chmod(%s,%o) failed, pressing on: %s", zp->z_source, sb.st_mode, strerror(errno)); (void) my_fclose(from_fp); } (void) unlink(tmpname); memput(tmpname, len); /* signal to read for lowest serial number */ zp->z_serial_ixfr_start = 0; ns_debug(ns_log_default, 3, "%s, size %ld max %ld\n", zp->z_ixfr_base, (long)sb.st_size, (long)zp->z_max_log_size_ixfr); if (error) return(-1); else return (0); } diff --git a/contrib/bind/bin/named/ns_lexer.c b/contrib/bind/bin/named/ns_lexer.c index b8271625d717..3c09cf03a049 100644 --- a/contrib/bind/bin/named/ns_lexer.c +++ b/contrib/bind/bin/named/ns_lexer.c @@ -1,825 +1,826 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_lexer.c,v 8.31 2002/05/24 03:05:03 marka Exp $"; +static const char rcsid[] = "$Id: ns_lexer.c,v 8.31.10.1 2003/06/02 09:56:35 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" #include "ns_parser.h" #include "ns_parseutil.h" #include "ns_lexer.h" typedef enum lexer_state { scan, number, identifier, ipv4, quoted_string } LexerState; #define LEX_EOF 0x01 #define LEXER_MAX_PUSHBACK 2 typedef struct lexer_file_context { char * name; FILE * stream; int line_number; LexerState state; u_int flags; int warnings; int errors; u_int pushback_count; char pushback[LEXER_MAX_PUSHBACK]; struct lexer_file_context * next; } *LexerFileContext; LexerFileContext current_file = NULL; #define LEX_LAST_WAS_DOT 0x01 #define LEX_CONSECUTIVE_DOTS 0x02 typedef struct lexer_identifier { char buffer[LEX_MAX_IDENT_SIZE+1]; int index; int num_dots; unsigned int flags; } *LexerIdentifier; static LexerIdentifier id; static char special_chars[256]; #define whitespace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') #define domain_char(c) (isalnum((c)) || (c) == '.' || (c) == '-') #define special_char(c) (special_chars[(c)] == 1) #define identifier_char(c) (!whitespace(c) && !special_char(c)) static int last_token; static YYSTYPE last_yylval; static int lexer_initialized = 0; /* * Problem Reporting */ static char * token_to_text(int token, YYSTYPE lval) { static char buffer[LEX_MAX_IDENT_SIZE+50]; if (token < 128) { if (token == 0) strcpy(buffer, ""); else sprintf(buffer, "'%c'", token); } else { switch (token) { case L_EOS: strcpy(buffer, ";"); break; case L_STRING: sprintf(buffer, "'%s'", lval.cp); break; case L_QSTRING: sprintf(buffer, "\"%s\"", lval.cp); break; case L_IPADDR: sprintf(buffer, "%s", inet_ntoa(lval.ip_addr)); break; case L_NUMBER: sprintf(buffer, "%ld", lval.num); break; case L_END_INCLUDE: sprintf(buffer, ""); break; default: sprintf(buffer, "%s", lval.cp); } } return (buffer); } static char where[MAXPATHLEN + 100]; static char message[20480]; static void parser_complain(int is_warning, int print_last_token, const char *format, va_list args) ISC_FORMAT_PRINTF(3, 0); static void parser_complain(int is_warning, int print_last_token, const char *format, va_list args) { LexerFileContext lf; int severity; if (is_warning) { severity = log_warning; } else { severity = log_error; } INSIST(current_file != NULL); if (current_file->next != NULL) { for (lf = current_file; lf != NULL; lf = lf->next) { log_write(log_ctx, ns_log_parser, severity, "%s '%s' line %d", (lf == current_file) ? "In" : "included from", lf->name, lf->line_number); } } sprintf(where, "%s:%d: ", current_file->name, current_file->line_number); vsprintf(message, format, args); if (print_last_token) log_write(log_ctx, ns_log_parser, severity, "%s%s near %s", where, message, token_to_text(last_token, last_yylval)); else log_write(log_ctx, ns_log_parser, severity, "%s%s", where, message); } int parser_warning(int print_last_token, const char *format, ...) { va_list args; va_start(args, format); parser_complain(1, print_last_token, format, args); va_end(args); current_file->warnings++; return (1); } int parser_error(int print_last_token, const char *format, ...) { va_list args; va_start(args, format); parser_complain(0, print_last_token, format, args); va_end(args); current_file->errors++; return (1); } void yyerror(const char *message) { parser_error(1, "%s", message); } /* * Keywords */ struct keyword { const char *name; int token; }; /* * "keywords" is an array of the keywords which are the fixed syntactic * elements of the configuration file. Each keyword has a string version * of the keyword and a token id, which should be an identifier which * matches that in a %token statement inside the parser.y file. */ static struct keyword keywords[] = { {"acl", T_ACL}, {"address", T_ADDRESS}, {"algorithm", T_ALGID}, {"allow", T_ALLOW}, {"allow-query", T_ALLOW_QUERY}, {"allow-recursion", T_ALLOW_RECURSION}, {"allow-transfer", T_ALLOW_TRANSFER}, {"allow-update", T_ALLOW_UPDATE}, #ifdef BIND_NOTIFY {"also-notify", T_ALSO_NOTIFY}, #endif {"auth-nxdomain", T_AUTH_NXDOMAIN}, {"blackhole", T_BLACKHOLE}, {"bogus", T_BOGUS}, {"category", T_CATEGORY}, {"class", T_CLASS}, {"channel", T_CHANNEL}, {"check-names", T_CHECK_NAMES}, {"cleaning-interval", T_CLEAN_INTERVAL}, {"controls", T_CONTROLS}, {"coresize", T_CORESIZE}, {"datasize", T_DATASIZE}, {"deallocate-on-exit", T_DEALLOC_ON_EXIT}, {"debug", T_DEBUG}, {"default", T_DEFAULT}, {"dialup", T_DIALUP}, {"directory", T_DIRECTORY}, {"dump-file", T_DUMP_FILE}, {"dynamic", T_DYNAMIC}, {"edns", T_EDNS}, + {"edns-udp-size", T_EDNS_UDP_SIZE}, {"explicit", T_EXPLICIT}, {"fail", T_FAIL}, {"fake-iquery", T_FAKE_IQUERY}, {"false", T_FALSE}, {"fetch-glue", T_FETCH_GLUE}, {"file", T_FILE}, {"files", T_FILES}, {"first", T_FIRST}, {"forward", T_FORWARD}, {"forwarders", T_FORWARDERS}, {"group", T_GROUP}, {"has-old-clients", T_HAS_OLD_CLIENTS}, {"heartbeat-interval", T_HEARTBEAT}, {"hint", T_HINT}, #ifdef HITCOUNTS {"hit-count", T_HITCOUNT}, #endif /* HITCOUNTS */ {"host-statistics", T_HOSTSTATS}, {"host-statistics-max", T_HOSTSTATSMAX}, {"hostname", T_HOSTNAME}, {"if-no-answer", T_IF_NO_ANSWER}, {"if-no-domain", T_IF_NO_DOMAIN}, {"ignore", T_IGNORE}, {"include", T_INCLUDE}, {"inet", T_INET}, {"interface-interval", T_INTERFACE_INTERVAL}, {"ixfr-base", T_FILE_IXFR}, {"ixfr-tmp-file", T_IXFR_TMP}, {"key", T_SEC_KEY}, {"keys", T_KEYS}, {"lame-ttl", T_LAME_TTL}, {"listen-on", T_LISTEN_ON}, {"logging", T_LOGGING}, {"maintain-ixfr-base", T_MAINTAIN_IXFR_BASE}, {"many-answers", T_MANY_ANSWERS}, {"master", T_MASTER}, {"masters", T_MASTERS}, {"max-ixfr-log-size", T_MAX_LOG_SIZE_IXFR}, {"max-ncache-ttl", T_MAX_NCACHE_TTL}, {"max-transfer-time-in", T_MAX_TRANSFER_TIME_IN}, {"memstatistics-file", T_MEMSTATS_FILE}, {"min-roots", T_MIN_ROOTS}, {"multiple-cnames", T_MULTIPLE_CNAMES}, {"name", T_NAME}, {"named-xfer", T_NAMED_XFER}, {"no", T_NO}, #ifdef BIND_NOTIFY {"notify", T_NOTIFY}, #endif {"null", T_NULL_OUTPUT}, {"one-answer", T_ONE_ANSWER}, {"only", T_ONLY}, {"order", T_ORDER}, {"options", T_OPTIONS}, {"owner", T_OWNER}, {"perm", T_PERM}, {"pid-file", T_PIDFILE}, {"port", T_PORT}, {"preferred-glue", T_PREFERRED_GLUE}, {"print-category", T_PRINT_CATEGORY}, {"print-severity", T_PRINT_SEVERITY}, {"print-time", T_PRINT_TIME}, {"pubkey", T_PUBKEY}, {"query-source", T_QUERY_SOURCE}, {"rfc2308-type1", T_RFC2308_TYPE1}, {"rrset-order", T_RRSET_ORDER}, {"recursion", T_RECURSION}, {"response", T_RESPONSE}, {"secret", T_SECRET}, {"serial-queries", T_SERIAL_QUERIES}, {"server", T_SERVER}, {"severity", T_SEVERITY}, {"size", T_SIZE}, {"slave", T_SLAVE}, {"sortlist", T_SORTLIST}, {"stacksize", T_STACKSIZE}, {"statistics-file", T_STATS_FILE}, {"statistics-interval", T_STATS_INTERVAL}, {"stub", T_STUB}, {"support-ixfr", T_SUPPORT_IXFR}, #ifdef BIND_NOTIFY {"suppress-initial-notify", T_NOTIFY_INITIAL}, #endif {"syslog", T_SYSLOG}, {"topology", T_TOPOLOGY}, {"transfer-format", T_TRANSFER_FORMAT}, {"transfer-source", T_TRANSFER_SOURCE}, {"transfers", T_TRANSFERS}, {"transfers-in", T_TRANSFERS_IN}, {"transfers-out", T_TRANSFERS_OUT}, {"transfers-per-ns", T_TRANSFERS_PER_NS}, {"treat-cr-as-space", T_TREAT_CR_AS_SPACE}, {"true", T_TRUE}, {"trusted-keys", T_TRUSTED_KEYS}, {"type", T_TYPE}, {"unix", T_UNIX}, {"unlimited", T_UNLIMITED}, {"use-id-pool", T_USE_ID_POOL}, {"use-ixfr", T_USE_IXFR}, {"version", T_VERSION}, {"versions", T_VERSIONS}, {"warn", T_WARN}, {"yes", T_YES}, {"zone", T_ZONE}, {(char *) NULL, 0}, }; /* * The table size should be a prime chosen to minimize collisions. */ #define KEYWORD_TABLE_SIZE 461 static symbol_table keyword_table = NULL; static void init_keywords() { struct keyword *k; symbol_value value; if (keyword_table != NULL) free_symbol_table(keyword_table); keyword_table = new_symbol_table(KEYWORD_TABLE_SIZE, NULL); for (k = keywords; k->name != NULL; k++) { value.integer = k->token; define_symbol(keyword_table, k->name, 0, value, 0); } dprint_symbol_table(99, keyword_table); } /* * File Contexts */ void lexer_begin_file(const char *filename, FILE *stream) { LexerFileContext lf; if (stream == NULL) { stream = fopen(filename, "r"); if (stream == NULL) { parser_error(0, "couldn't open include file '%s'", filename); return; } } lf = (LexerFileContext)memget(sizeof (struct lexer_file_context)); if (lf == NULL) panic("memget failed in lexer_begin_file", NULL); INSIST(stream != NULL); lf->stream = stream; lf->name = savestr(filename, 1); lf->line_number = 1; lf->state = scan; lf->flags = 0; lf->warnings = 0; lf->errors = 0; lf->pushback_count = 0; lf->next = current_file; current_file = lf; } void lexer_end_file(void) { LexerFileContext lf; INSIST(current_file != NULL); lf = current_file; current_file = lf->next; fclose(lf->stream); freestr(lf->name); memput(lf, sizeof *lf); } /* * Character Input */ #define LEXER_GETC(c, cf) \ do { \ if ((cf)->pushback_count > 0) { \ (cf)->pushback_count--; \ (c) = (cf)->pushback[(cf)->pushback_count]; \ } else \ (c) = getc((cf)->stream); \ } while (0); #define LEXER_UNGETC(c, cf) \ do { \ INSIST((cf)->pushback_count < LEXER_MAX_PUSHBACK); \ (cf)->pushback[(cf)->pushback_count++] = (c); \ } while (0); static void scan_to_comment_end(int c_plus_plus_style) { int c; int done = 0; int prev_was_star = 0; while (!done) { LEXER_GETC(c, current_file); switch (c) { case EOF: if (!c_plus_plus_style) parser_error(0, "EOF in comment"); current_file->flags |= LEX_EOF; done = 1; break; case '*': prev_was_star = 1; break; case '/': if (prev_was_star && !c_plus_plus_style) done = 1; prev_was_star = 0; break; case '\n': if (c_plus_plus_style) { /* don't consume the newline because we want it to be a delimiter for anything before the comment started */ LEXER_UNGETC(c, current_file); done = 1; } else { current_file->line_number++; } prev_was_star = 0; break; default: prev_was_star = 0; } } } static int get_next_char(int comment_ok) { int c, nc; if (current_file->flags & LEX_EOF) return (EOF); LEXER_GETC(c, current_file); if (comment_ok) { while (c == '/' || c == '#') { if (c == '#') { scan_to_comment_end(1); if (current_file->flags & LEX_EOF) return (EOF); LEXER_GETC(c, current_file); } else { LEXER_GETC(nc, current_file); switch (nc) { case EOF: current_file->flags |= LEX_EOF; return ('/'); case '*': case '/': scan_to_comment_end((nc == '/')); if (current_file->flags & LEX_EOF) return (EOF); LEXER_GETC(c, current_file); break; default: LEXER_UNGETC(nc, current_file); return ('/'); } } } } if (c == EOF) current_file->flags |= LEX_EOF; else if (c == '\n') current_file->line_number++; return (c); } static void put_back_char(int c) { if (c == EOF) current_file->flags |= LEX_EOF; else { LEXER_UNGETC(c, current_file); if (c == '\n') current_file->line_number--; } } /* * Identifiers */ static void clear_identifier(LexerIdentifier id) { INSIST(id != NULL); id->index = 0; id->num_dots = 0; id->flags = 0; } static char * dup_identifier(LexerIdentifier id) { char *duplicate; INSIST(id != NULL); duplicate = savestr(id->buffer, 1); return (duplicate); } static void finish_identifier(LexerIdentifier id) { INSIST(id != NULL && id->index < LEX_MAX_IDENT_SIZE); id->buffer[id->index] = '\0'; } static void add_to_identifier(LexerIdentifier id, int c) { INSIST(id != NULL); id->buffer[id->index] = c; id->index++; if (id->index >= LEX_MAX_IDENT_SIZE) { parser_error(0, "identifier too long"); current_file->state = scan; /* discard chars until we hit a non-identifier char */ while (c != EOF && identifier_char(c)) { c = get_next_char(1); } put_back_char(c); clear_identifier(id); } else { if (c == '.') { if (id->flags & LEX_LAST_WAS_DOT) id->flags |= LEX_CONSECUTIVE_DOTS; id->flags |= LEX_LAST_WAS_DOT; id->num_dots++; } else { id->flags &= ~LEX_LAST_WAS_DOT; } } } /* * yylex() -- return the next token from the current input stream */ int yylex() { int c; int comment_ok = 1; int token = -1; symbol_value value; while (token < 0) { c = get_next_char(comment_ok); switch(current_file->state) { case scan: if (c == EOF) { if (current_file->next == NULL) /* * We don't want to call * lexer_end_file() here because we * want to keep the toplevel file * context to log errors against. */ token = 0; else { lexer_end_file(); token = L_END_INCLUDE; } break; } if (whitespace(c)) break; if (identifier_char(c)) { if (isdigit(c)) current_file->state = number; else current_file->state = identifier; clear_identifier(id); add_to_identifier(id, c); } else if (special_char(c)) { if (c == ';') { token = L_EOS; break; } if (c == '"') { clear_identifier(id); current_file->state = quoted_string; comment_ok = 0; break; } token = c; } else { parser_error(0, "invalid character '%c'", c); } break; case number: if (c != EOF && identifier_char(c)) { if (!isdigit(c)) current_file->state = (c == '.') ? ipv4 : identifier; add_to_identifier(id, c); } else { put_back_char(c); current_file->state = scan; finish_identifier(id); yylval.num = strtol(id->buffer, (char**)0, 0); token = L_NUMBER; } break; case identifier: if (c != EOF && identifier_char(c)) { add_to_identifier(id, c); } else { put_back_char(c); current_file->state = scan; finish_identifier(id); /* is it a keyword? */ if (lookup_symbol(keyword_table, id->buffer, 0, &value)) { yylval.cp = id->buffer; token = value.integer; } else { yylval.cp = dup_identifier(id); token = L_STRING; } } break; case ipv4: if (c != EOF && identifier_char(c)) { if (!isdigit(c)) { if (c != '.' || (id->flags & LEX_CONSECUTIVE_DOTS)) current_file->state = identifier; } add_to_identifier(id, c); } else { put_back_char(c); if (id->num_dots > 3 || (id->flags & LEX_LAST_WAS_DOT)) current_file->state = identifier; else { if (id->num_dots == 1) { add_to_identifier(id, '.'); add_to_identifier(id, '0'); add_to_identifier(id, '.'); add_to_identifier(id, '0'); } else if (id->num_dots == 2) { add_to_identifier(id, '.'); add_to_identifier(id, '0'); } current_file->state = scan; finish_identifier(id); token = L_IPADDR; if (inet_aton(id->buffer, &(yylval.ip_addr))==0) { yylval.cp = dup_identifier(id); token = L_STRING; } } } break; case quoted_string: if (c == EOF) { parser_error(0, "EOF in quoted string"); return 0; } else { if (c == '"') { comment_ok = 1; current_file->state = scan; finish_identifier(id); yylval.cp = dup_identifier(id); token = L_QSTRING; } else { /* XXX add backslash escapes here */ add_to_identifier(id, c); } } break; default: panic("unhandled state in yylex", NULL); } } last_token = token; last_yylval = yylval; return (token); } /* * Initialization */ symbol_table constants; static void import_constants(const struct ns_sym *s, int type) { symbol_value value; for ((void)NULL; s != NULL && s->name != NULL; s++) { value.integer = s->number; define_symbol(constants, s->name, type, value, 0); } } static void import_res_constants(const struct res_sym *r, int type) { symbol_value value; for ((void)NULL; r != NULL && r->name != NULL; r++) { value.integer = r->number; define_symbol(constants, r->name, type, value, 0); } } #define CONSTANTS_TABLE_SIZE 397 /* should be prime */ static void import_all_constants() { constants = new_symbol_table(CONSTANTS_TABLE_SIZE, NULL); import_res_constants(__p_class_syms, SYM_CLASS); import_constants(category_constants, SYM_CATEGORY); import_constants(logging_constants, SYM_LOGGING); import_constants(syslog_constants, SYM_SYSLOG); } void lexer_initialize() { memset(special_chars, 0, sizeof special_chars); special_chars[';'] = 1; special_chars['{'] = 1; special_chars['}'] = 1; special_chars['!'] = 1; special_chars['/'] = 1; special_chars['"'] = 1; special_chars['*'] = 1; id = (LexerIdentifier)memget(sizeof (struct lexer_identifier)); if (id == NULL) panic("memget failed in lexer_initialize", NULL); init_keywords(); import_all_constants(); lexer_initialized = 1; } void lexer_setup(void) { REQUIRE(lexer_initialized); current_file = NULL; /* XXX should we INSIST(current_file==NULL)? */ INSIST(id != NULL); } void lexer_shutdown(void) { REQUIRE(lexer_initialized); free_symbol_table(keyword_table); free_symbol_table(constants); memput(id, sizeof (struct lexer_identifier)); id = NULL; lexer_initialized = 0; } diff --git a/contrib/bind/bin/named/ns_main.c b/contrib/bind/bin/named/ns_main.c index f12f2852f089..d192ff6cba8d 100644 --- a/contrib/bind/bin/named/ns_main.c +++ b/contrib/bind/bin/named/ns_main.c @@ -1,3003 +1,3056 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_main.c 4.55 (Berkeley) 7/1/91"; -static const char rcsid[] = "$Id: ns_main.c,v 8.160 2002/06/24 07:06:55 marka Exp $"; +static const char rcsid[] = "$Id: ns_main.c,v 8.162.6.2 2003/06/08 22:08:02 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1989, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if !defined(lint) && !defined(SABER) char copyright[] = "@(#) Copyright (c) 1986, 1989, 1990 The Regents of the University of California.\n" "portions Copyright (c) 1993 Digital Equipment Corporation\n" "portions Copyright (c) 1995-1999 Internet Software Consortium\n" "portions Copyright (c) 1999 Check Point Software Technologies\n" "All rights reserved.\n"; #endif /* not lint */ /* * Internet Name server (see RCF1035 & others). */ #include "port_before.h" #include #include #include #include #include #include #include #include #ifdef SVR4 /* XXX */ # include #else #ifndef __hpux # include #endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #ifdef HAVE_GETRUSAGE /* XXX */ #include #endif #define MAIN_PROGRAM #include "named.h" #undef MAIN_PROGRAM +#ifdef TRUCLUSTER5 +# include +#endif + typedef void (*handler)(void); typedef struct _savedg { struct sockaddr_in from; int dfd; interface * ifp; time_t gen; u_char * buf; u_int16_t buflen; } savedg; /* list of interfaces */ static LIST(struct _interface) iflist; static int iflist_initialized = 0; static int iflist_dont_rescan = 0; static const int drbufsize = 32 * 1024, /* UDP rcv buf size */ dsbufsize = 48 * 1024, /* UDP snd buf size */ sbufsize = 16 * 1024, /* TCP snd buf size */ #ifdef BROKEN_RECVFROM nudptrans = 1, #else nudptrans = 20, /* #/udps per select */ #endif listenmax = 50; static u_int16_t nsid_state; static u_int16_t *nsid_pool; /* optional query id pool */ static u_int16_t *nsid_vtable; /* optional shuffle table */ static u_int32_t nsid_hash_state; static u_int16_t nsid_a1, nsid_a2, nsid_a3; static u_int16_t nsid_c1, nsid_c2, nsid_c3; static u_int16_t nsid_state2; static int nsid_algorithm; static int needs = 0, needs_exit = 0, needs_restart = 0; static handler handlers[main_need_num]; static void savedg_waitfunc(evContext, void*, const void*); static void need_waitfunc(evContext, void *, const void *); static int drain_rcvbuf(evContext, interface *, int, int *, int *); static int drain_all_rcvbuf(evContext); static struct qstream *sq_add(void); static int opensocket_d(interface *), opensocket_s(interface *); static void sq_query(struct qstream *), dq_remove(interface *); static int sq_dowrite(struct qstream *); static void use_desired_debug(void); static void stream_write(evContext, void *, int, int); -static interface * if_find(struct in_addr, u_int16_t port); +static interface * if_find(struct in_addr, u_int16_t port, + int anyport); static void deallocate_everything(void), stream_accept(evContext, void *, int, const void *, int, const void *, int), stream_getlen(evContext, void *, int, int), stream_getmsg(evContext, void *, int, int), datagram_read(evContext, void *, int, int), dispatch_message(u_char *, int, int, struct qstream *, struct sockaddr_in, int, interface *); static void stream_send(evContext, void *, int, const void *, int, const void *, int); static int only_digits(const char *); static void init_needs(void), handle_needs(void), exit_handler(void); #ifndef HAVE_CUSTOM static void custom_init(void), custom_shutdown(void); #endif static void usage() { fprintf(stderr, "Usage: named [-d #] [-q] [-r] [-v] [-f] [-p port] [[-b|-c] configfile]\n"); #ifdef CAN_CHANGE_ID fprintf(stderr, " [-u (username|uid)] [-g (groupname|gid)]\n"); #endif #ifdef HAVE_CHROOT fprintf(stderr, " [-t directory]\n"); #endif exit(1); } static const char bad_p_option[] = "-p remote/local obsolete; use 'listen-on' in config file to specify local"; static const char bad_directory[] = "chdir failed for directory '%s': %s"; /*ARGSUSED*/ int main(int argc, char *argv[]) { int n; char *p; int ch; struct passwd *pw; struct group *gr; #ifdef _AUX_SOURCE set42sig(); #endif debugfile = savestr(_PATH_DEBUG, 1); user_id = getuid(); group_id = getgid(); ns_port = htons(NAMESERVER_PORT); desired_debug = debug; /* BSD has a better random number generator but it's not clear * that we need it here. */ gettime(&tt); srand(((unsigned)getpid()) + (unsigned)tt.tv_usec); (void) umask(022); /* Save argv[] before getopt() destroys it -- needed for execvp(). */ saved_argv = malloc(sizeof(char *) * (argc + 1)); INSIST(saved_argv != NULL); for (n = 0; n < argc; n++) { saved_argv[n] = strdup(argv[n]); INSIST(saved_argv[n] != NULL); } saved_argv[argc] = NULL; /* XXX we need to free() this for clean shutdowns. */ while ((ch = getopt(argc, argv, "b:c:d:g:p:t:u:vw:qrf")) != -1) { switch (ch) { case 'b': case 'c': if (conffile != NULL) (void)freestr(conffile); conffile = savestr(optarg, 1); break; case 'd': desired_debug = atoi(optarg); if (desired_debug <= 0) desired_debug = 1; break; case 'p': /* use nonstandard port number. * usage: -p remote/local * remote is the port number to which * we send queries. local is the port * on which we listen for queries. * local defaults to same as remote. */ ns_port = htons((u_int16_t) atoi(optarg)); p = strchr(optarg, '/'); if (p) { syslog(LOG_WARNING, bad_p_option); fprintf(stderr, bad_p_option); fputc('\n', stderr); } break; case 'w': working_dir = savestr(optarg, 1); break; #ifdef QRYLOG case 'q': qrylog = 1; break; #endif case 'r': ns_setoption(OPTION_NORECURSE); break; case 'f': foreground = 1; break; case 't': chroot_dir = savestr(optarg, 1); break; case 'v': fprintf(stdout, "%s\n", Version); exit(0); #ifdef CAN_CHANGE_ID case 'u': user_name = savestr(optarg, 1); if (only_digits(user_name)) user_id = atoi(user_name); else { pw = getpwnam(user_name); if (pw == NULL) { fprintf(stderr, "user \"%s\" unknown\n", user_name); exit(1); } user_id = pw->pw_uid; if (group_name == NULL) { char name[256]; sprintf(name, "%lu", (u_long)pw->pw_gid); group_name = savestr(name, 1); group_id = pw->pw_gid; } } break; case 'g': if (group_name != NULL) (void)freestr(group_name); group_name = savestr(optarg, 1); if (only_digits(group_name)) group_id = atoi(group_name); else { gr = getgrnam(group_name); if (gr == NULL) { fprintf(stderr, "group \"%s\" unknown\n", group_name); exit(1); } group_id = gr->gr_gid; } break; #endif /* CAN_CHANGE_ID */ case '?': default: usage(); } } argc -= optind; argv += optind; if (argc) { if (conffile != NULL) (void)freestr(conffile); conffile = savestr(*argv, 1); argc--, argv++; } if (argc) usage(); if (conffile == NULL) conffile = savestr(_PATH_CONF, 1); /* * Make sure we don't inherit any open descriptors * other than those that daemon() can deal with. */ for (n = sysconf(_SC_OPEN_MAX) - 1; n >= 0; n--) if (n != STDIN_FILENO && n != STDOUT_FILENO && n != STDERR_FILENO) (void) close(n); /* * Chroot if desired. */ if (chroot_dir != NULL) { #ifdef HAVE_CHROOT if (chroot(chroot_dir) < 0) { fprintf(stderr, "chroot %s failed: %s\n", chroot_dir, strerror(errno)); exit(1); } if (chdir("/") < 0) { fprintf(stderr, "chdir(\"/\") failed: %s\n", strerror(errno)); exit(1); } #else fprintf(stderr, "warning: chroot() not available\n"); chroot_dir = freestr(chroot_dir); #endif } /* * Set working directory. */ if (working_dir != NULL) { if (chdir(working_dir) < 0) { syslog(LOG_CRIT, bad_directory, working_dir, strerror(errno)); fprintf(stderr, bad_directory, working_dir, strerror(errno)); fputc('\n', stderr); exit(1); } } /* Establish global event context. */ evCreate(&ev); /* Establish global resolver context. */ res_ninit(&res); res.options &= ~(RES_DEFNAMES | RES_DNSRCH | RES_RECURSE); /* * Set up logging. */ n = LOG_PID; #ifdef LOG_NOWAIT n |= LOG_NOWAIT; #endif #ifdef LOG_NDELAY n |= LOG_NDELAY; #endif #if defined(LOG_CONS) && defined(USE_LOG_CONS) n |= LOG_CONS; #endif #ifdef SYSLOG_42BSD openlog("named", n); #else openlog("named", n, ISC_FACILITY); #endif init_logging(); set_assertion_failure_callback(ns_assertion_failed); #ifdef DEBUG use_desired_debug(); #endif /* Perform system-dependent initialization */ custom_init(); init_needs(); init_signals(); ns_notice(ns_log_default, "starting (%s). %s", conffile, Version); /* * Initialize and load database. */ gettime(&tt); buildservicelist(); buildprotolist(); confmtime = ns_init(conffile); time(&boottime); resettime = boottime; nsid_init(); /* * Fork and go into background now that * we've done any slow initialization * and are ready to answer queries. */ if (foreground == 0) { if (daemon(1, 0)) ns_panic(ns_log_default, 1, "daemon: %s", strerror(errno)); update_pid_file(); } /* Check that udp checksums are on. */ ns_udp(); /* * We waited until now to log this because we wanted logging to * be set up the way the user prefers. */ if (chroot_dir != NULL) ns_info(ns_log_security, "chrooted to %s", chroot_dir); #ifdef CAN_CHANGE_ID /* * Set user and group if desired. */ if (group_name != NULL) { if (setgid(group_id) < 0) ns_panic(ns_log_security, 1, "setgid(%s): %s", group_name, strerror(errno)); ns_info(ns_log_security, "group = %s", group_name); } if (user_name != NULL) { if (getuid() == 0 && initgroups(user_name, group_id) < 0) ns_panic(ns_log_security, 1, "initgroups(%s, %d): %s", user_name, (int)group_id, strerror(errno)); endgrent(); endpwent(); if (setuid(user_id) < 0) ns_panic(ns_log_security, 1, "setuid(%s): %s", user_name, strerror(errno)); ns_info(ns_log_security, "user = %s", user_name); if (user_id != 0) iflist_dont_rescan++; } #endif /* CAN_CHANGE_ID */ ns_notice(ns_log_default, "Ready to answer queries."); gettime(&tt); prime_cache(); while (!needs_exit) { evEvent event; ns_debug(ns_log_default, 15, "main loop"); if (needs != 0) handle_needs(); else if (evGetNext(ev, &event, EV_WAIT) != -1) INSIST_ERR(evDispatch(ev, event) != -1); else INSIST_ERR(errno == EINTR); } if (needs_restart) ns_info(ns_log_default, "named restarting"); else ns_info(ns_log_default, "named shutting down"); #ifdef BIND_UPDATE dynamic_about_to_exit(); #endif if (server_options && server_options->pid_filename) (void)unlink(server_options->pid_filename); ns_logstats(ev, NULL, evNowTime(), evConsTime(0, 0)); if (NS_OPTION_P(OPTION_DEALLOC_ON_EXIT)) deallocate_everything(); else shutdown_configuration(); if (needs_restart) execvp(saved_argv[0], saved_argv); else /* Cleanup for system-dependent stuff */ custom_shutdown(); return (0); } static int sq_closeone(void) { struct qstream *sp, *nextsp; struct qstream *candidate = NULL; time_t lasttime, maxctime = 0; int result = 0; gettime(&tt); for (sp = streamq; sp; sp = nextsp) { nextsp = sp->s_next; if (sp->s_refcnt) continue; lasttime = tt.tv_sec - sp->s_time; if (lasttime >= VQEXPIRY) { sq_remove(sp); result = 1; } else if (lasttime > maxctime) { candidate = sp; maxctime = lasttime; } } if (candidate) { sq_remove(candidate); result = 1; } return (result); } static int ns_socket(int domain, int type, int protocol) { int fd, tmp; again: fd = socket(domain, type, protocol); #ifdef F_DUPFD /* XXX */ /* * Leave a space for stdio to work in. */ if (fd >= 0 && fd <= 20) { int new; if ((new = fcntl(fd, F_DUPFD, 20)) == -1) ns_notice(ns_log_default, "fcntl(fd, F_DUPFD, 20): %s", strerror(errno)); tmp = errno; close(fd); errno = tmp; fd = new; } #endif tmp = errno; if (errno == EMFILE) if (sq_closeone()) goto again; errno = tmp; return (fd); } #ifndef IP_OPT_BUF_SIZE /* arbitrary size */ #define IP_OPT_BUF_SIZE 50 #endif static void stream_accept(evContext lev, void *uap, int rfd, const void *lav, int lalen, const void *rav, int ralen) { interface *ifp = uap; struct qstream *sp; struct iovec iov; ISC_SOCKLEN_T len; int n; const int on = 1; #ifdef IP_OPTIONS /* XXX */ u_char ip_opts[IP_OPT_BUF_SIZE]; #endif const struct sockaddr_in *la, *ra; UNUSED(lalen); UNUSED(ralen); la = (const struct sockaddr_in *)lav; ra = (const struct sockaddr_in *)rav; INSIST(ifp != NULL); #ifdef F_DUPFD /* * Leave a space for stdio to work in. */ if (rfd >= 0 && rfd <= 20) { int new, tmp; new = fcntl(rfd, F_DUPFD, 20); tmp = errno; if (new == -1) ns_notice(ns_log_default, "fcntl(rfd, F_DUPFD, 20): %s", strerror(errno)); close(rfd); errno = tmp; rfd = new; } #endif if (rfd < 0) { switch (errno) { case EINTR: case EAGAIN: #if (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case ECONNABORTED: #ifdef EPROTO case EPROTO: #endif case EHOSTUNREACH: case EHOSTDOWN: case ENETUNREACH: case ENETDOWN: case ECONNREFUSED: #ifdef ENONET case ENONET: #endif /* * These errors are expected and harmless, so * we ignore them. */ return; case EBADF: case ENOTSOCK: case EFAULT: /* * If one these happens, we're broken. */ ns_panic(ns_log_default, 1, "accept: %s", strerror(errno)); case EMFILE: /* * If we're out of file descriptors, find the least * busy fd and close it. Then we'll return to the * eventlib which will call us right back. */ if (streamq) { (void)sq_closeone(); return; } /* fall through */ default: /* * Either we got an error we didn't expect, or we * got EMFILE and didn't have anything left to close. * Log it and press on. */ ns_info(ns_log_default, "accept: %s", strerror(errno)); return; } } /* Condition the socket. */ #ifndef CANNOT_SET_SNDBUF if (setsockopt(rfd, SOL_SOCKET, SO_SNDBUF, (const char*)&sbufsize, sizeof sbufsize) < 0) { ns_info(ns_log_default, "setsockopt(rfd, SO_SNDBUF, %d): %s", sbufsize, strerror(errno)); (void) close(rfd); return; } #endif if (setsockopt(rfd, SOL_SOCKET, SO_KEEPALIVE, (const char *)&on, sizeof on) < 0) { ns_info(ns_log_default, "setsockopt(rfd, KEEPALIVE): %s", strerror(errno)); (void) close(rfd); return; } #ifdef USE_FIONBIO_IOCTL if (ioctl(ifp->dfd, FIONBIO, (char *) &on) == -1) { ns_info(ns_log_default, "ioctl(rfd, FIONBIO): %s", strerror(errno)); (void) close(rfd); return; } #else if ((n = fcntl(rfd, F_GETFL, 0)) == -1) { ns_info(ns_log_default, "fcntl(rfd, F_GETFL): %s", strerror(errno)); (void) close(rfd); return; } if (fcntl(rfd, F_SETFL, n|PORT_NONBLOCK) == -1) { ns_info(ns_log_default, "fcntl(rfd, NONBLOCK): %s", strerror(errno)); (void) close(rfd); return; } #endif /* * We don't like IP options. Turn them off if the connection came in * with any. log this event since it usually indicates a security * problem. */ #if defined(IP_OPTIONS) /* XXX */ len = sizeof ip_opts; if (getsockopt(rfd, IPPROTO_IP, IP_OPTIONS, (char *)ip_opts, &len) < 0) { ns_info(ns_log_default, "getsockopt(rfd, IP_OPTIONS): %s", strerror(errno)); (void) close(rfd); return; } if (len != 0) { nameserIncr(ra->sin_addr, nssRcvdOpts); if (!haveComplained(ina_ulong(ra->sin_addr), (u_long)"rcvd ip options")) { ns_info(ns_log_default, "rcvd IP_OPTIONS from %s (ignored)", sin_ntoa(*ra)); } if (setsockopt(rfd, IPPROTO_IP, IP_OPTIONS, NULL, 0) < 0) { ns_info(ns_log_default, "setsockopt(!IP_OPTIONS): %s", strerror(errno)); (void) close(rfd); } } #endif /* Create and populate a qsp for this socket. */ if ((sp = sq_add()) == NULL) { (void) close(rfd); return; } sp->s_rfd = rfd; /* stream file descriptor */ gettime(&tt); sp->s_time = tt.tv_sec; /* last transaction time */ sp->s_from = *ra; /* address to respond to */ sp->s_ifp = ifp; INSIST(sizeof sp->s_temp >= INT16SZ); iov = evConsIovec(sp->s_temp, INT16SZ); - INSIST_ERR(evRead(lev, rfd, &iov, 1, stream_getlen, sp, &sp->evID_r) - != -1); + if (evRead(lev, rfd, &iov, 1, stream_getlen, sp, &sp->evID_r) == -1) { + ns_error(ns_log_default, "evRead(fd %d): %s", + rfd, strerror(errno)); + sq_remove(sp); + return; + } sp->flags |= STREAM_READ_EV; ns_debug(ns_log_default, 1, "IP/TCP connection from %s (fd %d)", sin_ntoa(sp->s_from), rfd); } int tcp_send(struct qinfo *qp) { struct qstream *sp; struct sockaddr_in src; int on = 1, n; int fd; ns_debug(ns_log_default, 1, "tcp_send"); if ((fd = ns_socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) == -1) return (SERVFAIL); if (fd > evHighestFD(ev)) { close(fd); return (SERVFAIL); } if ((sp = sq_add()) == NULL) { close(fd); return (SERVFAIL); } sp->s_rfd = fd; if (setsockopt(sp->s_rfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)) < 0) ns_info(ns_log_default, "tcp_send: setsockopt(SO_REUSEADDR): %s", strerror(errno)); #ifdef SO_REUSEPORT if (setsockopt(sp->s_rfd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on)) < 0) ns_info(ns_log_default, "tcp_send: setsockopt(SO_REUSEPORT): %s", strerror(errno)); #endif src = server_options->query_source; src.sin_port = htons(0); if (bind(sp->s_rfd, (struct sockaddr *)&src, sizeof(src)) < 0) ns_info(ns_log_default, "tcp_send: bind(query_source): %s", strerror(errno)); if (fcntl(sp->s_rfd, F_SETFD, 1) < 0) { sq_remove(sp); return (SERVFAIL); } #ifdef USE_FIONBIO_IOCTL if (ioctl(sp->s_rfd, FIONBIO, (char *) &on) == -1) { sq_remove(sp); return (SERVFAIL); } #else if ((n = fcntl(sp->s_rfd, F_GETFL, 0)) == -1) { sq_remove(sp); return (SERVFAIL); } if (fcntl(sp->s_rfd, F_SETFL, n|PORT_NONBLOCK) == -1) { sq_remove(sp); return (SERVFAIL); } #endif if (sq_openw(sp, qp->q_msglen + INT16SZ) == -1) { sq_remove(sp); return (SERVFAIL); } if (sq_write(sp, qp->q_msg, qp->q_msglen) == -1) { sq_remove(sp); return (SERVFAIL); } if (setsockopt(sp->s_rfd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on)) < 0) ns_info(ns_log_default, "tcp_send: setsockopt(SO_KEEPALIVE): %s", strerror(errno)); gettime(&tt); sp->s_size = -1; sp->s_time = tt.tv_sec; /* last transaction time */ sp->s_refcnt = 1; sp->flags |= STREAM_DONE_CLOSE; sp->s_from = qp->q_addr[qp->q_curaddr].ns_addr; if (evConnect(ev, sp->s_rfd, &sp->s_from, sizeof(sp->s_from), stream_send, sp, &sp->evID_c) == -1) { sq_remove(sp); return (SERVFAIL); } sp->flags |= STREAM_CONNECT_EV; return (NOERROR); } static void stream_send(evContext lev, void *uap, int fd, const void *la, int lalen, const void *ra, int ralen) { struct qstream *sp = uap; UNUSED(lev); UNUSED(la); UNUSED(lalen); UNUSED(ra); UNUSED(ralen); ns_debug(ns_log_default, 1, "stream_send"); sp->flags &= ~STREAM_CONNECT_EV; if (fd == -1) { /* connect failed */ sq_remove(sp); return; } if (evSelectFD(ev, sp->s_rfd, EV_WRITE, stream_write, sp, &sp->evID_w) < 0) { sq_remove(sp); return; } sp->flags |= STREAM_WRITE_EV; } static void stream_write(evContext ctx, void *uap, int fd, int evmask) { struct qstream *sp = uap; struct iovec iov; ns_debug(ns_log_default, 1, "stream_write"); INSIST(evmask & EV_WRITE); INSIST(fd == sp->s_rfd); if (sq_dowrite(sp) < 0) { sq_remove(sp); return; } if (sp->s_wbuf_free != sp->s_wbuf_send) return; if (sp->s_wbuf) { memput(sp->s_wbuf, sp->s_wbuf_end - sp->s_wbuf); sp->s_wbuf_send = sp->s_wbuf_free = NULL; sp->s_wbuf_end = sp->s_wbuf = NULL; } (void) evDeselectFD(ev, sp->evID_w); sp->flags &= ~STREAM_WRITE_EV; sp->s_refcnt = 0; iov = evConsIovec(sp->s_temp, INT16SZ); - INSIST_ERR(evRead(ctx, fd, &iov, 1, stream_getlen, sp, &sp->evID_r) != - -1); + if (evRead(ctx, fd, &iov, 1, stream_getlen, sp, &sp->evID_r) == -1) { + ns_error(ns_log_default, "evRead(fd %d): %s", + fd, strerror(errno)); + sq_remove(sp); + return; + } sp->flags |= STREAM_READ_EV; } static void stream_getlen(evContext lev, void *uap, int fd, int bytes) { struct qstream *sp = uap; struct iovec iov; UNUSED(fd); sp->flags &= ~STREAM_READ_EV; if (bytes != INT16SZ) { /* * bytes == 0 is normal EOF; see if something unusual * happened. */ if (bytes < 0) { /* * ECONNRESET happens frequently and is not worth * logging. */ if (errno != ECONNRESET) ns_info(ns_log_default, "stream_getlen(%s): %s", sin_ntoa(sp->s_from), strerror(errno)); } else if (bytes != 0) ns_error(ns_log_default, "stream_getlen(%s): unexpected byte count %d", sin_ntoa(sp->s_from), bytes); sq_remove(sp); return; } /* * Unpack the size, allocate memory for the query. This is * tricky since in a low memory situation with possibly very * large (64KB) queries, we want to make sure we can read at * least the header since we need it to send back a SERVFAIL * (owing to the out-of-memory condition). */ sp->s_size = ns_get16(sp->s_temp); ns_debug(ns_log_default, 5, "stream message: %d bytes", sp->s_size); if (sp->s_size < HFIXEDSZ) { ns_error(ns_log_default, "stream_getlen(%s): request too small", sin_ntoa(sp->s_from)); sq_remove(sp); return; } if (!(sp->flags & STREAM_MALLOC)) { sp->s_bufsize = 64*1024-1; /* maximum tcp message size */ sp->s_buf = (u_char *)memget(sp->s_bufsize); if (sp->s_buf != NULL) sp->flags |= STREAM_MALLOC; else { sp->s_buf = sp->s_temp; sp->s_bufsize = HFIXEDSZ; } } iov = evConsIovec(sp->s_buf, (sp->s_size <= sp->s_bufsize) ? sp->s_size : sp->s_bufsize); if (evRead(lev, sp->s_rfd, &iov, 1, stream_getmsg, sp, &sp->evID_r) - == -1) - ns_panic(ns_log_default, 1, "evRead(fd %d): %s", + == -1) { + ns_error(ns_log_default, "evRead(fd %d): %s", sp->s_rfd, strerror(errno)); + sq_remove(sp); + return; + } sp->flags |= STREAM_READ_EV; } static void stream_getmsg(evContext lev, void *uap, int fd, int bytes) { struct qstream *sp = uap; UNUSED(lev); UNUSED(fd); sp->flags &= ~STREAM_READ_EV; if (bytes == -1) { ns_info(ns_log_default, "stream_getmsg(%s): %s", sin_ntoa(sp->s_from), strerror(errno)); sq_remove(sp); return; } gettime(&tt); sp->s_time = tt.tv_sec; if (ns_wouldlog(ns_log_default,5)) { ns_debug(ns_log_default, 5, "sp %p rfd %d size %d time %ld next %p", sp, sp->s_rfd, sp->s_size, (long)sp->s_time, sp->s_next); ns_debug(ns_log_default, 5, "\tbufsize %d bytes %d", sp->s_bufsize, bytes); } /* * Do we have enough memory for the query? If not, and if we have a * query id, then we will send a SERVFAIL error back to the client. */ if (bytes != sp->s_size) { HEADER *hp = (HEADER *)sp->s_buf; hp->qr = 1; hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); hp->ancount = htons(0); hp->qdcount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); hp->rcode = SERVFAIL; writestream(sp, sp->s_buf, HFIXEDSZ); sp->flags |= STREAM_DONE_CLOSE; return; } nameserIncr(sp->s_from.sin_addr, nssRcvdTCP); sq_query(sp); dispatch_message(sp->s_buf, bytes, sp->s_bufsize, sp, sp->s_from, -1, sp->s_ifp); } static void datagram_read(evContext lev, void *uap, int fd, int evmask) { interface *ifp = uap; struct sockaddr_in from; ISC_SOCKLEN_T from_len = sizeof from; int n, nudp; union { HEADER h; /* Force alignment of 'buf'. */ u_char buf[EDNS_MESSAGE_SZ+1]; } u; UNUSED(lev); UNUSED(evmask); tt = evTimeVal(evNowTime()); nudp = 0; more: n = recvfrom(fd, (char *)u.buf, sizeof u.buf, 0, (struct sockaddr *)&from, &from_len); if (n < 0) { switch (errno) { case EINTR: case EAGAIN: #if (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case EHOSTUNREACH: case EHOSTDOWN: case ENETUNREACH: case ENETDOWN: case ECONNREFUSED: #ifdef ENONET case ENONET: #endif /* * These errors are expected and harmless, so we * ignore them. */ return; default: /* * An error we don't expect. Log it and press * on. */ ns_info(ns_log_default, "recvfrom: %s", strerror(errno)); return; } } /* Handle bogosity on systems that need it. */ if (n == 0) return; if (ns_wouldlog(ns_log_default, 1)) { ns_debug(ns_log_default, 1, "datagram from %s, fd %d, len %d", sin_ntoa(from), fd, n); } if (n > EDNS_MESSAGE_SZ) { /* * The message is too big. It's probably a response to * one of our questions, so we truncate it and press on. */ n = trunc_adjust(u.buf, EDNS_MESSAGE_SZ, EDNS_MESSAGE_SZ); ns_debug(ns_log_default, 1, "truncated oversize UDP packet"); } dispatch_message(u.buf, n, EDNS_MESSAGE_SZ, NULL, from, fd, ifp); if (++nudp < nudptrans) goto more; } static void savedg_waitfunc(evContext ctx, void *uap, const void *tag) { savedg *dg = (savedg *)uap; UNUSED(ctx); UNUSED(tag); if (!EMPTY(iflist) && HEAD(iflist)->gen == dg->gen) { u_char buf[EDNS_MESSAGE_SZ]; memcpy(buf, dg->buf, dg->buflen); dispatch_message(buf, dg->buflen, sizeof buf, NULL, dg->from, dg->dfd, dg->ifp); } memput(dg->buf, dg->buflen); memput(dg, sizeof *dg); } static void dispatch_message(u_char *msg, int msglen, int buflen, struct qstream *qsp, struct sockaddr_in from, int dfd, interface *ifp) { HEADER *hp = (HEADER *)msg; if (msglen < HFIXEDSZ) { ns_debug(ns_log_default, 1, "dropping undersize message"); if (qsp) { qsp->flags |= STREAM_DONE_CLOSE; sq_done(qsp); } return; } if (server_options->blackhole_acl != NULL && ip_match_address(server_options->blackhole_acl, from.sin_addr) == 1) { ns_debug(ns_log_default, 1, "dropping blackholed %s from %s", hp->qr ? "response" : "query", sin_ntoa(from)); if (qsp) { qsp->flags |= STREAM_DONE_CLOSE; sq_done(qsp); } return; } /* Drop UDP packets from port zero. They are invariable forged. */ if (qsp == NULL && ntohs(from.sin_port) == 0) { ns_notice(ns_log_security, "dropping source port zero packet from %s", sin_ntoa(from)); return; } if (hp->qr) { ns_resp(msg, msglen, from, qsp); if (qsp) sq_done(qsp); /* Now is a safe time for housekeeping. */ if (needs_prime_cache) prime_cache(); } else if (ifp != NULL) ns_req(msg, msglen, buflen, qsp, from, dfd); else { ns_notice(ns_log_security, "refused query on non-query socket from %s", sin_ntoa(from)); if (qsp) { qsp->flags |= STREAM_DONE_CLOSE; sq_done(qsp); } /* XXX Send refusal here. */ } } void getnetconf(int periodic_scan) { struct ifconf ifc; struct ifreq ifreq; struct in_addr ina; interface *ifp; char *buf, *cp, *cplim; static int bufsiz = 4095; time_t my_generation = time(NULL); int s, cpsize, n; int found; listen_info li; ip_match_element ime; u_char *mask_ptr; struct in_addr mask; +#ifdef TRUCLUSTER5 + struct sockaddr clua_addr; + int clua_cnt, clua_tot; +#endif + int clua_buf; if (iflist_initialized) { if (iflist_dont_rescan) return; } else { INIT_LIST(iflist); iflist_initialized = 1; } ns_debug(ns_log_default, 1, "getnetconf(generation %lu)", (u_long)my_generation); /* Get interface list from system. */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { if (!periodic_scan) ns_panic(ns_log_default, 1, "socket(SOCK_RAW): %s", strerror(errno)); ns_error(ns_log_default, "socket(SOCK_RAW): %s", strerror(errno)); return; } if (local_addresses != NULL) free_ip_match_list(local_addresses); local_addresses = new_ip_match_list(); if (local_networks != NULL) free_ip_match_list(local_networks); local_networks = new_ip_match_list(); +#ifdef TRUCLUSTER5 + /* Find out how many cluster aliases there are */ + clua_cnt = 0; + clua_tot = 0; + while (clua_getaliasaddress(&clua_addr, &clua_cnt) == CLUA_SUCCESS) + clua_tot ++; + clua_buf = clua_tot * sizeof(ifreq); +#else + clua_buf = 0; +#endif + for (;;) { - buf = memget(bufsiz); + buf = memget(bufsiz + clua_buf); if (!buf) ns_panic(ns_log_default, 1, "memget(interface)"); ifc.ifc_len = bufsiz; ifc.ifc_buf = buf; #ifdef IRIX_EMUL_IOCTL_SIOCGIFCONF /* * This is a fix for IRIX OS in which the call to ioctl with * the flag SIOCGIFCONF may not return an entry for all the * interfaces like most flavors of Unix. */ if (emul_ioctl(&ifc) >= 0) break; #else if ((n = ioctl(s, SIOCGIFCONF, (char *)&ifc)) != -1) { /* * Some OS's just return what will fit rather * than set EINVAL if the buffer is too small * to fit all the interfaces in. If * ifc.ifc_len is too near to the end of the * buffer we will grow it just in case and * retry. */ if ((int)(ifc.ifc_len + 2 * sizeof(ifreq)) < bufsiz) break; } #endif if ((n == -1) && errno != EINVAL) ns_panic(ns_log_default, 1, "get interface configuration: %s", strerror(errno)); if (bufsiz > 1000000) ns_panic(ns_log_default, 1, "get interface configuration: maximum buffer size exceeded"); - memput(buf, bufsiz); + memput(buf, bufsiz + clua_buf); bufsiz += 4096; } +#ifdef TRUCLUSTER5 + /* Get the cluster aliases and create interface entries for them */ + clua_cnt = 0; + while (clua_tot--) { + memset(&ifreq, 0, sizeof (ifreq)); + if (clua_getaliasaddress(&ifreq.ifr_addr, &clua_cnt) != + CLUA_SUCCESS) + /* + * It is possible the count of aliases has changed; if + * it has increased, they won't be found this pass. + * If has decreased, stop the loop early. */ + break; + strcpy(ifreq.ifr_name, "lo0"); + memcpy(ifc.ifc_buf + ifc.ifc_len, &ifreq, sizeof (ifreq)); + ifc.ifc_len += sizeof (ifreq); + bufsiz += sizeof (ifreq); + } +#endif + ns_debug(ns_log_default, 2, "getnetconf: SIOCGIFCONF: ifc_len = %d", ifc.ifc_len); /* Parse system's interface list and open some sockets. */ cplim = buf + ifc.ifc_len; /* skip over if's with big ifr_addr's */ for (cp = buf; cp < cplim; cp += cpsize) { memcpy(&ifreq, cp, sizeof ifreq); #ifdef HAVE_SA_LEN #ifdef FIX_ZERO_SA_LEN if (ifreq.ifr_addr.sa_len == 0) ifreq.ifr_addr.sa_len = 16; #endif #ifdef HAVE_MINIMUM_IFREQ ns_debug(ns_log_default, 2, "%s sa_len = %d", ifreq.ifr_name, (int)ifreq.ifr_addr.sa_len); cpsize = sizeof ifreq; if (ifreq.ifr_addr.sa_len > sizeof (struct sockaddr)) cpsize += (int)ifreq.ifr_addr.sa_len - (int)(sizeof (struct sockaddr)); #else cpsize = sizeof ifreq.ifr_name + ifreq.ifr_addr.sa_len; #endif /* HAVE_MINIMUM_IFREQ */ #elif defined SIOCGIFCONF_ADDR cpsize = sizeof ifreq; #else cpsize = sizeof ifreq.ifr_name; if (ioctl(s, SIOCGIFADDR, (char *)&ifreq) < 0) { ns_notice(ns_log_default, "get interface addr (%s): %s", ifreq.ifr_name, strerror(errno)); continue; } #endif if (ifreq.ifr_addr.sa_family != AF_INET) { ns_debug(ns_log_default, 2, "getnetconf: %s AF %d != INET", ifreq.ifr_name, ifreq.ifr_addr.sa_family); continue; } ina = ina_get((u_char *)&((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr); ns_debug(ns_log_default, 1, "getnetconf: considering %s [%s]", ifreq.ifr_name, inet_ntoa(ina)); /* * Don't test IFF_UP, packets may still be received at this * address if any other interface is up. */ if (ina_hlong(ina) == INADDR_ANY) { ns_debug(ns_log_default, 2, "getnetconf: INADDR_ANY, ignoring."); continue; } INSIST(server_options != NULL); INSIST(server_options->listen_list != NULL); found=0; for (li = server_options->listen_list->first; li != NULL; li = li->next) { if (ip_match_address(li->list, ina) > 0) { found++; /* * Look for an already existing source * interface address/port pair. * This happens mostly when reinitializing. * Also, if the machine has multiple point to * point interfaces, then the local address * may appear more than once. */ - ifp = if_find(ina, li->port); + ifp = if_find(ina, li->port, 0); if (ifp != NULL) { ns_debug(ns_log_default, 1, "dup interface addr [%s].%u (%s)", inet_ntoa(ina), ntohs(li->port), ifreq.ifr_name); ifp->gen = my_generation; continue; } ifp = (interface *)memget(sizeof *ifp); if (!ifp) ns_panic(ns_log_default, 1, "memget(interface)"); memset(ifp, 0, sizeof *ifp); INIT_LINK(ifp, link); APPEND(iflist, ifp, link); ifp->addr = ina; ifp->port = li->port; ifp->gen = my_generation; ifp->flags = 0; ifp->dfd = -1; ifp->sfd = -1; if (opensocket_d(ifp) < 0 || opensocket_s(ifp) < 0) { dq_remove(ifp); found = 0; break; } ns_info(ns_log_default, "listening on [%s].%u (%s)", inet_ntoa(ina), ntohs(li->port), ifreq.ifr_name); } } if (!found) ns_debug(ns_log_default, 1, "not listening on addr [%s] (%s)", inet_ntoa(ina), ifreq.ifr_name); /* * Add this interface's address to the list of local * addresses if we haven't added it already. */ if (ip_match_address(local_addresses, ina) < 0) { ime = new_ip_match_pattern(ina, 32); add_to_ip_match_list(local_addresses, ime); } /* * Get interface flags. */ if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) { ns_notice(ns_log_default, "get interface flags: %s", strerror(errno)); continue; } if ((ifreq.ifr_flags & IFF_POINTOPOINT)) { /* * The local network for a PPP link is just the * two ends of the link, so for each endpoint we * add a pattern that will only match the endpoint. */ if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { ns_notice(ns_log_default, "get dst addr: %s", strerror(errno)); continue; } mask.s_addr = htonl(INADDR_BROADCAST); /* * Our end. * * Only add it if we haven't seen it before. */ if (ip_match_network(local_networks, ina, mask) < 0) { ime = new_ip_match_pattern(ina, 32); add_to_ip_match_list(local_networks, ime); } /* * The other end. */ ina = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr; /* * Only add it if we haven't seen it before. */ if (ip_match_network(local_networks, ina, mask) < 0) { ime = new_ip_match_pattern(ina, 32); add_to_ip_match_list(local_networks, ime); } } else { /* * Add this interface's network and netmask to the * list of local networks. */ #ifdef SIOCGIFNETMASK /* XXX */ if (ioctl(s, SIOCGIFNETMASK, (char *)&ifreq) < 0) { ns_notice(ns_log_default, "get netmask: %s", strerror(errno)); continue; } /* * Use ina_get because the ifreq structure might not * be aligned. */ mask_ptr = (u_char *) &((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr; mask = ina_get(mask_ptr); #else mask = net_mask(ina); #endif ina.s_addr &= mask.s_addr; /* make network address */ /* * Only add it if we haven't seen it before. */ if (ip_match_network(local_networks, ina, mask) < 0) { ime = new_ip_match_mask(ina, mask); add_to_ip_match_list(local_networks, ime); } } } close(s); memput(buf, bufsiz); ns_debug(ns_log_default, 7, "local addresses:"); dprint_ip_match_list(ns_log_default, local_addresses, 2, "", ""); ns_debug(ns_log_default, 7, "local networks:"); dprint_ip_match_list(ns_log_default, local_networks, 2, "", ""); /* * now go through the iflist and delete anything that * does not have the current generation number. this is * how we catch interfaces that go away or change their * addresses. note that 0.0.0.0 is the wildcard element * and should never be deleted by this code. */ dq_remove_gen(my_generation); if (EMPTY(iflist)) ns_warning(ns_log_default, "not listening on any interfaces"); } /* opensocket_d(ifp) * Open datagram socket bound to interface address. * Returns: * 0 on success. * -1 on failure. */ static int opensocket_d(interface *ifp) { struct sockaddr_in nsa; const int on = 1; ISC_SOCKLEN_T m; int n; memset(&nsa, 0, sizeof nsa); nsa.sin_family = AF_INET; nsa.sin_addr = ifp->addr; nsa.sin_port = ifp->port; if ((ifp->dfd = ns_socket(AF_INET, SOCK_DGRAM, 0)) < 0) { ns_error(ns_log_default, "socket(SOCK_DGRAM): %s", strerror(errno)); return (-1); } if (ifp->dfd > evHighestFD(ev)) { ns_error(ns_log_default, "socket too high: %d", ifp->dfd); close(ifp->dfd); return (-1); } #ifdef USE_FIONBIO_IOCTL if (ioctl(ifp->dfd, FIONBIO, (char *) &on) == -1) { ns_info(ns_log_default, "ioctl(ifp->dfd, FIONBIO): %s", strerror(errno)); (void) close(ifp->dfd); return (-1); } #else if ((n = fcntl(ifp->dfd, F_GETFL, 0)) == -1) { ns_info(ns_log_default, "fcntl(ifp->dfd, F_GETFL): %s", strerror(errno)); (void) close(ifp->dfd); return (-1); } if (fcntl(ifp->dfd, F_SETFL, n|PORT_NONBLOCK) == -1) { ns_info(ns_log_default, "fcntl(ifp->dfd, NONBLOCK): %s", strerror(errno)); (void) close(ifp->dfd); return (-1); } #endif if (fcntl(ifp->dfd, F_SETFD, 1) < 0) { ns_error(ns_log_default, "F_SETFD: %s", strerror(errno)); close(ifp->dfd); return (-1); } ns_debug(ns_log_default, 1, "ifp->addr %s d_dfd %d", sin_ntoa(nsa), ifp->dfd); if (setsockopt(ifp->dfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)) != 0) { ns_notice(ns_log_default, "setsockopt(REUSEADDR): %s", strerror(errno)); /* XXX press on regardless, this is not too serious. */ } #ifdef SO_RCVBUF /* XXX */ m = sizeof n; if ((getsockopt(ifp->dfd, SOL_SOCKET, SO_RCVBUF, (char*)&n, &m) >= 0) && (m == sizeof n) && (n < drbufsize)) { (void) setsockopt(ifp->dfd, SOL_SOCKET, SO_RCVBUF, (const char *)&drbufsize, sizeof drbufsize); } #endif /* SO_RCVBUF */ #ifndef CANNOT_SET_SNDBUF if (setsockopt(ifp->dfd, SOL_SOCKET, SO_SNDBUF, (const char*)&dsbufsize, sizeof dsbufsize) < 0) { ns_info(ns_log_default, "setsockopt(dfd=%d, SO_SNDBUF, %d): %s", ifp->dfd, dsbufsize, strerror(errno)); /* XXX press on regardless, this is not too serious. */ } #endif #ifdef SO_BSDCOMPAT if (setsockopt(ifp->dfd, SOL_SOCKET, SO_BSDCOMPAT, (char*)&on, sizeof on) < 0) { ns_info(ns_log_default, "setsockopt(dfd=%d, SO_BSDCOMPAT): %s", ifp->dfd, strerror(errno)); } #endif if (bind(ifp->dfd, (struct sockaddr *)&nsa, sizeof nsa)) { ns_error(ns_log_default, "bind(dfd=%d, %s): %s", ifp->dfd, sin_ntoa(nsa), strerror(errno)); return (-1); } if (evSelectFD(ev, ifp->dfd, EV_READ, datagram_read, ifp, &ifp->evID_d) == -1) { ns_error(ns_log_default, "evSelectFD(dfd=%d): %s", ifp->dfd, strerror(errno)); return (-1); } ifp->flags |= INTERFACE_FILE_VALID; return (0); } static int drain_rcvbuf(evContext ctx, interface *ifp, int fd, int *mread, int *mstore) { int drop = 0; drop = 0; for (; *mread > 0; (*mread)--) { union { HEADER h; u_char buf[EDNS_MESSAGE_SZ+1]; } u; struct sockaddr_in from; ISC_SOCKLEN_T from_len = sizeof from; savedg *dg; int n; n = recvfrom(fd, (char *)u.buf, sizeof u.buf, 0, (struct sockaddr *)&from, &from_len); if (n <= 0) break; /* Socket buffer assumed empty. */ drop++; /* Pessimistic assumption. */ if (n > EDNS_MESSAGE_SZ) continue; /* Oversize message - EDNS0 needed. */ if (from.sin_family != AF_INET) continue; /* Not IPv4 - IPv6 needed. */ if (u.h.opcode == ns_o_query && u.h.qr == 0) continue; /* Query - what we're here to axe. */ if (*mstore <= 0) continue; /* Reached storage quota, ignore. */ if ((dg = memget(sizeof *dg)) == NULL) continue; /* No memory - probably fatal. */ if ((dg->buf = memget(n)) == NULL) { memput(dg, sizeof *dg); continue; /* No memory - probably fatal. */ } dg->from = from; dg->dfd = fd; dg->ifp = ifp; dg->gen = ifp->gen; dg->buflen = n; memcpy(dg->buf, u.buf, n); if (evWaitFor(ctx, (void *)drain_all_rcvbuf, savedg_waitfunc, dg, NULL) < 0) { memput(dg->buf, dg->buflen); memput(dg, sizeof *dg); continue; /* No memory - probably fatal. */ } drop--; /* Pessimism was inappropriate. */ (*mstore)--; } return (drop); } static int drain_all_rcvbuf(evContext ctx) { interface *ifp; int mread = MAX_SYNCDRAIN; int mstore = MAX_SYNCSTORE; int drop = 0; for (ifp = HEAD(iflist); ifp != NULL; ifp = NEXT(ifp, link)) if (ifp->dfd != -1) drop += drain_rcvbuf(ctx, ifp, ifp->dfd, &mread, &mstore); if (mstore < MAX_SYNCSTORE) INSIST_ERR(evDo(ctx, (void *)drain_all_rcvbuf) != -1); return (drop); } /* opensocket_s(ifp) * Open stream (listener) socket bound to interface address. * Returns: * 0 on success. * -1 on failure. */ static int opensocket_s(interface *ifp) { struct sockaddr_in nsa; const int on = 1; int n; memset(&nsa, 0, sizeof nsa); nsa.sin_family = AF_INET; nsa.sin_addr = ifp->addr; nsa.sin_port = ifp->port; /* * Open stream (listener) port. */ n = 0; again: if ((ifp->sfd = ns_socket(AF_INET, SOCK_STREAM, 0)) < 0) { ns_error(ns_log_default, "socket(SOCK_STREAM): %s", strerror(errno)); return (-1); } if (ifp->sfd > evHighestFD(ev)) { ns_error(ns_log_default, "socket too high: %d", ifp->sfd); close(ifp->sfd); return (-1); } if (fcntl(ifp->sfd, F_SETFD, 1) < 0) { ns_error(ns_log_default, "F_SETFD: %s", strerror(errno)); close(ifp->sfd); return (-1); } if (setsockopt(ifp->sfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof on) != 0) { ns_notice(ns_log_default, "setsockopt(REUSEADDR): %s", strerror(errno)); /* Consider that your first warning of trouble to come. */ } if (bind(ifp->sfd, (struct sockaddr *)&nsa, sizeof nsa) < 0) { if (errno != EADDRINUSE || ++n > 4) { if (errno == EADDRINUSE) ns_error(ns_log_default, "There may be a name server already running on %s", sin_ntoa(nsa)); else ns_error(ns_log_default, "bind(sfd=%d, %s): %s", ifp->sfd, sin_ntoa(nsa), strerror(errno)); return (-1); } /* Retry opening the socket a few times */ close(ifp->sfd); ifp->sfd = -1; sleep(30); goto again; } if (evListen(ev, ifp->sfd, listenmax, stream_accept, ifp, &ifp->evID_s) == -1) { ns_error(ns_log_default, "evListen(sfd=%d): %s", ifp->sfd, strerror(errno)); return (-1); } ifp->flags |= INTERFACE_CONN_VALID; return (0); } /* opensocket_f() * Open datagram socket bound to no particular interface; use for ns_forw * and sysquery. */ void opensocket_f() { static struct sockaddr_in prev_qsrc; static int been_here; static interface *prev_ifp; struct sockaddr_in nsa; const int on = 1; ISC_SOCKLEN_T n; int need_close; interface *ifp; need_close = 0; if (been_here) { if (prev_ifp != NULL) prev_ifp->flags &= ~INTERFACE_FORWARDING; else if (server_options->query_source.sin_port == htons(0) || prev_qsrc.sin_addr.s_addr != server_options->query_source.sin_addr.s_addr || prev_qsrc.sin_port != server_options->query_source.sin_port) need_close = 1; } else ds = -1; been_here = 1; INSIST(server_options != NULL); if (need_close) { evDeselectFD(ev, ds_evID); close(ds); ds = -1; } /* * If we're already listening on the query_source address and port, * we don't need to open another socket. We mark the interface, so * we'll notice we're in trouble if it goes away. */ ifp = if_find(server_options->query_source.sin_addr, - server_options->query_source.sin_port); + server_options->query_source.sin_port, 0); if (ifp != NULL) { ifp->flags |= INTERFACE_FORWARDING; prev_ifp = ifp; ds = ifp->dfd; ns_info(ns_log_default, "forwarding source address is %s", sin_ntoa(server_options->query_source)); return; } /* * If we're already using the correct query source, we're done. */ if (ds >= 0) return; prev_qsrc = server_options->query_source; prev_ifp = NULL; if ((ds = socket(AF_INET, SOCK_DGRAM, 0)) < 0) ns_panic(ns_log_default, 1, "socket(SOCK_DGRAM): %s", strerror(errno)); if (ds > evHighestFD(ev)) ns_panic(ns_log_default, 1, "socket too high: %d", ds); if (fcntl(ds, F_SETFD, 1) < 0) ns_panic(ns_log_default, 1, "F_SETFD: %s", strerror(errno)); if (setsockopt(ds, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof on) != 0) { ns_notice(ns_log_default, "setsockopt(REUSEADDR): %s", strerror(errno)); /* XXX press on regardless, this is not too serious. */ } #ifdef SO_BSDCOMPAT if (setsockopt(ds, SOL_SOCKET, SO_BSDCOMPAT, (char *)&on, sizeof on) != 0) { ns_notice(ns_log_default, "setsockopt(BSDCOMPAT): %s", strerror(errno)); /* XXX press on regardless, this is not too serious. */ } #endif if (bind(ds, (struct sockaddr *)&server_options->query_source, sizeof server_options->query_source) < 0) ns_panic(ns_log_default, 0, "opensocket_f: bind(%s): %s", sin_ntoa(server_options->query_source), strerror(errno)); n = sizeof nsa; if (getsockname(ds, (struct sockaddr *)&nsa, &n) < 0) ns_panic(ns_log_default, 1, "opensocket_f: getsockaddr: %s", strerror(errno)); ns_debug(ns_log_default, 1, "fwd ds %d addr %s", ds, sin_ntoa(nsa)); ns_info(ns_log_default, "Forwarding source address is %s", sin_ntoa(nsa)); if (evSelectFD(ev, ds, EV_READ, datagram_read, NULL, &ds_evID) == -1) ns_panic(ns_log_default, 1, "evSelectFD(fd %d): %s", ds, strerror(errno)); /* XXX: should probably use a different FileFunc that only accepts * responses, since requests on this socket make no sense. */ } static void setdebug(int new_debug) { #ifdef DEBUG int old_debug; if (!new_debug) ns_debug(ns_log_default, 1, "Debug off"); old_debug = debug; debug = new_debug; log_option(log_ctx, LOG_OPTION_DEBUG, debug); log_option(log_ctx, LOG_OPTION_LEVEL, debug); if (old_debug && !debug) log_close_debug_channels(log_ctx); evSetDebug(ev, debug, log_get_stream(eventlib_channel)); if (debug) { if (!old_debug) open_special_channels(); ns_debug(ns_log_default, 1, "Debug level %d", debug); if (!old_debug) { ns_debug(ns_log_default, 1, "Version = %s", Version); ns_debug(ns_log_default, 1, "conffile = %s", conffile); } } #endif } /* ** Routines for managing stream queue */ static struct qstream * sq_add() { struct qstream *sqp; if (!(sqp = (struct qstream *)memget(sizeof *sqp))) { ns_error(ns_log_default, "sq_add: memget: %s", strerror(errno)); return (NULL); } memset(sqp, 0, sizeof *sqp); ns_debug(ns_log_default, 3, "sq_add(%#lx)", (u_long)sqp); sqp->flags = 0; /* XXX should init other fields too? */ sqp->s_next = streamq; streamq = sqp; return (sqp); } /* sq_remove(qp) * remove stream queue structure `qp'. * no current queries may refer to this stream when it is removed. * side effects: * memory is deallocated. sockets are closed. lists are relinked. */ void sq_remove(struct qstream *qp) { struct qstream *qsp; ns_debug(ns_log_default, 2, "sq_remove(%#lx, %d) rfcnt=%d", (u_long)qp, qp->s_rfd, qp->s_refcnt); if (qp->s_wbuf != NULL) { memput(qp->s_wbuf, qp->s_wbuf_end - qp->s_wbuf); qp->s_wbuf_send = qp->s_wbuf_free = NULL; qp->s_wbuf_end = qp->s_wbuf = NULL; } if (qp->flags & STREAM_MALLOC) memput(qp->s_buf, qp->s_bufsize); if (qp->flags & STREAM_READ_EV) INSIST_ERR(evCancelRW(ev, qp->evID_r) != -1); if (qp->flags & STREAM_WRITE_EV) INSIST_ERR(evDeselectFD(ev, qp->evID_w) != -1); if (qp->flags & STREAM_CONNECT_EV) INSIST_ERR(evCancelConn(ev, qp->evID_c) != -1); if (qp->flags & STREAM_AXFR || qp->flags & STREAM_AXFRIXFR) ns_freexfr(qp); (void) close(qp->s_rfd); if (qp == streamq) streamq = qp->s_next; else { for (qsp = streamq; qsp && (qsp->s_next != qp); qsp = qsp->s_next) (void)NULL; if (qsp) qsp->s_next = qp->s_next; } memput(qp, sizeof *qp); } /* void * sq_flush(allbut) * call sq_remove() on all open streams except `allbut' * side effects: * global list `streamq' modified * idiocy: * is N^2 due to the scan inside of sq_remove() */ void sq_flush(struct qstream *allbut) { struct qstream *sp, *spnext; for (sp = streamq; sp != NULL; sp = spnext) { spnext = sp->s_next; if (sp != allbut) sq_remove(sp); } } /* int * sq_openw(qs, buflen) * add a write buffer to a stream * return: * 0 = success * -1 = failure (check errno) */ int sq_openw(struct qstream *qs, int buflen) { #ifdef DO_SO_LINGER /* XXX */ static const struct linger ll = { 1, 120 }; #endif INSIST(qs->s_wbuf == NULL); qs->s_wbuf = (u_char *)memget(buflen); if (qs->s_wbuf == NULL) return (-1); qs->s_wbuf_send = qs->s_wbuf; qs->s_wbuf_free = qs->s_wbuf; qs->s_wbuf_end = qs->s_wbuf + buflen; #ifdef DO_SO_LINGER /* XXX */ /* kernels that map pages for IO end up failing if the pipe is full * at exit and we take away the final buffer. this is really a kernel * bug but it's harmless on systems that are not broken, so... */ setsockopt(qs->s_rfd, SOL_SOCKET, SO_LINGER, (char *)&ll, sizeof ll); #endif return (0); } /* static void * sq_dowrite(qs) * try to submit data to the system, remove it from our queue. */ static int sq_dowrite(struct qstream *qs) { if (qs->s_wbuf_free > qs->s_wbuf_send) { int n = write(qs->s_rfd, qs->s_wbuf_send, qs->s_wbuf_free - qs->s_wbuf_send); INSIST(qs->s_wbuf != NULL); if (n < 0) { if (errno != EINTR && errno != EAGAIN #if (EWOULDBLOCK != EAGAIN) && errno != EWOULDBLOCK #endif ) return (-1); return (0); } qs->s_wbuf_send += n; if (qs->s_wbuf_free > qs->s_wbuf_send) { /* XXX: need some kind of delay here during which the * socket will be deselected so we don't spin. */ n = qs->s_wbuf_free - qs->s_wbuf_send; memmove(qs->s_wbuf, qs->s_wbuf_send, n); qs->s_wbuf_send = qs->s_wbuf; qs->s_wbuf_free = qs->s_wbuf + n; } } if (qs->s_wbuf_free == qs->s_wbuf_send) qs->s_wbuf_free = qs->s_wbuf_send = qs->s_wbuf; return (0); } /* void * sq_flushw(qs) * called when the socket becomes writable and we want to flush our * buffers and the system's socket buffers. use as a closure with * sq_writeh(). */ void sq_flushw(struct qstream *qs) { if (qs->s_wbuf_free == qs->s_wbuf_send) { sq_writeh(qs, NULL); sq_done(qs); } } /* static void * sq_writable(ctx, uap, fd, evmask) * glue between eventlib closures and qstream closures */ static void sq_writable(evContext ctx, void *uap, int fd, int evmask) { struct qstream *qs = uap; UNUSED(ctx); INSIST(evmask & EV_WRITE); INSIST(fd == qs->s_rfd); if (sq_dowrite(qs) < 0) { sq_remove(qs); return; } if (qs->s_wbuf_closure && qs->s_wbuf_end - qs->s_wbuf_free >= HFIXEDSZ+2) /* XXX guess */ (*qs->s_wbuf_closure)(qs); if (sq_dowrite(qs) < 0) { sq_remove(qs); return; } } /* int * sq_writeh(qs, closure) * register a closure to be called when a stream becomes writable * return: * 0 = success * -1 = failure (check errno) */ int sq_writeh(struct qstream *qs, sq_closure c) { if (c) { if (!qs->s_wbuf_closure) { if (evSelectFD(ev, qs->s_rfd, EV_WRITE, sq_writable, qs, &qs->evID_w) < 0) { return (-1); } qs->flags |= STREAM_WRITE_EV; } } else { (void) evDeselectFD(ev, qs->evID_w); qs->flags &= ~STREAM_WRITE_EV; } qs->s_wbuf_closure = c; return (0); } /* int * sq_write(qs, buf, len) * queue a message onto the stream, prepended by a two byte length field * return: * 0 = success * -1 = failure (check errno; E2BIG means we can't handle this right now) */ int sq_write(struct qstream *qs, const u_char *buf, int len) { INSIST(qs->s_wbuf != NULL); if (NS_INT16SZ + len > qs->s_wbuf_end - qs->s_wbuf_free) { if (sq_dowrite(qs) < 0) return (-1); if (NS_INT16SZ + len > qs->s_wbuf_end - qs->s_wbuf_free) { errno = E2BIG; return (-1); } } - __putshort(len, qs->s_wbuf_free); + ns_put16(len, qs->s_wbuf_free); qs->s_wbuf_free += NS_INT16SZ; memcpy(qs->s_wbuf_free, buf, len); qs->s_wbuf_free += len; return (0); } /* * Initiate query on stream; * mark as referenced and stop selecting for input. */ static void sq_query(struct qstream *sp) { sp->s_refcnt++; } /* * Note that the current request on a stream has completed, * and that we should continue looking for requests on the stream. */ void sq_done(struct qstream *sp) { struct iovec iov; if (sp->s_wbuf != NULL) { INSIST(sp->s_wbuf_send == sp->s_wbuf_free); memput(sp->s_wbuf, sp->s_wbuf_end - sp->s_wbuf); sp->s_wbuf_send = sp->s_wbuf_free = NULL; sp->s_wbuf_end = sp->s_wbuf = NULL; } if (sp->flags & STREAM_AXFR || sp->flags & STREAM_AXFRIXFR) ns_freexfr(sp); sp->s_refcnt = 0; sp->s_time = tt.tv_sec; if (sp->flags & STREAM_DONE_CLOSE) { /* XXX */ sq_remove(sp); return; } iov = evConsIovec(sp->s_temp, INT16SZ); if (evRead(ev, sp->s_rfd, &iov, 1, stream_getlen, sp, &sp->evID_r) == - -1) - ns_panic(ns_log_default, 1, "evRead(fd %d): %s", + -1) { + ns_error(ns_log_default, "evRead(fd %d): %s", sp->s_rfd, strerror(errno)); + sq_remove(sp); + return; + } sp->flags |= STREAM_READ_EV; } /* void * dq_remove_gen(gen) * close/deallocate all the udp sockets (except 0.0.0.0) which are * not from the current generation. * side effects: * global list `iflist' is modified. */ void dq_remove_gen(time_t gen) { interface *this, *next; for (this = HEAD(iflist); this != NULL; this = next) { next = NEXT(this, link); if (this->gen != gen && ina_hlong(this->addr) != INADDR_ANY) dq_remove(this); } } /* void * dq_remove_all() * close/deallocate all interfaces. * side effects: * global list `iflist' is modified. */ void dq_remove_all() { interface *this, *next; for (this = HEAD(iflist); this != NULL; this = next) { next = NEXT(this, link); /* * Clear the forwarding flag so we don't panic the server. */ this->flags &= ~INTERFACE_FORWARDING; dq_remove(this); } } /* void * dq_remove(interface *this) * close/deallocate an interface's sockets. called on errors * or if the interface disappears. * side effects: * global list `iflist' is modified. */ static void dq_remove(interface *this) { ns_notice(ns_log_default, "deleting interface [%s].%u", inet_ntoa(this->addr), ntohs(this->port)); if ((this->flags & INTERFACE_FORWARDING) != 0) ns_panic(ns_log_default, 0, "forwarding interface [%s].%u gone", inet_ntoa(this->addr), ntohs(this->port)); /* Deallocate fields. */ if ((this->flags & INTERFACE_FILE_VALID) != 0) (void) evDeselectFD(ev, this->evID_d); if (this->dfd >= 0) (void) close(this->dfd); if ((this->flags & INTERFACE_CONN_VALID) != 0) (void) evCancelConn(ev, this->evID_s); if (this->sfd >= 0) (void) close(this->sfd); UNLINK(iflist, this, link); memput(this, sizeof *this); } /* struct in_addr * net_mask(ina) * makes a classful assumption in a classless world, and returns it. */ struct in_addr net_mask(struct in_addr ina) { u_long hl = ina_hlong(ina); struct in_addr ret; if (IN_CLASSA(hl)) hl = IN_CLASSA_NET; else if (IN_CLASSB(hl)) hl = IN_CLASSB_NET; else if (IN_CLASSC(hl)) hl = IN_CLASSC_NET; else hl = INADDR_BROADCAST; ina_ulong(ret) = htonl(hl); return (ret); } /* aIsUs(addr) * scan our list of interface addresses for "addr". * returns: * 0: address isn't one of our interfaces * >0: address is one of our interfaces, or INADDR_ANY */ int aIsUs(struct in_addr addr) { - if (ina_hlong(addr) == INADDR_ANY || if_find(addr, 0) != NULL) + if (ina_hlong(addr) == INADDR_ANY || if_find(addr, 0, 1) != NULL) return (1); return (0); } /* interface * - * if_find(addr, port) + * if_find(addr, port, anyport) * scan our list of interface addresses for "addr" and port. - * port == 0 means match any port * returns: * pointer to interface with this address/port, or NULL if there isn't * one. */ static interface * -if_find(struct in_addr addr, u_int16_t port) { +if_find(struct in_addr addr, u_int16_t port, int anyport) { interface *ifp; for (ifp = HEAD(iflist); ifp != NULL; ifp = NEXT(ifp, link)) if (ina_equal(addr, ifp->addr)) - if (port == 0 || ifp->port == port) + if (anyport || ifp->port == port) break; return (ifp); } /* * These are here in case we ever want to get more clever, like perhaps * using a bitmap to keep track of outstanding queries and a random * allocation scheme to make it a little harder to predict them. Note * that the resolver will need the same protection so the cleverness * should be put there rather than here; this is just an interface layer. * * This is true but ... most clients only send out a few queries, they * use varying port numbers, and the queries aren't sent to the outside * world which we know is full of spoofers. Doing a good job of randomizing * ids may also be to expensive for each client. Queries forwarded by the * server always come from the same port (unless you let 8.x pick a port * and restart it periodically - maybe it should open several and use * them randomly). The server sends out lots more queries, and if it's * cache is corrupted, it has the potential to affect more clients. * NOTE: - randomizing the ID or source port doesn't help a bit if the * queries can be sniffed. * -- DL */ /* * Allow the user to pick one of two ID randomization algorithms. * * The first algorithm is an adaptation of the sequence shuffling * algorithm discovered by Carter Bays and S. D. Durham [ACM Trans. Math. * Software 2 (1976), 59-64], as documented as Algorithm B in Chapter * 3.2.2 in Volume 2 of Knuth's "The Art of Computer Programming". We use * a randomly selected linear congruential random number generator with a * modulus of 2^16, whose increment is a randomly picked odd number, and * whose multiplier is picked from a set which meets the following * criteria: * Is of the form 8*n+5, which ensures "high potency" according to * principle iii in the summary chapter 3.6. This form also has a * gcd(a-1,m) of 4 which is good according to principle iv. * * Is between 0.01 and 0.99 times the modulus as specified by * principle iv. * * Passes the spectral test "with flying colors" (ut >= 1) in * dimensions 2 through 6 as calculated by Algorithm S in Chapter * 3.3.4 and the ratings calculated by formula 35 in section E. * * Of the multipliers that pass this test, pick the set that is * best according to the theoretical bounds of the serial * correlation test. This was calculated using a simplified * version of Knuth's Theorem K in Chapter 3.3.3. * * These criteria may not be important for this use, but we might as well * pick from the best generators since there are so many possible ones and * we don't have that many random bits to do the picking. * * We use a modulus of 2^16 instead of something bigger so that we will * tend to cycle through all the possible IDs before repeating any, * however the shuffling will perturb this somewhat. Theoretically there * is no minimimum interval between two uses of the same ID, but in * practice it seems to be >64000. * * Our adaptatation of Algorithm B mixes the hash state which has * captured various random events into the shuffler to perturb the * sequence. * * One disadvantage of this algorithm is that if the generator parameters * were to be guessed, it would be possible to mount a limited brute force * attack on the ID space since the IDs are only shuffled within a limited * range. * * The second algorithm uses the same random number generator to populate * a pool of 65536 IDs. The hash state is used to pick an ID from a window * of 4096 IDs in this pool, then the chosen ID is swapped with the ID * at the beginning of the window and the window position is advanced. * This means that the interval between uses of the ID will be no less * than 65536-4096. The ID sequence in the pool will become more random * over time. * * For both algorithms, two more linear congruential random number generators * are selected. The ID from the first part of algorithm is used to seed * the first of these generators, and its output is used to seed the second. * The strategy is use these generators as 1 to 1 hashes to obfuscate the * properties of the generator used in the first part of either algorithm. * * The first algorithm may be suitable for use in a client resolver since * its memory requirements are fairly low and it's pretty random out of * the box. It is somewhat succeptible to a limited brute force attack, * so the second algorithm is probably preferable for a longer running * program that issues a large number of queries and has time to randomize * the pool. */ #define NSID_SHUFFLE_TABLE_SIZE 100 /* Suggested by Knuth */ /* * Pick one of the next 4096 IDs in the pool. * There is a tradeoff here between randomness and how often and ID is reused. */ #define NSID_LOOKAHEAD 4096 /* Must be a power of 2 */ #define NSID_SHUFFLE_ONLY 1 /* algorithm 1 */ #define NSID_USE_POOL 2 /* algorithm 2 */ /* * Keep a running hash of various bits of data that we'll use to * stir the ID pool or perturb the ID generator */ void nsid_hash(u_char *data, size_t len) { /* * Hash function similar to the one we use for hashing names. * We don't fold case or toss the upper bit here, though. * This hash doesn't do much interesting when fed binary zeros, * so there may be a better hash function. * This function doesn't need to be very strong since we're * only using it to stir the pool, but it should be reasonably * fast. */ while (len-- > 0) { nsid_hash_state = HASHROTATE(nsid_hash_state); nsid_hash_state += *data++; } } /* * Table of good linear congruential multipliers for modulus 2^16 * in order of increasing serial correlation bounds (so trim from * the end). */ static const u_int16_t nsid_multiplier_table[] = { 17565, 25013, 11733, 19877, 23989, 23997, 24997, 25421, 26781, 27413, 35901, 35917, 35973, 36229, 38317, 38437, 39941, 40493, 41853, 46317, 50581, 51429, 53453, 53805, 11317, 11789, 12045, 12413, 14277, 14821, 14917, 18989, 19821, 23005, 23533, 23573, 23693, 27549, 27709, 28461, 29365, 35605, 37693, 37757, 38309, 41285, 45261, 47061, 47269, 48133, 48597, 50277, 50717, 50757, 50805, 51341, 51413, 51581, 51597, 53445, 11493, 14229, 20365, 20653, 23485, 25541, 27429, 29421, 30173, 35445, 35653, 36789, 36797, 37109, 37157, 37669, 38661, 39773, 40397, 41837, 41877, 45293, 47277, 47845, 49853, 51085, 51349, 54085, 56933, 8877, 8973, 9885, 11365, 11813, 13581, 13589, 13613, 14109, 14317, 15765, 15789, 16925, 17069, 17205, 17621, 17941, 19077, 19381, 20245, 22845, 23733, 24869, 25453, 27213, 28381, 28965, 29245, 29997, 30733, 30901, 34877, 35485, 35613, 36133, 36661, 36917, 38597, 40285, 40693, 41413, 41541, 41637, 42053, 42349, 45245, 45469, 46493, 48205, 48613, 50861, 51861, 52877, 53933, 54397, 55669, 56453, 56965, 58021, 7757, 7781, 8333, 9661, 12229, 14373, 14453, 17549, 18141, 19085, 20773, 23701, 24205, 24333, 25261, 25317, 27181, 30117, 30477, 34757, 34885, 35565, 35885, 36541, 37957, 39733, 39813, 41157, 41893, 42317, 46621, 48117, 48181, 49525, 55261, 55389, 56845, 7045, 7749, 7965, 8469, 9133, 9549, 9789, 10173, 11181, 11285, 12253, 13453, 13533, 13757, 14477, 15053, 16901, 17213, 17269, 17525, 17629, 18605, 19013, 19829, 19933, 20069, 20093, 23261, 23333, 24949, 25309, 27613, 28453, 28709, 29301, 29541, 34165, 34413, 37301, 37773, 38045, 38405, 41077, 41781, 41925, 42717, 44437, 44525, 44613, 45933, 45941, 47077, 50077, 50893, 52117, 5293, 55069, 55989, 58125, 59205, 6869, 14685, 15453, 16821, 17045, 17613, 18437, 21029, 22773, 22909, 25445, 25757, 26541, 30709, 30909, 31093, 31149, 37069, 37725, 37925, 38949, 39637, 39701, 40765, 40861, 42965, 44813, 45077, 45733, 47045, 50093, 52861, 52957, 54181, 56325, 56365, 56381, 56877, 57013, 5741, 58101, 58669, 8613, 10045, 10261, 10653, 10733, 11461, 12261, 14069, 15877, 17757, 21165, 23885, 24701, 26429, 26645, 27925, 28765, 29197, 30189, 31293, 39781, 39909, 40365, 41229, 41453, 41653, 42165, 42365, 47421, 48029, 48085, 52773, 5573, 57037, 57637, 58341, 58357, 58901, 6357, 7789, 9093, 10125, 10709, 10765, 11957, 12469, 13437, 13509, 14773, 15437, 15773, 17813, 18829, 19565, 20237, 23461, 23685, 23725, 23941, 24877, 25461, 26405, 29509, 30285, 35181, 37229, 37893, 38565, 40293, 44189, 44581, 45701, 47381, 47589, 48557, 4941, 51069, 5165, 52797, 53149, 5341, 56301, 56765, 58581, 59493, 59677, 6085, 6349, 8293, 8501, 8517, 11597, 11709, 12589, 12693, 13517, 14909, 17397, 18085, 21101, 21269, 22717, 25237, 25661, 29189, 30101, 31397, 33933, 34213, 34661, 35533, 36493, 37309, 40037, 4189, 42909, 44309, 44357, 44389, 4541, 45461, 46445, 48237, 54149, 55301, 55853, 56621, 56717, 56901, 5813, 58437, 12493, 15365, 15989, 17829, 18229, 19341, 21013, 21357, 22925, 24885, 26053, 27581, 28221, 28485, 30605, 30613, 30789, 35437, 36285, 37189, 3941, 41797, 4269, 42901, 43293, 44645, 45221, 46893, 4893, 50301, 50325, 5189, 52109, 53517, 54053, 54485, 5525, 55949, 56973, 59069, 59421, 60733, 61253, 6421, 6701, 6709, 7101, 8669, 15797, 19221, 19837, 20133, 20957, 21293, 21461, 22461, 29085, 29861, 30869, 34973, 36469, 37565, 38125, 38829, 39469, 40061, 40117, 44093, 47429, 48341, 50597, 51757, 5541, 57629, 58405, 59621, 59693, 59701, 61837, 7061, 10421, 11949, 15405, 20861, 25397, 25509, 25893, 26037, 28629, 28869, 29605, 30213, 34205, 35637, 36365, 37285, 3773, 39117, 4021, 41061, 42653, 44509, 4461, 44829, 4725, 5125, 52269, 56469, 59085, 5917, 60973, 8349, 17725, 18637, 19773, 20293, 21453, 22533, 24285, 26333, 26997, 31501, 34541, 34805, 37509, 38477, 41333, 44125, 46285, 46997, 47637, 48173, 4925, 50253, 50381, 50917, 51205, 51325, 52165, 52229, 5253, 5269, 53509, 56253, 56341, 5821, 58373, 60301, 61653, 61973, 62373, 8397, 11981, 14341, 14509, 15077, 22261, 22429, 24261, 28165, 28685, 30661, 34021, 34445, 39149, 3917, 43013, 43317, 44053, 44101, 4533, 49541, 49981, 5277, 54477, 56357, 57261, 57765, 58573, 59061, 60197, 61197, 62189, 7725, 8477, 9565, 10229, 11437, 14613, 14709, 16813, 20029, 20677, 31445, 3165, 31957, 3229, 33541, 36645, 3805, 38973, 3965, 4029, 44293, 44557, 46245, 48917, 4909, 51749, 53709, 55733, 56445, 5925, 6093, 61053, 62637, 8661, 9109, 10821, 11389, 13813, 14325, 15501, 16149, 18845, 22669, 26437, 29869, 31837, 33709, 33973, 34173, 3677, 3877, 3981, 39885, 42117, 4421, 44221, 44245, 44693, 46157, 47309, 5005, 51461, 52037, 55333, 55693, 56277, 58949, 6205, 62141, 62469, 6293, 10101, 12509, 14029, 17997, 20469, 21149, 25221, 27109, 2773, 2877, 29405, 31493, 31645, 4077, 42005, 42077, 42469, 42501, 44013, 48653, 49349, 4997, 50101, 55405, 56957, 58037, 59429, 60749, 61797, 62381, 62837, 6605, 10541, 23981, 24533, 2701, 27333, 27341, 31197, 33805, 3621, 37381, 3749, 3829, 38533, 42613, 44381, 45901, 48517, 51269, 57725, 59461, 60045, 62029, 13805, 14013, 15461, 16069, 16157, 18573, 2309, 23501, 28645, 3077, 31541, 36357, 36877, 3789, 39429, 39805, 47685, 47949, 49413, 5485, 56757, 57549, 57805, 58317, 59549, 62213, 62613, 62853, 62933, 8909, 12941, 16677, 20333, 21541, 24429, 26077, 26421, 2885, 31269, 33381, 3661, 40925, 42925, 45173, 4525, 4709, 53133, 55941, 57413, 57797, 62125, 62237, 62733, 6773, 12317, 13197, 16533, 16933, 18245, 2213, 2477, 29757, 33293, 35517, 40133, 40749, 4661, 49941, 62757, 7853, 8149, 8573, 11029, 13421, 21549, 22709, 22725, 24629, 2469, 26125, 2669, 34253, 36709, 41013, 45597, 46637, 52285, 52333, 54685, 59013, 60997, 61189, 61981, 62605, 62821, 7077, 7525, 8781, 10861, 15277, 2205, 22077, 28517, 28949, 32109, 33493, 3685, 39197, 39869, 42621, 44997, 48565, 5221, 57381, 61749, 62317, 63245, 63381, 23149, 2549, 28661, 31653, 33885, 36341, 37053, 39517, 42805, 45853, 48997, 59349, 60053, 62509, 63069, 6525, 1893, 20181, 2365, 24893, 27397, 31357, 32277, 33357, 34437, 36677, 37661, 43469, 43917, 50997, 53869, 5653, 13221, 16741, 17893, 2157, 28653, 31789, 35301, 35821, 61613, 62245, 12405, 14517, 17453, 18421, 3149, 3205, 40341, 4109, 43941, 46869, 48837, 50621, 57405, 60509, 62877, 8157, 12933, 12957, 16501, 19533, 3461, 36829, 52357, 58189, 58293, 63053, 17109, 1933, 32157, 37701, 59005, 61621, 13029, 15085, 16493, 32317, 35093, 5061, 51557, 62221, 20765, 24613, 2629, 30861, 33197, 33749, 35365, 37933, 40317, 48045, 56229, 61157, 63797, 7917, 17965, 1917, 1973, 20301, 2253, 33157, 58629, 59861, 61085, 63909, 8141, 9221, 14757, 1581, 21637, 26557, 33869, 34285, 35733, 40933, 42517, 43501, 53653, 61885, 63805, 7141, 21653, 54973, 31189, 60061, 60341, 63357, 16045, 2053, 26069, 33997, 43901, 54565, 63837, 8949, 17909, 18693, 32349, 33125, 37293, 48821, 49053, 51309, 64037, 7117, 1445, 20405, 23085, 26269, 26293, 27349, 32381, 33141, 34525, 36461, 37581, 43525, 4357, 43877, 5069, 55197, 63965, 9845, 12093, 2197, 2229, 32165, 33469, 40981, 42397, 8749, 10853, 1453, 18069, 21693, 30573, 36261, 37421, 42533 }; #define NSID_MULT_TABLE_SIZE \ ((sizeof nsid_multiplier_table)/(sizeof nsid_multiplier_table[0])) void nsid_init(void) { struct timeval now; pid_t mypid; u_int16_t a1ndx, a2ndx, a3ndx, c1ndx, c2ndx, c3ndx; int i; if (nsid_algorithm != 0) return; gettimeofday(&now, NULL); mypid = getpid(); /* Initialize the state */ nsid_hash_state = 0; nsid_hash((u_char *)&now, sizeof now); nsid_hash((u_char *)&mypid, sizeof mypid); /* * Select our random number generators and initial seed. * We could really use more random bits at this point, * but we'll try to make a silk purse out of a sows ear ... */ /* generator 1 */ a1ndx = ((u_long) NSID_MULT_TABLE_SIZE * (nsid_hash_state & 0xFFFF)) >> 16; nsid_a1 = nsid_multiplier_table[a1ndx]; c1ndx = (nsid_hash_state >> 9) & 0x7FFF; nsid_c1 = 2*c1ndx + 1; /* generator 2, distinct from 1 */ a2ndx = ((u_long) (NSID_MULT_TABLE_SIZE - 1) * ((nsid_hash_state >> 10) & 0xFFFF)) >> 16; if (a2ndx >= a1ndx) a2ndx++; nsid_a2 = nsid_multiplier_table[a2ndx]; c2ndx = nsid_hash_state % 32767; if (c2ndx >= c1ndx) c2ndx++; nsid_c2 = 2*c2ndx + 1; /* generator 3, distinct from 1 and 2 */ a3ndx = ((u_long) (NSID_MULT_TABLE_SIZE - 2) * ((nsid_hash_state >> 20) & 0xFFFF)) >> 16; if (a3ndx >= a1ndx || a3ndx >= a2ndx) a3ndx++; if (a3ndx >= a1ndx && a3ndx >= a2ndx) a3ndx++; nsid_a3 = nsid_multiplier_table[a3ndx]; c3ndx = nsid_hash_state % 32766; if (c3ndx >= c1ndx || c3ndx >= c2ndx) c3ndx++; if (c3ndx >= c1ndx && c3ndx >= c2ndx) c3ndx++; nsid_c3 = 2*c3ndx + 1; nsid_state = ((nsid_hash_state >> 16) ^ (nsid_hash_state)) & 0xFFFF; /* Do the algorithm specific initialization */ INSIST(server_options != NULL); if (NS_OPTION_P(OPTION_USE_ID_POOL) == 0) { /* Algorithm 1 */ nsid_algorithm = NSID_SHUFFLE_ONLY; nsid_vtable = memget(NSID_SHUFFLE_TABLE_SIZE * (sizeof(u_int16_t)) ); if (!nsid_vtable) ns_panic(ns_log_default, 1, "memget(nsid_vtable)"); for (i = 0; i < NSID_SHUFFLE_TABLE_SIZE; i++) { nsid_vtable[i] = nsid_state; nsid_state = (((u_long) nsid_a1 * nsid_state) + nsid_c1) & 0xFFFF; } nsid_state2 = nsid_state; } else { /* Algorithm 2 */ nsid_algorithm = NSID_USE_POOL; nsid_pool = memget(0x10000 * (sizeof(u_int16_t))); if (!nsid_pool) ns_panic(ns_log_default, 1, "memget(nsid_pool)"); for (i = 0; ; i++) { nsid_pool[i] = nsid_state; nsid_state = (((u_long) nsid_a1 * nsid_state) + nsid_c1) & 0xFFFF; if (i == 0xFFFF) break; } } } #define NSID_RANGE_MASK (NSID_LOOKAHEAD - 1) #define NSID_POOL_MASK 0xFFFF /* used to wrap the pool index */ u_int16_t nsid_next() { u_int16_t id, compressed_hash; compressed_hash = ((nsid_hash_state >> 16) ^ (nsid_hash_state)) & 0xFFFF; if (nsid_algorithm == NSID_SHUFFLE_ONLY) { u_int16_t j; /* * This is the original Algorithm B * j = ((u_long) NSID_SHUFFLE_TABLE_SIZE * nsid_state2) * >> 16; * * We'll perturb it with some random stuff ... */ j = ((u_long) NSID_SHUFFLE_TABLE_SIZE * (nsid_state2 ^ compressed_hash)) >> 16; nsid_state2 = id = nsid_vtable[j]; nsid_state = (((u_long) nsid_a1 * nsid_state) + nsid_c1) & 0xFFFF; nsid_vtable[j] = nsid_state; } else if (nsid_algorithm == NSID_USE_POOL) { u_int16_t pick; pick = compressed_hash & NSID_RANGE_MASK; id = nsid_pool[(nsid_state + pick) & NSID_POOL_MASK]; if (pick != 0) { /* Swap two IDs to stir the pool */ nsid_pool[(nsid_state + pick) & NSID_POOL_MASK] = nsid_pool[nsid_state]; nsid_pool[nsid_state] = id; } /* increment the base pointer into the pool */ if (nsid_state == 65535) nsid_state = 0; else nsid_state++; } else { id = 0; /* silence compiler */ ns_panic(ns_log_default, 1, "Unknown ID algorithm"); } /* Now lets obfuscate ... */ id = (((u_long) nsid_a2 * id) + nsid_c2) & 0xFFFF; id = (((u_long) nsid_a3 * id) + nsid_c3) & 0xFFFF; return (id); } /* Note: this function CAN'T deallocate the saved_argv[]. */ static void deallocate_everything(void) { FILE *f; f = write_open(server_options->memstats_filename); ns_freestats(); qflush(); sq_flush(NULL); free_addinfo(); ns_shutdown(); dq_remove_all(); db_lame_destroy(); if (local_addresses != NULL) free_ip_match_list(local_addresses); if (local_networks != NULL) free_ip_match_list(local_networks); destroyservicelist(); destroyprotolist(); shutdown_logging(); evDestroy(ev); if (conffile != NULL) freestr(conffile); conffile = NULL; if (debugfile != NULL) freestr(debugfile); debugfile = NULL; if (user_name != NULL) freestr(user_name); user_name = NULL; if (group_name != NULL) freestr(group_name); group_name = NULL; if (chroot_dir != NULL) freestr(chroot_dir); chroot_dir = NULL; if (working_dir != NULL) freestr(working_dir); working_dir = NULL; if (nsid_pool != NULL) memput(nsid_pool, 0x10000 * (sizeof(u_int16_t))); nsid_pool = NULL; if (nsid_vtable != NULL) memput(nsid_vtable, NSID_SHUFFLE_TABLE_SIZE * (sizeof(u_int16_t))); nsid_vtable = NULL; irs_destroy(); if (f != NULL) { memstats(f); (void)fclose(f); } if (memactive()) abort(); } static void ns_restart(void) { needs_restart = 1; needs_exit = 1; } static void use_desired_debug(void) { #ifdef DEBUG sigset_t set; /* Protect against race conditions by blocking debugging signals. */ if (sigemptyset(&set) < 0) { ns_error(ns_log_os, "sigemptyset failed in use_desired_debug: %s", strerror(errno)); return; } if (sigaddset(&set, SIGUSR1) < 0) { ns_error(ns_log_os, "sigaddset SIGUSR1 failed in use_desired_debug: %s", strerror(errno)); return; } if (sigaddset(&set, SIGUSR2) < 0) { ns_error(ns_log_os, "sigaddset SIGUSR2 failed in use_desired_debug: %s", strerror(errno)); return; } if (sigprocmask(SIG_BLOCK, &set, NULL) < 0) { ns_error(ns_log_os, "sigprocmask to block USR1 and USR2 failed: %s", strerror(errno)); return; } setdebug(desired_debug); if (sigprocmask(SIG_UNBLOCK, &set, NULL) < 0) ns_error(ns_log_os, "sigprocmask to unblock USR1 and USR2 failed: %s", strerror(errno)); #endif } void toggle_qrylog(void) { qrylog = !qrylog; ns_notice(ns_log_default, "query log %s\n", qrylog ?"on" :"off"); } static void wild(void) { ns_panic(ns_log_default, 1, "wild need"); } /* * This is a functional interface to the global needs and options. */ static void init_needs(void) { int need; for (need = 0; need < main_need_num; need++) handlers[need] = wild; handlers[main_need_zreload] = ns_zreload; handlers[main_need_reload] = ns_reload; handlers[main_need_reconfig] = ns_reconfig; handlers[main_need_endxfer] = endxfer; handlers[main_need_zoneload] = loadxfer; handlers[main_need_dump] = doadump; handlers[main_need_statsdump] = ns_stats; handlers[main_need_statsdumpandclear] = ns_stats_dumpandclear; handlers[main_need_exit] = exit_handler; handlers[main_need_qrylog] = toggle_qrylog; handlers[main_need_debug] = use_desired_debug; handlers[main_need_restart] = ns_restart; handlers[main_need_reap] = reapchild; handlers[main_need_noexpired] = ns_noexpired; handlers[main_need_tryxfer] = tryxfer; } static void handle_needs(void) { int need, queued = 0; ns_debug(ns_log_default, 15, "handle_needs()"); block_signals(); for (need = 0; need < main_need_num; need++) if ((needs & (1 << need)) != 0) { INSIST_ERR(evWaitFor(ev, (void *)handle_needs, need_waitfunc, (void *)handlers[need], NULL) != -1); queued++; } needs = 0; unblock_signals(); ns_debug(ns_log_default, 15, "handle_needs(): queued %d", queued); if (queued != 0) { INSIST_ERR(evDo(ev, (void *)handle_needs) != -1); return; } ns_panic(ns_log_default, 1, "ns_handle_needs: queued == 0"); } static void need_waitfunc(evContext ctx, void *uap, const void *tag) { handler hand = (handler) uap; time_t begin; long syncdelay; UNUSED(tag); begin = time(NULL); (*hand)(); syncdelay = time(NULL) - begin; if (syncdelay > MAX_SYNCDELAY) ns_notice(ns_log_default, "drained %d queries (delay %ld sec)", drain_all_rcvbuf(ctx), syncdelay); } void ns_need(enum need need) { block_signals(); ns_need_unsafe(need); unblock_signals(); } /* Note: this function should only be called with signals blocked. */ void ns_need_unsafe(enum need need) { needs |= (1 << need); } static void exit_handler(void) { needs_exit = 1; } void ns_setoption(int option) { ns_warning(ns_log_default, "used obsolete ns_setoption(%d)", option); } void writestream(struct qstream *sp, const u_char *msg, int msglen) { if (sq_openw(sp, msglen + INT16SZ) == -1) { sq_remove(sp); return; } if (sq_write(sp, msg, msglen) == -1) { sq_remove(sp); return; } sq_writeh(sp, sq_flushw); } static int only_digits(const char *s) { if (*s == '\0') return (0); while (*s != '\0') { if (!isdigit(*s)) return (0); s++; } return (1); } #if defined(__GNUC__) && defined(__BOUNDS_CHECKING_ON) /* Use bounds checking malloc, etc. */ void * memget(size_t len) { return (malloc(len)); } void memput(void *addr, size_t len) { free(addr); } int meminit(size_t init_max_size, size_t target_size) { return (0); } void * memget_debug(size_t size, const char *file, int line) { void *ptr; ptr = __memget(size); fprintf(stderr, "%s:%d: memget(%lu) -> %p\n", file, line, (u_long)size, ptr); return (ptr); } void memput_debug(void *ptr, size_t size, const char *file, int line) { fprintf(stderr, "%s:%d: memput(%p, %lu)\n", file, line, ptr, (u_long)size); __memput(ptr, size); } void memstats(FILE *out) { fputs("No memstats\n", out); } #endif #ifndef HAVE_CUSTOM /* Standard implementation has nothing here */ static void custom_init(void) { /* Noop. */ } static void custom_shutdown(void) { /* Noop. */ } #endif diff --git a/contrib/bind/bin/named/ns_maint.c b/contrib/bind/bin/named/ns_maint.c index be4b6f9c53ef..0618ab9a9419 100644 --- a/contrib/bind/bin/named/ns_maint.c +++ b/contrib/bind/bin/named/ns_maint.c @@ -1,2073 +1,2092 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_maint.c 4.39 (Berkeley) 3/2/91"; -static const char rcsid[] = "$Id: ns_maint.c,v 8.136 2002/06/26 03:27:20 marka Exp $"; +static const char rcsid[] = "$Id: ns_maint.c,v 8.137.8.1 2003/06/02 05:34:25 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1988 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "port_before.h" #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 "port_after.h" #include "named.h" static int nxfers(struct zoneinfo *), bottom_of_zone(struct databuf *, int); static void startxfer(struct zoneinfo *), abortxfer(struct zoneinfo *), purge_z_2(struct hashbuf *, int); static int purge_nonglue_2(const char *, struct hashbuf *, - int, int, int); + int, int, int, int); #ifndef HAVE_SPAWNXFER static pid_t spawnxfer(char **, struct zoneinfo *); #endif /* State of all running zone transfers */ static struct { pid_t xfer_pid; int xfer_state; /* see below */ WAIT_T xfer_status; struct in_addr xfer_addr; } xferstatus[MAX_XFERS_RUNNING]; #define XFER_IDLE 0 #define XFER_RUNNING 1 #define XFER_DONE 2 /* * Perform routine zone maintenance. */ void zone_maint(struct zoneinfo *zp) { gettime(&tt); ns_debug(ns_log_maint, 1, "zone_maint('%s'); now %lu", zp->z_origin[0] == '\0' ? "." : zp->z_origin, (u_long)tt.tv_sec); #ifdef DEBUG if (debug >= 2) printzoneinfo((zp - zones), ns_log_maint, 2); #endif switch (zp->z_type) { case Z_SECONDARY: /*FALLTHROUGH*/ #ifdef STUBS case Z_STUB: #endif if (zp->z_serial != 0 && ((zp->z_lastupdate+zp->z_expire) < (u_int32_t)tt.tv_sec)) { if ((zp->z_flags & Z_NOTIFY) != 0) ns_stopnotify(zp->z_origin, zp->z_class); /* calls purge_zone */ - do_reload(zp->z_origin, zp->z_type, zp->z_class, 0); + do_reload(zp, 0); /* reset zone state */ if (!haveComplained((u_long)zp, (u_long)stale)) { ns_notice(ns_log_default, "%s zone \"%s\" expired", zoneTypeString(zp->z_type), zp->z_origin); } zp->z_flags &= ~Z_AUTH; zp->z_flags |= Z_EXPIRED; zp->z_refresh = INIT_REFRESH; zp->z_retry = INIT_REFRESH; zp->z_serial = 0; } if ((zp->z_flags & (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL)) != 0) { ns_retrytime(zp, tt.tv_sec); break; } if (zp->z_flags & Z_XFER_RUNNING) { abortxfer(zp); /* * Check again in 30 seconds in case the first * abort doesn't work. */ if (zp->z_time != 0 && zp->z_time <= tt.tv_sec) zp->z_time = tt.tv_sec + 30; break; } /* * If we don't have the zone loaded or dialup is off * or we attempted a qserial_query before and the queue was * full attempt to verify / load the zone. */ if ((zp->z_serial == 0) || (zp->z_flags & Z_NEED_QSERIAL) || (zp->z_dialup == zdialup_no) || (zp->z_dialup == zdialup_use_default && NS_OPTION_P(OPTION_NODIALUP))) qserial_query(zp); else { ns_info(ns_log_default, "Suppressed qserial_query(%s)", *(zp->z_origin) ? zp->z_origin : "."); ns_refreshtime(zp, tt.tv_sec); } break; #ifdef BIND_UPDATE case Z_PRIMARY: if ((zp->z_flags & Z_DYNAMIC) == 0) break; if (tt.tv_sec >= zp->z_soaincrtime && zp->z_soaincrintvl > 0 && zp->z_flags & Z_NEED_SOAUPDATE) { if (incr_serial(zp) < 0) { /* Try again later. */ ns_error(ns_log_maint, "error updating serial number for %s from %d", zp->z_origin, zp->z_serial); zp->z_soaincrtime = 0; (void)schedule_soa_update(zp, 0); } } if (tt.tv_sec >= zp->z_dumptime && zp->z_dumpintvl > 0 && zp->z_flags & Z_NEED_DUMP) { if (zonedump(zp, ISNOTIXFR) < 0) { /* Try again later. */ ns_error(ns_log_maint, "zone dump for '%s' failed, rescheduling", zp->z_origin); zp->z_dumptime = 0; (void)schedule_dump(zp); } } if (zp->z_maintain_ixfr_base) ixfr_log_maint(zp); break; #endif /* BIND_UPDATE */ default: break; } if (zp->z_time != 0 && zp->z_time < tt.tv_sec) zp->z_time = tt.tv_sec; sched_zone_maint(zp); } static void do_zone_maint(evContext ctx, void *uap, struct timespec due, struct timespec inter) { ztimer_info zti = uap; struct zoneinfo *zp; UNUSED(ctx); UNUSED(due); UNUSED(inter); INSIST(zti != NULL); ns_debug(ns_log_maint, 1, "do_zone_maint for zone %s (class %s)", zti->name, p_class(zti->class)); zp = find_zone(zti->name, zti->class); if (zp == NULL) { ns_error(ns_log_maint, "do_zone_maint: %s zone '%s' (class %s) is not authoritative", zoneTypeString(zti->type), zti->name, p_class(zti->class)); return; } if (zp->z_type != zti->type) { ns_error(ns_log_maint, "do_zone_maint: %s zone '%s' (class %s) has changed its type", zoneTypeString(zti->type), zti->name, p_class(zti->class)); return; } free_zone_timerinfo(zp); zp->z_flags &= ~Z_TIMER_SET; zone_maint(zp); } /* * Figure out the next maintenance time for the zone and set a timer. */ void sched_zone_maint(struct zoneinfo *zp) { time_t next_maint = (time_t)0; ztimer_info zti; if (zp->z_time != 0) next_maint = zp->z_time; #ifdef BIND_UPDATE if (zp->z_type == z_master && (zp->z_flags & Z_DYNAMIC) != 0) { if (zp->z_soaincrintvl > 0 && (next_maint == 0 || next_maint > zp->z_soaincrtime)) next_maint = zp->z_soaincrtime; if (zp->z_dumpintvl > 0 && (next_maint == 0 || next_maint > zp->z_dumptime)) next_maint = zp->z_dumptime; } #endif if (next_maint != 0) { if (next_maint < tt.tv_sec) next_maint = tt.tv_sec; if (zp->z_flags & Z_TIMER_SET) { if (next_maint == zp->z_nextmaint) { ns_debug(ns_log_maint, 1, "no schedule change for zone '%s'", zp->z_origin[0] == '\0' ? "." : zp->z_origin); return; } if (evResetTimer(ev, zp->z_timer, do_zone_maint, zp->z_timerinfo, evConsTime(next_maint, 0), evConsTime(0, 0)) < 0) { ns_error(ns_log_maint, "evChangeTimer failed in sched_zone_maint for zone '%s': %s", zp->z_origin[0] == '\0' ? "." : zp->z_origin, strerror(errno)); return; } } else { zti = (ztimer_info)memget(sizeof *zti); if (zti == NULL) ns_panic(ns_log_maint, 1, "memget failed in sched_zone_maint"); zti->name = savestr(zp->z_origin, 1); zti->class = zp->z_class; zti->type = zp->z_type; if (evSetTimer(ev, do_zone_maint, zti, evConsTime(next_maint, 0), evConsTime(0, 0), &zp->z_timer) < 0) { ns_error(ns_log_maint, "evSetTimer failed in sched_zone_maint for zone '%s': %s", zp->z_origin[0] == '\0' ? "." : zp->z_origin, strerror(errno)); return; } zp->z_flags |= Z_TIMER_SET; zp->z_timerinfo = zti; } ns_debug(ns_log_maint, 1, "next maintenance for zone '%s' in %lu sec", zp->z_origin[0] == '\0' ? "." : zp->z_origin, (u_long)(next_maint - tt.tv_sec)); } else { if (zp->z_flags & Z_TIMER_SET) { free_zone_timerinfo(zp); if (evClearTimer(ev, zp->z_timer) < 0) ns_error(ns_log_maint, "evClearTimer failed in sched_zone_maint for zone '%s': %s", zp->z_origin[0] == '\0' ? "." : zp->z_origin, strerror(errno)); zp->z_flags &= ~Z_TIMER_SET; } ns_debug(ns_log_maint, 1, "no scheduled maintenance for zone '%s'", zp->z_origin[0] == '\0' ? "." : zp->z_origin); } zp->z_nextmaint = next_maint; } void ns_cleancache(evContext ctx, void *uap, struct timespec due, struct timespec inter) { int deleted; UNUSED(ctx); UNUSED(due); UNUSED(inter); gettime(&tt); INSIST(uap == NULL); deleted = clean_cache(hashtab, 0); ns_info(ns_log_maint, "Cleaned cache of %d RRset%s", deleted, (deleted==1) ? "" : "s"); } void ns_heartbeat(evContext ctx, void *uap, struct timespec due, struct timespec inter) { struct zoneinfo *zp; UNUSED(ctx); UNUSED(due); UNUSED(inter); gettime(&tt); INSIST(uap == NULL); for (zp = zones; zp < &zones[nzones]; zp++) { enum zonetype zt = zp->z_type; if ((zt == z_nil) || (zp->z_dialup == zdialup_no) || (zp->z_dialup == zdialup_use_default && NS_OPTION_P(OPTION_NODIALUP))) continue; /* * Perform the refresh query that was suppressed. */ if ((zt == z_slave || zt == z_stub) && (zp->z_flags & (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL|Z_XFER_RUNNING) ) == 0) { ns_info(ns_log_default, "Heartbeat: qserial \"%s\"", *(zp->z_origin) ? zp->z_origin : "."); qserial_query(zp); } #ifdef BIND_NOTIFY /* * Trigger a refresh query while the link is up by * sending a notify. */ if (((zp->z_notify == notify_yes) || (zp->z_notify == notify_explicit) || ((zp->z_notify == notify_use_default) && server_options->notify != notify_no)) && (zt == z_master || zt == z_slave) && !loading && ((zp->z_flags & Z_AUTH) != 0)) ns_notify(zp->z_origin, zp->z_class, ns_t_soa); #endif } } /* * Mark a zone "up to date" after named-xfer tells us this or we * discover it through the qserial_*() logic. * The caller is responsible for calling sched_zone_maint(zp). */ static void markUpToDate(struct zoneinfo *zp) { struct stat f_time; zp->z_flags &= ~Z_SYSLOGGED; zp->z_lastupdate = tt.tv_sec; ns_refreshtime(zp, tt.tv_sec); /* * Restore Z_AUTH in case expired, * but only if there were no errors * in the zone file. */ if ((zp->z_flags & Z_DB_BAD) == 0) { zp->z_flags |= Z_AUTH; zp->z_flags &= ~Z_EXPIRED; } if (zp->z_source) { struct timeval t[2]; t[0] = tt; t[1] = tt; (void) utimes(zp->z_source, t); } /* we use "stat" to set zp->z_ftime instead of just setting it to tt.tv_sec in order to avoid any possible rounding problems in utimes(). */ if (stat(zp->z_source, &f_time) != -1) zp->z_ftime = f_time.st_mtime; /* XXX log if stat fails? */ } void qserial_retrytime(struct zoneinfo *zp, time_t timebase) { zp->z_time = timebase + 5 + (rand() % 25); } /* * Query for the serial number of a zone, so that we can check to see if * we need to transfer it. If there are too many outstanding serial * number queries, we'll try again later. * The caller is responsible for calling sched_zone_maint(zp). */ void qserial_query(struct zoneinfo *zp) { struct qinfo *qp; ns_debug(ns_log_default, 1, "qserial_query(%s)", zp->z_origin); if (qserials_running >= server_options->serial_queries) { qserial_retrytime(zp, tt.tv_sec); zp->z_flags |= Z_NEED_QSERIAL; return; } qp = sysquery(zp->z_origin, zp->z_class, T_SOA, zp->z_addr, zp->z_keys, zp->z_addrcnt, ntohs(zp->z_port) ? zp->z_port : ns_port, QUERY, 0); if (qp == NULL) { ns_debug(ns_log_default, 1, "qserial_query(%s): sysquery FAILED", zp->z_origin); /* XXX - this is bad, we should do something */ qserial_retrytime(zp, tt.tv_sec); zp->z_flags |= Z_NEED_QSERIAL; return; } qp->q_flags |= Q_ZSERIAL; qp->q_zquery = zp; zp->z_flags |= Z_QSERIAL; zp->z_flags &= ~Z_NEED_QSERIAL; zp->z_xaddrcnt = 0; ns_refreshtime(zp, tt.tv_sec); qserials_running++; ns_debug(ns_log_default, 1, "qserial_query(%s) QUEUED", zp->z_origin); } static int qserv_compare(const void *a, const void *b) { const struct qserv *qs1 = a, *qs2 = b; u_int32_t s1 = qs1->serial, s2 = qs2->serial; /* Note that we sort the "best" serial numbers to the front. */ if (s1 == s2) return (0); if (s1 == 0) return (-1); if (s2 == 0) return (1); if (!SEQ_GT(s1, s2)) return (1); assert(SEQ_GT(s1, s2)); return (-1); } void qserial_answer(struct qinfo *qp) { struct zoneinfo *zp = qp->q_zquery; struct qserv *qs = NULL; u_int32_t serial = 0; int n, cnt = 0; /* Take this query out of the global quotas. */ zp->z_flags &= ~Z_QSERIAL; qp->q_flags &= ~Q_ZSERIAL; /* keeps us from being called twice */ qserials_running--; /* Find best serial among those returned. */ for (n = 0; n < qp->q_naddr; n++) { qs = &qp->q_addr[n]; ns_debug(ns_log_default, 1, "qserial_answer(%s): [%s] -> %lu", zp->z_origin, inet_ntoa(qs->ns_addr.sin_addr), (unsigned long)qs->serial); /* Don't consider serials which weren't set by a response. */ if (qs->serial == 0) continue; /* Count valid answers. */ cnt++; /* Remove from consideration serials which aren't "better." */ if (zp->z_serial != 0 && !SEQ_GT(qs->serial, zp->z_serial)) { if (serial == 0 && qs->serial == zp->z_serial) serial = qs->serial; if (qs->serial != zp->z_serial) ns_notice(ns_log_xfer_in, "Zone \"%s\" (%s) SOA serial# (%lu) rcvd from [%s] is < ours (%lu)%s", zp->z_origin, p_class(zp->z_class), (u_long) qs->serial, inet_ntoa(qs->ns_addr.sin_addr), (u_long) zp->z_serial, qp->q_naddr!=1 ? ": skipping" : ""); qs->serial = 0; continue; } if (serial == 0 || SEQ_GT(qs->serial, serial)) serial = qs->serial; } /* If we have an existing serial number, then sort by "better." */ if (zp->z_serial != 0) { qsort(qp->q_addr, qp->q_naddr, sizeof(struct qserv), qserv_compare); for (n = 0; n < qp->q_naddr; n++) { qs = &qp->q_addr[n]; ns_debug(ns_log_default, 1, "qserial_answer after sort: [%s] -> %lu", inet_ntoa(qs->ns_addr.sin_addr), (unsigned long)qs->serial); } } /* Now see about kicking off an inbound transfer. */ if (serial == 0) { /* An error occurred, or the all queries timed out. */ if (qp->q_naddr != cnt) ns_info(ns_log_xfer_in, "Err/TO getting serial# for \"%s\"", zp->z_origin); addxfer(zp); } else if (zp->z_serial == 0 || SEQ_GT(serial, zp->z_serial)) { ns_debug(ns_log_xfer_in, 1, "qserial_answer: zone is out of date"); /* Use all servers whose serials are better than ours. */ zp->z_xaddrcnt = 0; for (n = 0; n < qp->q_naddr; n++) { qs = &qp->q_addr[n]; if (qs->serial != 0) zp->z_xaddr[zp->z_xaddrcnt++] = qs->ns_addr.sin_addr; } addxfer(zp); } else if (zp->z_serial == serial) { ns_debug(ns_log_xfer_in, 1, "qserial_answer: zone serial is still OK"); markUpToDate(zp); sched_zone_maint(zp); } } /* * Writes TSIG key info for an address to a file, optionally opening it first. * Returns: * -1: Error. * 0: No action taken. * 1: Tsig info successfully written. */ static int write_tsig_info(struct zoneinfo *zp, struct in_addr addr, char *name, int *fd) { server_info si; DST_KEY *dst_key = NULL; int tsig_fd = *fd; char tsig_str[1024], secret_buf64[172]; u_char secret_buf[128]; int secret_len, len; int i; for (i = 0; i < zp->z_addrcnt ; i++) if (memcmp(&addr, &zp->z_addr[i], sizeof(addr)) == 0) { dst_key = zp->z_keys[i]; break; } if (dst_key == NULL) { si = find_server(addr); if (si == NULL || si->key_list == NULL || si->key_list->first == NULL) return(0); dst_key = si->key_list->first->key; } if (tsig_fd == -1) { *fd = tsig_fd = mkstemp(name); if (tsig_fd < 0) { ns_warning(ns_log_default, "write_tsig_info: mkstemp(%s) for TSIG info failed", name); return(-1); } (void) fchown(tsig_fd, user_id, group_id); } memset(secret_buf, 0, sizeof(secret_buf)); secret_len = dst_key_to_buffer(dst_key, secret_buf, sizeof(secret_buf)); if (secret_len == 0) return (-1); len = b64_ntop(secret_buf, secret_len, secret_buf64, sizeof(secret_buf64)); if (len == -1) return (-1); /* We need snprintf! */ if (strlen(dst_key->dk_key_name) + len + sizeof("XXX.XXX.XXX.XXX") + sizeof("123") + 5 > sizeof(tsig_str)) return (-1); sprintf(tsig_str, "%s\n%s\n%d\n%s\n", inet_ntoa(addr), dst_key->dk_key_name, dst_key->dk_alg, secret_buf64); len = strlen(tsig_str); if (write(tsig_fd, tsig_str, strlen(tsig_str)) != len) return (-1); return (1); } /* * Returns number of tsigs written or -1. */ static int write_tsigs(struct zoneinfo *zp, char *tsig_name) { struct in_addr a; int tsig_ret; int tsig_fd = -1; int cnt; int records = 0; for (cnt = 0; cnt < zp->z_xaddrcnt; cnt++) { a = zp->z_xaddr[cnt]; if (aIsUs(a) && ns_port == zp->z_port) continue; tsig_ret = write_tsig_info(zp, a, tsig_name, &tsig_fd); switch (tsig_ret) { case -1: goto error; case 0: break; case 1: records++; break; } } if (tsig_fd != -1) close(tsig_fd); return (records); error: if (tsig_fd != -1) { unlink(tsig_name); close(tsig_fd); } return (-1); } #ifdef BIND_IXFR static int supports_ixfr(struct zoneinfo *zp) { int cnt = 0; for (cnt = 0; cnt < zp->z_xaddrcnt; cnt++) { struct in_addr a; server_info si; a = zp->z_xaddr[cnt]; if (aIsUs(a) && ns_port == zp->z_port) continue; si = find_server(a); if (si != NULL && (si->flags & SERVER_INFO_SUPPORT_IXFR) != 0) return(1); } return(0); } #endif /* * Start an asynchronous zone transfer for a zone. Depends on current time * being in tt. Caller must do a sched_zone_maint(zp) after we return. */ static void startxfer(struct zoneinfo *zp) { char *argv[NSMAX*2 + 20]; char argv_ns[NSMAX][MAXDNAME]; int argc = 0, argc_ns = 0, i; pid_t pid; u_int cnt; char debug_str[10]; char serial_str[10]; char port_str[10]; char class_str[10]; char src_str[20]; char tsig_name[MAXPATHLEN+1]; int tsig_ret = 0; ns_debug(ns_log_default, 1, "startxfer() %s", zp->z_origin[0] != '\0' ? zp->z_origin : "."); argv[argc++] = server_options->named_xfer; DE_CONST("-z", argv[argc++]); DE_CONST(*zp->z_origin ? zp->z_origin : ".", argv[argc++]); DE_CONST("-f", argv[argc++]); argv[argc++] = zp->z_source; #ifdef BIND_IXFR if (supports_ixfr(zp) && zp->z_ixfr_tmp != NULL) { DE_CONST("-i", argv[argc++]); argv[argc++] = zp->z_ixfr_tmp; } #endif if (zp->z_serial != 0) { DE_CONST("-s", argv[argc++]); sprintf(serial_str, "%u", zp->z_serial); argv[argc++] = serial_str; } if (zp->z_axfr_src.s_addr != 0 || server_options->axfr_src.s_addr != 0) { DE_CONST("-x", argv[argc++]); argv[argc++] = strcpy(src_str, inet_ntoa( (zp->z_axfr_src.s_addr != 0) ? zp->z_axfr_src : server_options->axfr_src)); } DE_CONST("-C", argv[argc++]); sprintf(class_str, "%d", zp->z_class); argv[argc++] = class_str; if (zp->z_flags & Z_SYSLOGGED) DE_CONST("-q", argv[argc++]); DE_CONST("-P", argv[argc++]); sprintf(port_str, "%d", ntohs(zp->z_port) != 0 ? zp->z_port : ns_port); argv[argc++] = port_str; #ifdef STUBS if (zp->z_type == Z_STUB) DE_CONST("-S", argv[argc++]); #endif #ifdef DEBUG if (debug) { DE_CONST("-d", argv[argc++]); sprintf(debug_str, "%d", debug); argv[argc++] = debug_str; DE_CONST("-l", argv[argc++]); DE_CONST(_PATH_XFERDDT, argv[argc++]); if (debug > 5) { DE_CONST("-t", argv[argc++]); DE_CONST(_PATH_XFERTRACE, argv[argc++]); } } #endif if (zp->z_xaddrcnt == 0) { for (zp->z_xaddrcnt = 0; zp->z_xaddrcnt < zp->z_addrcnt; zp->z_xaddrcnt++) zp->z_xaddr[zp->z_xaddrcnt] = zp->z_addr[zp->z_xaddrcnt]; } /* * Store TSIG keys if we have them. */ strcpy(tsig_name, "tsigs.XXXXXX"); tsig_ret = write_tsigs(zp, tsig_name); if (tsig_ret == -1) { ns_error(ns_log_xfer_in, "unable to write tsig info: '%s'", zp->z_origin); return; } if (tsig_ret != 0) { DE_CONST("-T", argv[argc++]); argv[argc++] = tsig_name; } /* * Copy the server ip addresses into argv, after converting * to ascii and saving the static inet_ntoa result. * Also, send TSIG key info into a file for the child. */ for (cnt = 0; cnt < zp->z_xaddrcnt; cnt++) { struct in_addr a; a = zp->z_xaddr[cnt]; if (aIsUs(a) && ns_port == zp->z_port) { if (!haveComplained((u_long)zp, (u_long)startxfer)) ns_notice(ns_log_default, "attempted to fetch zone %s from self (%s)", zp->z_origin, inet_ntoa(a)); continue; } argv[argc++] = strcpy(argv_ns[argc_ns++], inet_ntoa(a)); #ifdef BIND_IXFR if (zp->z_ixfr_tmp != NULL) { server_info si = find_server(a); if (si != NULL && (si->flags & SERVER_INFO_SUPPORT_IXFR) != 0) DE_CONST("ixfr", argv[argc++]); else DE_CONST("axfr", argv[argc++]); } #endif } argv[argc] = NULL; #ifdef DEBUG if (debug >= 1) { char buffer[1024]; char *curr, *last; int len; curr = buffer; last = &buffer[sizeof buffer - 1]; /* leave room for \0 */ for (i = 0; i < argc; i++) { len = strlen(argv[i]); - if (curr + len + 1 >= last) { + if (len + 1 >= last - curr) { ns_debug(ns_log_xfer_in, 1, "xfer args debug printout truncated"); break; } strncpy(curr, argv[i], len); curr += len; *curr = ' '; curr++; } *curr = '\0'; ns_debug(ns_log_xfer_in, 1, "%s", buffer); } #endif /* DEBUG */ gettime(&tt); for (i = 0; i < MAX_XFERS_RUNNING; i++) if (xferstatus[i].xfer_pid == 0) break; if (i == MAX_XFERS_RUNNING) { ns_warning(ns_log_default, "startxfer: too many xfers running"); zp->z_time = tt.tv_sec + 10; return; } if ((pid = spawnxfer(argv, zp)) == -1) { unlink(tsig_name); return; } xferstatus[i].xfer_state = XFER_RUNNING; xferstatus[i].xfer_pid = pid; /* XXX - small race condition here if we * can't hold signals */ xferstatus[i].xfer_addr = zp->z_xaddr[0]; ns_debug(ns_log_default, 1, "started xfer child %d", pid); zp->z_flags &= ~Z_NEED_XFER; zp->z_flags |= Z_XFER_RUNNING; zp->z_xferpid = pid; xfers_running++; xfers_deferred--; if (zp->z_max_transfer_time_in) zp->z_time = tt.tv_sec + zp->z_max_transfer_time_in; else zp->z_time = tt.tv_sec + server_options->max_transfer_time_in; } const char * zoneTypeString(u_int type) { static char ret[sizeof "(4294967296?)"]; /* 2^32 */ switch (type) { case Z_MASTER: return ("master"); case Z_SLAVE: return ("slave"); #ifdef STUBS case Z_STUB: return ("stub"); #endif case Z_HINT: return ("hint"); case Z_CACHE: return ("cache"); case Z_FORWARD: return ("forward"); default: sprintf(ret, "(%u?)", type); return (ret); } } #ifdef DEBUG void printzoneinfo(int zonenum, int category, int level) { struct timeval tt; struct zoneinfo *zp = &zones[zonenum]; if (debug == 0) return; if (!zp->z_origin) return; gettime(&tt); ns_debug(category, level, "zone %d: %s, class %s, type %s", zonenum, zp->z_origin[0] ? zp->z_origin : ".", p_class(zp->z_class), zoneTypeString(zp->z_type)); if (zp->z_source) ns_debug(category, level, "\tsource %s", zp->z_source); ns_debug(category, level, "\tflags %lx, serial %u, minimum %u", (u_long)zp->z_flags, zp->z_serial, zp->z_minimum); ns_debug(category, level, "\trefresh %u, retry %u, expire %u", zp->z_refresh, zp->z_retry, zp->z_expire); if (zp->z_time) ns_debug(category, level, "\tz_time %lu (now %lu, left: %lu)", zp->z_time, (u_long)tt.tv_sec, (u_long)(zp->z_time - tt.tv_sec)); else ns_debug(category, level, "\tz_time %lu", zp->z_time); #ifdef BIND_UPDATE if (zp->z_type == z_master && (zp->z_flags & Z_DYNAMIC) != 0) { ns_debug(category, level, "\tdumpintvl %lu, soaincrintvl %lu deferupdcnt %lu", (unsigned long)zp->z_dumpintvl, (unsigned long)zp->z_soaincrintvl, (unsigned long)zp->z_deferupdcnt); if (zp->z_soaincrtime) ns_debug(category, level, "\tz_soaincrtime %lu (now %lu, left: %lu)", zp->z_soaincrtime, (u_long)tt.tv_sec, (u_long)(zp->z_soaincrtime - tt.tv_sec)); else ns_debug(category, level, "\tz_soaincrtime %lu", zp->z_soaincrtime); if (zp->z_dumptime) ns_debug(category, level, "\tz_dumptime %lu (now %lu, left: %lu)", zp->z_dumptime, (u_long)tt.tv_sec, (u_long)(zp->z_dumptime - tt.tv_sec)); else ns_debug(category, level, "\tz_dumptime %lu", zp->z_dumptime); } #endif } #endif /* DEBUG */ /* * Remove all cached data below dname, class independent. */ void clean_cache_from(char *dname, struct hashbuf *htp) { const char *fname; struct databuf *dp, *pdp; struct namebuf *np; struct hashbuf *phtp = htp; int root_zone = 0; ns_debug(ns_log_default, 1, "clean_cache_from(%s)", dname); if ((np = nlookup(dname, &phtp, &fname, 0)) && dname == fname && !ns_wildcard(NAME(*np))) { for (pdp = NULL, dp = np->n_data; dp != NULL; (void)NULL) { if (dp->d_zone == DB_Z_CACHE) dp = rm_datum(dp, np, pdp, NULL); else { pdp = dp; dp = dp->d_next; } } if (*dname == '\0') root_zone = 1; if (np->n_hash != NULL || root_zone) { struct hashbuf *h; if (root_zone) h = htp; else h = np->n_hash; (void)clean_cache(h, 1); if (h->h_cnt == 0 && !root_zone) { rm_hash(np->n_hash); np->n_hash = NULL; } } if (!root_zone && np->n_hash == NULL && np->n_data == NULL) (void) purge_node(htp, np); } } /* clean_cache(htp, all) * Scan the entire cache looking for expired TTL's on nonauthoritative * data, and remove it. if `all' is true, ignore TTL and rm everything. * notes: * this should be lazy and eventlib driven. * return: * number of deleted RRs (all=1) or RRsets (all=0). */ int clean_cache(struct hashbuf *htp, int all) { struct databuf *dp, *pdp; struct namebuf *np, *pnp, *npn; struct namebuf **npp, **nppend; int deleted = 0; nppend = htp->h_tab + htp->h_size; for (npp = htp->h_tab; npp < nppend; npp++) { for (pnp = NULL, np = *npp; np != NULL; np = npn) { again: for (pdp = NULL, dp = np->n_data; dp != NULL; (void)NULL) { if (all && dp->d_zone == DB_Z_CACHE) { dp = rm_datum(dp, np, pdp, NULL); deleted++; } else if (dp->d_zone == DB_Z_CACHE && stale(dp)) { delete_all(np, dp->d_class, dp->d_type); deleted++; goto again; } else { pdp = dp; dp = dp->d_next; } } /*for(pdp)*/ if (np->n_hash) { /* Call recursively to remove subdomains. */ deleted += clean_cache(np->n_hash, all); /* If now empty, free it */ if (np->n_hash->h_cnt == 0) { rm_hash(np->n_hash); np->n_hash = NULL; } } if (np->n_hash == NULL && np->n_data == NULL) { npn = rm_name(np, npp, pnp); htp->h_cnt--; } else { npn = np->n_next; pnp = np; } } /*for(pnp)*/ } /*for(npp)*/ return (deleted); } /* struct namebuf * * purge_node(htp, np) * Remove entry from cache. * Prerequisites: * Node is empty and has no children. * Paramters: * htp - root of recursive hash table this node is part of. * np - the node to be deleted. * Return: * pointer to parent. */ struct namebuf * purge_node(struct hashbuf *htp, struct namebuf *np) { struct namebuf **npp, **nppend; struct namebuf *npn, *pnp, *nnp, *parent; struct hashbuf *phtp; ns_debug(ns_log_default, 3, "purge_node: cleaning cache"); INSIST(np->n_hash == NULL && np->n_data == NULL); /* Walk parent hashtable looking for ourself. */ parent = np->n_parent; if (parent != NULL) phtp = parent->n_hash; else phtp = htp; if (phtp == NULL) { /* XXX why shouldn't we panic? */ } else { nppend = phtp->h_tab + phtp->h_size; for (npp = phtp->h_tab; npp < nppend; npp++) { for (pnp = NULL, nnp = *npp; nnp != NULL; nnp = npn) { if (nnp == np) { ns_debug(ns_log_default, 3, "purge_node: found ourself"); npn = rm_name(nnp, npp, pnp); phtp->h_cnt--; } else { npn = nnp->n_next; pnp = nnp; } } } } return (parent); } void remove_zone(struct zoneinfo *zp, const char *verb) { #ifdef BIND_UPDATE /* * A dynamic zone might have changed, so we * need to dump it before removing it. */ if ((zp->z_flags & Z_DYNAMIC) != 0 && ((zp->z_flags & Z_NEED_SOAUPDATE) != 0 || (zp->z_flags & Z_NEED_DUMP) != 0)) (void) zonedump(zp, ISNOTIXFR); #endif if ((zp->z_flags & Z_NOTIFY) != 0) ns_stopnotify(zp->z_origin, zp->z_class); if ((zp->z_flags & Z_NEED_XFER) != 0) { zp->z_flags &= ~Z_NEED_XFER; xfers_deferred--; } ns_stopxfrs(zp); if ((zp->z_flags & Z_XFER_RUNNING) != 0) { int i; /* Kill and abandon the current transfer. */ for (i = 0; i < MAX_XFERS_RUNNING; i++) { if (xferstatus[i].xfer_pid == zp->z_xferpid) { xferstatus[i].xfer_pid = 0; xferstatus[i].xfer_state = XFER_IDLE; xfers_running--; break; } } (void)kill(zp->z_xferpid, SIGTERM); zp->z_flags &= ~(Z_XFER_RUNNING|Z_XFER_ABORTED|Z_XFER_GONE); zp->z_xferpid = 0; ns_need(main_need_tryxfer); } - do_reload(zp->z_origin, zp->z_type, zp->z_class, 1); + do_reload(zp, 1); ns_notice(ns_log_config, "%s zone \"%s\" (%s) %s", zoneTypeString(zp->z_type), zp->z_origin, p_class(zp->z_class), verb); free_zone_contents(zp, 1); memset(zp, 0, sizeof(*zp)); zp->z_type = z_nil; /* Pedantic; memset() did it. */ INIT_LINK(zp, z_reloadlink); INIT_LINK(zp, z_freelink); free_zone(zp); } int -purge_nonglue(const char *dname, struct hashbuf *htp, int class, int log) { +purge_nonglue(struct zoneinfo *zp, struct hashbuf *htp, int log) { + const char *dname = zp->z_origin; const char *fname; struct namebuf *np; struct hashbuf *phtp = htp; int root_zone = 0; int errs = 0; + int zone = zp - zones; + struct databuf *pdp, *dp; + int class = zp->z_class; - ns_debug(ns_log_default, 1, "purge_zone(%s,%d)", dname, class); + ns_debug(ns_log_default, 1, "purge_nonglue(%s/%d)", dname, class); if ((np = nlookup(dname, &phtp, &fname, 0)) && dname == fname && !ns_wildcard(NAME(*np))) { + for (pdp = NULL, dp = np->n_data; dp != NULL; (void)NULL) { + if (dp->d_class == class && dp->d_zone != zone) + dp = rm_datum(dp, np, pdp, NULL); + else { + pdp = dp; + dp = dp->d_next; + } + } + if (*dname == '\0') root_zone = 1; if (np->n_hash != NULL || root_zone) { struct hashbuf *h; if (root_zone) h = htp; else h = np->n_hash; - errs += purge_nonglue_2(dname, h, class, 0, log); + errs += purge_nonglue_2(dname, h, class, 0, log, zone); if (h->h_cnt == 0 && !root_zone) { rm_hash(np->n_hash); np->n_hash = NULL; } } } return (errs); } static int valid_glue(struct databuf *dp, char *name, int belowcut) { /* NS records are only valid glue at the zone cut */ if (belowcut && dp->d_type == T_NS) return(0); if (ISVALIDGLUE(dp)) /* T_NS/T_A/T_AAAA/T_A6 */ return (1); if (belowcut) return (0); /* Parent NXT record? */ if (dp->d_type == T_NXT && !ns_samedomain((char*)dp->d_data, name) && ns_samedomain((char*)dp->d_data, zones[dp->d_zone].z_origin)) return (1); /* KEY RRset may be in the parent */ if (dp->d_type == T_KEY) return (1); /* NXT & KEY records may be signed */ if (!belowcut && dp->d_type == T_SIG && (SIG_COVERS(dp) == T_NXT || SIG_COVERS(dp) == T_KEY)) return (1); return (0); } static int purge_nonglue_2(const char *dname, struct hashbuf *htp, int class, - int belowcut, int log) + int belowcut, int log, int zone) { struct databuf *dp, *pdp; struct namebuf *np, *pnp, *npn; struct namebuf **npp, **nppend; int errs = 0; int zonecut; char name[MAXDNAME]; nppend = htp->h_tab + htp->h_size; for (npp = htp->h_tab; npp < nppend; npp++) { for (pnp = NULL, np = *npp; np != NULL; np = npn) { if (!bottom_of_zone(np->n_data, class)) { zonecut = belowcut; for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (match(dp, class, ns_t_ns)) { zonecut = 1; break; } } getname(np, name, sizeof name); for (pdp = NULL, dp = np->n_data; dp != NULL; (void)NULL) { - if (dp->d_class == class && - zonecut && + int delete = 0; + if (!zonecut && + dp->d_class == class && + dp->d_zone != zone) + delete = 1; + if (zonecut && + dp->d_class == class && !valid_glue(dp, name, belowcut)) { - if (log) + if (log && + dp->d_zone == zone) { ns_error(ns_log_load, "zone: %s/%s: non-glue record %s bottom of zone: %s/%s", *dname ? dname : ".", p_class(dp->d_class), belowcut ? "below" : "at", *name ? name : ".", p_type(dp->d_type)); + errs++; + } + delete = 1; + } + if (delete) dp = rm_datum(dp, np, pdp, NULL); - if (log) - errs++; - } else { + else { pdp = dp; dp = dp->d_next; } } if (np->n_hash) { /* * call recursively to clean * subdomains */ errs += purge_nonglue_2(dname, np->n_hash, class, zonecut || belowcut, - log); + log, zone); /* if now empty, free it */ if (np->n_hash->h_cnt == 0) { rm_hash(np->n_hash); np->n_hash = NULL; } } } if (np->n_hash == NULL && np->n_data == NULL) { npn = rm_name(np, npp, pnp); htp->h_cnt--; } else { npn = np->n_next; pnp = np; } } } return (errs); } void -purge_zone(const char *dname, struct hashbuf *htp, int class) { +purge_zone(struct zoneinfo *zp, struct hashbuf *htp) { const char *fname; struct databuf *dp, *pdp; struct namebuf *np; struct hashbuf *phtp = htp; int root_zone = 0; + int zone = zp - zones; + char *dname = zp->z_origin; - ns_debug(ns_log_default, 1, "purge_zone(%s,%d)", dname, class); + ns_debug(ns_log_default, 1, "purge_zone(%s)", dname); if ((np = nlookup(dname, &phtp, &fname, 0)) && dname == fname && !ns_wildcard(NAME(*np))) { for (pdp = NULL, dp = np->n_data; dp != NULL; (void)NULL) { - if (dp->d_class == class) + if (dp->d_zone == zone) dp = rm_datum(dp, np, pdp, NULL); else { pdp = dp; dp = dp->d_next; } } if (*dname == '\0') root_zone = 1; if (np->n_hash != NULL || root_zone) { struct hashbuf *h; if (root_zone) h = htp; else h = np->n_hash; - purge_z_2(h, class); + purge_z_2(h, zone); if (h->h_cnt == 0 && !root_zone) { rm_hash(np->n_hash); np->n_hash = NULL; } } if (!root_zone && np->n_hash == NULL && np->n_data == NULL) (void) purge_node(htp, np); } } static void -purge_z_2(htp, class) - struct hashbuf *htp; - int class; -{ +purge_z_2(struct hashbuf *htp, int zone) { struct databuf *dp, *pdp; struct namebuf *np, *pnp, *npn; struct namebuf **npp, **nppend; nppend = htp->h_tab + htp->h_size; for (npp = htp->h_tab; npp < nppend; npp++) { for (pnp = NULL, np = *npp; np != NULL; np = npn) { - if (!bottom_of_zone(np->n_data, class)) { - for (pdp = NULL, dp = np->n_data; - dp != NULL; - (void)NULL) { - if (dp->d_class == class) - dp = rm_datum(dp, np, pdp, - NULL); - else { - pdp = dp; - dp = dp->d_next; - } + for (pdp = NULL, dp = np->n_data; + dp != NULL; + (void)NULL) { + if (dp->d_zone == zone) + dp = rm_datum(dp, np, pdp, + NULL); + else { + pdp = dp; + dp = dp->d_next; } - if (np->n_hash) { - /* call recursively to rm subdomains */ - purge_z_2(np->n_hash, class); + } + if (np->n_hash) { + /* call recursively to rm subdomains */ + purge_z_2(np->n_hash, zone); - /* if now empty, free it */ - if (np->n_hash->h_cnt == 0) { - rm_hash(np->n_hash); - np->n_hash = NULL; - } + /* if now empty, free it */ + if (np->n_hash->h_cnt == 0) { + rm_hash(np->n_hash); + np->n_hash = NULL; } } if (np->n_hash == NULL && np->n_data == NULL) { npn = rm_name(np, npp, pnp); htp->h_cnt--; } else { npn = np->n_next; pnp = np; } } } } static int bottom_of_zone(struct databuf *dp, int class) { int ret = 0; for ((void)NULL; dp; dp = dp->d_next) { if (dp->d_class != class) continue; if (dp->d_zone == DB_Z_CACHE) continue; if (dp->d_rcode) /* This should not occur. */ continue; if (dp->d_type != T_SOA) continue; ret = 1; break; } ns_debug(ns_log_default, 3, "bottom_of_zone() == %d", ret); return (ret); } /* * Handle XFER limit for a nameserver. */ static int nxfers(struct zoneinfo *zp) { struct in_addr nsa; int ret; int i; if (zp->z_xaddrcnt != 0) nsa = zp->z_xaddr[0]; /* first ns holds zone's xfer limit */ else if (zp->z_addrcnt != 0) nsa = zp->z_addr[0]; /* first ns holds zone's xfer limit */ else return (-1); ret = 0; for (i = 0; i < MAX_XFERS_RUNNING; i++) if (xferstatus[i].xfer_status == XFER_RUNNING && xferstatus[i].xfer_addr.s_addr == nsa.s_addr) ret++; return (ret); } /* * Abort an xfer that has taken too long. */ static void abortxfer(struct zoneinfo *zp) { if (zp->z_flags & (Z_XFER_GONE|Z_XFER_ABORTED)) { int i; for (i = 0; i < MAX_XFERS_RUNNING; i++) { if (xferstatus[i].xfer_pid == zp->z_xferpid) { xferstatus[i].xfer_pid = 0; xferstatus[i].xfer_state = XFER_IDLE; break; } } if (zp->z_flags & Z_XFER_GONE) ns_warning(ns_log_default, "zone transfer timeout for \"%s\"; pid %lu missing", zp->z_origin, (u_long)zp->z_xferpid); else if (kill(zp->z_xferpid, SIGKILL) == -1) ns_warning(ns_log_default, "zone transfer timeout for \"%s\"; kill pid %lu: %s", zp->z_origin, (u_long)zp->z_xferpid, strerror(errno)); else ns_warning(ns_log_default, "zone transfer timeout for \"%s\"; second kill \ pid %lu - forgetting, processes may accumulate", zp->z_origin, (u_long)zp->z_xferpid); zp->z_xferpid = 0; xfers_running--; zp->z_flags &= ~(Z_XFER_RUNNING|Z_XFER_ABORTED|Z_XFER_GONE); } else if (kill(zp->z_xferpid, SIGTERM) == -1) { if (errno == ESRCH) /* No warning on first time, it may have just exited */ zp->z_flags |= Z_XFER_GONE; else { ns_warning(ns_log_default, "zone transfer timeout for \"%s\"; pid %lu kill failed %s", zp->z_origin, (u_long)zp->z_xferpid, strerror(errno)); zp->z_flags |= Z_XFER_ABORTED; } } else { ns_notice(ns_log_default, "zone transfer timeout for \"%s\"; pid %lu killed", zp->z_origin, (u_long)zp->z_xferpid); zp->z_flags |= Z_XFER_ABORTED; } } /* * Process exit of xfer's. */ void reapchild(void) { int i; pid_t pid; WAIT_T status; gettime(&tt); while ((pid = (pid_t)waitpid(-1, &status, WNOHANG)) > 0) { for (i = 0; i < MAX_XFERS_RUNNING; i++) { if (xferstatus[i].xfer_pid == pid) { xferstatus[i].xfer_status = status; xferstatus[i].xfer_state = XFER_DONE; ns_need(main_need_endxfer); break; } } } } /* * Finish processing of of finished xfers */ void endxfer() { struct zoneinfo *zp; int exitstatus, i; pid_t pid; WAIT_T status; gettime(&tt); for (i = 0; i < MAX_XFERS_RUNNING; i++) { if (xferstatus[i].xfer_state != XFER_DONE) continue; pid = xferstatus[i].xfer_pid; status = xferstatus[i].xfer_status; exitstatus = WIFEXITED(status) ? WEXITSTATUS(status) : 0; for (zp = zones; zp < &zones[nzones]; zp++) { if (zp->z_xferpid != pid) continue; xfers_running--; zp->z_xferpid = 0; zp->z_flags &= ~(Z_XFER_RUNNING|Z_XFER_ABORTED|Z_XFER_GONE); ns_debug(ns_log_default, 1, "\nendxfer: child %d zone %s returned status=%d termsig=%d", pid, zp->z_origin, exitstatus, WIFSIGNALED(status) ? WTERMSIG(status) : -1); if (WIFSIGNALED(status)) { if (WTERMSIG(status) != SIGKILL) { ns_notice(ns_log_default, "named-xfer \"%s\" exited with signal %d", zp->z_origin[0]?zp->z_origin:".", WTERMSIG(status)); } ns_retrytime(zp, tt.tv_sec); sched_zone_maint(zp); } else { switch (exitstatus) { case XFER_UPTODATE: markUpToDate(zp); sched_zone_maint(zp); break; case XFER_SUCCESSAXFR: case XFER_SUCCESSAXFRIXFRFILE: zp->z_xferpid = XFER_ISAXFR; if (exitstatus == XFER_SUCCESSAXFRIXFRFILE) { zp->z_xferpid = XFER_ISAXFRIXFR; if (zp->z_ixfr_tmp != NULL) isc_movefile( zp->z_ixfr_tmp, zp->z_source); } /* XXX should incorporate loadxfer() */ zp->z_flags |= Z_NEED_RELOAD; zp->z_flags &= ~Z_SYSLOGGED; ns_need(main_need_zoneload); break; case XFER_SUCCESSIXFR: zp->z_xferpid = XFER_ISIXFR; ns_notice(ns_log_default, "IXFR Success %s", zp->z_ixfr_tmp); if (merge_logs(zp, zp->z_ixfr_tmp) >= 0) { ns_notice(ns_log_default, "IXFR Merge success %s", zp->z_ixfr_tmp); (void)unlink(zp->z_updatelog); (void)unlink(zp->z_ixfr_base); isc_movefile(zp->z_ixfr_tmp, zp->z_ixfr_base); (void)unlink(zp->z_ixfr_tmp); if (zonedump(zp, ISIXFR) < 0) ns_warning(ns_log_db, "error in write ixfr updates to zone file %s", zp ->z_source); ns_refreshtime(zp, tt.tv_sec); sched_zone_maint(zp); } else { ns_notice(ns_log_default, "IXFR Merge failed %s", zp->z_ixfr_tmp); ns_retrytime(zp, tt.tv_sec); sched_zone_maint(zp); } break; case XFER_TIMEOUT: if (!(zp->z_flags & Z_SYSLOGGED)) { zp->z_flags |= Z_SYSLOGGED; ns_notice(ns_log_default, "zoneref: Masters for slave zone \"%s\" unreachable", zp->z_origin); } ns_retrytime(zp, tt.tv_sec); sched_zone_maint(zp); break; case XFER_REFUSED: if (!(zp->z_flags & Z_SYSLOGGED)) { zp->z_flags |= Z_SYSLOGGED; ns_error(ns_log_xfer_in, "zoneref: Masters for slave zone \"%s\" REFUSED transfer", zp->z_origin); } ns_retrytime(zp, tt.tv_sec); sched_zone_maint(zp); break; default: if (!(zp->z_flags & Z_SYSLOGGED)) { zp->z_flags |= Z_SYSLOGGED; ns_notice(ns_log_default, "named-xfer for \"%s\" exited %d", zp->z_origin, exitstatus); } /* FALLTHROUGH */ case XFER_FAIL: zp->z_flags |= Z_SYSLOGGED; ns_retrytime(zp, tt.tv_sec); sched_zone_maint(zp); break; } break; } } xferstatus[i].xfer_state = XFER_IDLE; xferstatus[i].xfer_pid = 0; } tryxfer(); } /* * Try to start some xfers - new "fair scheduler" by Bob Halley @DEC (1995) */ void tryxfer() { static struct zoneinfo *zp = NULL; static struct zoneinfo *lastzones = NULL; static int lastnzones = 0; struct zoneinfo *startzp, *stopzp; /* initialize, and watch out for changes in zones! */ if (lastzones != zones) { if (lastzones != NULL) ns_debug(ns_log_default, 3, "zones changed: %p != %p", lastzones, zones); lastzones = zones; zp = zones; } /* did zones shrink? */ if (lastnzones > nzones) { ns_debug(ns_log_default, 3, "zones shrunk"); zp = zones; } lastnzones = nzones; if (zp == zones) stopzp = &zones[nzones-1]; else stopzp = zp - 1; ns_debug(ns_log_default, 3, "tryxfer start zp=%p stopzp=%p def=%d running=%d", zp, stopzp, xfers_deferred, xfers_running); startzp = zp; for (;;) { int xfers; if (!xfers_deferred || xfers_running >= server_options->transfers_in) break; if ((xfers = nxfers(zp)) != -1 && xfers < server_options->transfers_per_ns && (zp->z_flags & Z_NEED_XFER)) { startxfer(zp); sched_zone_maint(zp); } if (zp == stopzp) { ns_debug(ns_log_default, 3, "tryxfer stop mark"); zp = startzp; break; } zp++; /* wrap around? */ if (zp == &zones[nzones]) zp = zones; } ns_debug(ns_log_default, 3, "tryxfer stop zp=%p", zp); } /* * Reload zones whose transfers have completed. */ void loadxfer(void) { struct zoneinfo *zp; u_int32_t old_serial,new_serial; char *tmpnom; int isixfr; gettime(&tt); for (zp = zones; zp < &zones[nzones]; zp++) { if (zp->z_flags & Z_NEED_RELOAD) { ns_debug(ns_log_default, 1, "loadxfer() \"%s\"", zp->z_origin[0] ? zp->z_origin : "."); zp->z_flags &= ~(Z_NEED_RELOAD|Z_AUTH); /* XXX this is bad, should be done in ns_zreload() for primary changes. */ ns_stopxfrs(zp); old_serial = zp->z_serial; if (zp->z_xferpid == XFER_ISIXFR) { tmpnom = zp->z_ixfr_tmp; isixfr = ISIXFR; } else { tmpnom = zp->z_source; - purge_zone(zp->z_origin, hashtab, zp->z_class); + purge_zone(zp, hashtab); isixfr = ISNOTIXFR; } if (zp->z_xferpid == XFER_ISAXFRIXFR) { tmpnom= zp->z_source; - purge_zone(zp->z_origin, hashtab, zp->z_class); + purge_zone(zp, hashtab); isixfr = ISNOTIXFR; } if (!db_load(tmpnom, zp->z_origin, zp, NULL, isixfr)) { zp->z_flags |= Z_AUTH; zp->z_flags &= ~Z_EXPIRED; if (isixfr == ISIXFR) { new_serial= zp ->z_serial; ns_warning(ns_log_db, "ISIXFR"); ns_warning(ns_log_db, "error in updating ixfr data base file %s from %s", zp -> z_ixfr_base, zp ->z_ixfr_tmp); if (zonedump(zp,ISIXFR)<0) ns_warning(ns_log_db, "error in write ixfr updates to zone file %s", zp ->z_source); } } zp->z_xferpid = 0; if (zp->z_flags & Z_TMP_FILE) (void) unlink(zp->z_source); sched_zone_maint(zp); } } } /* * Add this zone to the set of those needing transfers. */ void addxfer(struct zoneinfo *zp) { if (!(zp->z_flags & Z_NEED_XFER)) { zp->z_flags |= Z_NEED_XFER; xfers_deferred++; tryxfer(); } } /* * Mark one zone as requiring a reload. * Note that it should be called with signals blocked, * and should not allocate memory (since it can be called from a sighandler). */ const char * deferred_reload_unsafe(struct zoneinfo *zp) { INSIST(zp->z_type != z_nil); if (!zonefile_changed_p(zp)) return ("Zone file has not changed."); if (LINKED(zp, z_reloadlink)) return ("Zone is already scheduled for reloading."); APPEND(reloadingzones, zp, z_reloadlink); ns_need_unsafe(main_need_zreload); return ("Zone is now scheduled for reloading."); } /* * If we've loaded this file, and the file has not been modified and contains * no $INCLUDE, then there's no need to reload. */ int zonefile_changed_p(struct zoneinfo *zp) { struct stat sb; INSIST(zp->z_type != z_nil); return ((zp->z_flags & Z_INCLUDE) != 0 || stat(zp->z_source, &sb) == -1 || zp->z_ftime != sb.st_mtime); } int reload_master(struct zoneinfo *zp) { INSIST(zp->z_type == z_master); zp->z_flags &= ~Z_AUTH; ns_stopxfrs(zp); /* XXX what about parent zones? */ #ifdef BIND_UPDATE /* * A dynamic zone might have changed, so we * need to dump it before reloading it. */ if ((zp->z_flags & Z_DYNAMIC) != 0 && ((zp->z_flags & Z_NEED_SOAUPDATE) != 0 || (zp->z_flags & Z_NEED_DUMP) != 0)) (void) zonedump(zp, ISNOTIXFR); #endif - purge_zone(zp->z_origin, hashtab, zp->z_class); + purge_zone(zp, hashtab); ns_debug(ns_log_config, 1, "reloading zone"); #ifdef BIND_UPDATE if ((zp->z_flags & Z_DYNAMIC) != 0) { struct stat sb; if (stat(zp->z_source, &sb) < 0) ns_error(ns_log_config, "stat(%s) failed: %s", zp->z_source, strerror(errno)); else { if ((sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0) ns_warning(ns_log_config, "dynamic zone file '%s' is writable", zp->z_source); } } #endif if (!db_load(zp->z_source, zp->z_origin, zp, NULL, ISNOTIXFR)) zp->z_flags |= Z_AUTH; zp->z_refresh = 0; /* no maintenance needed */ zp->z_time = 0; #ifdef BIND_UPDATE zp->z_lastupdate = 0; if ((zp->z_flags & Z_DYNAMIC) != 0) if (merge_logs(zp, zp->z_updatelog) == 1) return (1); #endif return (0); } /* * Called by main() when main_need_zreload has been set. Should pull one * zone off of the reloadingzones list and reload it, then if the list is * not then empty, should turn main_need_zreload on again for the next call. * It is not an error to call this when the reloadingzones list is empty. */ void ns_zreload(void) { struct zoneinfo *zp; block_signals(); if (EMPTY(reloadingzones)) { unblock_signals(); return; } zp = HEAD(reloadingzones); UNLINK(reloadingzones, zp, z_reloadlink); unblock_signals(); reload_master(zp); block_signals(); if (!EMPTY(reloadingzones)) ns_need_unsafe(main_need_zreload); unblock_signals(); } /* * Flush and reload configuration file and data base. */ void ns_reload(void) { ns_notice(ns_log_default, "%s %snameserver", (reconfiging != 0) ? "reconfiguring" : "reloading", (noexpired == 1) ? "(-noexpired) " : ""); INSIST(reloading == 0); qflush(); sq_flush(NULL); reloading++; /* To force transfer if slave and backing up. */ confmtime = ns_init(conffile); time(&resettime); reloading--; ns_notice(ns_log_default, "Ready to answer queries."); } /* * Reload configuration, look for new or deleted zones, not changed ones * also ignore expired zones. */ void ns_noexpired(void) { INSIST(noexpired == 0); noexpired++; /* To ignore zones which are expired */ ns_reconfig(); noexpired--; } /* * Reload configuration, look for new or deleted zones, not changed ones. */ void ns_reconfig(void) { INSIST(reconfiging == 0); reconfiging++; /* To ignore zones which aren't new or deleted. */ ns_reload(); reconfiging--; } void make_new_zones(void) { struct zoneinfo *zp; int n; int newzones = (nzones == 0) ? INITIALZONES : NEWZONES; ns_debug(ns_log_config, 1, "Adding %d template zones", NEWZONES); zp = (struct zoneinfo *) memget((nzones + newzones) * sizeof(struct zoneinfo)); if (zp == NULL) panic("no memory for more zones", NULL); memset(zp, 0, (nzones + newzones) * sizeof(struct zoneinfo)); if (zones != NULL) { memcpy(zp, zones, nzones * sizeof(struct zoneinfo)); memput(zones, nzones * sizeof(struct zoneinfo)); } zones = zp; block_signals(); for (n = 0; n < newzones; n++) { INIT_LINK(&zones[nzones], z_reloadlink); INIT_LINK(&zones[nzones], z_freelink); if (nzones != 0) free_zone(&zones[nzones]); nzones++; } unblock_signals(); } void free_zone(struct zoneinfo *zp) { if (LINKED(zp, z_reloadlink)) panic("freeing reloading zone", NULL); if (zp->z_type != z_nil) panic("freeing unfree zone", NULL); APPEND(freezones, zp, z_freelink); } #ifndef HAVE_SPAWNXFER static pid_t spawnxfer(char **argv, struct zoneinfo *zp) { pid_t pid = (pid_t)vfork(); if (pid == -1) { ns_error(ns_log_default, "xfer vfork: %s", strerror(errno)); zp->z_time = tt.tv_sec + 10; return (pid); } if (pid == 0) { /* Child. */ execv(server_options->named_xfer, argv); ns_error(ns_log_default, "can't exec %s: %s", server_options->named_xfer, strerror(errno)); _exit(XFER_FAIL); /* Avoid duplicate buffer flushes. */ } return (pid); } #endif struct zoneinfo * find_auth_zone(const char *zname, ns_class zclass) { struct zoneinfo *zp; struct hashbuf *htp; struct namebuf *np; const char *fname; int zn; zp = find_zone(zname, zclass); if (zp != NULL && (zp->z_type == z_slave || zp->z_type == z_master || zp->z_type == z_stub)) return (zp); htp = hashtab; np = nlookup(zname, &htp, &fname, 0); if (np != NULL && (zn = findMyZone(np, zclass)) != DB_Z_CACHE) return (&zones[zn]); return (NULL); } diff --git a/contrib/bind/bin/named/ns_ncache.c b/contrib/bind/bin/named/ns_ncache.c index 8e79793983d2..efabc3300923 100644 --- a/contrib/bind/bin/named/ns_ncache.c +++ b/contrib/bind/bin/named/ns_ncache.c @@ -1,269 +1,269 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_ncache.c,v 8.29.4.1 2002/11/14 13:41:31 marka Exp $"; +static const char rcsid[] = "$Id: ns_ncache.c,v 8.30 2002/11/17 14:51:51 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" #define BOUNDS_CHECK(ptr, count) \ do { \ if ((ptr) + (count) > eom) { \ return; \ } \ } while (0) void cache_n_resp(u_char *msg, int msglen, struct sockaddr_in from, const char *qname, int qclass, int qtype) { struct databuf *dp; HEADER *hp; u_char *cp, *eom, *rdatap; char dname[MAXDNAME]; int n, type, class, flags; u_int ancount, nscount, dlen; #ifdef RETURNSOA u_int32_t ttl; u_int16_t atype; u_char *sp, *cp1; u_char data[MAXDATA]; u_char *eod = data + sizeof(data); #endif nameserIncr(from.sin_addr, nssRcvdNXD); hp = (HEADER *)msg; cp = msg + HFIXEDSZ; eom = msg + msglen; switch (ntohs(hp->qdcount)) { case 0: dname[sizeof dname - 1] = '\0'; strncpy(dname, qname, sizeof dname); if (dname[sizeof dname - 1] != '\0') { ns_debug(ns_log_ncache, 1, "qp->qname too long (%d)", strlen(qname)); hp->rcode = FORMERR; return; } class = qclass; type = qtype; break; case 1: n = dn_expand(msg, eom, cp, dname, sizeof dname); if (n < 0) { ns_debug(ns_log_ncache, 1, "Query expand name failed: cache_n_resp"); hp->rcode = FORMERR; return; } cp += n; BOUNDS_CHECK(cp, 2 * INT16SZ); GETSHORT(type, cp); GETSHORT(class, cp); if (class > CLASS_MAX) { ns_debug(ns_log_ncache, 1, "bad class in cache_n_resp"); hp->rcode = FORMERR; return; } break; default: ns_debug(ns_log_ncache, 1, "QDCOUNT>1 (%d) in cache_n_resp", ntohs(hp->qdcount)); hp->rcode = FORMERR; return; } ns_debug(ns_log_ncache, 1, "ncache: dname %s, type %d, class %d", dname, type, class); ancount = ntohs(hp->ancount); nscount = ntohs(hp->nscount); while (ancount--) { u_int32_t ttl; int atype, aclass; n = dn_skipname(cp, eom); if (n < 0) { ns_debug(ns_log_ncache, 3, "ncache: form error"); return; } cp += n; BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); GETSHORT(atype, cp); GETSHORT(aclass, cp); if (atype != T_CNAME || aclass != class) { ns_debug(ns_log_ncache, 3, "ncache: not CNAME (%s) or wrong class (%s)", p_type(atype), p_class(aclass)); return; } GETLONG(ttl, cp); GETSHORT(dlen, cp); BOUNDS_CHECK(cp, dlen); rdatap = cp; n = dn_expand(msg, msg + msglen, cp, dname, sizeof dname); if (n < 0) { ns_debug(ns_log_ncache, 3, "ncache: bad cname target"); return; } cp += n; if (cp != rdatap + dlen) { ns_debug(ns_log_ncache, 3, "ncache: bad cname rdata"); return; } } dp = NULL; #ifdef RETURNSOA while (nscount--) { sp = cp; /* we store NXDOMAIN as T_SOA regardless of the query type */ if (hp->rcode == NXDOMAIN) type = T_SOA; /* store ther SOA record */ n = dn_skipname(cp, msg + msglen); if (n < 0) { ns_debug(ns_log_ncache, 3, "ncache: form error"); return; } cp += n; BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); GETSHORT(atype, cp); /* type */ cp += INT16SZ; /* class */ GETLONG(ttl, cp); /* ttl */ GETSHORT(dlen, cp); /* dlen */ BOUNDS_CHECK(cp, dlen); if (atype != T_SOA) { ns_debug(ns_log_ncache, 3, "ncache: type (%d) != T_SOA", atype); cp += dlen; continue; } rdatap = cp; /* origin */ n = dn_expand(msg, msg + msglen, cp, (char*)data, eod - data); if (n < 0) { ns_debug(ns_log_ncache, 3, "ncache: origin form error"); return; } cp += n; n = strlen((char*)data) + 1; cp1 = data + n; /* mail */ n = dn_expand(msg, msg + msglen, cp, (char*)cp1, eod - cp1); if (n < 0) { ns_debug(ns_log_ncache, 3, "ncache: mail form error"); return; } cp += n; n = strlen((char*)cp1) + 1; cp1 += n; n = 5 * INT32SZ; if (n > (eod - cp1)) /* Can't happen. See MAXDATA. */ return; BOUNDS_CHECK(cp, n); memcpy(cp1, cp, n); /* serial, refresh, retry, expire, min */ cp1 += n; cp += n; if (cp != rdatap + dlen) { ns_debug(ns_log_ncache, 3, "ncache: form error"); return; } /* store the zone of the soa record */ n = dn_expand(msg, msg + msglen, sp, (char*)cp1, eod - cp1); if (n < 0) { ns_debug(ns_log_ncache, 3, "ncache: form error 2"); return; } n = strlen((char*)cp1) + 1; cp1 += n; /* * we only want to store these long enough so that * ns_resp can find it. */ if (qtype == T_SOA && hp->rcode == NXDOMAIN) ttl = 0; dp = savedata(class, type, MIN(ttl, server_options->max_ncache_ttl) + tt.tv_sec, data, cp1 - data); break; } #endif if (dp == NULL) #ifdef STRICT_RFC2308 dp = savedata(class, type, tt.tv_sec, NULL, 0); #else dp = savedata(class, type, NTTL + tt.tv_sec, NULL, 0); #endif dp->d_zone = DB_Z_CACHE; dp->d_cred = hp->aa ? DB_C_AUTH : DB_C_ANSWER; dp->d_secure = DB_S_INSECURE; /* BEW - should be UNCHECKED */ dp->d_clev = 0; if(hp->rcode == NXDOMAIN) { dp->d_rcode = NXDOMAIN; flags = DB_NODATA|DB_NOTAUTH|DB_NOHINTS; } else { dp->d_rcode = NOERROR_NODATA; flags = DB_NOTAUTH|DB_NOHINTS; } n = db_update(dname, dp, dp, NULL, flags, hashtab, from); if (n != OK) ns_debug(ns_log_ncache, 1, "db_update failed (%d), cache_n_resp()", n); else ns_debug(ns_log_ncache, 4, "ncache succeeded: [%s %s %s] rcode:%d ttl:%ld", dname, p_type(type), p_class(class), dp->d_rcode, (long)(dp->d_ttl - tt.tv_sec)); db_detach(&dp); } diff --git a/contrib/bind/bin/named/ns_parser.y b/contrib/bind/bin/named/ns_parser.y index 79f34b1adaab..c8ddc72f29a4 100644 --- a/contrib/bind/bin/named/ns_parser.y +++ b/contrib/bind/bin/named/ns_parser.y @@ -1,2067 +1,2079 @@ %{ #if !defined(lint) && !defined(SABER) -static char rcsid[] = "$Id: ns_parser.y,v 8.80 2002/05/24 03:05:01 marka Exp $"; +static char rcsid[] = "$Id: ns_parser.y,v 8.81.8.1 2003/06/02 09:56:35 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Global C stuff goes here. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" #include "ns_parseutil.h" #include "ns_lexer.h" #define SYM_ZONE 0x010000 #define SYM_SERVER 0x020000 #define SYM_KEY 0x030000 #define SYM_ACL 0x040000 #define SYM_CHANNEL 0x050000 #define SYM_PORT 0x060000 #define SYMBOL_TABLE_SIZE 29989 /* should always be prime */ static symbol_table symtab; #define AUTH_TABLE_SIZE 397 /* should always be prime */ static symbol_table authtab = NULL; static symbol_table channeltab = NULL; static zone_config current_zone; static int should_install; static options current_options; static int seen_options; static int logged_options_error; static controls current_controls; static int seen_topology; static server_config current_server; static int seen_server; static char *current_algorithm; static char *current_secret; static log_config current_logging; static int current_category; static int chan_type; static int chan_level; static u_int chan_flags; static int chan_facility; static char *chan_name; static int chan_versions; static u_long chan_max_size; static log_channel lookup_channel(char *); static void define_channel(const char *, log_channel); static char *canonical_name(char *); int yyparse(); %} %union { char * cp; int s_int; long num; u_long ul_int; u_int16_t us_int; struct in_addr ip_addr; ip_match_element ime; ip_match_list iml; rrset_order_list rol; rrset_order_element roe; struct dst_key * keyi; enum axfr_format axfr_fmt; } /* Lexical analyzer return values. */ %token L_EOS %token L_IPADDR %token L_NUMBER %token L_STRING %token L_QSTRING %token L_END_INCLUDE /* Include support */ %token T_INCLUDE /* Items related to the "options" statement: */ %token T_OPTIONS %token T_DIRECTORY T_PIDFILE T_NAMED_XFER %token T_DUMP_FILE T_STATS_FILE T_MEMSTATS_FILE %token T_FAKE_IQUERY T_RECURSION T_FETCH_GLUE %token T_HITCOUNT T_PREFERRED_GLUE %token T_QUERY_SOURCE T_LISTEN_ON T_PORT T_ADDRESS %token T_RRSET_ORDER T_ORDER T_NAME T_CLASS %token T_CONTROLS T_INET T_UNIX T_PERM T_OWNER T_GROUP T_ALLOW %type in_port %type maybe_port %type maybe_zero_port %type maybe_wild_port %type maybe_wild_addr %token T_DATASIZE T_STACKSIZE T_CORESIZE %token T_DEFAULT T_UNLIMITED %token T_FILES T_VERSION T_HOSTNAME %token T_HOSTSTATS T_HOSTSTATSMAX T_DEALLOC_ON_EXIT %token T_TRANSFERS_IN T_TRANSFERS_OUT T_TRANSFERS_PER_NS %token T_TRANSFER_FORMAT T_MAX_TRANSFER_TIME_IN %token T_SERIAL_QUERIES T_ONE_ANSWER T_MANY_ANSWERS %type transfer_format %token T_NOTIFY T_EXPLICIT T_NOTIFY_INITIAL T_AUTH_NXDOMAIN %token T_MULTIPLE_CNAMES T_USE_IXFR T_MAINTAIN_IXFR_BASE %token T_CLEAN_INTERVAL T_INTERFACE_INTERVAL T_STATS_INTERVAL %token T_MAX_LOG_SIZE_IXFR %token T_HEARTBEAT T_USE_ID_POOL %token T_MAX_NCACHE_TTL T_HAS_OLD_CLIENTS T_RFC2308_TYPE1 %token T_LAME_TTL T_MIN_ROOTS %token T_TREAT_CR_AS_SPACE +%token T_EDNS_UDP_SIZE /* Items used for the "logging" statement: */ %token T_LOGGING T_CATEGORY T_CHANNEL T_SEVERITY T_DYNAMIC %token T_FILE T_VERSIONS T_SIZE %token T_SYSLOG T_DEBUG T_NULL_OUTPUT %token T_PRINT_TIME T_PRINT_CATEGORY T_PRINT_SEVERITY %type category %type category_name channel_name facility_name %type maybe_syslog_facility /* Items used for the "sortlist" statement: */ %token T_SORTLIST /* Items used for the "topology" statement: */ %token T_TOPOLOGY %type ordering_class %type ordering_type %type ordering_name %type rrset_ordering_list %type rrset_ordering_element /* ip_match_list */ %type address_match_simple address_match_element address_name %type address_match_list /* Items used for "server" statements: */ %token T_SERVER %token T_LONG_AXFR %token T_BOGUS %token T_TRANSFERS %token T_KEYS %token T_SUPPORT_IXFR %token T_EDNS /* Items used for "zone" statements: */ %token T_ZONE %type optional_class %type zone_type %token T_IN T_CHAOS T_HESIOD %token T_TYPE %token T_MASTER T_SLAVE T_STUB T_RESPONSE %token T_HINT %token T_MASTERS T_TRANSFER_SOURCE %token T_PUBKEY %token T_ALSO_NOTIFY %token T_DIALUP %token T_FILE_IXFR %token T_IXFR_TMP /* Items used for "trusted-keys" statements: */ %token T_TRUSTED_KEYS /* Items used for access control lists and "allow" clauses: */ %token T_ACL %token T_ALLOW_UPDATE T_ALLOW_QUERY T_ALLOW_TRANSFER %token T_ALLOW_RECURSION %token T_BLACKHOLE /* Items related to the "key" statement: */ %token T_SEC_KEY T_ALGID T_SECRET %type key_ref %type algorithm_id secret /* Items used for "size_spec" clauses: */ %type size_spec /* Items used for a "check-names" clause: */ %token T_CHECK_NAMES %type check_names_type %type check_names_opt %token T_WARN T_FAIL T_IGNORE /* Items used for "forward" clauses: */ %token T_FORWARD T_FORWARDERS %token T_ONLY T_FIRST T_IF_NO_ANSWER T_IF_NO_DOMAIN /* Items used for yes/no responses: */ %type yea_or_nay %token T_YES T_TRUE T_NO T_FALSE /* Miscellaneous items (used in several places): */ %type any_string %% config_file: statement_list { if (EMPTY(current_controls)) ns_ctl_defaults(¤t_controls); ns_ctl_install(¤t_controls); } ; statement_list: statement | statement_list statement ; statement: include_stmt | options_stmt L_EOS | controls_stmt L_EOS | logging_stmt L_EOS | server_stmt L_EOS | zone_stmt L_EOS | trusted_keys_stmt L_EOS | acl_stmt L_EOS | key_stmt L_EOS | L_END_INCLUDE | error L_EOS | error L_END_INCLUDE ; include_stmt: T_INCLUDE L_QSTRING L_EOS { lexer_begin_file($2, NULL); (void)freestr($2); } ; /* * Options */ options_stmt: T_OPTIONS { if (seen_options) parser_error(0, "cannot redefine options"); current_options = new_options(); } '{' options '}' { if (!seen_options) set_options(current_options, 0); else free_options(current_options); current_options = NULL; seen_options = 1; } ; options: option L_EOS | options option L_EOS ; option: /* Empty */ | T_HOSTNAME L_QSTRING { if (current_options->hostname != NULL) (void)freestr(current_options->hostname); current_options->hostname = $2; } | T_VERSION L_QSTRING { if (current_options->version != NULL) (void)freestr(current_options->version); current_options->version = $2; } | T_DIRECTORY L_QSTRING { if (current_options->directory != NULL) (void)freestr(current_options->directory); current_options->directory = $2; } | T_NAMED_XFER L_QSTRING { if (current_options->named_xfer != NULL) (void)freestr(current_options->named_xfer); current_options->named_xfer = $2; } | T_PIDFILE L_QSTRING { if (current_options->pid_filename != NULL) (void)freestr(current_options->pid_filename); current_options->pid_filename = $2; } | T_STATS_FILE L_QSTRING { if (current_options->stats_filename != NULL) (void)freestr(current_options->stats_filename); current_options->stats_filename = $2; } | T_MEMSTATS_FILE L_QSTRING { if (current_options->memstats_filename != NULL) (void)freestr(current_options->memstats_filename); current_options->memstats_filename = $2; } | T_DUMP_FILE L_QSTRING { if (current_options->dump_filename != NULL) (void)freestr(current_options->dump_filename); current_options->dump_filename = $2; } | T_PREFERRED_GLUE L_STRING { current_options->preferred_glue = strcasecmp($2, "aaaa") ? T_A : T_AAAA; } | T_FAKE_IQUERY yea_or_nay { set_global_boolean_option(current_options, OPTION_FAKE_IQUERY, $2); } | T_RECURSION yea_or_nay { set_global_boolean_option(current_options, OPTION_NORECURSE, !$2); } | T_FETCH_GLUE yea_or_nay { set_global_boolean_option(current_options, OPTION_NOFETCHGLUE, !$2); } | T_HITCOUNT yea_or_nay { set_global_boolean_option(current_options, OPTION_HITCOUNT, $2); } | T_NOTIFY T_EXPLICIT { current_options->notify = notify_explicit; } | T_NOTIFY yea_or_nay { if ($2) current_options->notify = notify_yes; else current_options->notify = notify_no; } | T_NOTIFY_INITIAL yea_or_nay { if (initial_configuration && $2) ns_notice(ns_log_default, "suppressing initial notifies"); set_global_boolean_option(current_options, OPTION_SUPNOTIFY_INITIAL, $2); } | T_HOSTSTATS yea_or_nay { set_global_boolean_option(current_options, OPTION_HOSTSTATS, $2); } | T_DEALLOC_ON_EXIT yea_or_nay { set_global_boolean_option(current_options, OPTION_DEALLOC_ON_EXIT, $2); } | T_USE_IXFR yea_or_nay { set_global_boolean_option(current_options, OPTION_USE_IXFR, $2); } | T_MAINTAIN_IXFR_BASE yea_or_nay { set_global_boolean_option(current_options, OPTION_MAINTAIN_IXFR_BASE, $2); } | T_HAS_OLD_CLIENTS yea_or_nay { set_global_boolean_option(current_options, OPTION_NORFC2308_TYPE1, $2); set_global_boolean_option(current_options, OPTION_NONAUTH_NXDOMAIN, !$2); } | T_AUTH_NXDOMAIN yea_or_nay { set_global_boolean_option(current_options, OPTION_NONAUTH_NXDOMAIN, !$2); } | T_MULTIPLE_CNAMES yea_or_nay { set_global_boolean_option(current_options, OPTION_MULTIPLE_CNAMES, $2); } | T_CHECK_NAMES check_names_type check_names_opt { current_options->check_names[$2] = (enum severity)$3; } | T_USE_ID_POOL yea_or_nay { set_global_boolean_option(current_options, OPTION_USE_ID_POOL, $2); } | T_RFC2308_TYPE1 yea_or_nay { set_global_boolean_option(current_options, OPTION_NORFC2308_TYPE1, !$2); } | T_LISTEN_ON maybe_port '{' address_match_list '}' { char port_string[10]; symbol_value value; (void)sprintf(port_string, "%u", $2); if (lookup_symbol(symtab, port_string, SYM_PORT, NULL)) parser_error(0, "cannot redefine listen-on for port %u", ntohs($2)); else { add_listen_on(current_options, $2, $4); value.pointer = NULL; define_symbol(symtab, port_string, SYM_PORT, value, 0); } } | T_FORWARD forward_opt | T_FORWARDERS { if (current_options->fwdtab) { free_forwarders(current_options->fwdtab); current_options->fwdtab = NULL; } } '{' opt_forwarders_list '}' | T_QUERY_SOURCE query_source | T_TRANSFER_SOURCE maybe_wild_addr { current_options->axfr_src = $2; } | T_ALLOW_QUERY '{' address_match_list '}' { if (current_options->query_acl) { parser_warning(0, "options allow-query acl already set; skipping"); free_ip_match_list($3); } else current_options->query_acl = $3; } | T_ALLOW_RECURSION '{' address_match_list '}' { if (current_options->recursion_acl) { parser_warning(0, "options allow-recursion acl already set; skipping"); free_ip_match_list($3); } else current_options->recursion_acl = $3; } | T_ALLOW_TRANSFER '{' address_match_list '}' { if (current_options->transfer_acl) { parser_warning(0, "options allow-transfer acl already set; skipping"); free_ip_match_list($3); } else current_options->transfer_acl = $3; } | T_SORTLIST '{' address_match_list '}' { if (current_options->sortlist) { parser_warning(0, "options sortlist already set; skipping"); free_ip_match_list($3); } else current_options->sortlist = $3; } | T_ALSO_NOTIFY { if (current_options->also_notify) { parser_warning(0, "duplicate also-notify clause: overwriting"); free_also_notify(current_options); current_options->also_notify = NULL; } } '{' opt_also_notify_list '}' | T_BLACKHOLE '{' address_match_list '}' { if (current_options->blackhole_acl) { parser_warning(0, "options blackhole already set; skipping"); free_ip_match_list($3); } else current_options->blackhole_acl = $3; } | T_TOPOLOGY '{' address_match_list '}' { if (current_options->topology) { parser_warning(0, "options topology already set; skipping"); free_ip_match_list($3); } else current_options->topology = $3; } | size_clause { /* To get around the $$ = $1 default rule. */ } | transfer_clause | T_TRANSFER_FORMAT transfer_format { current_options->transfer_format = $2; } | T_MAX_TRANSFER_TIME_IN L_NUMBER { current_options->max_transfer_time_in = $2 * 60; } | T_SERIAL_QUERIES L_NUMBER { current_options->serial_queries = $2; } | T_CLEAN_INTERVAL L_NUMBER { current_options->clean_interval = $2 * 60; } | T_INTERFACE_INTERVAL L_NUMBER { current_options->interface_interval = $2 * 60; } | T_STATS_INTERVAL L_NUMBER { current_options->stats_interval = $2 * 60; } | T_HOSTSTATSMAX L_NUMBER { current_options->max_host_stats = $2; } | T_MAX_LOG_SIZE_IXFR size_spec { current_options->max_log_size_ixfr = $2; } | T_MAX_NCACHE_TTL L_NUMBER { current_options->max_ncache_ttl = $2; } | T_LAME_TTL L_NUMBER { current_options->lame_ttl = $2; } | T_HEARTBEAT L_NUMBER { current_options->heartbeat_interval = $2 * 60; } | T_DIALUP yea_or_nay { set_global_boolean_option(current_options, OPTION_NODIALUP, !$2); } | T_RRSET_ORDER '{' rrset_ordering_list '}' { if (current_options->ordering) free_rrset_order_list(current_options->ordering); current_options->ordering = $3; } | T_TREAT_CR_AS_SPACE yea_or_nay { set_global_boolean_option(current_options, OPTION_TREAT_CR_AS_SPACE, $2); } | T_MIN_ROOTS L_NUMBER { if ($2 >= 1) current_options->minroots = $2; } + | + | T_EDNS_UDP_SIZE L_NUMBER + { + if ($2 < 512) + current_options->edns_udp_size = 512; + else if ($2 > EDNS_MESSAGE_SZ) + current_options->edns_udp_size = EDNS_MESSAGE_SZ; + else + current_options->edns_udp_size = $2; + } | error ; /* * Controls. */ controls_stmt: T_CONTROLS '{' controls '}' ; controls: control L_EOS | controls control L_EOS ; control: /* Empty */ | T_INET maybe_wild_addr T_PORT in_port T_ALLOW '{' address_match_list '}' { ns_ctl_add(¤t_controls, ns_ctl_new_inet($2, $4, $7)); } | T_INET maybe_wild_addr T_ALLOW '{' address_match_list '}' T_KEYS '{' dummy_key_list '}' { parser_warning(0, "Ignoring BIND 9 inet control clause"); free_ip_match_list($5); } | T_INET maybe_wild_addr T_PORT in_port T_ALLOW '{' address_match_list '}' T_KEYS '{' dummy_key_list '}' { parser_warning(0, "Ignoring BIND 9 inet control clause"); free_ip_match_list($7); } | T_UNIX L_QSTRING T_PERM L_NUMBER T_OWNER L_NUMBER T_GROUP L_NUMBER { #ifndef NO_SOCKADDR_UN ns_ctl_add(¤t_controls, ns_ctl_new_unix($2, $4, $6, $8)); #endif freestr($2); } | error ; rrset_ordering_list: rrset_ordering_element L_EOS { rrset_order_list rol; rol = new_rrset_order_list(); if ($1 != NULL) { add_to_rrset_order_list(rol, $1); } $$ = rol; } | rrset_ordering_list rrset_ordering_element L_EOS { if ($2 != NULL) { add_to_rrset_order_list($1, $2); } $$ = $1; } ; ordering_class: /* nothing */ { $$ = C_ANY; } | T_CLASS any_string { symbol_value value; if (lookup_symbol(constants, $2, SYM_CLASS, &value)) $$ = value.integer; else { parser_error(0, "unknown class '%s'; using ANY", $2); $$ = C_ANY; } (void)freestr($2); } ; ordering_type: /* nothing */ { $$ = ns_t_any; } | T_TYPE any_string { int success; if (strcmp($2, "*") == 0) { $$ = ns_t_any; } else { $$ = __sym_ston(__p_type_syms, $2, &success); if (success == 0) { $$ = ns_t_any; parser_error(0, "unknown type '%s'; assuming ANY", $2); } } (void)freestr($2); } + ; ordering_name: /* nothing */ { $$ = savestr("*", 1); } | T_NAME L_QSTRING { if (strcmp(".",$2) == 0 || strcmp("*.",$2) == 0) { $$ = savestr("*", 1); (void)freestr($2); } else { $$ = $2 ; } /* XXX Should do any more name validation here? */ } - + ; rrset_ordering_element: ordering_class ordering_type ordering_name T_ORDER L_STRING { enum ordering o; if (strlen($5) == 0) { parser_error(0, "null order name"); $$ = NULL ; } else { o = lookup_ordering($5); if (o == unknown_order) { o = (enum ordering)DEFAULT_ORDERING; parser_error(0, "invalid order name '%s'; using %s", $5, p_order(o)); } (void)freestr($5); $$ = new_rrset_order_element($1, $2, $3, o); } } - + ; transfer_format: T_ONE_ANSWER { $$ = axfr_one_answer; } | T_MANY_ANSWERS { $$ = axfr_many_answers; } ; maybe_wild_addr: L_IPADDR { $$ = $1; } | '*' { $$.s_addr = htonl(INADDR_ANY); } ; maybe_wild_port: in_port { $$ = $1; } | '*' { $$ = htons(0); } ; query_source_address: T_ADDRESS maybe_wild_addr { current_options->query_source.sin_addr = $2; } ; query_source_port: T_PORT maybe_wild_port { current_options->query_source.sin_port = $2; } ; query_source: query_source_address | query_source_port | query_source_address query_source_port | query_source_port query_source_address ; maybe_port: /* nothing */ { $$ = htons(NS_DEFAULTPORT); } | T_PORT in_port { $$ = $2; } ; maybe_zero_port: /* nothing */ { $$ = htons(0); } | T_PORT in_port { $$ = $2; } ; yea_or_nay: T_YES { $$ = 1; } | T_TRUE { $$ = 1; } | T_NO { $$ = 0; } | T_FALSE { $$ = 0; } | L_NUMBER { if ($1 == 1 || $1 == 0) { $$ = $1; } else { parser_warning(0, "number should be 0 or 1; assuming 1"); $$ = 1; } } ; check_names_type: T_MASTER { $$ = primary_trans; } | T_SLAVE { $$ = secondary_trans; } | T_RESPONSE { $$ = response_trans; } ; check_names_opt: T_WARN { $$ = warn; } | T_FAIL { $$ = fail; } | T_IGNORE { $$ = ignore; } ; forward_opt: T_ONLY { set_global_boolean_option(current_options, OPTION_FORWARD_ONLY, 1); } | T_FIRST { set_global_boolean_option(current_options, OPTION_FORWARD_ONLY, 0); } | T_IF_NO_ANSWER { parser_warning(0, "forward if-no-answer is unimplemented"); } | T_IF_NO_DOMAIN { parser_warning(0, "forward if-no-domain is unimplemented"); } ; size_clause: T_DATASIZE size_spec { current_options->data_size = $2; } | T_STACKSIZE size_spec { current_options->stack_size = $2; } | T_CORESIZE size_spec { current_options->core_size = $2; } | T_FILES size_spec { current_options->files = $2; } ; size_spec: any_string { u_long result; if (unit_to_ulong($1, &result)) $$ = result; else { parser_error(0, "invalid unit string '%s'", $1); /* 0 means "use default" */ $$ = 0; } (void)freestr($1); } | L_NUMBER { $$ = (u_long)$1; } | T_DEFAULT { $$ = 0; } | T_UNLIMITED { $$ = ULONG_MAX; } ; transfer_clause: T_TRANSFERS_IN L_NUMBER { current_options->transfers_in = (u_long) $2; } | T_TRANSFERS_OUT L_NUMBER { current_options->transfers_out = (u_long) $2; } | T_TRANSFERS_PER_NS L_NUMBER { current_options->transfers_per_ns = (u_long) $2; } ; opt_forwarders_list: /* nothing */ | forwarders_in_addr_list ; forwarders_in_addr_list: forwarders_in_addr L_EOS { /* nothing */ } | forwarders_in_addr_list forwarders_in_addr L_EOS { /* nothing */ } ; forwarders_in_addr: L_IPADDR { add_global_forwarder(current_options, $1); } ; opt_also_notify_list: /* nothing */ | also_notify_in_addr_list ; also_notify_in_addr_list: also_notify_in_addr L_EOS { /* nothing */ } | also_notify_in_addr_list also_notify_in_addr L_EOS { /* nothing */ } ; also_notify_in_addr: L_IPADDR { add_global_also_notify(current_options, $1); } ; /* * Logging */ logging_stmt: T_LOGGING { current_logging = begin_logging(); } '{' logging_opts_list '}' { end_logging(current_logging, 1); current_logging = NULL; } ; logging_opts_list: logging_opt L_EOS | logging_opts_list logging_opt L_EOS | error ; logging_opt: T_CATEGORY category { current_category = $2; } '{' channel_list '}' | T_CHANNEL channel_name { chan_type = log_null; chan_flags = 0; chan_level = log_info; } '{' channel_opt_list '}' { log_channel current_channel = NULL; if (lookup_channel($2) != NULL) { parser_error(0, "can't redefine channel '%s'", $2); } else { switch (chan_type) { case log_file: current_channel = log_new_file_channel(chan_flags, chan_level, chan_name, NULL, chan_versions, chan_max_size); log_set_file_owner(current_channel, user_id, group_id); chan_name = freestr(chan_name); break; case log_syslog: current_channel = log_new_syslog_channel(chan_flags, chan_level, chan_facility); break; case log_null: current_channel = log_new_null_channel(); break; default: ns_panic(ns_log_parser, 1, "unknown channel type: %d", chan_type); } if (current_channel == NULL) ns_panic(ns_log_parser, 0, "couldn't create channel"); define_channel($2, current_channel); } (void)freestr($2); } ; channel_severity: any_string { symbol_value value; if (lookup_symbol(constants, $1, SYM_LOGGING, &value)) { chan_level = value.integer; } else { parser_error(0, "unknown severity '%s'", $1); chan_level = log_debug(99); } (void)freestr($1); } | T_DEBUG { chan_level = log_debug(1); } | T_DEBUG L_NUMBER { chan_level = $2; } | T_DYNAMIC { chan_level = 0; chan_flags |= LOG_USE_CONTEXT_LEVEL|LOG_REQUIRE_DEBUG; } ; version_modifier: T_VERSIONS L_NUMBER { chan_versions = $2; } | T_VERSIONS T_UNLIMITED { chan_versions = LOG_MAX_VERSIONS; } ; size_modifier: T_SIZE size_spec { chan_max_size = $2; } ; maybe_file_modifiers: /* nothing */ { chan_versions = 0; chan_max_size = ULONG_MAX; } | version_modifier { chan_max_size = ULONG_MAX; } | size_modifier { chan_versions = 0; } | version_modifier size_modifier | size_modifier version_modifier ; channel_file: T_FILE L_QSTRING maybe_file_modifiers { chan_flags |= LOG_CLOSE_STREAM; chan_type = log_file; chan_name = $2; } ; facility_name: any_string { $$ = $1; } | T_SYSLOG { $$ = savestr("syslog", 1); } ; maybe_syslog_facility: /* nothing */ { $$ = LOG_DAEMON; } | facility_name { symbol_value value; if (lookup_symbol(constants, $1, SYM_SYSLOG, &value)) { $$ = value.integer; } else { parser_error(0, "unknown facility '%s'", $1); $$ = LOG_DAEMON; } (void)freestr($1); } ; channel_syslog: T_SYSLOG maybe_syslog_facility { chan_type = log_syslog; chan_facility = $2; } ; channel_opt: channel_file { /* nothing to do */ } | channel_syslog { /* nothing to do */ } | T_NULL_OUTPUT { chan_type = log_null; } | T_SEVERITY channel_severity { /* nothing to do */ } | T_PRINT_TIME yea_or_nay { if ($2) chan_flags |= LOG_TIMESTAMP; else chan_flags &= ~LOG_TIMESTAMP; } | T_PRINT_CATEGORY yea_or_nay { if ($2) chan_flags |= LOG_PRINT_CATEGORY; else chan_flags &= ~LOG_PRINT_CATEGORY; } | T_PRINT_SEVERITY yea_or_nay { if ($2) chan_flags |= LOG_PRINT_LEVEL; else chan_flags &= ~LOG_PRINT_LEVEL; } ; channel_opt_list: channel_opt L_EOS | channel_opt_list channel_opt L_EOS | error ; channel_name: any_string | T_NULL_OUTPUT { $$ = savestr("null", 1); } ; channel: channel_name { log_channel channel; if (current_category >= 0) { channel = lookup_channel($1); if (channel != NULL) { add_log_channel(current_logging, current_category, channel); } else parser_error(0, "unknown channel '%s'", $1); } (void)freestr($1); } ; channel_list: channel L_EOS | channel_list channel L_EOS | error ; category_name: any_string | T_DEFAULT { $$ = savestr("default", 1); } | T_NOTIFY { $$ = savestr("notify", 1); } ; category: category_name { symbol_value value; if (lookup_symbol(constants, $1, SYM_CATEGORY, &value)) $$ = value.integer; else { parser_error(0, "invalid logging category '%s'", $1); $$ = -1; } (void)freestr($1); } ; /* * Server Information */ server_stmt: T_SERVER L_IPADDR { const char *ip_printable; symbol_value value; ip_printable = inet_ntoa($2); value.pointer = NULL; if (lookup_symbol(symtab, ip_printable, SYM_SERVER, NULL)) seen_server = 1; else seen_server = 0; if (seen_server) parser_error(0, "cannot redefine server '%s'", ip_printable); else define_symbol(symtab, ip_printable, SYM_SERVER, value, 0); current_server = begin_server($2); } '{' server_info_list '}' { end_server(current_server, !seen_server); } ; server_info_list: server_info L_EOS | server_info_list server_info L_EOS ; server_info: T_BOGUS yea_or_nay { set_server_option(current_server, SERVER_INFO_BOGUS, $2); } | T_SUPPORT_IXFR yea_or_nay { set_server_option(current_server, SERVER_INFO_SUPPORT_IXFR, $2); } | T_TRANSFERS L_NUMBER { set_server_transfers(current_server, (int)$2); } | T_TRANSFER_FORMAT transfer_format { set_server_transfer_format(current_server, $2); } | T_KEYS '{' key_list '}' | T_EDNS yea_or_nay { set_server_option(current_server, SERVER_INFO_EDNS, $2); } | error ; /* * Address Matching */ address_match_list: address_match_element L_EOS { ip_match_list iml; iml = new_ip_match_list(); if ($1 != NULL) add_to_ip_match_list(iml, $1); $$ = iml; } | address_match_list address_match_element L_EOS { if ($2 != NULL) add_to_ip_match_list($1, $2); $$ = $1; } ; address_match_element: address_match_simple | '!' address_match_simple { if ($2 != NULL) ip_match_negate($2); $$ = $2; } | T_SEC_KEY L_STRING { char *key_name; struct dst_key *dst_key; key_name = canonical_name($2); if (key_name == NULL) { parser_error(0, "can't make key name '%s' canonical", $2); key_name = savestr("__bad_key__", 1); } dst_key = find_key(key_name, NULL); if (dst_key == NULL) { parser_error(0, "key \"%s\" not found", key_name); $$ = NULL; } else $$ = new_ip_match_key(dst_key); (void)freestr(key_name); freestr($2); } ; address_match_simple: L_IPADDR { $$ = new_ip_match_pattern($1, 32); } | L_IPADDR '/' L_NUMBER { if ($3 < 0 || $3 > 32) { parser_error(0, "mask bits out of range; skipping"); $$ = NULL; } else { $$ = new_ip_match_pattern($1, $3); if ($$ == NULL) parser_error(0, "address/mask mismatch; skipping"); } } | L_NUMBER '/' L_NUMBER { struct in_addr ia; if ($1 > 255) { parser_error(0, "address out of range; skipping"); $$ = NULL; } else { if ($3 < 0 || $3 > 32) { parser_error(0, "mask bits out of range; skipping"); $$ = NULL; } else { ia.s_addr = htonl(($1 & 0xff) << 24); $$ = new_ip_match_pattern(ia, $3); if ($$ == NULL) parser_error(0, "address/mask mismatch; skipping"); } } } | address_name | '{' address_match_list '}' { char name[256]; /* * We want to be able to clean up this iml later so * we give it a name and treat it like any other acl. */ sprintf(name, "__internal_%p", $2); define_acl(name, $2); $$ = new_ip_match_indirect($2); } ; address_name: any_string { ip_match_list iml; iml = lookup_acl($1); if (iml == NULL) { parser_error(0, "unknown ACL '%s'", $1); $$ = NULL; } else $$ = new_ip_match_indirect(iml); (void)freestr($1); } ; /* * Keys */ key_ref: any_string { struct dst_key *dst_key; char *key_name; key_name = canonical_name($1); if (key_name == NULL) { parser_error(0, "can't make key name '%s' canonical", $1); $$ = NULL; } else { dst_key = lookup_key(key_name); if (dst_key == NULL) { parser_error(0, "unknown key '%s'", key_name); $$ = NULL; } else $$ = dst_key; key_name = freestr(key_name); } (void)freestr($1); } ; key_list_element: key_ref { if ($1 == NULL) parser_error(0, "empty key not added to server list "); else add_server_key_info(current_server, $1); } ; key_list: key_list_element L_EOS | key_list key_list_element L_EOS | error ; -dummy_key_list_element: key_ref; +dummy_key_list_element: key_ref { /* empty */ } ; dummy_key_list: dummy_key_list_element L_EOS | dummy_key_list dummy_key_list_element L_EOS | error ; key_stmt: T_SEC_KEY { current_algorithm = NULL; current_secret = NULL; } any_string '{' key_definition '}' { struct dst_key *dst_key; char *key_name; key_name = canonical_name($3); if (key_name == NULL) { parser_error(0, "can't make key name '%s' canonical", $3); } else if (lookup_key(key_name) != NULL) { parser_error(0, "can't redefine key '%s'", key_name); } else { if (current_algorithm == NULL || current_secret == NULL) { parser_error(0, "skipping bad key '%s'", key_name); } else { dst_key = new_key_info(key_name, current_algorithm, current_secret); if (dst_key != NULL) { define_key(key_name, dst_key); if (secretkey_info == NULL) secretkey_info = new_key_info_list(); add_to_key_info_list(secretkey_info, dst_key); } } } if (key_name != NULL) key_name = freestr(key_name); if (current_algorithm != NULL) current_algorithm = freestr(current_algorithm); if (current_secret != NULL) current_secret = freestr(current_secret); (void)freestr($3); } ; key_definition: algorithm_id secret { current_algorithm = $1; current_secret = $2; } | secret algorithm_id { current_algorithm = $2; current_secret = $1; } | error { current_algorithm = NULL; current_secret = NULL; } ; algorithm_id: T_ALGID any_string L_EOS { $$ = $2; } ; secret: T_SECRET any_string L_EOS { $$ = $2; } ; /* * ACLs */ acl_stmt: T_ACL any_string '{' address_match_list '}' { if (lookup_acl($2) != NULL) { parser_error(0, "can't redefine ACL '%s'", $2); } else define_acl($2, $4); (void)freestr($2); } ; /* * Zones */ zone_stmt: T_ZONE L_QSTRING optional_class { int sym_type; symbol_value value; char *zone_name; if (!seen_options && !logged_options_error) { parser_error(0, "no options statement before first zone; using previous/default"); logged_options_error = 1; } sym_type = SYM_ZONE | ($3 & 0xffff); value.pointer = NULL; zone_name = canonical_name($2); if (zone_name == NULL) { parser_error(0, "can't make zone name '%s' canonical", $2); should_install = 0; zone_name = savestr("__bad_zone__", 1); } else { if (lookup_symbol(symtab, zone_name, sym_type, NULL)) { should_install = 0; parser_error(0, "cannot redefine zone '%s' class %s", *zone_name ? zone_name : ".", p_class($3)); } else { should_install = 1; define_symbol(symtab, zone_name, sym_type, value, 0); } } (void)freestr($2); current_zone = begin_zone(zone_name, $3); } optional_zone_options_list { end_zone(current_zone, should_install); } ; optional_zone_options_list: /* Empty */ | '{' zone_option_list '}' ; optional_class: /* Empty */ { $$ = C_IN; } | any_string { symbol_value value; if (lookup_symbol(constants, $1, SYM_CLASS, &value)) $$ = value.integer; else { /* the zone validator will give the error */ $$ = C_NONE; } (void)freestr($1); } ; zone_type: T_MASTER { $$ = Z_MASTER; } | T_SLAVE { $$ = Z_SLAVE; } | T_HINT { $$ = Z_HINT; } | T_STUB { $$ = Z_STUB; } | T_FORWARD { $$ = Z_FORWARD; } ; zone_option_list: zone_option L_EOS | zone_option_list zone_option L_EOS ; zone_option: T_TYPE zone_type { if (!set_zone_type(current_zone, $2)) parser_warning(0, "zone type already set; skipping"); } | T_FILE L_QSTRING { if (!set_zone_filename(current_zone, $2)) parser_warning(0, "zone filename already set; skipping"); } | T_FILE_IXFR L_QSTRING { if (!set_zone_ixfr_file(current_zone, $2)) parser_warning(0, "zone ixfr data base already set; skipping"); } | T_IXFR_TMP L_QSTRING { if (!set_zone_ixfr_tmp(current_zone, $2)) parser_warning(0, "zone ixfr temp filename already set; skipping"); } | T_MASTERS maybe_zero_port '{' master_in_addr_list '}' { set_zone_master_port(current_zone, $2); } | T_TRANSFER_SOURCE maybe_wild_addr { set_zone_transfer_source(current_zone, $2); } | T_CHECK_NAMES check_names_opt { if (!set_zone_checknames(current_zone, (enum severity)$2)) parser_warning(0, "zone checknames already set; skipping"); } | T_ALLOW_UPDATE '{' address_match_list '}' { if (!set_zone_update_acl(current_zone, $3)) parser_warning(0, "zone update acl already set; skipping"); } | T_ALLOW_QUERY '{' address_match_list '}' { if (!set_zone_query_acl(current_zone, $3)) parser_warning(0, "zone query acl already set; skipping"); } | T_ALLOW_TRANSFER '{' address_match_list '}' { if (!set_zone_transfer_acl(current_zone, $3)) parser_warning(0, "zone transfer acl already set; skipping"); } | T_FORWARD zone_forward_opt | T_FORWARDERS { struct zoneinfo *zp = current_zone.opaque; if (zp->z_fwdtab) { free_forwarders(zp->z_fwdtab); zp->z_fwdtab = NULL; } } '{' opt_zone_forwarders_list '}' | T_MAX_TRANSFER_TIME_IN L_NUMBER { if (!set_zone_transfer_time_in(current_zone, $2*60)) parser_warning(0, "zone max transfer time (in) already set; skipping"); } | T_MAX_LOG_SIZE_IXFR size_spec { set_zone_max_log_size_ixfr(current_zone, $2); } | T_NOTIFY T_EXPLICIT { set_zone_notify(current_zone, notify_explicit); } | T_NOTIFY yea_or_nay { if ($2) set_zone_notify(current_zone, notify_yes); else set_zone_notify(current_zone, notify_no); } | T_MAINTAIN_IXFR_BASE yea_or_nay { set_zone_maintain_ixfr_base(current_zone, $2); } | T_PUBKEY L_NUMBER L_NUMBER L_NUMBER L_QSTRING { /* flags proto alg key */ set_zone_pubkey(current_zone, $2, $3, $4, $5); } | T_PUBKEY L_STRING L_NUMBER L_NUMBER L_QSTRING { /* flags proto alg key */ char *endp; int flags = (int) strtol($2, &endp, 0); if (*endp != '\0') ns_panic(ns_log_parser, 1, "Invalid flags string: %s", $2); set_zone_pubkey(current_zone, flags, $3, $4, $5); } | T_ALSO_NOTIFY '{' opt_notify_in_addr_list '}' | T_DIALUP yea_or_nay { set_zone_dialup(current_zone, $2); } | error ; master_in_addr_list: master_in_addr L_EOS { /* nothing */ } | master_in_addr_list master_in_addr L_EOS { /* nothing */ } ; master_in_addr: L_IPADDR { add_zone_master(current_zone, $1, NULL); } | L_IPADDR T_SEC_KEY key_ref { add_zone_master(current_zone, $1, $3); } ; opt_notify_in_addr_list: /* nothing */ | notify_in_addr_list ; notify_in_addr_list: notify_in_addr L_EOS { /* nothing */ } | notify_in_addr_list notify_in_addr L_EOS { /* nothing */ } ; notify_in_addr: L_IPADDR { add_zone_notify(current_zone, $1); } ; zone_forward_opt: T_ONLY { set_zone_boolean_option(current_zone, OPTION_FORWARD_ONLY, 1); } | T_FIRST { set_zone_boolean_option(current_zone, OPTION_FORWARD_ONLY, 0); } ; opt_zone_forwarders_list: /* nothing */ { set_zone_forward(current_zone); } | zone_forwarders_in_addr_list ; zone_forwarders_in_addr_list: zone_forwarders_in_addr L_EOS { /* nothing */ } | zone_forwarders_in_addr_list zone_forwarders_in_addr L_EOS { /* nothing */ } ; zone_forwarders_in_addr: L_IPADDR { add_zone_forwarder(current_zone, $1); } ; /* * Trusted Key statement */ trusted_keys_stmt: T_TRUSTED_KEYS '{' trusted_keys_list '}' { } ; trusted_keys_list: trusted_key L_EOS { /* nothing */ } | trusted_keys_list trusted_key L_EOS { /* nothing */ } ; trusted_key: L_STRING L_NUMBER L_NUMBER L_NUMBER L_QSTRING { /* name flags proto alg key */ set_trusted_key($1, $2, $3, $4, $5); } | L_STRING L_STRING L_NUMBER L_NUMBER L_QSTRING { /* name flags proto alg key */ char *endp; int flags = (int) strtol($2, &endp, 0); if (*endp != '\0') ns_panic(ns_log_parser, 1, "Invalid flags string: %s", $2); set_trusted_key($1, flags, $3, $4, $5); } ; /* * Misc. */ in_port: L_NUMBER { if ($1 < 0 || $1 > 65535) { parser_warning(0, "invalid IP port number '%d'; setting port to 0", (int)$1); $1 = 0; } else $$ = htons($1); } ; any_string: L_STRING | L_QSTRING ; %% static char * canonical_name(char *name) { char canonical[MAXDNAME]; if (strlen(name) >= MAXDNAME) return (NULL); strcpy(canonical, name); if (makename(canonical, ".", sizeof canonical) < 0) return (NULL); return (savestr(canonical, 0)); } static void init_acls() { ip_match_element ime; ip_match_list iml; struct in_addr address; /* Create the predefined ACLs */ address.s_addr = 0U; /* ACL "any" */ ime = new_ip_match_pattern(address, 0); iml = new_ip_match_list(); add_to_ip_match_list(iml, ime); define_acl("any", iml); /* ACL "none" */ ime = new_ip_match_pattern(address, 0); ip_match_negate(ime); iml = new_ip_match_list(); add_to_ip_match_list(iml, ime); define_acl("none", iml); /* ACL "localhost" */ ime = new_ip_match_localhost(); iml = new_ip_match_list(); add_to_ip_match_list(iml, ime); define_acl("localhost", iml); /* ACL "localnets" */ ime = new_ip_match_localnets(); iml = new_ip_match_list(); add_to_ip_match_list(iml, ime); define_acl("localnets", iml); } static void free_sym_value(int type, void *value) { ns_debug(ns_log_parser, 99, "free_sym_value: type %06x value %p", type, value); type &= ~0xffff; switch (type) { case SYM_ACL: free_ip_match_list(value); break; case SYM_KEY: free_key_info(value); break; case SYM_CHANNEL: INSIST(log_free_channel(value) == 0); break; default: ns_panic(ns_log_parser, 1, "unhandled case in free_sym_value()"); /* NOTREACHED */ break; } } static log_channel lookup_channel(char *name) { symbol_value value; if (lookup_symbol(channeltab, name, SYM_CHANNEL, &value)) return ((log_channel)(value.pointer)); return (NULL); } static void define_channel(const char *name, log_channel channel) { symbol_value value; value.pointer = channel; INSIST(log_inc_references(channel) == 0); define_symbol(channeltab, name, SYM_CHANNEL, value, SYMBOL_FREE_VALUE); } static void define_builtin_channels() { define_channel("default_syslog", syslog_channel); define_channel("default_debug", debug_channel); define_channel("default_stderr", stderr_channel); define_channel("null", null_channel); } static void parser_setup() { seen_options = 0; logged_options_error = 0; seen_topology = 0; symtab = new_symbol_table(SYMBOL_TABLE_SIZE, NULL); if (authtab != NULL) free_symbol_table(authtab); authtab = new_symbol_table(AUTH_TABLE_SIZE, free_sym_value); if (channeltab != NULL) free_symbol_table(channeltab); channeltab = new_symbol_table(AUTH_TABLE_SIZE, free_sym_value); init_acls(); define_builtin_channels(); INIT_LIST(current_controls); } static void parser_cleanup() { if (symtab != NULL) free_symbol_table(symtab); symtab = NULL; /* * We don't clean up authtab here because the ip_match_lists are in * use. */ } /* * Public Interface */ ip_match_list lookup_acl(const char *name) { symbol_value value; if (lookup_symbol(authtab, name, SYM_ACL, &value)) return ((ip_match_list)(value.pointer)); return (NULL); } void define_acl(const char *name, ip_match_list iml) { symbol_value value; INSIST(name != NULL); INSIST(iml != NULL); value.pointer = iml; define_symbol(authtab, name, SYM_ACL, value, SYMBOL_FREE_VALUE); ns_debug(ns_log_parser, 7, "acl %s", name); dprint_ip_match_list(ns_log_parser, iml, 2, "allow ", "deny "); } struct dst_key * lookup_key(char *name) { symbol_value value; if (lookup_symbol(authtab, name, SYM_KEY, &value)) return ((struct dst_key *)(value.pointer)); return (NULL); } void define_key(const char *name, struct dst_key *dst_key) { symbol_value value; INSIST(name != NULL); INSIST(dst_key != NULL); value.pointer = dst_key; define_symbol(authtab, name, SYM_KEY, value, SYMBOL_FREE_VALUE); dprint_key_info(dst_key); } time_t parse_configuration(const char *filename) { FILE *config_stream; struct stat sb; config_stream = fopen(filename, "r"); if (config_stream == NULL) ns_panic(ns_log_parser, 0, "can't open '%s'", filename); if (fstat(fileno(config_stream), &sb) == -1) ns_panic(ns_log_parser, 0, "can't stat '%s'", filename); lexer_setup(); parser_setup(); lexer_begin_file(filename, config_stream); (void)yyparse(); lexer_end_file(); parser_cleanup(); return (sb.st_mtime); } void parser_initialize(void) { lexer_initialize(); } void parser_shutdown(void) { if (authtab != NULL) free_symbol_table(authtab); if (channeltab != NULL) free_symbol_table(channeltab); lexer_shutdown(); } diff --git a/contrib/bind/bin/named/ns_req.c b/contrib/bind/bin/named/ns_req.c index 3b4a0bfc610c..1272f048f473 100644 --- a/contrib/bind/bin/named/ns_req.c +++ b/contrib/bind/bin/named/ns_req.c @@ -1,2545 +1,2560 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_req.c 4.47 (Berkeley) 7/1/91"; -static const char rcsid[] = "$Id: ns_req.c,v 8.169.2.1 2002/11/14 13:02:48 marka Exp $"; +static const char rcsid[] = "$Id: ns_req.c,v 8.175.6.2 2003/06/02 09:56:35 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1988, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ /* * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" struct addinfo { char *a_dname; /* domain name */ char *a_rname; /* referred by */ u_int16_t a_rtype; /* referred by */ u_int16_t a_type; /* type for data */ u_int16_t a_class; /* class for data */ }; #ifndef BIND_UPDATE enum req_action { Finish, Refuse, Return }; #endif static struct addinfo addinfo[NADDRECS]; static void addname(const char *, const char *, u_int16_t, u_int16_t, u_int16_t); static void copyCharString(u_char **, const char *); static enum req_action req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, int *buflenp, int *msglenp, u_char *msg, int dfd, int *ra, struct sockaddr_in from, struct tsig_record *in_tsig, u_int16_t udpsize); static enum req_action req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp, struct sockaddr_in from); #ifdef BIND_NOTIFY static enum req_action req_notify(HEADER *hp, u_char **cpp, u_char *eom, u_char *msg,struct sockaddr_in from); #endif /* * See if there is a OPT record at the end of the message. * * Results: * -1 FORMERR * 0 last RR is not a OPT record * n>0 lenght of OPT record */ int ns_get_opt(u_char *msg, u_char *eom, u_int8_t *versionp, u_int16_t *rcodep, u_int16_t *flagp, u_int16_t *bufsizep, u_char **optionsp, size_t *optsizep) { HEADER *hp = (HEADER *) msg; u_char *start, *options, *cp; u_int8_t version; u_int16_t rdlen, type, bufsize, flags, optsize, rcode; int i, n, root; if (msg == NULL || eom == NULL || (msg + HFIXEDSZ) > eom) return (-1); if (ntohs(hp->arcount) == 0) return (0); cp = msg + HFIXEDSZ; n = ns_skiprr(cp, eom, ns_s_qd, ntohs(hp->qdcount)); if (n < 0) return (-1); cp += n; n = ns_skiprr(cp, eom, ns_s_an, ntohs(hp->ancount)); if (n < 0) return (-1); cp += n; n = ns_skiprr(cp, eom, ns_s_ns, ntohs(hp->nscount)); if (n < 0) return (-1); cp += n; i = ntohs(hp->arcount); while (i-- > 0) { start = cp; if (cp >= eom) return (-1); root = (*cp == 0); n = dn_skipname(cp, eom); if (n < 0) return (-1); cp += n; if (cp + (2 + 2 + 4 + 2) > eom) return (-1); GETSHORT(type, cp); if (type != ns_t_opt) { cp += INT16SZ + INT32SZ; /* class, ttl */ GETSHORT(rdlen, cp); if (cp + rdlen > eom) return (-1); cp += rdlen; continue; } /* We have the OPT record. Check it out in detail. */ if (!root) return (-1); GETSHORT(bufsize, cp); rcode = (*cp++ <<4) + hp->rcode ; version = *cp++; GETSHORT(flags, cp); GETSHORT(rdlen, cp); if (cp + rdlen > eom) return (-1); options = cp; optsize = rdlen; if (versionp != NULL) *versionp = version; if (rcodep != NULL) *rcodep = rcode; if (flagp != NULL) *flagp = flags; if (bufsizep != NULL) *bufsizep = bufsize; if (optionsp != NULL) *optionsp = options; if (optsizep != NULL) *optsizep = optsize; return (cp - start); } /* OPT not found */ return (0); } int ns_add_opt(u_char *msg, u_char *cp, size_t buflen, u_int8_t version, u_int16_t rcode, u_int16_t size, u_int16_t flags, u_char *options, size_t optlen) { HEADER *hp = (HEADER *) msg; if ((cp + 1 + 2 + 2 + 4 + 2 + optlen) > (msg + buflen)) return (-1); *cp++ = 0; /* "." */ PUTSHORT(ns_t_opt, cp); /* type */ PUTSHORT(size, cp); /* class (udp size) */ *cp++ = (rcode >> 4) & 0xff; /* ttl (rcode + version + flags) */ hp->rcode = rcode & 0xf; *cp++ = version; PUTSHORT(flags, cp); PUTSHORT(optlen, cp); /* rdlen */ memcpy(cp, options, optlen); /* options */ hp->arcount = htons(ntohs(hp->arcount) + 1); return (1 + 2 + 2 + 4 + 2 + optlen); } /* * Process request using database; assemble and send response. */ void ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, struct sockaddr_in from, int dfd) { HEADER *hp = (HEADER *) msg; u_char *cp, *eom; enum req_action action = Return; int n, ra, has_tsig, tsig_size = 0, opt_size = 0, sig2len; u_char *tsigstart; u_char sig[TSIG_SIG_SIZE], sig2[TSIG_SIG_SIZE]; struct tsig_record *in_tsig = NULL; int error = NOERROR; int msglen_orig = msglen; int buflen_orig = buflen; int siglen = sizeof sig; DST_KEY *key = NULL; time_t tsig_time; int opt = 0; u_int8_t version = 0; u_int16_t rcode = ns_r_noerror; u_int16_t udpsize = 0; int drop; int tsig_adjust = 0; #ifdef DEBUG if (debug > 3) { ns_debug(ns_log_packet, 3, "ns_req(from %s)", sin_ntoa(from)); fp_nquery(msg, msglen, log_get_stream(packet_channel)); } #endif drop = drop_port(ntohs(from.sin_port)); if (qsp == NULL && drop == 1) return; tsigstart = ns_find_tsig(msg, msg + msglen); if (tsigstart == NULL) has_tsig = 0; else { char buf[MAXDNAME]; u_char tmp[NS_MAXCDNAME]; has_tsig = 1; n = ns_name_unpack(msg, msg + msglen, tsigstart, tmp, sizeof tmp); if (n > 0) { tsig_adjust = dn_skipname(tmp, tmp + sizeof(tmp)) - n; if (ns_name_ntop(tmp, buf, sizeof buf) == -1) n = -1; else if (buf[0] == '.') buf[0] = '\0'; } if (n < 0) { ns_debug(ns_log_default, 1, "ns_req: bad TSIG key name"); error = ns_r_formerr; hp->rcode = ns_r_formerr; key = NULL; } else if ((key = find_key(buf, NULL)) == NULL) { error = ns_r_badkey; hp->rcode = ns_r_notauth; ns_debug(ns_log_default, 1, "ns_req: TSIG verify failed - unknown key %s", buf); } #ifdef LOG_TSIG_BUG if (n < 0 || key == NULL) ns_error(ns_log_security, "SECURITY: POSSIBLE ATTEMPT TO EXERCISE \"TSIG BUG\" FROM %s: %s%s%s", sin_ntoa(from), (n < 0) ? "bad key (formerr)" : "unknown key (", (n < 0) ? "" : (buf[0] != '\0' ? buf : "."), (n < 0) ? "" : ")"); #endif } if (has_tsig && key != NULL) { n = ns_verify(msg, &msglen, key, NULL, 0, sig, &siglen, &tsig_time, 0); if (n != 0) { hp->rcode = ns_r_notauth; /* A query should never have an error code set */ if (n == ns_r_badsig || n == ns_r_badkey || n == ns_r_badtime) { ns_debug(ns_log_default, 1, "ns_req: TSIG verify failed - query had error %s (%d) set", p_rcode(n), n); error = n; action = Return; } /* If there's a processing error just respond */ else if (n == -ns_r_badsig || n == -ns_r_badkey || n == -ns_r_badtime) { n = -n; ns_debug(ns_log_default, 1, "ns_req: TSIG verify failed - %s (%d)", p_rcode(n), n); error = n; } else { ns_debug(ns_log_default, 1, "ns_req: TSIG verify failed - FORMERR"); error = ns_r_formerr; } action = Finish; } in_tsig = memget(sizeof(struct tsig_record)); if (in_tsig == NULL) ns_panic(ns_log_default, 1, "memget failed"); in_tsig->key = key; in_tsig->siglen = siglen; memcpy(in_tsig->sig, sig, siglen); tsig_size = msglen_orig - msglen; /* AXFR/IXFR need the uncompressed tsig size. */ in_tsig->tsig_size = tsig_size + tsig_adjust; } else if (has_tsig) { action = Finish; in_tsig = memget(sizeof(struct tsig_record)); if (in_tsig == NULL) ns_panic(ns_log_default, 1, "memget failed"); in_tsig->key = NULL; in_tsig->siglen = 0; tsig_size = msg + msglen - tsigstart; in_tsig->tsig_size = tsig_size; msglen = tsigstart - msg; } /* Hash some stuff so it's nice and random */ nsid_hash((u_char *)&tt, sizeof(tt)); nsid_hash(msg, (msglen > 512) ? 512 : msglen); if (error == NOERROR) { opt = ns_get_opt(msg, msg + msglen, &version, NULL, NULL, &udpsize, NULL, NULL); if (opt < 0) { rcode = ns_r_formerr; action = Finish; } else if (opt == 0) { if (qsp == NULL && buflen > PACKETSZ) buflen_orig = buflen = PACKETSZ; } else if (opt > 0) { if (version != 0) { rcode = ns_r_badvers; action = Finish; } opt_size = 11; if (udpsize < 512) udpsize = 512; if (qsp == NULL && buflen > udpsize) buflen_orig = buflen = udpsize; } } else if (qsp == NULL && buflen > PACKETSZ) buflen_orig = buflen = PACKETSZ; /* * It's not a response so these bits have no business * being set. will later simplify work if we can * safely assume these are always 0 when a query * comes in. */ #ifdef BIND_NOTIFY if (hp->opcode != ns_o_notify) #endif hp->aa = 0; hp->ra = 0; ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); if (error == NOERROR) hp->rcode = ns_r_noerror; if (rcode == ns_r_noerror) rcode = hp->rcode; cp = msg + HFIXEDSZ; eom = msg + msglen; buflen -= HFIXEDSZ; free_addinfo(); /* sets addcount to zero */ dnptrs[0] = NULL; if (error == NOERROR && rcode == ns_r_noerror) { switch (hp->opcode) { case ns_o_query: action = req_query(hp, &cp, eom, qsp, &buflen, &msglen, msg, dfd, &ra, from, in_tsig, udpsize); break; case ns_o_iquery: action = req_iquery(hp, &cp, eom, &buflen, from); break; #ifdef BIND_NOTIFY case ns_o_notify: action = req_notify(hp, &cp, eom, msg, from); break; #endif #ifdef BIND_UPDATE case ns_o_update: action = req_update(hp, cp, eom, msg, from, in_tsig); break; #endif /* BIND_UPDATE */ default: ns_debug(ns_log_default, 1, "ns_req: Opcode %d not implemented", hp->opcode); /* XXX - should syslog, limited by haveComplained */ hp->qdcount = htons(0); hp->ancount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); hp->rcode = ns_r_notimpl; action = Finish; } rcode = hp->rcode; } if (in_tsig != NULL) { memput(in_tsig, sizeof(struct tsig_record)); in_tsig = NULL; } /* * Loop advoidance. */ if (qsp == NULL && drop == 2 && (hp->rcode == FORMERR || hp->rcode == NOTIMP)) action = Return; /* * Vector via internal opcode. */ switch (action) { case Return: return; case Refuse: rcode = hp->rcode = ns_r_refused; cp = eom; /*FALLTHROUGH*/ case Finish: /* rest of the function handles this case */ break; default: panic("ns_req: bad action variable", NULL); /*NOTREACHED*/ } /* * Apply final polish. */ hp->qr = 1; /* set Response flag */ hp->ra = ra; /* init above, may be modified by req_query */ if (!hp->tc && (has_tsig > 0 || opt > 0) && buflen < (tsig_size + opt_size)) hp->tc = 1; /* * If there was a format error, then we don't know what the msg has. */ if (hp->rcode == ns_r_formerr || rcode == ns_r_badvers) { hp->qdcount = htons(0); hp->ancount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); cp = msg + HFIXEDSZ; } /* * If the query had a TSIG / OPT and the message is truncated or * there was a TSIG error, build a new message with no data and a * TSIG / OPT. */ if ((hp->tc || error != NOERROR) && (has_tsig > 0 || opt > 0)) { sign_again: hp->ancount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); cp = msg + HFIXEDSZ; cp += ns_skiprr(cp, msg + msglen, ns_s_qd, ntohs(hp->qdcount)); ns_name_rollback(cp, (const u_char **)dnptrs, (const u_char **)dnptrs_end); if (opt > 0) { - n = ns_add_opt(msg, cp, buflen_orig, 0, - rcode, EDNS_MESSAGE_SZ, 0, NULL, 0); + n = ns_add_opt(msg, cp, buflen_orig, 0, rcode, + server_options->edns_udp_size, + 0, NULL, 0); if (n < 0) { hp->qdcount = htons(0); goto sign_again; } cp += n; } if (has_tsig > 0) { sig2len = sizeof sig2; msglen = cp - msg; buflen = buflen_orig - msglen; n = ns_sign2(msg, &msglen, msglen + buflen, error, key, sig, siglen, sig2, &sig2len, tsig_time, dnptrs, dnptrs_end); if (n == NS_TSIG_ERROR_NO_SPACE && ntohs(hp->qdcount) != 0) { hp->qdcount = htons(0); goto sign_again; } if (n != 0) ns_info(ns_log_default, "ns_req: unable to sign response"); cp = msg + msglen; } } /* Either the message is not truncated or there was no TSIG & OPT */ else { /* * Reserve space for tsig if required. */ if (has_tsig > 0 || opt_size != 0) buflen -= tsig_size + opt_size; INSIST(buflen >= 0); msglen = cp - msg; n = doaddinfo(hp, cp, buflen); cp += n; buflen -= n; msglen += n; if (opt > 0) { buflen += opt_size; - n = ns_add_opt(msg, cp, msglen + buflen, 0, - rcode, EDNS_MESSAGE_SZ, 0, NULL, 0); + n = ns_add_opt(msg, cp, msglen + buflen, 0, rcode, + server_options->edns_udp_size, + 0, NULL, 0); INSIST(n > 0); cp += n; buflen -= n; msglen += n; } if (has_tsig > 0) { buflen += tsig_size; sig2len = sizeof sig2; n = ns_sign2(msg, &msglen, msglen + buflen, error, key, sig, siglen, sig2, &sig2len, tsig_time, dnptrs, dnptrs_end); if (n != 0) { INSIST(0); } cp = msg + msglen; } } #ifdef DEBUG if (ns_wouldlog(ns_log_default, 1)) { ns_debug(ns_log_default, 1, "ns_req: answer -> %s fd=%d id=%d size=%d rc=%d", sin_ntoa(from), (qsp == NULL) ? dfd : qsp->s_rfd, ntohs(hp->id), cp - msg, hp->rcode); } if (debug >= 10) res_pquery(&res, msg, cp - msg, log_get_stream(packet_channel)); #endif /*DEBUG*/ if (qsp == NULL) { if (sendto(dfd, (char*)msg, cp - msg, 0, (struct sockaddr *)&from, sizeof from) < 0) { if (!haveComplained(ina_ulong(from.sin_addr), (u_long)sendtoStr)) ns_info(ns_log_default, "ns_req: sendto(%s): %s", sin_ntoa(from), strerror(errno)); nameserIncr(from.sin_addr, nssSendtoErr); } nameserIncr(from.sin_addr, nssSentAns); if (hp->rcode == ns_r_nxdomain) nameserIncr(from.sin_addr, nssSentNXD); if (!hp->aa) nameserIncr(from.sin_addr, nssSentNaAns); } else writestream(qsp, msg, cp - msg); /* Is now a safe time? */ if (needs_prime_cache) prime_cache(); } #ifdef BIND_NOTIFY int findZonePri(const struct zoneinfo *zp, const struct sockaddr_in from) { struct in_addr ina; int i; ina = from.sin_addr; for (i = 0; (u_int)i < zp->z_addrcnt; i++) if (ina_equal(zp->z_addr[i], ina)) return (i); return (-1); } static enum req_action req_notify(HEADER *hp, u_char **cpp, u_char *eom, u_char *msg, struct sockaddr_in from) { int n, type, class; char dnbuf[MAXDNAME]; struct zoneinfo *zp; /* valid notify's are authoritative */ if (!hp->aa) { ns_debug(ns_log_notify, 1, "FORMERR Notify request without AA"); #ifdef not_yet hp->rcode = ns_r_formerr; return (Finish); #endif } hp->aa = 0; /* valid notify's have one question */ if (ntohs(hp->qdcount) != 1) { ns_debug(ns_log_notify, 1, "FORMERR Notify header counts wrong"); hp->rcode = ns_r_formerr; return (Finish); } n = dn_expand(msg, eom, *cpp, dnbuf, sizeof dnbuf); if (n < 0) { ns_debug(ns_log_notify, 1, "FORMERR Query expand name failed"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += n; if (*cpp + 2 * INT16SZ > eom) { ns_debug(ns_log_notify, 1, "FORMERR notify too short"); hp->rcode = ns_r_formerr; return (Finish); } GETSHORT(type, *cpp); GETSHORT(class, *cpp); ns_info(ns_log_notify, "rcvd NOTIFY(%s, %s, %s) from %s", dnbuf, p_class(class), p_type(type), sin_ntoa(from)); /* XXX - when answers are allowed, we'll need to do compression * correctly here, and we will need to check for packet underflow. */ /* * We are ignoring the other field, make sure the header reflects * *cpp. */ hp->ancount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); /* Find the zone this NOTIFY refers to. */ zp = find_auth_zone(dnbuf, class); if (zp == NULL) { ns_info(ns_log_notify, "rcvd NOTIFY for \"%s\", name not one of our zones", dnbuf); hp->rcode = ns_r_servfail; return (Finish); } /* Access control. */ switch (type) { case T_SOA: if (zp->z_type != z_slave) { /* * This can come if a user did an AXFR of some zone * somewhere and that zone's server now wants to * tell us that the SOA has changed. AXFR's always * come from nonpriv ports so it isn't possible to * know whether it was the server or just "dig". * This condition can be avoided by using secure * zones since that way only real secondaries can * AXFR from you. */ ns_info(ns_log_notify, "NOTIFY(SOA) for non-slave zone (%s), from %s", dnbuf, sin_ntoa(from)); goto refuse; } if (ns_samename(dnbuf, zp->z_origin) != 1) { ns_info(ns_log_notify, "NOTIFY(SOA) for non-origin (%s), from %s", dnbuf, sin_ntoa(from)); goto refuse; } if (findZonePri(zp, from) == -1) { ns_debug(ns_log_notify, 1, "NOTIFY(SOA) from non-master server (zone %s), from %s", zp->z_origin, sin_ntoa(from)); goto refuse; } break; default: /* No access requirements defined for other types. */ break; } /* The work occurs here. */ switch (type) { case T_SOA: if (zp->z_flags & (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL|Z_XFER_RUNNING)) { ns_info(ns_log_notify, "NOTIFY(SOA) for zone already xferring (%s)", dnbuf); zp->z_flags |= Z_NEEDREFRESH; goto noerror; } zp->z_time = tt.tv_sec; qserial_query(zp); sched_zone_maint(zp); break; default: /* * Unimplemented, but it's not a protocol error, just * something to be ignored. */ hp->rcode = ns_r_notimpl; return (Finish); } noerror: hp->rcode = ns_r_noerror; hp->aa = 1; return (Finish); refuse: hp->rcode = ns_r_refused; return (Finish); } #endif /*BIND_NOTIFY*/ static int add_bind(HEADER *hp, u_char **cpp, u_char *msg, int *msglenp, const char *label, const char *data) { u_char *tp; hp->ancount = htons(1); hp->nscount = htons(0); hp->arcount = htons(0); hp->rcode = ns_r_noerror; hp->aa = 1; hp->ra = 0; copyCharString(cpp, label); /* Name */ copyCharString(cpp, "BIND"); *(*cpp)++ = 0x00; PUTSHORT(T_TXT, *cpp); /* Type */ PUTSHORT(C_CHAOS, *cpp); /* Class */ PUTLONG(0, *cpp); /* TTL */ tp = *cpp; /* Temp RdLength */ PUTSHORT(0, *cpp); copyCharString(cpp, data); PUTSHORT((*cpp) - (tp + INT16SZ), tp); /* Real RdLength */ *msglenp = *cpp - msg; /* Total message length */ return (Finish); } static enum req_action req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, int *buflenp, int *msglenp, u_char *msg, int dfd, int *ra, struct sockaddr_in from, struct tsig_record *in_tsig, u_int16_t udpsize) { int n, class, type, count, zone, foundname, founddata, omsglen, cname; int recursion_blocked_by_acl; u_int16_t id; u_int32_t serial_ixfr = 0; int ixfr_found; int ixfr_error = 0; char dnbuf2[MAXDNAME]; u_char **dpp, *omsg, *answers, *afterq; char dnbuf[MAXDNAME], *dname; const char *fname; struct hashbuf *htp; struct databuf *nsp[NSMAX]; struct namebuf *np, *anp; struct qinfo *qp; struct zoneinfo *zp; struct databuf *dp; DST_KEY *in_key = (in_tsig != NULL) ? in_tsig->key : NULL; int access_class; int adjustlen = 0; + int pass = 0; + char tsig_keyname_mesg[15+MAXDNAME] = ""; + int glueok; nameserIncr(from.sin_addr, nssRcvdQ); nsp[0] = NULL; dpp = dnptrs; *dpp++ = msg; *dpp = NULL; /* * Make gcc happy. */ omsglen = 0; omsg = NULL; id = 0; recursion_blocked_by_acl = 0; /* valid queries have one question and zero answers */ if ((ntohs(hp->qdcount) != 1) || ntohs(hp->ancount) != 0) { ns_debug(ns_log_default, 1, "FORMERR Query header counts wrong"); hp->rcode = ns_r_formerr; return (Finish); } if (ntohs(hp->arcount) != 0) { ns_debug(ns_log_default, 1, "Ignoring addition section"); hp->arcount = htons(0); adjustlen = 1; } /* * Get domain name, class, and type. */ if ((**cpp & INDIR_MASK) == 0) *dpp++ = *cpp; /* remember name for compression */ *dpp = NULL; n = dn_expand(msg, eom, *cpp, dnbuf, sizeof dnbuf); if (n < 0) { ns_debug(ns_log_default, 1, "FORMERR Query expand name failed"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += n; - answers = *cpp; if (*cpp + 2 * INT16SZ > eom) { ns_debug(ns_log_default, 1, "FORMERR Query message length short"); hp->rcode = ns_r_formerr; return (Finish); } GETSHORT(type, *cpp); GETSHORT(class, *cpp); if (*cpp < eom && type != ns_t_ixfr) { if (!adjustlen) ns_debug(ns_log_default, 6, "message length > received message"); *msglenp = *cpp - msg; } if (((ntohs(hp->nscount) != 0) && (type != ns_t_ixfr)) || ((ntohs(hp->nscount) != 1) && (type == ns_t_ixfr))) { ns_debug(ns_log_default, 1, "FORMERR Query nscount wrong"); hp->rcode = ns_r_formerr; return (Finish); } afterq = *cpp; qtypeIncr(type); /* * Process query. */ if (type == ns_t_ixfr) { ns_info(ns_log_security, "Request %s from %s", p_type(type), sin_ntoa(from)); hp->nscount = htons(0); hp->rd = 0; /* Force IXFR queries to be non recursive. */ n = dn_expand(msg, eom, *cpp, dnbuf2, sizeof dnbuf2); if (n < 0) { ns_debug(ns_log_default, 1, "FORMERR Query expand name failed"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += n; if (*cpp + 3 * INT16SZ + INT32SZ > eom) { ns_debug(ns_log_default, 1, "ran out of data in IXFR query"); hp->rcode = ns_r_formerr; return (Finish); } GETSHORT(n, *cpp); if (n != ns_t_soa || ns_samename(dnbuf, dnbuf2) != 1) { ns_debug(ns_log_default, 1, "FORMERR SOA record expected"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += INT32SZ + INT16SZ * 2; /* skip class, ttl, dlen */ if (0 >= (n = dn_skipname(*cpp, eom))) { ns_debug(ns_log_default, 1, "FORMERR Query expand name failed"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += n; /* mname */ if (0 >= (n = dn_skipname(*cpp, eom))) { ns_debug(ns_log_default, 1, "FORMERR Query expand name failed"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += n; /* rname */ if (*cpp + 5 * INT32SZ > eom) { ns_debug(ns_log_default, 1, "ran out of data in IXFR query"); hp->rcode = ns_r_formerr; return (Finish); } GETLONG(serial_ixfr, *cpp); /* ignore other soa counters */ if ((*cpp + (4 * INT32SZ)) < eom && !adjustlen) ns_debug(ns_log_default, 6, "ixfr: message length > received message"); /* Reset msglenp to cover just the question. */ *msglenp = afterq - msg; } *cpp = afterq; if (!ns_t_udp_p(type)) { /* Refuse request if not a TCP connection. */ if (qsp == NULL) { ns_info(ns_log_default, "rejected UDP %s from %s for \"%s\"", p_type(type), sin_ntoa(from), *dnbuf ? dnbuf : "."); return (Refuse); } /* The position of this is subtle. */ nameserIncr(from.sin_addr, nssRcvdAXFR); hp->rd = 0; /* Recursion not possible. */ } *buflenp -= (*msglenp - HFIXEDSZ); count = 0; founddata = 0; dname = dnbuf; cname = 0; #ifdef QRYLOG if (qrylog) { ns_info(ns_log_queries, "%s/%s/%s/%s/%s", (hp->rd) ? "XX+" : "XX ", inet_ntoa(from.sin_addr), (dname[0] == '\0') ? "." : dname, p_type(type), p_class(class)); } #endif /*QRYLOG*/ try_again: + pass++; foundname = 0; ns_debug(ns_log_default, 1, "req: nlookup(%s) id %d type=%d class=%d", dname, ntohs(hp->id), type, class); htp = hashtab; /* lookup relative to root */ if ((anp = np = nlookup(dname, &htp, &fname, 0)) == NULL) fname = ""; ns_debug(ns_log_default, 1, "req: %s '%s' as '%s' (cname=%d)", np == NULL ? "missed" : "found", dname, fname, cname); #ifdef YPKLUDGE /* Some braindamaged resolver software will not recognize internet addresses in dot notation and send out address queries for "names" such as 128.93.8.1. This kludge will prevent those from flooding higher level servers. We simply claim to be authoritative and that the domain doesn't exist. Note that we could return the address but we don't do that in order to encourage that broken software is fixed. */ if (!np && type == T_A && class == C_IN && dname) { struct in_addr ina; if (inet_aton(dname, &ina)) { hp->rcode = ns_r_nxdomain; hp->aa = 1; ns_debug(ns_log_default, 3, "ypkludge: hit as '%s'", dname); return (Finish); } } #endif /*YPKLUDGE*/ /* * Don't accept in a query names which would be rejected in responses. * (This is primarily in case we have to forward it, but it's also a * matter of architectural symmetry.) */ if (!ns_nameok(NULL, dname, class, NULL, response_trans, ns_ownercontext(type, response_trans), dname, from.sin_addr)) { ns_debug(ns_log_default, 1, "bad name in query"); hp->rcode = ns_r_formerr; return (Refuse); } /* * Begin Access Control Point */ zone = DB_Z_CACHE; /* * Map class ANY to to class IN for the purpose of access control. */ access_class = (class == C_ANY && !ns_t_xfr_p(type)) ? C_IN : class; if (np) { #ifndef FORWARD_ALLOWS struct namebuf *access_np; /* * Find out which zone this will be answered from. Note * that we look for a zone with the same class as ours. * The np that we found in the database might not be the * one we asked for (i.e. dname might not equal fname). This * is OK, since if a name doesn't exist, we need to go up * the tree until we find the closest enclosing zone that * is of the same class. */ for (access_np = np; access_np != NULL; access_np = np_parent(access_np)) { dp = access_np->n_data; while (dp && dp->d_class != access_class) dp = dp->d_next; if (dp != NULL) { zone = dp->d_zone; break; } } #else /* * Try looking for forward zone. It can be deeper than * any entry in the cache. */ if (zone == DB_Z_CACHE) { char *s = dname; int escape = 0; while ((zp = find_zone(s, access_class)) == NULL) { if (*s == '\0') break; while (*s != '\0' && (escape || *s != '.')) { escape = escape ? 0 : (*s == '\\'); s++; } if (*s == '.') s++; } if (zp != NULL) zone = zp - zones; } #endif } zp = &zones[zone]; ixfr_found = 0; if (type == ns_t_ixfr && zone != DB_Z_CACHE) { if (SEQ_GT(serial_ixfr, zp->z_serial)) ixfr_found = 0; else { ixfr_error = ixfr_have_log(zp, serial_ixfr, zp->z_serial); if (ixfr_error < 0) { ns_info(ns_log_security, "No %s log from %d for \"%s\"", p_type(type), serial_ixfr, *dname ? dname : "."); ns_debug(ns_log_default, 1, "ixfr_have_log(%d %d) failed %d", serial_ixfr, zp->z_serial, ixfr_error); ixfr_found = 0; /* Refuse IXFR and send AXFR */ } else if (ixfr_error == 1) { ixfr_found = 1; } } ns_debug(ns_log_default, 1, "IXFR log lowest serial: %d", zp->z_serial_ixfr_start); } /* * If recursion is turned on, we need to check recursion ACL * if it exists - and return result to caller. */ { ip_match_list recursion_acl; recursion_acl = server_options->recursion_acl; if (!NS_OPTION_P(OPTION_NORECURSE) && recursion_acl != NULL && !ip_address_allowed(recursion_acl, from.sin_addr)) { recursion_blocked_by_acl = 1; *ra = 0; } } /* * Are queries allowed from this host? */ if (!ns_t_xfr_p(type)) { ip_match_list query_acl; if (zp->z_query_acl != NULL) query_acl = zp->z_query_acl; else query_acl = server_options->query_acl; if (query_acl != NULL && !ip_addr_or_key_allowed(query_acl, from.sin_addr, in_key)) { /* * If this is *not* a zone acl and we would not * have recursed and we have some answer return * what we have with a referral. */ if ((zp->z_query_acl == NULL) && (!hp->rd || NS_OPTION_P(OPTION_NORECURSE) || recursion_blocked_by_acl) && (ntohs(hp->ancount) != 0)) { goto fetchns; } /* * See if we would have made a referral from * an enclosing zone if we are actually in the * cache. */ if (zp->z_type == z_cache && np != NULL) { struct namebuf *access_np; zone = DB_Z_CACHE; for (access_np = np; access_np != NULL; access_np = np_parent(access_np)) { dp = access_np->n_data; while (dp && (dp->d_class != access_class || dp->d_zone == DB_Z_CACHE)) dp = dp->d_next; if (dp != NULL) { zone = dp->d_zone; np = access_np; break; } } zp = &zones[zone]; if (zp->z_type != z_cache && zp->z_query_acl != NULL && ip_addr_or_key_allowed(zp->z_query_acl, from.sin_addr, in_key) && (!hp->rd || recursion_blocked_by_acl || NS_OPTION_P(OPTION_NORECURSE))) { goto fetchns; } } #ifdef NXDOMAIN_ON_DENIAL hp->rcode = ns_r_nxdomain; return (Finish); #else ns_notice(ns_log_security, "denied query from %s for \"%s\" %s/%s", sin_ntoa(from), *dname ? dname : ".", p_type(type), p_class(class)); nameserIncr(from.sin_addr, nssRcvdUQ); return (Refuse); #endif } } else { ip_match_list transfer_acl; /* Do they have permission to do a zone transfer? */ if (zp->z_transfer_acl != NULL) transfer_acl = zp->z_transfer_acl; else transfer_acl = server_options->transfer_acl; if (transfer_acl != NULL && !ip_addr_or_key_allowed(transfer_acl, from.sin_addr, in_key)) { ns_notice(ns_log_security, "denied %s from %s for \"%s\" %s (acl)", p_type(type), sin_ntoa(from), *dname ? dname : ".", p_class(class)); nameserIncr(from.sin_addr, nssRcvdUXFR); if (type == ns_t_ixfr) { hp->rcode = ns_r_refused; return (Finish); } return (Refuse); } /* Are we master or slave? */ if (zp->z_type != z_master && zp->z_type != z_slave) { ns_notice(ns_log_security, "denied %s from %s for \"%s\" (not master/slave)", p_type(type), sin_ntoa(from), *dname ? dname : "."); nameserIncr(from.sin_addr, nssRcvdUXFR); if (type == ns_t_ixfr) { hp->rcode = ns_r_refused; return (Finish); } return (Refuse); } /* Are we authoritative? */ if ((zp->z_flags & Z_AUTH) == 0) { ns_notice(ns_log_security, "denied %s from %s for \"%s\" %s (not authoritative)", p_type(type), sin_ntoa(from), *dname ? dname : ".", p_class(class)); nameserIncr(from.sin_addr, nssRcvdUXFR); if (type == ns_t_ixfr) { hp->rcode = ns_r_refused; return (Finish); } return (Refuse); } /* Is the name at a zone cut? */ if (ns_samename(zp->z_origin, dname) != 1) { ns_notice(ns_log_security, "denied %s from %s for \"%s\" %s (not zone top)", p_type(type), sin_ntoa(from), *dname ? dname : ".", p_class(class)); nameserIncr(from.sin_addr, nssRcvdUXFR); if (type == ns_t_ixfr) { hp->rcode = ns_r_refused; return (Finish); } return (Refuse); } - if (type == ns_t_ixfr) { - ns_info(ns_log_security, "approved %s from %s for \"%s\"", - (ixfr_found) ? p_type(type) : "IXFR/AXFR", - sin_ntoa(from), *dname ? dname : "."); - } else - ns_info(ns_log_security, "approved %s from %s for \"%s\"", - p_type(type), sin_ntoa(from), *dname ? dname : "."); + if (in_key != NULL) + sprintf(tsig_keyname_mesg, " (TSIG key \"%s\")", + in_key->dk_key_name); + + if (type == ns_t_ixfr) + ns_info(ns_log_security, "approved %s from %s for \"%s\"%s", + (ixfr_found) ? p_type(type) : "IXFR/AXFR", + sin_ntoa(from), *dname ? dname : ".", + tsig_keyname_mesg); + else + ns_info(ns_log_security, "approved %s from %s for \"%s\"%s", + p_type(type), sin_ntoa(from), *dname ? dname : ".", + tsig_keyname_mesg); } /* * End Access Control Point */ /* * Yow! */ if (class == ns_c_chaos && type == ns_t_txt && ns_samename(dnbuf, "VERSION.BIND") == 1 && server_options->version != NULL && server_options->version[0] != '\0') return (add_bind(hp, cpp, msg, msglenp, "VERSION", server_options->version)); if (class == ns_c_chaos && type == ns_t_txt && ns_samename(dnbuf, "HOSTNAME.BIND") == 1 && server_options->hostname != NULL && server_options->hostname[0] != '\0') return (add_bind(hp, cpp, msg, msglenp, "HOSTNAME", server_options->hostname)); /* * If we don't know anything about the requested name, * go look for nameservers. */ if (!np || fname != dname) goto fetchns; foundname++; answers = *cpp; count = *cpp - msg; /* The response is authoritative until we add insecure data */ hp->ad = 1; /* Look for NXDOMAIN record with appropriate class * if found return immediately */ for (dp = np->n_data; dp; dp = dp->d_next) { if (!stale(dp) && (dp->d_rcode == ns_r_nxdomain) && (dp->d_class == class)) { #ifdef RETURNSOA n = finddata(np, class, T_SOA, hp, &dname, - buflenp, &count); + buflenp, &count, pass, 1); if (n != 0) { if (count) { *cpp += n; *buflenp -= n; *msglenp += n; hp->nscount = htons((u_int16_t)count); } if (hp->rcode == NOERROR_NODATA) { /* this should not occur */ hp->rcode = ns_r_noerror; return (Finish); } } #else count = 0; #endif hp->rcode = ns_r_nxdomain; /* * XXX forcing AA all the time isn't right, but * we have to work that way by default * for compatibility with older servers. */ if (!NS_OPTION_P(OPTION_NONAUTH_NXDOMAIN)) hp->aa = 1; ns_debug(ns_log_default, 3, "NXDOMAIN aa = %d", hp->aa); if ((count == 0) || NS_OPTION_P(OPTION_NORFC2308_TYPE1)) return (Finish); founddata = 1; goto fetchns; } } /* * If not NXDOMAIN, the NOERROR_NODATA record might be * anywhere in the chain. Have to go through the grind. */ - - n = finddata(np, class, type, hp, &dname, buflenp, &count); + glueok = !NS_OPTION_P(OPTION_NORECURSE); + n = finddata(np, class, type, hp, &dname, buflenp, &count, pass, + glueok); if (n == 0) { /* * NO data available. Refuse transfer requests, or * look for better servers for other requests. */ if (ns_t_xfr_p(type)) { ns_debug(ns_log_default, 1, "transfer refused: no data"); return (Refuse); } goto fetchns; } if (hp->rcode == NOERROR_NODATA) { hp->rcode = ns_r_noerror; #ifdef RETURNSOA if (count) { *cpp += n; *buflenp -= n; *msglenp += n; hp->nscount = htons(count); } #endif founddata = 1; ns_debug(ns_log_default, 1, "count = %d", count); if ((count == 0) || NS_OPTION_P(OPTION_NORFC2308_TYPE1)) return (Finish); goto fetchns; } *cpp += n; *buflenp -= n; *msglenp += n; hp->ancount = htons(ntohs(hp->ancount) + (u_int16_t)count); if (fname != dname && type != T_CNAME && type != T_ANY) { if (cname++ >= MAXCNAMES) { ns_debug(ns_log_default, 3, "resp: leaving, MAXCNAMES exceeded"); hp->rcode = ns_r_servfail; return (Finish); } goto try_again; } founddata = 1; ns_debug(ns_log_default, 3, "req: foundname=%d, count=%d, founddata=%d, cname=%d", foundname, count, founddata, cname); if (ns_t_xfr_p(type)) { #ifdef BIND_UPDATE if ((zp->z_flags & Z_NEED_SOAUPDATE) != 0) if (incr_serial(zp) < 0) ns_error(ns_log_default, "error updating serial number for %s from %d", zp->z_origin, zp->z_serial); #endif /* * Just return SOA if "up to date". */ if (type == ns_t_ixfr) { hp->aa = 1; if ((SEQ_GT(serial_ixfr, zp->z_serial) || serial_ixfr == zp->z_serial)) { return (Finish); } } /* * We don't handle UDP based IXFR queries (yet). * Tell client to retry with TCP by returning SOA. */ if (qsp == NULL) return (Finish); else { if (!ixfr_found && type == ns_t_ixfr) { qsp->flags |= STREAM_AXFRIXFR; hp->qdcount = htons(1); } ns_xfr(qsp, np, zone, class, type, hp->opcode, ntohs(hp->id), serial_ixfr, in_tsig); } return (Return); } if (count > 1 && type == T_A && !NS_OPTION_P(OPTION_NORECURSE) && hp->rd) sort_response(answers, *cpp, count, &from); fetchns: /* * If we're already out of room in the response, we're done. */ if (hp->tc) return (Finish); if (hp->ancount == 0) hp->ad = 0; /* * Look for name servers to refer to and fill in the authority * section or record the address for forwarding the query * (recursion desired). */ free_nsp(nsp); nsp[0] = NULL; count = 0; switch (findns(&np, class, nsp, &count, 0)) { case NXDOMAIN: /* We are authoritative for this np. */ if (!foundname) hp->rcode = ns_r_nxdomain; ns_debug(ns_log_default, 3, "req: leaving (%s, rcode %d)", dname, hp->rcode); if (class != C_ANY) { - hp->aa = 1; + if (!cname) + hp->aa = 1; if (np && (!foundname || !founddata)) { n = doaddauth(hp, *cpp, *buflenp, np, nsp[0]); *cpp += n; *buflenp -= n; #ifdef ADDAUTH } else if (ntohs(hp->ancount) != 0) { /* don't add NS records for NOERROR NODATA as some servers can get confused */ free_nsp(nsp); switch (findns(&np, class, nsp, &count, 1)) { case NXDOMAIN: case SERVFAIL: break; default: if (np && (type != T_NS || np != anp) ) { n = add_data(np, nsp, *cpp, *buflenp, &count); if (n < 0) { hp->tc = 1; n = (-n); } *cpp += n; *buflenp -= n; hp->nscount = htons((u_int16_t) count); } } #endif /*ADDAUTH*/ } } free_nsp(nsp); return (Finish); case SERVFAIL: /* We're authoritative but the zone isn't loaded. */ if (!founddata && !(NS_ZOPTION_P(zp, OPTION_FORWARD_ONLY) && NS_ZFWDTAB(zp))) { hp->rcode = ns_r_servfail; free_nsp(nsp); return (Finish); } } if (!founddata && hp->rd && recursion_blocked_by_acl) { ns_notice(ns_log_security, "denied recursion for query from %s for %s %s", sin_ntoa(from), *dname ? dname : ".", p_class(class)); nameserIncr(from.sin_addr, nssRcvdURQ); } /* * If we successfully found the answer in the cache, * or this is not a recursive query, or we are purposely * never recursing, or recursion is prohibited by ACL, then * add the nameserver references("authority section") here * and we're done. */ if (founddata || !hp->rd || NS_OPTION_P(OPTION_NORECURSE) || recursion_blocked_by_acl) { /* * If the qtype was NS, and the np of the authority is * the same as the np of the data, we don't need to add * another copy of the answer here in the authority * section. */ if (!founddata || type != T_NS || anp != np) { n = add_data(np, nsp, *cpp, *buflenp, &count); if (n < 0) { hp->tc = 1; n = (-n); } *cpp += n; *buflenp -= n; hp->nscount = htons(ntohs(hp->nscount) + (u_int16_t)count); } free_nsp(nsp); /* Our caller will handle the Additional section. */ return (Finish); } /* * At this point, we don't have the answer, but we do * have some NS's to try. If the user would like us * to recurse, create the initial query. If a cname * is involved, we need to build a new query and save * the old one in cmsg/cmsglen. */ if (cname) { omsg = (u_char *)memget((unsigned) *msglenp); if (omsg == NULL) { ns_info(ns_log_default, "ns_req: Out Of Memory"); hp->rcode = ns_r_servfail; free_nsp(nsp); return (Finish); } id = hp->id; omsglen = *msglenp; memcpy(omsg, msg, omsglen); n = res_nmkquery(&res, QUERY, dname, class, type, NULL, 0, NULL, msg, *msglenp + *buflenp); if (n < 0) { ns_info(ns_log_default, "res_mkquery(%s) failed", dname); memcpy(msg, omsg, omsglen); memput(omsg, omsglen); hp->rcode = ns_r_servfail; free_nsp(nsp); return (Finish); } *msglenp = n; } n = ns_forw(nsp, msg, *msglenp, from, qsp, dfd, &qp, dname, class, type, np, 0, in_tsig); if (n != FW_OK && cname) { memcpy(msg, omsg, omsglen); memput(omsg, omsglen); *msglenp = omsglen; omsg = NULL; } switch (n) { case FW_OK: if (cname) { qp->q_cname = cname; qp->q_cmsg = omsg; qp->q_cmsglen = omsglen; qp->q_cmsgsize = omsglen; qp->q_id = id; } if (udpsize != 0) { qp->q_flags |= Q_EDNS; qp->q_udpsize = udpsize; } else qp->q_udpsize = PACKETSZ; break; case FW_DUP: break; /* Duplicate request dropped */ case FW_NOSERVER: /* * Don't go into an infinite loop if * the admin gave root NS records in the cache * file without giving address records * for the root servers. */ if (np) { if (NAME(*np)[0] == '\0') { ns_notice(ns_log_default, "ns_req: no address for root server"); hp->rcode = ns_r_servfail; free_nsp(nsp); return (Finish); } for (dp = np->n_data; dp ; dp = dp->d_next) if (dp->d_zone && match(dp, class, T_NS)) break; if (dp) { /* * we know the child zone exists but are * missing glue. * * nslookup has called sysquery() to get the * missing glue. * * for UDP, drop the response and let the * client retry. for TCP, we should probably * (XXX) hold open the TCP connection for a * while in case the sysquery() comes back * soon. meanwhile we SERVFAIL. */ if (qsp) goto do_servfail; break; } np = np_parent(np); } goto fetchns; /* Try again. */ case FW_SERVFAIL: do_servfail: hp->rcode = ns_r_servfail; free_nsp(nsp); return (Finish); } free_nsp(nsp); return (Return); } static enum req_action req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp, struct sockaddr_in from) { u_int rdata_offset; size_t alen; int dlen, n; ns_type type; ns_class class; u_char anbuf[PACKETSZ], *anptr; char dnbuf[MAXDNAME]; nameserIncr(from.sin_addr, nssRcvdIQ); if (ntohs(hp->ancount) != 1 || ntohs(hp->qdcount) != 0 || ntohs(hp->nscount) != 0 || ntohs(hp->arcount) != 0) { ns_debug(ns_log_default, 1, "FORMERR IQuery header counts wrong"); hp->rcode = ns_r_formerr; return (Finish); } /* * Skip domain name, get class, and type. */ anptr = *cpp; n = dn_skipname(*cpp, eom); if (n < 0) { ns_debug(ns_log_default, 1, "FORMERR IQuery packet name problem"); hp->rcode = ns_r_formerr; return (Finish); } *cpp += n; if (*cpp + 3 * INT16SZ + INT32SZ > eom) { ns_debug(ns_log_default, 1, "FORMERR IQuery message too short"); hp->rcode = ns_r_formerr; return (Finish); } GETSHORT(type, *cpp); GETSHORT(class, *cpp); *cpp += INT32SZ; /* ttl */ GETSHORT(dlen, *cpp); if (*cpp + dlen != eom) { ns_debug(ns_log_default, 1, "FORMERR IQuery message length off"); hp->rcode = ns_r_formerr; return (Finish); } rdata_offset = *cpp - anptr; *cpp += dlen; INSIST(*cpp == eom); /* * Not all inverse queries are handled. */ if (type != ns_t_a) { ns_warning(ns_log_security, "unsupported iquery type from %s", inet_ntoa(from.sin_addr)); return (Refuse); } if (dlen != INT32SZ) { ns_warning(ns_log_security, "bad iquery from %s", inet_ntoa(from.sin_addr)); return (Refuse); } if (!NS_OPTION_P(OPTION_FAKE_IQUERY)) return (Refuse); ns_debug(ns_log_default, 1, "req: IQuery class %d type %d", class, type); alen = eom - anptr; if (alen > sizeof anbuf) { ns_warning(ns_log_security, "bad iquery from %s", inet_ntoa(from.sin_addr)); return (Refuse); } memcpy(anbuf, anptr, alen); *cpp = anptr; *buflenp -= HFIXEDSZ; #ifdef QRYLOG if (qrylog) { char tmp[sizeof "255.255.255.255"]; strcpy(tmp, inet_ntoa(from.sin_addr)); ns_info(ns_log_queries, "XX /%s/%s/-%s", tmp, inet_ntoa(ina_get(&anbuf[rdata_offset])), p_type(type)); } #endif /*QRYLOG*/ /* * We can only get here if the option "fake-iquery" is on in the boot * file. * * What we do here is send back a bogus response of "[dottedquad]". * A better strategy would be to turn this into a PTR query, but that * would legitimize inverse queries in a way they do not deserve. */ sprintf(dnbuf, "[%s]", inet_ntoa(ina_get(&anbuf[rdata_offset]))); *buflenp -= QFIXEDSZ; n = dn_comp(dnbuf, *cpp, *buflenp, NULL, NULL); if (n < 0) { hp->tc = 1; return (Finish); } *cpp += n; *buflenp -= n; PUTSHORT((u_int16_t)type, *cpp); *buflenp -= INT16SZ; PUTSHORT((u_int16_t)class, *cpp); *buflenp -= INT16SZ; hp->qdcount = htons(1); if ((int)alen > *buflenp) { hp->tc = 1; return (Finish); } memcpy(*cpp, anbuf, alen); *cpp += alen; *buflenp -= alen; return (Finish); } /* * Test a datum for validity and return non-zero if it is out of date. */ int stale(struct databuf *dp) { struct zoneinfo *zp = &zones[dp->d_zone]; #ifdef CHECK_MAGIC INSIST(dp->d_magic == DATABUF_MAGIC); #endif switch (zp->z_type) { case z_master: return (0); #ifdef STUBS case z_stub: /* root stub zones have DB_F_HINT set */ if (dp->d_flags & DB_F_HINT) return (0); /* FALLTROUGH */ #endif case z_slave: /* * Check to see whether a slave zone has expired or * time warped; if so clear authority flag for zone, * schedule the zone for immediate maintenance, and * return true. */ if ((int32_t)(tt.tv_sec - zp->z_lastupdate) > (int32_t)zp->z_expire) { ns_debug(ns_log_default, 1, "stale: slave zone %s expired", zp->z_origin); if (!haveComplained((u_long)zp, (u_long)stale)) { ns_notice(ns_log_default, "slave zone \"%s\" expired", zp->z_origin); } zp->z_flags &= ~Z_AUTH; if ((zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING)) == 0) { zp->z_time = tt.tv_sec; sched_zone_maint(zp); } return (1); } if (zp->z_lastupdate > tt.tv_sec) { if (!haveComplained((u_long)zp, (u_long)stale)) { ns_notice(ns_log_default, "slave zone \"%s\" time warp", zp->z_origin); } zp->z_flags &= ~Z_AUTH; if ((zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING)) == 0) { zp->z_time = tt.tv_sec; sched_zone_maint(zp); } return (1); } return (0); case z_hint: case z_cache: if (dp->d_flags & DB_F_HINT || dp->d_ttl >= (u_int32_t)tt.tv_sec) return (0); ns_debug(ns_log_default, 3, "stale: ttl %d %ld (x%lx)", dp->d_ttl, (long)(dp->d_ttl - tt.tv_sec), (u_long)dp->d_flags); return (1); default: /* FALLTHROUGH */ ; } panic("stale: impossible condition", NULL); /* NOTREACHED */ return (0); /* Make gcc happy. */ } /* * Copy databuf into a resource record for replies. * Return size of RR if OK, -1 if buffer is full. */ int make_rr(const char *name, struct databuf *dp, u_char *buf, int buflen, int doadd, u_char **comp_ptrs, u_char **edp, int use_minimum) { u_char *cp; u_char *cp1, *sp; struct zoneinfo *zp; int32_t n; int16_t type = dp->d_type; u_int32_t ttl; u_char naptr_flag; ns_debug(ns_log_default, 5, "make_rr(%s, %lx, %lx, %d, %d) %d zone %d ttl %lu", name, (u_long)dp, (u_long)buf, buflen, doadd, dp->d_size, dp->d_zone, (u_long)dp->d_ttl); if (dp->d_rcode && dp->d_size == 0) panic("make_rr: impossible d_rcode value", NULL); zp = &zones[dp->d_zone]; /* check for outdated RR before updating comp_ptrs[] by dn_comp() */ if (zp->z_type == Z_CACHE) { if ((dp->d_flags & DB_F_HINT) != 0 || dp->d_ttl < (u_int32_t)tt.tv_sec) { ttl = 0; } else ttl = dp->d_ttl - (u_int32_t) tt.tv_sec; } else { if (dp->d_ttl != USE_MINIMUM && !use_minimum) ttl = dp->d_ttl; else ttl = zp->z_minimum; /* really default */ } buflen -= RRFIXEDSZ; if (buflen < 0) return (-1); #ifdef RETURNSOA if (dp->d_rcode) { name = (char *)dp->d_data; name += strlen(name) +1; name += strlen(name) +1; name += 5 * INT32SZ; type = T_SOA; } #endif if ((n = dn_comp(name, buf, buflen, comp_ptrs, edp)) < 0) goto cleanup; cp = buf + n; buflen -= n; if (buflen < 0) goto cleanup; PUTSHORT((u_int16_t)type, cp); PUTSHORT((u_int16_t)dp->d_class, cp); PUTLONG(ttl, cp); sp = cp; cp += INT16SZ; switch (type) { case T_CNAME: case T_MG: case T_MR: case T_PTR: n = dn_comp((char *)dp->d_data, cp, buflen, comp_ptrs, edp); if (n < 0) goto cleanup; PUTSHORT((u_int16_t)n, sp); cp += n; break; case T_MB: case T_NS: /* Store domain name in answer */ n = dn_comp((char *)dp->d_data, cp, buflen, comp_ptrs, edp); if (n < 0) goto cleanup; PUTSHORT((u_int16_t)n, sp); cp += n; if (doadd) { addname((char*)dp->d_data, name, type, T_A, dp->d_class); addname(name, name, type, T_KEY, dp->d_class); } break; case T_SOA: case T_MINFO: case T_RP: cp1 = dp->d_data; n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp); if (n < 0) goto cleanup; cp += n; buflen -= type == T_SOA ? n + 5 * INT32SZ : n; if (buflen < 0) goto cleanup; cp1 += strlen((char *)cp1) + 1; n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp); if (n < 0) goto cleanup; cp += n; if (type == T_SOA) { cp1 += strlen((char *)cp1) + 1; #ifdef BIND_UPDATE if (zp->z_flags & Z_NEED_SOAUPDATE) if (incr_serial(zp) < 0) ns_error(ns_log_default, "error updating serial number for %s from %d", zp->z_origin, zp->z_serial); #endif n = 5 * INT32SZ; memcpy(cp, cp1, n); cp += n; if (doadd) addname(name, name, type, T_KEY, dp->d_class); } n = (u_int16_t)((cp - sp) - INT16SZ); PUTSHORT((u_int16_t)n, sp); break; case T_NAPTR: /* cp1 == our data/ cp == data of RR */ cp1 = dp->d_data; /* copy order */ buflen -= INT16SZ; if (buflen < 0) goto cleanup; memcpy(cp, cp1, INT16SZ); cp += INT16SZ; cp1 += INT16SZ; n = (u_int16_t)((cp - sp) - INT16SZ); ns_debug(ns_log_default, 1, "current size n = %u", n); /* copy preference */ buflen -= INT16SZ; if (buflen < 0) goto cleanup; memcpy(cp, cp1, INT16SZ); cp += INT16SZ; cp1 += INT16SZ; n = (u_int16_t)((cp - sp) - INT16SZ); ns_debug(ns_log_default, 1, "current size n = %u", n); /* Flags */ n = *cp1++; ns_debug(ns_log_default, 1, "size of n at flags = %d", n); buflen -= n + 1; if (buflen < 0) goto cleanup; naptr_flag = (n == 1) ? *cp1 : 0; *cp++ = n; memcpy(cp, cp1, n); cp += n; cp1 += n; n = (u_int16_t)((cp - sp) - INT16SZ); ns_debug(ns_log_default, 1, "current size n = %u", n); /* Service */ n = *cp1++; buflen -= n + 1; if (buflen < 0) goto cleanup; *cp++ = n; memcpy(cp, cp1, n); cp += n; cp1 += n; n = (u_int16_t)((cp - sp) - INT16SZ); ns_debug(ns_log_default, 1, "current size n = %u", n); /* Regexp */ n = *cp1++; buflen -= n + 1; if (buflen < 0) goto cleanup; *cp++ = n; memcpy(cp, cp1, n); cp += n; cp1 += n; n = (u_int16_t)((cp - sp) - INT16SZ); ns_debug(ns_log_default, 1, "current size n = %u", n); /* Replacement */ ns_debug(ns_log_default, 1, "Replacement = %s", cp1); n = dn_comp((char *)cp1, cp, buflen, NULL, NULL); ns_debug(ns_log_default, 1, "dn_comp's n = %u", n); if (n < 0) goto cleanup; cp += n; if (doadd && *cp1 != 0) { if (naptr_flag == 's' || naptr_flag == 'S') addname((char*)cp1, name, type, T_SRV, dp->d_class); if (naptr_flag == 'a' || naptr_flag == 'A') addname((char*)cp1, name, type, T_A, dp->d_class); } /* save data length */ n = (u_int16_t)((cp - sp) - INT16SZ); ns_debug(ns_log_default, 1, "saved size n = %u", n); PUTSHORT((u_int16_t)n, sp); break; case T_MX: case T_AFSDB: case T_RT: case T_SRV: /* cp1 == our data/ cp == data of RR */ cp1 = dp->d_data; if ((buflen -= INT16SZ) < 0) goto cleanup; /* copy preference */ memcpy(cp, cp1, INT16SZ); cp += INT16SZ; cp1 += INT16SZ; if (type == T_SRV) { buflen -= INT16SZ*2; if (buflen < 0) goto cleanup; memcpy(cp, cp1, INT16SZ*2); cp += INT16SZ*2; cp1 += INT16SZ*2; } n = dn_comp((char *)cp1, cp, buflen, (type == ns_t_mx) ? comp_ptrs : NULL, (type == ns_t_mx) ? edp : NULL); if (n < 0) goto cleanup; cp += n; /* save data length */ n = (u_int16_t)((cp - sp) - INT16SZ); PUTSHORT((u_int16_t)n, sp); if (doadd) addname((char*)cp1, name, type, T_A, dp->d_class); break; case T_PX: cp1 = dp->d_data; if ((buflen -= INT16SZ) < 0) goto cleanup; /* copy preference */ memcpy(cp, cp1, INT16SZ); cp += INT16SZ; cp1 += INT16SZ; n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp); if (n < 0) goto cleanup; cp += n; buflen -= n; cp1 += strlen((char *)cp1) + 1; n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp); if (n < 0) goto cleanup; cp += n; /* save data length */ n = (u_int16_t)((cp - sp) - INT16SZ); PUTSHORT((u_int16_t)n, sp); break; case T_SIG: /* cp1 == our data; cp == data of target RR */ cp1 = dp->d_data; /* first just copy over the type_covered, algorithm, */ /* labels, orig ttl, two timestamps, and the footprint */ if (buflen < 18) goto cleanup; /* out of room! */ memcpy(cp, cp1, 18); cp += 18; cp1 += 18; buflen -= 18; /* then the signer's name */ n = dn_comp((char *)cp1, cp, buflen, NULL, NULL); if (n < 0) goto cleanup; cp += n; buflen -= n; cp1 += strlen((char*)cp1)+1; /* finally, we copy over the variable-length signature */ n = dp->d_size - (u_int16_t)((cp1 - dp->d_data)); if (n > buflen) goto cleanup; /* out of room! */ memcpy(cp, cp1, n); cp += n; /* save data length & return */ n = (u_int16_t)((cp - sp) - INT16SZ); PUTSHORT((u_int16_t)n, sp); break; case T_NXT: cp1 = dp->d_data; n = dn_comp((char *)cp1, cp, buflen, NULL, NULL); if (n < 0) goto cleanup; cp += n; buflen -=n; cp1 += strlen((char *)cp1) + 1; /* copy nxt bit map */ n = dp->d_size - (u_int16_t)((cp1 - dp->d_data)); if (n > buflen) goto cleanup; /* out of room! */ memcpy(cp, cp1, n); cp += n; buflen -= n; n = (u_int16_t)((cp - sp) - INT16SZ); PUTSHORT((u_int16_t)n, sp); break; default: if ((type == T_A || type == T_AAAA) && doadd) addname(name, name, type, T_KEY, dp->d_class); if (dp->d_size > buflen) goto cleanup; memcpy(cp, dp->d_data, dp->d_size); PUTSHORT((u_int16_t)dp->d_size, sp); cp += dp->d_size; } return (cp - buf); cleanup: /* Rollback RR. */ ns_name_rollback(buf, (const u_char **)comp_ptrs, (const u_char **)edp); return (-1); } static void addname(const char *dname, const char *rname, u_int16_t rtype, u_int16_t type, u_int16_t class) { struct addinfo *ap; int n; for (ap = addinfo, n = addcount; --n >= 0; ap++) if (ns_samename(ap->a_dname, dname) == 1 && ap->a_type == type) return; /* add domain name to additional section */ if (addcount < NADDRECS) { addcount++; ap->a_dname = savestr(dname, 1); ap->a_rname = savestr(rname, 1); ap->a_rtype = rtype; ap->a_type = type; ap->a_class = class; } } /* * Lookup addresses/keys for names in addinfo and put into the message's * additional section. */ int doaddinfo(HEADER *hp, u_char *msg, int msglen) { register struct namebuf *np; register struct databuf *dp; register struct addinfo *ap; register u_char *cp; struct hashbuf *htp; const char *fname; register int n, count; register int ns_logging; int pass = 0; int i, doadd; if (!addcount) return (0); ns_logging = ns_wouldlog(ns_log_default, 3); if (ns_logging) ns_debug(ns_log_default, 3, "doaddinfo() addcount = %d", addcount); if (hp->tc) { ns_debug(ns_log_default, 4, "doaddinfo(): tc already set, bailing"); return (0); } count = 0; cp = msg; loop: for (ap = addinfo, i = 0; i < addcount; ap++, i++) { int auth = 0, + drop = 0, founda = 0, foundaaaa = 0, - founda6 = 0, foundcname = 0, save_count = count, save_msglen = msglen; u_char *save_cp = cp; if ((pass != 0 && (pass != 1 || server_options->preferred_glue == 0) && ap->a_type == T_A) || (pass != 0 && ap->a_type == T_SRV) || (pass != 2 && ap->a_type == T_KEY)) continue; if (ns_logging) ns_debug(ns_log_default, 3, "do additional \"%s\" (from \"%s\")", ap->a_dname, ap->a_rname); htp = hashtab; /* because "nlookup" stomps on arg. */ np = nlookup(ap->a_dname, &htp, &fname, 0); if (np == NULL || fname != ap->a_dname) goto next_rr; if (ns_logging) ns_debug(ns_log_default, 3, "found it"); /* look for the data */ (void)delete_stale(np); for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (dp->d_class != ap->a_class) continue; if (dp->d_rcode == NXDOMAIN) { - founda = founda6 = foundaaaa = 1; + founda = foundaaaa = 1; continue; } switch (dp->d_type) { case ns_t_a: founda = 1; break; - case ns_t_a6: founda6 = 1; break; case ns_t_aaaa: foundaaaa = 1; break; } if (!dp->d_rcode && dp->d_type == T_CNAME) { foundcname++; break; } if (auth == 0 && ap->a_type == T_A && - (dp->d_type == ns_t_a || dp->d_type == ns_t_a6 || - dp->d_type == ns_t_aaaa) && + (dp->d_type == ns_t_a || dp->d_type == ns_t_aaaa) && (zones[dp->d_zone].z_type == z_master || zones[dp->d_zone].z_type == z_slave)) auth = 1; if (pass == 0 && ap->a_type == T_A && server_options->preferred_glue != 0 && !match(dp, (int)ap->a_class, server_options->preferred_glue)) { continue; } if (pass != 0 && ap->a_type == T_A && server_options->preferred_glue != 0 && match(dp, (int)ap->a_class, server_options->preferred_glue)) { continue; } if (ap->a_type == T_A && !match(dp, (int)ap->a_class, T_A) && - !match(dp, (int)ap->a_class, T_AAAA) && - !match(dp, (int)ap->a_class, ns_t_a6)) { + !match(dp, (int)ap->a_class, T_AAAA)) { continue; } if (ap->a_type == T_KEY && !match(dp, (int)ap->a_class, T_KEY)) continue; if (ap->a_type == T_SRV && !match(dp, (int)ap->a_class, T_SRV)) continue; if (dp->d_rcode) continue; + if (drop) + continue; /* * Should be smart and eliminate duplicate * data here. XXX */ doadd = 0; if (ap->a_type == T_SRV) doadd = 1; if ((n = make_rr(ap->a_dname, dp, cp, msglen, doadd, dnptrs, dnptrs_end, 0)) < 0) { /* truncation in the additional-data section * is not all that serious. we do not set TC, * since the answer and authority sections are * OK; however, since we're not setting TC we * have to make sure that none of the RR's for * this name go out (!TC implies that all * {name,type} appearances are complete -- and * since we only do A RR's here, the name is * the key). vixie, 23apr93 */ ns_debug(ns_log_default, 5, "addinfo: not enough room, remaining msglen = %d", save_msglen); /* Rollback RRset. */ ns_name_rollback(save_cp, (const u_char **)dnptrs, (const u_char **)dnptrs_end); cp = save_cp; msglen = save_msglen; count = save_count; - break; + /* + * Continue processing list to prevent + * unnecessary fetches for glue. + * Prevent partial RRsets being sent by + * setting drop. + */ + drop = 1; + continue; } ns_debug(ns_log_default, 5, "addinfo: adding address data n = %d", n); cp += n; msglen -= n; count++; } next_rr: if (!NS_OPTION_P(OPTION_NOFETCHGLUE) && !foundcname && ap->a_type == T_A) { /* ask a real server for this info */ if (!founda && !auth) (void) sysquery(ap->a_dname, (int)ap->a_class, ns_t_a, NULL, NULL, 0, ns_port, QUERY, 0); if (!foundaaaa && !auth) (void) sysquery(ap->a_dname, (int)ap->a_class, ns_t_aaaa, NULL, NULL, 0, ns_port, QUERY, 0); - if (!founda6 && !auth) - (void) sysquery(ap->a_dname, (int)ap->a_class, - ns_t_a6, NULL, NULL, 0, ns_port, - QUERY, 0); } if (foundcname) { if (!haveComplained(nhash(ap->a_dname), nhash(ap->a_rname))) { ns_info(ns_log_cname, "\"%s %s %s\" points to a CNAME (%s)", ap->a_rname, p_class(ap->a_class), p_type(ap->a_rtype), ap->a_dname); } } } if (pass++ < 2) goto loop; /* now do the KEYs... */ hp->arcount = htons((u_int16_t)count); for (ap = addinfo, i = 0; i < addcount; ap++, i++) { ap->a_dname = freestr(ap->a_dname); ap->a_rname = freestr(ap->a_rname); } addcount = 0; return (cp - msg); } int doaddauth(HEADER *hp, u_char *cp, int buflen, struct namebuf *np, struct databuf *dp) { char dnbuf[MAXDNAME]; int n; getname(np, dnbuf, sizeof dnbuf); if (stale(dp)) { ns_debug(ns_log_default, 1, "doaddauth: can't add stale '%s' (%d)", dnbuf, buflen); return (0); } n = make_rr(dnbuf, dp, cp, buflen, 1, dnptrs, dnptrs_end, 1); if (n <= 0) { ns_debug(ns_log_default, 1, "doaddauth: can't add oversize '%s' (%d) (n=%d)", dnbuf, buflen, n); if (n < 0) { hp->tc = 1; } return (0); } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; hp->nscount = htons(ntohs(hp->nscount) + 1); return (n); } void free_addinfo() { struct addinfo *ap; for (ap = addinfo; --addcount >= 0; ap++) { ap->a_dname = freestr(ap->a_dname); ap->a_rname = freestr(ap->a_rname); } addcount = 0; } void free_nsp(struct databuf **nsp) { while (*nsp) db_detach(nsp++); } static void copyCharString(u_char **dst, const char *src) { size_t len = strlen(src) & 0xff; *(*dst)++ = (u_char) len; memcpy(*dst, src, len); *dst += len; } /* * Questionable source ports for queries / responses. */ int drop_port(u_int16_t port) { switch (port) { case 7: /* echo */ case 13: /* daytime */ case 19: /* chargen */ case 37: /* time */ return (1); case 464: /* kpasswd */ return (2); } return (0); } diff --git a/contrib/bind/bin/named/ns_resp.c b/contrib/bind/bin/named/ns_resp.c index f2d0a7c181a4..0174f9f02034 100644 --- a/contrib/bind/bin/named/ns_resp.c +++ b/contrib/bind/bin/named/ns_resp.c @@ -1,4136 +1,4135 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_resp.c 4.65 (Berkeley) 3/3/91"; -static const char rcsid[] = "$Id: ns_resp.c,v 8.178.2.2 2002/11/14 13:39:13 marka Exp $"; +static const char rcsid[] = "$Id: ns_resp.c,v 8.186.6.4 2003/06/02 09:56:35 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1986, 1988, 1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ /* * Portions Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "named.h" static u_int8_t norootlogged[MAXCLASS]; /* XXX- should be a bitmap */ static const char skipnameFailedAnswer[] = "skipname failed in answer", skipnameFailedAuth[] = "skipname failed in authority", skipnameFailedQuery[] = "skipname failed in query", outofDataQuery[] = "ran out of data in query", outofDataAnswer[] = "ran out of data in answer", notSingleQuery[] = "not exactly one query", expandFailedQuery[] = "dn_expand failed in query", expandFailedAnswer[] = "dn_expand failed in answer", expandFailedAuth[] = "dn_expand failed in authority", outofDataAuth[] = "ran out of data in authority", dlenOverrunAnswer[] = "dlen overrun in answer", dlenOverrunAuth[] = "dlen overrun in authority", dlenUnderrunAnswer[] = "dlen underrun in answer", outofDataFinal[] = "out of data in final pass", outofDataAFinal[] = "out of data after final pass", badNameFound[] = "found an invalid domain name", wrongQuestion[] = "answer to wrong question", danglingCname[] = "dangling CNAME pointer", nonRecursiveForwarder[]= "non-recursive forwarder"; struct db_list { struct db_list *db_next; struct databuf *db_dp; }; struct flush_set { char * fs_name; int fs_type; int fs_class; u_int fs_cred; struct db_list *fs_list; struct db_list *fs_last; }; static void rrsetadd(struct flush_set *, const char *, struct databuf *), rrsetupdate(struct flush_set *, int flags, struct sockaddr_in, int), flushrrset(struct flush_set *, struct sockaddr_in), free_flushset(struct flush_set *, int), check_hints(struct flush_set *); static int rrsetcmp(char *, struct db_list *, struct hashbuf *), check_root(void), check_ns(void), wanted(const struct databuf *, int, int), wantedsig(const struct databuf *, int, int), rrextract(u_char *, int, u_char *, struct databuf **, char *, int, struct sockaddr_in, char **); static void mark_bad(struct qinfo *qp, struct sockaddr_in from); static void mark_lame(struct qinfo *qp, struct sockaddr_in from); static int mark_noedns(struct qinfo *qp, struct sockaddr_in from, int cache); static void fast_retry(struct qinfo *qp, struct sockaddr_in from, int samehost); static void add_related_additional(char *); static void free_related_additional(void); static int related_additional(char *); static void freestr_maybe(char **); static enum ordering match_order(const struct namebuf *, int, int); static int match_name(const struct namebuf *, const char *, size_t); #define MAX_RELATED 100 static int num_related = 0; static char *related[MAX_RELATED]; static char * learntFrom(struct qinfo *qp, struct sockaddr_in *server) { static char *buf = NULL; const char *a, *ns, *na; struct databuf *db; int i; char nsbuf[20]; char abuf[20]; static const char fmt[] = " '%s': learnt (A=%s,NS=%s)"; a = ns = na = ""; for (i = 0; (u_int)i < qp->q_naddr; i++) { if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, server->sin_addr)) { db = qp->q_addr[i].ns; if (db != NULL) { if (db->d_addr.s_addr != htonl(0)) { strcpy(nsbuf, inet_ntoa(db->d_addr)); ns = nsbuf; } else { ns = zones[db->d_zone].z_origin; } if (db->d_rcode == 0) na = (char*)qp->q_addr[i].ns->d_data; } db = qp->q_addr[i].nsdata; if (db != NULL) { if (db->d_addr.s_addr != htonl(0)) { strcpy(abuf, inet_ntoa(db->d_addr)); a = abuf; } else { a = zones[db->d_zone].z_origin; } } break; } } if (a == ns && ns == na) /* all "UNKNOWN" */ return (NULL); if (*a == '\0') a = "\".\""; if (*ns == '\0') ns = "\".\""; if (*na == '\0') na = "\".\""; buf = newstr(sizeof fmt + strlen(na) + strlen(a) + strlen(ns), 0); if (buf == NULL) return (NULL); sprintf(buf, fmt, na, a, ns); return (buf); } void ns_resp(u_char *msg, int msglen, struct sockaddr_in from, struct qstream *qsp) { struct qinfo *qp; HEADER *hp; struct qserv *qs = NULL; struct databuf *ns, *ns2; u_char *cp, *answers, *eom = msg + msglen; struct flush_set *flushset = NULL; int flushset_size = 0; struct sockaddr_in *nsa; struct databuf *nsp[NSMAX]; int i, c, n, qdcount, ancount, aucount, nscount, arcount, arfirst; int soacount; u_int qtype, qclass; - int restart; /* flag for processing cname response */ int validanswer, dbflags; int cname, lastwascname, externalcname; int count, founddata, foundname; int buflen; int newmsglen; char name[MAXDNAME], qname[MAXDNAME], aname[MAXDNAME]; char msgbuf[MAXDNAME+100]; char *dname, tmpdomain[MAXDNAME]; const char *fname; const char *formerrmsg = "brain damage"; - u_char newmsg[EDNS_MESSAGE_SZ]; + u_char newmsg[NS_MAXMSG]; u_char **dpp, *tp; time_t rtrip; struct hashbuf *htp; struct namebuf *np; struct fwdinfo *fwd; struct databuf *dp; char *tname = NULL; int sendto_errno = 0; int has_tsig, oldqlen = 0; u_char *oldqbuf = NULL; u_char *smsg = NULL; int smsglen, smsgsize = 0, siglen; u_char sig[TSIG_SIG_SIZE]; time_t tsig_time; DST_KEY *key; int expect_cname; + int pass = 0; nameserIncr(from.sin_addr, nssRcvdR); nsp[0] = NULL; hp = (HEADER *) msg; if ((qp = qfindid(hp->id)) == NULL ) { ns_debug(ns_log_default, 1, "DUP? dropped (id %d)", ntohs(hp->id)); nameserIncr(from.sin_addr, nssRcvdDupR); return; } if (ns_wouldlog(ns_log_default, 2)) { ns_debug(ns_log_default, 2, "Response (%s %s %s) nsid=%d id=%d", (qp->q_flags & Q_SYSTEM) ?"SYSTEM" :"USER", (qp->q_flags & Q_PRIMING) ?"PRIMING" :"NORMAL", (qp->q_flags & Q_ZSERIAL) ?"ZSERIAL" :"-", ntohs(qp->q_nsid), ntohs(qp->q_id)); } if (qp->q_nstsig == NULL) has_tsig = 0; else { int ret; ret = ns_verify(msg, &msglen, qp->q_nstsig->key, qp->q_nstsig->sig, qp->q_nstsig->siglen, NULL, NULL, &tsig_time, 0); if (ret == 0) has_tsig = 1; else { if (hp->rcode == NOERROR) hp->rcode = NOTAUTH; ns_debug(ns_log_default, 1, "resp: error bad tsig, record dropped"); return; } } /* * Here we handle high level formatting problems by parsing the header. */ qdcount = ntohs(hp->qdcount); ancount = ntohs(hp->ancount); aucount = ntohs(hp->nscount); arcount = ntohs(hp->arcount); free_addinfo(); /* sets addcount to zero */ cp = msg + HFIXEDSZ; dpp = dnptrs; *dpp++ = msg; if ((*cp & INDIR_MASK) == 0) *dpp++ = cp; *dpp = NULL; if (qdcount == 1) { n = dn_expand(msg, eom, cp, qname, sizeof(qname)); if (n <= 0) { formerrmsg = expandFailedQuery; goto formerr; } cp += n; if (cp + 2 * INT16SZ > eom) { formerrmsg = outofDataQuery; goto formerr; } GETSHORT(qtype, cp); GETSHORT(qclass, cp); if (!ns_nameok(qp, qname, qclass, NULL, response_trans, ns_ownercontext(qtype, response_trans), qname, from.sin_addr)) { formerrmsg = badNameFound; goto refused; } if (cp > eom) { formerrmsg = outofDataQuery; goto formerr; } if (qp->q_msg && qp->q_msglen && !res_nameinquery(qname, qtype, qclass, qp->q_msg, qp->q_msg + qp->q_msglen)) { sprintf(msgbuf, "query section mismatch (%s %s %s)", qname, p_class(qclass), p_type(qtype)); formerrmsg = msgbuf; goto formerr; } if (ns_samename(qp->q_name, qname) != 1 || qp->q_class != qclass || qp->q_type != qtype) { formerrmsg = wrongQuestion; goto formerr; } } else { strcpy(qname, qp->q_name); qclass = qp->q_class; qtype = qp->q_type; } /* cp now points after the query section. */ /* * Here we handle bad responses from servers. * Several possibilities come to mind: * The server is sick and returns SERVFAIL * The server returns some garbage opcode (it's sick) * The server can't understand our query and return FORMERR * In all these cases, we drop the packet, disable retries on * this server and immediately force a retry. */ if ((hp->rcode != NOERROR && hp->rcode != NXDOMAIN) || (hp->opcode != QUERY #ifdef BIND_NOTIFY && hp->opcode != NS_NOTIFY_OP #endif )) { int noedns = 1; ns_debug(ns_log_default, 2, "resp: error (ret %d, op %d), dropped", hp->rcode, hp->opcode); switch (hp->rcode) { case SERVFAIL: nameserIncr(from.sin_addr, nssRcvdFail); noedns = mark_noedns(qp, from, 0); break; case FORMERR: nameserIncr(from.sin_addr, nssRcvdFErr); noedns = mark_noedns(qp, from, 1); break; case NOTIMP: nameserIncr(from.sin_addr, nssRcvdErr); noedns = mark_noedns(qp, from, 1); break; default: nameserIncr(from.sin_addr, nssRcvdErr); break; } if (ns_samename(qp->q_name, qp->q_domain) == 1 && hp->rcode == SERVFAIL && hp->opcode == QUERY && noedns) mark_lame(qp, from); if (noedns) mark_bad(qp, from); fast_retry(qp, from, noedns ? 0 : 1); return; } if (qdcount != 1) { /* We don't generate or forward these (yet). */ formerrmsg = notSingleQuery; goto formerr; } /* * Determine if the response came from a forwarder. Packets from * anyplace not listed as a forwarder or as a server to whom we * might have forwarded the query will be dropped. * XXX - should put this in STATS somewhere. */ for (fwd = NS_ZFWDTAB(qp->q_fzone); fwd; fwd = fwd->next) if (ina_equal(fwd->fwddata->fwdaddr.sin_addr, from.sin_addr)) break; /* * find the qinfo pointer and update * the rtt and fact that we have called on this server before. */ { struct timeval *stp; for (n = 0, qs = qp->q_addr; (u_int)n < qp->q_naddr; n++, qs++) if (ina_equal(qs->ns_addr.sin_addr, from.sin_addr)) break; if ((u_int)n >= qp->q_naddr) { if (!haveComplained(ina_ulong(from.sin_addr), (u_long)"unexpected source")) { ns_info(ns_log_default, "Response from unexpected source (%s) for query \"%s %s %s\"", sin_ntoa(from), *(qp->q_name) ? qp->q_name : ".", p_class(qp->q_class), p_type(qp->q_type)); } /* * We don't know who this response came from so it * gets dropped on the floor. */ return; } stp = &qs->stime; /* Handle response from different (untried) interface. */ if (qs->ns != NULL && stp->tv_sec == 0) { ns = qs->ns; while (qs > qp->q_addr && (qs->stime.tv_sec == 0 || qs->ns != ns)) qs--; *stp = qs->stime; /* XXX - sometimes stp still ends up pointing to * a zero timeval, in spite of the above attempt. * Why? What should we do about it? */ /* XXX - catch aliases here */ } /* compute query round trip time */ /* XXX - avoid integer overflow, which is quite likely if stp * points to a zero timeval (see above). * rtrip is of type time_t, which we assume is at least * as big as an int. */ if ((tt.tv_sec - stp->tv_sec) > (INT_MAX-999)/1000) { rtrip = INT_MAX; } else { rtrip = ((tt.tv_sec - stp->tv_sec) * 1000 + (tt.tv_usec - stp->tv_usec) / 1000); } if (ns_wouldlog(ns_log_default, 3)) { ns_debug(ns_log_default, 3, "stime %lu/%lu now %lu/%lu rtt %ld", (u_long)stp->tv_sec, (u_long)stp->tv_usec, (u_long)tt.tv_sec, (u_long)tt.tv_usec, (long)rtrip); } /* prevent floating point overflow, limit to 1000 sec */ if (rtrip > 1000000) { rtrip = 1000000; } ns = qs->nsdata; /* * Don't update nstime if this doesn't look * like an address databuf now. XXX */ if (ns && ns->d_type == T_A && ns->d_class == qs->ns->d_class) { u_long t; if (ns->d_nstime == 0) t = rtrip; else t = ns->d_nstime * ALPHA + (1 - ALPHA) * rtrip; if (t > 65535) t = 65535; else if (t == 0) t = 1; ns->d_nstime = (u_int16_t)t; } /* * Record the source so that we do not use this NS again. */ if (ns && qs->ns && (qp->q_nusedns < NSMAX)) { qp->q_usedns[qp->q_nusedns++] = qs->ns; if (ns_wouldlog(ns_log_default, 2)) { ns_debug(ns_log_default, 2, "NS #%d addr %s used, rtt %d", n, sin_ntoa(qs->ns_addr), ns->d_nstime); } } /* * Penalize those who had earlier chances but failed * by multiplying round-trip times by BETA (>1). * Improve nstime for unused addresses by applying GAMMA. * The GAMMA factor makes unused entries slowly * improve, so they eventually get tried again. * GAMMA should be slightly less than 1. * Watch out for records that may have timed out * and are no longer the correct type. XXX */ for (n = 0, qs = qp->q_addr; (u_int)n < qp->q_naddr; n++, qs++) { u_long t; ns2 = qs->nsdata; if (!ns2 || ns2 == ns) continue; if (ns2->d_type != T_A || ns2->d_class != qs->ns->d_class) /* XXX */ continue; if (qs->stime.tv_sec) { if (ns2->d_nstime == 0) t = (rtrip * BETA) + 1; else t = ns2->d_nstime * BETA + (1 - ALPHA) * rtrip + 1; } else t = ns2->d_nstime * GAMMA; if (t > 65535) t = 65535; else if (t == 0) t = 1; ns2->d_nstime = (u_int16_t)t; if (ns_wouldlog(ns_log_default, 2)) { ns_debug(ns_log_default, 2, "NS #%d %s rtt now %d", n, sin_ntoa(qs->ns_addr), ns2->d_nstime); } } } #ifdef BIND_NOTIFY /* * For now, NOTIFY isn't defined for ANCOUNT!=0, AUCOUNT!=0, * or ADCOUNT!=0. Therefore the only real work to be done for * a NOTIFY-QR is to remove it from the query queue. */ if (hp->opcode == NS_NOTIFY_OP) { ns_info(ns_log_notify, "Received NOTIFY answer (%sAA) from %s for \"%s %s %s\"", hp->aa ? "" : "!", inet_ntoa(from.sin_addr), *(qp->q_name) ? qp->q_name : ".", p_class(qp->q_class), p_type(qp->q_type)); qremove(qp); return; } #endif if ((qp->q_flags & Q_ZSERIAL) != 0) { if (hp->aa && ancount > 0 && hp->rcode == NOERROR && qtype == T_SOA && (qclass == C_IN || qclass == C_HS)) { int n; u_int type, class, dlen; u_int32_t serial; u_char *tp = cp; u_char *rdatap; n = dn_expand(msg, eom, tp, name, sizeof name); if (n < 0) { formerrmsg = expandFailedAnswer; goto formerr; } tp += n; /* name */ if (tp + 3 * INT16SZ + INT32SZ > eom) { formerrmsg = outofDataAnswer; goto formerr; } GETSHORT(type, tp); /* type */ GETSHORT(class, tp); /* class */ tp += INT32SZ; /* ttl */ GETSHORT(dlen, tp); /* dlen */ rdatap = tp; /* start of rdata */ if (!ns_nameok(qp, name, class, NULL, response_trans, ns_ownercontext(type, response_trans), name, from.sin_addr)) { formerrmsg = badNameFound; goto refused; } if (ns_samename(qname, name) != 1 || qtype != type || qclass != class) { sprintf(msgbuf, "qserial answer mismatch (%s %s %s)", name, p_class(class), p_type(type)); formerrmsg = msgbuf; goto formerr; } if (0 >= (n = dn_skipname(tp, eom))) { formerrmsg = skipnameFailedAnswer; goto formerr; } tp += n; /* mname */ if (0 >= (n = dn_skipname(tp, eom))) { formerrmsg = skipnameFailedAnswer; goto formerr; } tp += n; /* rname */ if (tp + 5 * INT32SZ > eom) { formerrmsg = dlenUnderrunAnswer; goto formerr; } GETLONG(serial, tp); tp += 4 * INT32SZ; /* Skip rest of SOA. */ if ((u_int)(tp - rdatap) != dlen) { formerrmsg = dlenOverrunAnswer; goto formerr; } for (n = 0, qs = qp->q_addr; (u_int)n < qp->q_naddr; n++, qs++) if (ina_equal(qs->ns_addr.sin_addr, from.sin_addr)) break; if (n == qp->q_naddr) { qserial_answer(qp); qremove(qp); return; } qs->serial = serial; } retry(qp, 0); return; } /* * Non-authoritative, no answer, no error, with referral. */ if (hp->rcode == NOERROR && !hp->tc && !hp->aa && ancount == 0 && aucount > 0 #ifdef BIND_NOTIFY && hp->opcode != NS_NOTIFY_OP #endif ) { u_char *tp; int type, class = 0, dlen; int foundns, foundsoa; #ifdef DEBUG if (debug > 0) res_pquery(&res, msg, msglen, log_get_stream(packet_channel)); #endif /* * Since there is no answer section (ancount == 0), * we must be pointing at the authority section (aucount > 0). */ tp = cp; foundns = foundsoa = 0; for (i = 0 ; i < aucount ; i++) { n = dn_expand(msg, eom, tp, name, sizeof name); if (n < 0) { formerrmsg = expandFailedAuth; goto formerr; } tp += n; if (tp + 3 * INT16SZ + INT32SZ > eom) { formerrmsg = outofDataAuth; goto formerr; } GETSHORT(type, tp); GETSHORT(class, tp); tp += INT32SZ; /* ttl */ GETSHORT(dlen, tp); if (!ns_nameok(qp, name, class, NULL, response_trans, ns_ownercontext(type, response_trans), name, from.sin_addr)) { formerrmsg = badNameFound; goto refused; } /* skip rest of record */ if (tp + dlen > eom) { formerrmsg = outofDataAuth; goto formerr; } tp += dlen; if (type == T_NS) { strcpy(aname, name); foundns = 1; } if (type == T_SOA) foundsoa = 1; } /* * If the answer delegates us either to the same level in * the hierarchy or closer to the root, we consider this * server lame. Note that for now we only log the message * if the T_NS was C_IN, which is technically wrong (NS is * visible in all classes) but necessary anyway (non-IN * classes tend to not have good strong delegation graphs). */ if (foundns && !foundsoa && ns_samedomain(qp->q_domain, aname)) { if (fwd == NULL) { nameserIncr(from.sin_addr, nssRcvdLDel); mark_lame(qp, from); } mark_bad(qp, from); if (class == C_IN && fwd == NULL && !haveComplained(ina_ulong(from.sin_addr), nhash(qp->q_domain))) { char *learnt_from = learntFrom(qp, &from); ns_info(ns_log_lame_servers, "Lame server on '%s' (in '%s'?): %s%s", qname, qp->q_domain, sin_ntoa(from), (learnt_from == NULL) ? "" : learnt_from); if (learnt_from != NULL) learnt_from = freestr(learnt_from); } else if (fwd != NULL) { if (!haveComplained(ina_ulong(from.sin_addr), (u_long)nonRecursiveForwarder)) ns_warning(ns_log_default, "%s: %s", nonRecursiveForwarder, sin_ntoa(from)); } fast_retry(qp, from, 0); return; } } /* * Add the info received in the response to the data base. */ arfirst = ancount + aucount; c = arfirst + arcount; /* Don't return if it's a TSIG signed truncated message */ if (has_tsig > 0 && hp->tc) goto tcp_retry; /* -ve $ing non-existence of record, must handle non-authoritative * NOERRORs with c == 0. */ if (!hp->aa && !hp->tc && hp->rcode == NOERROR && c == 0) goto return_msg; if (qp->q_flags & Q_SYSTEM) dbflags = DB_NOTAUTH | DB_NODATA; else dbflags = DB_NOTAUTH | DB_NODATA | DB_NOHINTS; count = c; if (qp->q_flags & Q_PRIMING) dbflags |= DB_PRIMING; if (hp->tc) { count -= arcount; /* truncation had to affect this */ if (!arcount) { count -= aucount; /* guess it got this too */ } if (!(arcount || aucount)) { count -= ancount; /* things are pretty grim */ } tcp_retry: /* retry using tcp provided this was not a tcp query */ if (!(qp->q_flags & Q_USEVC)) { qp->q_flags |= Q_USEVC; unsched(qp); schedretry(qp, 60); nsa = Q_NEXTADDR(qp, 0); key = qp->q_keys[0]; if (key != NULL) key = qp->q_keys[0] = tsig_key_from_addr(nsa->sin_addr); if (key != NULL) { smsgsize = qp->q_msglen + TSIG_BUF_SIZE; smsg = memget(smsgsize); smsglen = qp->q_msglen; siglen = sizeof(sig); memcpy(smsg, qp->q_msg, qp->q_msglen); n = ns_sign(smsg, &smsglen, smsgsize, NOERROR, key, NULL, 0, sig, &siglen, 0); if (n == 0) { oldqbuf = qp->q_msg; oldqlen = qp->q_msglen; qp->q_msglen = smsglen; qp->q_msg = smsg; has_tsig = 1; free_tsig(qp->q_nstsig); qp->q_nstsig = new_tsig(key, sig, siglen); } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; INSIST(0); } } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; } if (tcp_send(qp) != NOERROR) /* * We're probably in trouble if tcp_send * failed, but we'll try to press on because * there isn't anything else to do. */ retry(qp, 0); if (has_tsig == 1) { memput(qp->q_msg, smsgsize); qp->q_msg = oldqbuf; qp->q_msglen = oldqlen; } return; } else if (!qsp) { /* outstanding udp response */ return; } /* XXX truncated tcp response */ ns_error(ns_log_default, "ns_resp: TCP truncated: \"%s\" %s %s from %s", qname, p_class(qclass), p_type(qtype), sin_ntoa(from)); /* mark this server as bad */ mark_bad(qp, from); /* try another server, it may have a bigger write buffer */ retry(qp, 0); return; } tp = cp; - restart = 0; validanswer = -1; nscount = 0; soacount = 0; cname = 0; lastwascname = 0; externalcname = 0; strcpy(aname, qname); if (count) { /* allocate 1 extra record for end of set detection */ flushset_size = (count + 1) * sizeof *flushset; flushset = memget(flushset_size); if (flushset == NULL) panic("flushset: out of memory", NULL); memset(flushset, 0, flushset_size); } else flushset = NULL; expect_cname = 1; for (i = 0; i < count; i++) { struct databuf *dp; int type; freestr_maybe(&tname); if (cp >= eom) { free_related_additional(); if (flushset != NULL) free_flushset(flushset, flushset_size); formerrmsg = outofDataFinal; goto formerr; } n = rrextract(msg, msglen, cp, &dp, name, sizeof name, from, &tname); if (n < 0) { free_related_additional(); freestr_maybe(&tname); if (flushset != NULL) free_flushset(flushset, flushset_size); formerrmsg = outofDataFinal; if (hp->rcode == REFUSED) goto refused; else goto formerr; } cp += n; if (!dp) continue; type = dp->d_type; if (i < ancount) { /* Answer section. */ /* * Check for attempts to overflow the buffer in * getnameanswer. */ if (type == ns_t_cname && !expect_cname) { ns_warning(ns_log_security, "late CNAME in answer section for %s %s from %s", *qname ? qname : ".", p_type(qtype), sin_ntoa(from)); } else if (type != ns_t_cname && type != ns_t_dname && type != ns_t_sig) expect_cname = 0; if (externalcname || ns_samename(name, aname) != 1) { if (!externalcname) ns_info(ns_log_resp_checks, "wrong ans. name (%s != %s)", name[0] ? name : ".", aname[0] ? aname : "."); else ns_debug(ns_log_resp_checks, 3, "ignoring answer '%s' after external cname", name); db_detach(&dp); validanswer = 0; continue; } if (type == T_CNAME && qtype != T_CNAME && qtype != T_ANY) { strcpy(aname, (char *)dp->d_data); if (!ns_samedomain(aname, qp->q_domain)) externalcname = 1; cname++; lastwascname = 1; } else { if (validanswer) validanswer = 1; lastwascname = 0; } if (tname != NULL) { add_related_additional(tname); tname = NULL; } + /* Cache for current tick. */ + if (type == T_SOA) + dp->d_ttl = tt.tv_sec; + dp->d_cred = (hp->aa && ns_samename(name, qname) == 1) ? DB_C_AUTH : DB_C_ANSWER; } else { /* After answer section. */ if (lastwascname) { ns_debug(ns_log_resp_checks, 3, "last was cname, ignoring auth. and add."); db_detach(&dp); validanswer = 0; break; } if (i < arfirst) { /* Authority section. */ switch (type) { case T_NS: case T_SOA: if (!ns_samedomain(aname, name)) { ns_info(ns_log_resp_checks, "bad referral (%s !< %s) from %s", aname[0] ? aname : ".", name[0] ? name : ".", sin_ntoa(from)); db_detach(&dp); validanswer = 0; continue; } else if (!ns_samedomain(name, qp->q_domain)) { if (fwd == NULL && !externalcname) ns_info(ns_log_resp_checks, "bad referral (%s !< %s) from %s", name[0] ? name : ".", qp->q_domain[0] ? qp->q_domain : ".", sin_ntoa(from)); db_detach(&dp); validanswer = 0; continue; } if (type == T_NS) { nscount++; add_related_additional(tname); tname = NULL; } if (type == T_SOA) { soacount++; + /* -ve caching only. */ + db_detach(&dp); + continue; } break; case T_NXT: /* XXX check */ break; case T_SIG: /* XXX check that it relates to an NS or SOA or NXT */ break; default: ns_info(ns_log_resp_checks, "invalid RR type '%s' in authority section (name = '%s') from %s", p_type(type), name, sin_ntoa(from)); db_detach(&dp); validanswer = 0; continue; } dp->d_cred = (hp->aa && (cname == 0)) ? DB_C_AUTH : (qp->q_flags & Q_PRIMING) ? DB_C_ANSWER : DB_C_ADDITIONAL; } else { /* Additional section. */ switch (type) { case T_A: case ns_t_a6: case T_AAAA: case T_SRV: if (externalcname || !ns_samedomain(name, qp->q_domain)) { ns_debug(ns_log_resp_checks, 3, "ignoring additional info '%s' type %s", name, p_type(type)); db_detach(&dp); validanswer = 0; continue; } if (!related_additional(name)) { ns_info(ns_log_resp_checks, "unrelated additional info '%s' type %s from %s", name, p_type(type), sin_ntoa(from)); db_detach(&dp); validanswer = 0; continue; } if (type == T_SRV && tname != NULL) { add_related_additional(tname); tname = NULL; } break; case T_KEY: /* XXX check? */ break; case T_SIG: /* * XXX a SIG RR should relate * to some other RR in this section, * although if it's the last RR * it might be a transaction signature. */ break; case ns_t_opt: /* * OPT does not get cached. */ db_detach(&dp); validanswer = 0; continue; default: ns_info(ns_log_resp_checks, "invalid RR type '%s' in additional section (name = '%s') from %s", p_type(type), name, sin_ntoa(from)); db_detach(&dp); validanswer = 0; continue; } dp->d_cred = (qp->q_flags & Q_PRIMING) ? DB_C_ANSWER : DB_C_ADDITIONAL; } } #ifdef HITCOUNTS ++dp->d_hitcnt; ++db_total_hits; #endif /* HITCOUNTS */ rrsetadd(flushset, name, dp); db_detach(&dp); } free_related_additional(); freestr_maybe(&tname); if (flushset != NULL) { if ((qp->q_flags & Q_SYSTEM) && (qp->q_flags & Q_PRIMING)) { check_hints(flushset); /* before rrsetupdate */ rrsetupdate(flushset, dbflags, from, 1); } else rrsetupdate(flushset, dbflags, from, 0); free_flushset(flushset, flushset_size); } if (lastwascname && !externalcname) ns_debug(ns_log_cname, 3, "%s (%s) q(%s %s %s) %s qd(%s)", danglingCname, aname, (qname && *qname) ? qname : ".", p_class(qclass), p_type(qtype), sin_ntoa(from), qp->q_domain); if (cp > eom) { formerrmsg = outofDataAFinal; goto formerr; } if ((qp->q_flags & Q_SYSTEM) && ancount) { if ((qp->q_flags & Q_PRIMING) && !check_root()) { /* mark server as bad */ mark_bad(qp, from); fast_retry(qp, from, 0); return; } ns_debug(ns_log_default, 3, "resp: leaving, SYSQUERY ancount %d", ancount); #ifdef BIND_NOTIFY if (qp->q_notifyzone != DB_Z_CACHE) { struct zoneinfo *zp = &zones[qp->q_notifyzone]; qp->q_notifyzone = DB_Z_CACHE; ns_notify(zp->z_origin, zp->z_class, ns_t_soa); } #endif qremove(qp); return; } - if (ancount && count && validanswer != 1) { - /* - * Everything passed validation but we didn't get the - * final answer. The response must have contained - * a dangling CNAME. Force a restart of the query. - * - * Don't set restart if count==0, since this means - * the response was truncated in the answer section, - * causing us to set count to 0 which will cause - * validanswer to be 0 as well even though the answer - * section probably contained valid RRs (just not - * a complete set). - * XXX - this works right if we can just forward this - * response to the client, but not if we found a CNAME - * in a prior response and restarted the query. - */ - restart = 1; - } - - if (!restart && !qp->q_cmsglen && ancount > 1 && qtype == T_A) - sort_response(tp, eom, ancount, &qp->q_from); - - /* - * An answer to a T_ANY query or a successful answer to a - * regular query with no indirection, then just return answer. - */ - if (!restart && ancount && (qtype == T_ANY || !qp->q_cmsglen)) { - ns_debug(ns_log_default, 3, - "resp: got as much answer as there is"); - goto return_msg; - } - /* * We might want to cache this negative answer. * * if ancount != 0 and rcode == NOERROR we cannot determine if the * CNAME chain has been processed to completion or not, so just * restart the query. DNS needs a NODATA return code! * * As some servers incorrectly return a NODATA indication when * there is a CNAME chain instead of NXDOMAIN, we requery to get * a definitive answer. */ if ((hp->rcode == NXDOMAIN && cname == ancount) || (hp->rcode == NOERROR && ancount == 0 && (nscount == 0 || soacount != 0) ) ) { cache_n_resp(msg, msglen, from, qp->q_name, qp->q_class, qp->q_type); if (!qp->q_cmsglen && validanswer) { ns_debug(ns_log_default, 3, "resp: leaving NO: auth = %d", hp->aa); goto return_msg; } } /* * All messages in here need further processing. i.e. they * are either CNAMEs or we got referred again. */ count = 0; founddata = 0; dname = name; /* * XXX - the restart stuff doesn't work if any of the answer RRs * is not cacheable (TTL==0 or unknown RR type), since all of the * answer must pass through the cache and be re-assembled. */ if (qp->q_cmsglen != 0) { ns_debug(ns_log_default, 1, "Cname second pass"); newmsglen = MIN(EDNS_MESSAGE_SZ, qp->q_cmsglen); memcpy(newmsg, qp->q_cmsg, newmsglen); } else { newmsglen = MIN(EDNS_MESSAGE_SZ, msglen); memcpy(newmsg, msg, newmsglen); } hp = (HEADER *) newmsg; hp->ancount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); hp->rcode = NOERROR; dnptrs[0] = newmsg; dnptrs[1] = NULL; cp = newmsg + HFIXEDSZ; /* * Keep in mind that none of this code works when QDCOUNT>1. * cp ends up pointed just past the query section in both cases. */ /* * Arrange for dname to contain the query name. The query * name can be either the original query name if restart==0 * or the target of the last CNAME if we are following a * CNAME chain and were referred. */ n = dn_expand(newmsg, newmsg + newmsglen, cp, dname, sizeof name); if (n < 0) { ns_debug(ns_log_default, 1, "dn_expand failed"); goto servfail; } if (!res_dnok(dname)) { ns_debug(ns_log_default, 1, "bad name (%s)", dname); goto servfail; } cp += n + QFIXEDSZ; - buflen = sizeof(newmsg) - (cp - newmsg); - + buflen = (qp->q_stream != NULL) ? NS_MAXMSG : + MIN(EDNS_MESSAGE_SZ, qp->q_udpsize); + buflen -= (cp - newmsg); + /* + * Reserve space for TSIG / EDNS + */ + if (qp->q_tsig != NULL) + buflen -= qp->q_tsig->tsig_size; + if ((qp->q_flags & Q_EDNS) != 0) + buflen -= 11; cname = 0; try_again: + pass++; ns_debug(ns_log_default, 1, "resp: nlookup(%s) qtype=%d", dname, qtype); foundname = 0; fname = ""; htp = hashtab; /* lookup relative to root */ np = nlookup(dname, &htp, &fname, 0); ns_debug(ns_log_default, 1, "resp: %s '%s' as '%s' (cname=%d)", np == NULL ? "missed" : "found", dname, fname, cname); if (np == NULL || fname != dname) goto fetch_ns; foundname++; answers = cp; count = cp - newmsg; /* * Look for NXDOMAIN record. */ for (dp = np->n_data; dp; dp = dp->d_next) { if (!stale(dp) && (dp->d_rcode == NXDOMAIN) && (dp->d_class == (int)qclass)) { #ifdef RETURNSOA n = finddata(np, qclass, T_SOA, hp, &dname, - &buflen, &count); + &buflen, &count, pass, 1); if ( n != 0) { if (count) { cp += n; buflen -= n; newmsglen += n; hp->nscount = htons((u_int16_t)count); } if (hp->rcode == NOERROR_NODATA) { hp->rcode = NOERROR; goto return_newmsg; } } #else count = 0; #endif hp->rcode = NXDOMAIN; /* * XXX forcing AA all the time isn't right, but * we have to work that way by default * for compatibility with older servers. */ if (!NS_OPTION_P(OPTION_NONAUTH_NXDOMAIN)) hp->aa = 1; ns_debug(ns_log_default, 3, "resp: NXDOMAIN aa = %d", hp->aa); if ((count == 0) || NS_OPTION_P(OPTION_NORFC2308_TYPE1)) goto return_newmsg; founddata = 1; goto fetch_ns; } } - n = finddata(np, qclass, qtype, hp, &dname, &buflen, &count); + n = finddata(np, qclass, qtype, hp, &dname, &buflen, &count, pass, 1); if (n == 0) goto fetch_ns; /* NO data available */ if (hp->rcode) { if (hp->rcode == NOERROR_NODATA) hp->rcode = NOERROR; #ifdef RETURNSOA if (count) { cp += n; buflen -= n; hp->nscount = htons((u_int16_t)count); } #endif if ((count == 0) || NS_OPTION_P(OPTION_NORFC2308_TYPE1)) goto return_newmsg; founddata = 1; goto fetch_ns; } cp += n; buflen -= n; hp->ancount = htons(ntohs(hp->ancount) + (u_int16_t)count); if (fname != dname && qtype != T_CNAME && qtype != T_ANY) { cname++; goto try_again; } founddata = 1; ns_debug(ns_log_default, 3, "resp: foundname=%d, count=%d, founddata=%d, cname=%d", foundname, count, founddata, cname); if (count > 1 && qtype == T_A) sort_response(answers, cp, count, &qp->q_from); fetch_ns: if (hp->tc) goto return_newmsg; /* * Look for name servers to refer to and fill in the authority * section or record the address for forwarding the query * (recursion desired). */ free_nsp(nsp); switch (findns(&np, qclass, nsp, &count, 0)) { case NXDOMAIN: /* shouldn't happen */ ns_debug(ns_log_default, 3, "req: leaving (%s, rcode %d)", dname, hp->rcode); if (!foundname) hp->rcode = NXDOMAIN; if (qclass != C_ANY) { - hp->aa = 1; + if (!cname) + hp->aa = 1; if (np && (!foundname || !founddata)) { n = doaddauth(hp, cp, buflen, np, nsp[0]); cp += n; buflen -= n; } } goto return_newmsg; case SERVFAIL: goto servfail; } if (founddata) { hp = (HEADER *)newmsg; n = add_data(np, nsp, cp, buflen, &count); if (n < 0) { hp->tc = 1; n = (-n); } cp += n; buflen -= n; hp->nscount = htons((u_int16_t)count + ntohs(hp->nscount)); goto return_newmsg; } /* * If we get here, we don't have the answer yet and are about * to iterate to try and get it. First, infinite loop avoidance. */ if (qp->q_nqueries++ > MAXQUERIES) { ns_debug(ns_log_default, 1, "resp: MAXQUERIES exceeded (%s %s %s)", dname, p_class(qclass), p_type(qtype)); ns_info(ns_log_default, "MAXQUERIES exceeded, possible data loop in resolving (%s)", dname); goto servfail; } /* Reset the query control structure */ ns_freeqns(qp); qp->q_naddr = 0; qp->q_curaddr = 0; nsfwdadd(qp, NS_ZFWDTAB(qp->q_fzone)); if (qp->q_domain != NULL) (void)freestr(qp->q_domain); getname(np, tmpdomain, sizeof tmpdomain); qp->q_domain = savestr(tmpdomain, 1); if (NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) n = 0; else if ((n = nslookup(nsp, qp, dname, "ns_resp")) <= 0) { if (n < 0) { if (n == -1) ns_debug(ns_log_default, 3, "resp: nslookup reports danger"); if (cname) /* a remote CNAME that does not have data */ goto return_newmsg; goto servfail; } else { ns_debug(ns_log_default, 3, "resp: no addrs found for NS's"); /* * Timeout while sysquery looks up the NS addresses. * * Hopefully we'll have them when the client asks * again. * * too bad we can't just wait for the sysquery * response to restart this query (it's too hard). * * We could try to crawl back up the tree looking * for reachable servers, but we may have just * gotten delegated down here by a response with * no A RRs for the servers. If we blindly tried * this strategy, we bang on the same server forever. */ goto timeout; } } for (n = 0; (u_int)n < qp->q_naddr; n++) qp->q_addr[n].stime.tv_sec = 0; qp->q_addr[0].stime = tt; if (cname) { if (qp->q_cname++ == MAXCNAMES) { ns_debug(ns_log_default, 3, "resp: leaving, MAXCNAMES exceeded"); goto servfail; } ns_debug(ns_log_default, 1, "q_cname = %d", qp->q_cname); ns_debug(ns_log_default, 3, "resp: building recursive query; nslookup"); if (qp->q_cmsg == NULL) { qp->q_cmsg = qp->q_msg; qp->q_cmsglen = qp->q_msglen; qp->q_cmsgsize = qp->q_msgsize; } else if (qp->q_msg != NULL) memput(qp->q_msg, qp->q_msgsize); qp->q_msg = (u_char *)memget(PACKETSZ); if (qp->q_msg == NULL) { ns_notice(ns_log_default, "resp: memget error"); goto servfail; } qp->q_msgsize = PACKETSZ; n = res_nmkquery(&res, QUERY, dname, qclass, qtype, NULL, 0, NULL, qp->q_msg, PACKETSZ); if (n < 0) { ns_info(ns_log_default, "resp: res_mkquery(%s) failed", dname); goto servfail; } if (qp->q_name != NULL) (void)freestr(qp->q_name); qp->q_name = savestr(dname, 1); qp->q_msglen = n; hp = (HEADER *) qp->q_msg; hp->rd = 0; } else hp = (HEADER *) qp->q_msg; hp->id = qp->q_nsid = htons(nsid_next()); hp->rd = (qp->q_addr[0].forwarder ? 1 : 0); unsched(qp); schedretry(qp, retrytime(qp)); nsa = Q_NEXTADDR(qp, 0); if (ns_wouldlog(ns_log_default, 1)) { ns_debug(ns_log_default, 1, "resp: forw -> %s ds=%d nsid=%d id=%d %dms", sin_ntoa(*nsa), ds, ntohs(qp->q_nsid), ntohs(qp->q_id), (qp->q_addr[0].nsdata != NULL) ? qp->q_addr[0].nsdata->d_nstime : -1); } #ifdef DEBUG if (debug >= 10) res_pquery(&res, qp->q_msg, qp->q_msglen, log_get_stream(packet_channel)); #endif key = qp->q_keys[0]; if (key == NULL) key = qp->q_keys[0] = tsig_key_from_addr(nsa->sin_addr); if (key != NULL || !qp->q_addr[0].noedns) { smsgsize = qp->q_msglen + TSIG_BUF_SIZE + 11; smsg = memget(smsgsize); smsglen = qp->q_msglen; siglen = sizeof(sig); memcpy(smsg, qp->q_msg, qp->q_msglen); } if (!qp->q_addr[0].noedns) smsglen += ns_add_opt(smsg, smsg + smsglen, smsgsize, 0, 0, - EDNS_MESSAGE_SZ, 0, NULL, 0); + server_options->edns_udp_size, + 0, NULL, 0); if (key != NULL) { n = ns_sign(smsg, &smsglen, smsgsize, NOERROR, key, NULL, 0, sig, &siglen, 0); if (n == 0) { has_tsig = 1; free_tsig(qp->q_nstsig); qp->q_nstsig = new_tsig(key, sig, siglen); } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; INSIST(0); } } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; } if (smsg != NULL) { oldqbuf = qp->q_msg; oldqlen = qp->q_msglen; qp->q_msglen = smsglen; qp->q_msg = smsg; } if (qp->q_flags & Q_USEVC) { if (tcp_send(qp) != NOERROR) { if (!haveComplained(ina_ulong(nsa->sin_addr), (u_long)tcpsendStr)) ns_info(ns_log_default, "ns_resp: tcp_send(%s) failed: %s", sin_ntoa(*nsa), strerror(errno)); } } else if (sendto(ds, (char*)qp->q_msg, qp->q_msglen, 0, (struct sockaddr *)nsa, sizeof(struct sockaddr_in)) < 0) { sendto_errno = errno; if (!haveComplained(ina_ulong(nsa->sin_addr), (u_long)sendtoStr)) ns_info(ns_log_default, "ns_resp: sendto(%s): %s", sin_ntoa(*nsa), strerror(errno)); nameserIncr(nsa->sin_addr, nssSendtoErr); } if (smsgsize != 0) { memput(smsg, smsgsize); qp->q_msg = oldqbuf; qp->q_msglen = oldqlen; } hp->rd = 0; /* leave set to 0 for dup detection */ nameserIncr(nsa->sin_addr, nssSentFwdR); nameserIncr(qp->q_from.sin_addr, nssRcvdFwdR); ns_debug(ns_log_default, 3, "resp: Query sent."); free_nsp(nsp); switch (sendto_errno) { case ENETDOWN: case ENETUNREACH: case EHOSTDOWN: case EHOSTUNREACH: unsched(qp); schedretry(qp, (time_t) 0); } return; formerr: if (!haveComplained(ina_ulong(from.sin_addr), (u_long)formerrmsg)) ns_info(ns_log_resp_checks, "Malformed response from %s (%s)", sin_ntoa(from), formerrmsg); fast_retry(qp, from, 0); free_nsp(nsp); return; return_msg: nameserIncr(from.sin_addr, nssRcvdFwdR); nameserIncr(qp->q_from.sin_addr, nssSentFwdR); nameserIncr(qp->q_from.sin_addr, nssSentAns); if (!hp->aa) nameserIncr(qp->q_from.sin_addr, nssSentNaAns); if (hp->rcode == NXDOMAIN) nameserIncr(qp->q_from.sin_addr, nssSentNXD); /* The "standard" return code */ hp->qr = 1; hp->id = qp->q_id; hp->rd = 1; hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); (void) send_msg(msg, msglen, qp); qremove(qp); free_nsp(nsp); return; return_newmsg: nameserIncr(qp->q_from.sin_addr, nssSentAns); if (!hp->aa) nameserIncr(qp->q_from.sin_addr, nssSentNaAns); if (hp->rcode == NXDOMAIN) nameserIncr(qp->q_from.sin_addr, nssSentNXD); n = doaddinfo(hp, cp, buflen); cp += n; buflen -= n; hp->qr = 1; hp->id = qp->q_id; hp->rd = 1; hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); (void) send_msg(newmsg, cp - newmsg, qp); qremove(qp); free_nsp(nsp); return; refused: hp = (HEADER *)(qp->q_cmsglen ? qp->q_cmsg : qp->q_msg); hp->rcode = REFUSED; hp->qr = 1; hp->id = qp->q_id; hp->rd = 1; hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); (void) send_msg((u_char *)hp, (qp->q_cmsglen ? qp->q_cmsglen : qp->q_msglen), qp); qremove(qp); free_nsp(nsp); return; servfail: nameserIncr(qp->q_from.sin_addr, nssSentFail); hp = (HEADER *)(qp->q_cmsglen ? qp->q_cmsg : qp->q_msg); hp->rcode = SERVFAIL; hp->qr = 1; hp->id = qp->q_id; hp->rd = 1; hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); (void) send_msg((u_char *)hp, (qp->q_cmsglen ? qp->q_cmsglen : qp->q_msglen), qp); qremove(qp); free_nsp(nsp); return; timeout: if (qp->q_stream) sq_remove(qp->q_stream); qremove(qp); free_nsp(nsp); return; } #define BOUNDS_CHECK(ptr, count) \ do { \ if ((ptr) + (count) > eom) { \ hp->rcode = FORMERR; \ return (-1); \ } \ } while (0) static int rrextract(u_char *msg, int msglen, u_char *rrp, struct databuf **dpp, char *dname, int namelen, struct sockaddr_in from, char **tnamep) { u_char *cp, *eom, *rdatap; u_int class, type, dlen; int n, n1, n2; u_int32_t ttl; u_char *cp1, data[MAXDATA*2]; HEADER *hp = (HEADER *)msg; enum context context; if (tnamep != NULL) *tnamep = NULL; *dpp = NULL; cp = rrp; eom = msg + msglen; if ((n = dn_expand(msg, eom, cp, dname, namelen)) < 0) { hp->rcode = FORMERR; return (-1); } cp += n; BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ); GETSHORT(type, cp); GETSHORT(class, cp); if (type != ns_t_opt && class > CLASS_MAX) { ns_debug(ns_log_default, 3, "bad class in rrextract"); hp->rcode = FORMERR; return (-1); } GETLONG(ttl, cp); if (ttl > MAXIMUM_TTL) { ns_debug(ns_log_default, 5, "%s: converted TTL > %u to 0", dname, MAXIMUM_TTL); ttl = 0; } GETSHORT(dlen, cp); BOUNDS_CHECK(cp, dlen); rdatap = cp; if (!ns_nameok(NULL, dname, class, NULL, response_trans, ns_ownercontext(type, response_trans), dname, from.sin_addr)) { hp->rcode = REFUSED; return (-1); } ns_debug(ns_log_default, 3, "rrextract: dname %s type %d class %d ttl %d", dname, type, class, ttl); /* * Convert the resource record data into the internal * database format. * * On entry to the switch: * CP points to the RDATA section of the wire-format RR. * DLEN is its length. * The memory area at DATA is available for processing. * * On exit from the switch: * CP has been incremented past the RR. * CP1 points to the RDATA section of the database-format RR. * N contains the length of the RDATA section of the dbase-format RR. * * The new data at CP1 for length N will be copied into the database, * so it need not be in any particular storage location. */ switch (type) { case T_A: if (dlen != INT32SZ) { hp->rcode = FORMERR; return (-1); } /*FALLTHROUGH*/ case T_WKS: case T_HINFO: case T_TXT: case T_X25: case T_ISDN: case T_NSAP: case T_AAAA: case T_LOC: case T_KEY: case ns_t_cert: case ns_t_opt: cp1 = cp; n = dlen; cp += n; break; case T_CNAME: case T_MB: case T_MG: case T_MR: case T_NS: case T_PTR: n = dn_expand(msg, eom, cp, (char *)data, sizeof data); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)data, class, NULL, response_trans, type == T_PTR ?ns_ptrcontext(dname) :domain_ctx, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 = data; n = strlen((char *)data) + 1; if (tnamep != NULL && (type == T_NS || type == T_MB)) *tnamep = savestr((char *)cp1, 1); break; case T_SOA: context = hostname_ctx; goto soa_rp_minfo; case T_RP: case T_MINFO: context = mailname_ctx; /* FALLTHROUGH */ soa_rp_minfo: n = dn_expand(msg, eom, cp, (char *)data, sizeof data); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)data, class, NULL, response_trans, context, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; /* * The next use of 'cp' is dn_expand(), so we don't have * to BOUNDS_CHECK() here. */ cp1 = data + (n = strlen((char *)data) + 1); n1 = sizeof(data) - n; if (type == T_SOA) n1 -= 5 * INT32SZ; n = dn_expand(msg, eom, cp, (char *)cp1, n1); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (type == T_RP) context = domain_ctx; else context = mailname_ctx; if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, context, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 += strlen((char *)cp1) + 1; if (type == T_SOA) { n = 5 * INT32SZ; BOUNDS_CHECK(cp, n); memcpy(cp1, cp, n); cp += n; cp1 += n; } n = cp1 - data; cp1 = data; if (tnamep != NULL && type == T_SOA) *tnamep = savestr((char *)cp1, 1); break; case T_NAPTR: /* Grab weight and port. */ BOUNDS_CHECK(cp, INT16SZ*2); memcpy(data, cp, INT16SZ*2); cp1 = data + INT16SZ*2; cp += INT16SZ*2; /* Flags */ BOUNDS_CHECK(cp, 1); n = *cp++; BOUNDS_CHECK(cp, n); *cp1++ = n; memcpy(cp1, cp, n); cp += n; cp1 += n; /* Service */ BOUNDS_CHECK(cp, 1); n = *cp++; BOUNDS_CHECK(cp, n); *cp1++ = n; memcpy(cp1, cp, n); cp += n; cp1 += n; /* Regexp */ BOUNDS_CHECK(cp, 1); n = *cp++; BOUNDS_CHECK(cp, n); *cp1++ = n; memcpy(cp1, cp, n); cp += n; cp1 += n; /* Replacement */ n = dn_expand(msg, eom, cp, (char *)cp1, sizeof data - (cp1 - data)); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, hostname_ctx, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; if (tnamep != NULL && *cp1 != 0) *tnamep = savestr((char *)cp1, 1); /* compute end of data */ cp1 += strlen((char *)cp1) + 1; /* compute size of data */ n = cp1 - data; cp1 = data; break; case T_MX: case T_AFSDB: case T_RT: case T_SRV: /* grab preference */ BOUNDS_CHECK(cp, INT16SZ); memcpy(data, cp, INT16SZ); cp1 = data + INT16SZ; cp += INT16SZ; if (type == T_SRV) { /* Grab weight and port. */ BOUNDS_CHECK(cp, INT16SZ*2); memcpy(cp1, cp, INT16SZ*2); cp1 += INT16SZ*2; cp += INT16SZ*2; } /* get name */ n = dn_expand(msg, eom, cp, (char *)cp1, sizeof data - (cp1 - data)); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, hostname_ctx, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; if (tnamep != NULL) *tnamep = savestr((char *)cp1, 1); /* compute end of data */ cp1 += strlen((char *)cp1) + 1; /* compute size of data */ n = cp1 - data; cp1 = data; break; case T_PX: /* grab preference */ BOUNDS_CHECK(cp, INT16SZ); memcpy(data, cp, INT16SZ); cp1 = data + INT16SZ; cp += INT16SZ; /* get MAP822 name */ n = dn_expand(msg, eom, cp, (char *)cp1, sizeof data - INT16SZ); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, domain_ctx, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; /* * The next use of 'cp' is dn_expand(), so we don't have * to BOUNDS_CHECK() here. */ cp1 += (n = strlen((char *)cp1) + 1); n1 = sizeof(data) - n - INT16SZ; n = dn_expand(msg, eom, cp, (char *)cp1, n1); if (n < 0) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, domain_ctx, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 += strlen((char *)cp1) + 1; n = cp1 - data; cp1 = data; break; case T_SIG: { u_int32_t origTTL, exptime, signtime, timetilexp, now; u_int8_t alg; /* Check signature time, expiration, and adjust TTL. */ /* This code is similar to that in db_load.c. */ /* Skip coveredType, save alg, skip labels */ BOUNDS_CHECK(cp, INT16SZ + 1 + 1 + 3*INT32SZ); cp1 = cp + INT16SZ; alg = *cp1++; cp1++; GETLONG(origTTL, cp1); GETLONG(exptime, cp1); GETLONG(signtime, cp1); now = time(NULL); /* Get current time in GMT/UTC */ /* Don't let bogus name servers increase the signed TTL */ if (ttl > origTTL) { ns_debug(ns_log_default, 3, "shrinking SIG TTL from %lu to origTTL %lu", (unsigned long)ttl, (unsigned long)origTTL); ttl = origTTL; } /* * Check that expire and signature times are internally * consistant. */ if (!SEQ_GT(exptime, signtime) && exptime != signtime) { ns_debug(ns_log_default, 3, "ignoring SIG: signature expires before it was signed"); return ((cp - rrp) + dlen); } /* Don't let bogus signers "sign" in the future. */ if (SEQ_GT(signtime, now)) { ns_debug(ns_log_default, 3, "ignoring SIG: signature date %s is in the future", p_secstodate (signtime)); return ((cp - rrp) + dlen); } /* Ignore received SIG RR's that are already expired. */ if (SEQ_GT(now, exptime)) { ns_debug(ns_log_default, 3, "ignoring SIG: expiration %s is in the past", p_secstodate (exptime)); return ((cp - rrp) + dlen); } /* Lop off the TTL at the expiration time. */ timetilexp = exptime - now; if (timetilexp < ttl) { ns_debug(ns_log_default, 3, "shrinking expiring %s SIG TTL from %d to %d", p_secstodate (exptime), ttl, timetilexp); ttl = timetilexp; } /* The following code is copied from named-xfer.c. */ cp1 = (u_char *)data; /* first just copy over the type_covered, algorithm, */ /* labels, orig ttl, two timestamps, and the footprint */ BOUNDS_CHECK(cp, 18); memcpy(cp1, cp, 18); cp += 18; cp1 += 18; /* then the signer's name */ n = dn_expand(msg, eom, cp, (char *)cp1, (sizeof data) - 18); if (n < 0 || n + NS_SIG_SIGNER > (int)dlen) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 += strlen((char*)cp1)+1; /* finally, we copy over the variable-length signature. Its size is the total data length, minus what we copied. */ n = dlen - (NS_SIG_SIGNER + n); if (n > (int)(sizeof data) - (cp1 - (u_char *)data)) { hp->rcode = FORMERR; return (-1); /* out of room! */ } switch (alg) { case NS_ALG_MD5RSA: if (n < NS_MD5RSA_MIN_SIZE || n > NS_MD5RSA_MAX_SIZE) hp->rcode = FORMERR; break; case NS_ALG_DSA: if (n != NS_DSA_SIG_SIZE) hp->rcode = FORMERR; break; default: break; } if (hp->rcode == FORMERR) return (-1); memcpy(cp1, cp, n); cp += n; cp1 += n; /* compute size of data */ n = cp1 - (u_char *)data; cp1 = (u_char *)data; break; } case T_NXT: n = dn_expand(msg, eom, cp, (char *)data, sizeof data); /* * By testing if n >= dlen, we are requiring that the type * bitmap be at least one octet. This is reasonable * because we always have to look at the 0 bit to see if * this is a "different format" NXT or not. */ if (n < 0 || n >= (int)dlen) { hp->rcode = FORMERR; return (-1); } if (!ns_nameok(NULL, (char *)data, class, NULL, response_trans, domain_ctx, dname, from.sin_addr)) { hp->rcode = FORMERR; return (-1); } cp += n; n1 = strlen((char *)data) + 1; cp1 = data + n1; /* * We don't need to BOUNDS_CHECK() cp here because we've * previously checked that 'dlen' bytes are in bounds, and * we know that n < dlen. */ n2 = dlen - n; /* * The first bit of the first octet determines the format * of the NXT record. A format for types >= 128 has not * yet been defined, so if bit zero is set, we just copy * what's there because we don't understand it. */ if ((*cp & 0x80) == 0) { /* * Bit zero is not set; this is an ordinary NXT * record. The bitmap must be at least 4 octets * because the NXT bit should be set. It should be * less than or equal to 16 octets because this NXT * format is only defined for types < 128. */ if (n2 < 4 || n2 > 16) { hp->rcode = FORMERR; return (-1); } } if (n2 > (int)(sizeof data - n1)) { hp->rcode = FORMERR; return (-1); } memcpy(cp1, cp, n2); cp += n2; cp1 += n2; /* compute size of data */ n = cp1 - (u_char *)data; cp1 = (u_char *)data; break; default: /* treat as opaque data */ ns_debug(ns_log_default, 3, "unknown type %d", type); cp1 = cp; n = dlen; cp += n; } if (cp > eom) { hp->rcode = FORMERR; return (-1); } if ((u_int)(cp - rdatap) != dlen) { ns_debug(ns_log_default, 3, "encoded rdata length is %u, but actual length was %u", dlen, (u_int)(cp - rdatap)); hp->rcode = FORMERR; return (-1); } if (n > MAXDATA) { ns_debug(ns_log_default, 1, "update type %d: %d bytes is too much data", type, n); hp->rcode = FORMERR; return (-1); } ttl += tt.tv_sec; if (type == ns_t_opt) class = 0; /* Lie. */ *dpp = savedata(class, type, ttl, cp1, n); return (cp - rrp); } int send_msg(u_char *msg, int msglen, struct qinfo *qp) { HEADER *hp = (HEADER *) msg; u_char *oldmsg; int oldlen = 0; int msgsize; int ret; int trunc; int adjust = 0; if (qp->q_flags & Q_SYSTEM) return (1); - trunc = (qp->q_stream != NULL) ? 65535 : qp->q_udpsize; + trunc = (qp->q_stream != NULL) ? NS_MAXMSG : qp->q_udpsize; if (qp->q_tsig != NULL) adjust += qp->q_tsig->tsig_size; if ((qp->q_flags & Q_EDNS) != 0) adjust += 11; if (msglen > trunc - adjust) msglen = trunc_adjust(msg, msglen, trunc - adjust); if (ns_wouldlog(ns_log_default, 1)) { ns_debug(ns_log_default, 1, "send_msg -> %s (%s %d) id=%d", sin_ntoa(qp->q_from), qp->q_stream == NULL ? "UDP" : "TCP", qp->q_stream == NULL ? qp->q_dfd : qp->q_stream->s_rfd, ntohs(qp->q_id)); } #ifdef DEBUG if (ns_wouldlog(ns_log_default, 4)) { struct qinfo *tqp; for (tqp = nsqhead; tqp != NULL; tqp = tqp->q_link) { ns_debug(ns_log_default, 4, "qp %#lx q_id: %d q_nsid: %d q_msglen: %d", (u_long)tqp, tqp->q_id, tqp->q_nsid, tqp->q_msglen); ns_debug(ns_log_default, 4, "\tq_naddr: %d q_curaddr: %d", tqp->q_naddr, tqp->q_curaddr); ns_debug(ns_log_default, 4, "\tq_next: %#lx q_link: %#lx", (u_long)qp->q_next, (u_long)qp->q_link); } } #endif /* DEBUG */ if (adjust != 0) { oldmsg = msg; oldlen = msglen; msgsize = msglen + adjust; msg = memget(msgsize); memcpy(msg, oldmsg, oldlen); } else msgsize = msglen; /* silence compiler */ if ((qp->q_flags & Q_EDNS) != 0) - msglen += ns_add_opt(msg, msg + msglen, msgsize, 0, - hp->rcode, EDNS_MESSAGE_SZ, 0, NULL, 0); + msglen += ns_add_opt(msg, msg + msglen, msgsize, 0, hp->rcode, + server_options->edns_udp_size, + 0, NULL, 0); if (qp->q_tsig != NULL) { u_char sig[TSIG_SIG_SIZE]; int siglen = sizeof(sig); ret = ns_sign(msg, &msglen, msgsize, NOERROR, qp->q_tsig->key, qp->q_tsig->sig, qp->q_tsig->siglen, sig, &siglen, 0); if (ret != 0) { INSIST(0); } } #ifdef DEBUG if (debug >= 6) res_pquery(&res, msg, msglen, log_get_stream(packet_channel)); #endif /* DEBUG */ if (qp->q_stream == NULL) { /* * Don't send FORMERR to certian well known ports. */ if (hp->rcode == FORMERR && drop_port(ntohs(qp->q_from.sin_port))) return (-1); if (sendto(qp->q_dfd, (char*)msg, msglen, 0, (struct sockaddr *)&qp->q_from, sizeof(qp->q_from)) < 0) { if (!haveComplained(ina_ulong(qp->q_from.sin_addr), (u_long)sendtoStr)) #if defined(SPURIOUS_ECONNREFUSED) if (errno != ECONNREFUSED) #endif ns_info(ns_log_default, "send_msg: sendto(%s): %s", sin_ntoa(qp->q_from), strerror(errno)); nameserIncr(qp->q_from.sin_addr, nssSendtoErr); return (1); } } else writestream(qp->q_stream, (u_char*)msg, msglen); if (adjust != 0) memput(msg, oldlen + adjust); return (0); } static int root_server_p(ns_class class) { struct zoneinfo *zp = find_zone("", class); return (zp != NULL && (zp->z_type == z_master || zp->z_type == z_slave)); } void prime_cache(void) { int root = root_server_p(ns_c_in); ns_debug(ns_log_default, 1, "prime_cache: priming = %d, root = %d", priming, root); if (!priming && !root) { struct qinfo *qp = sysquery("", ns_c_in, ns_t_ns, NULL, NULL, 0, ns_port, ns_o_query, 0); if (qp != NULL) { qp->q_flags |= (Q_SYSTEM | Q_PRIMING); priming++; } } needs_prime_cache = 0; } struct qinfo * sysquery(const char *dname, int class, int type, struct in_addr *nss, struct dst_key **keys, int nsc, u_int16_t port, int opcode, int distance) { struct qinfo *qp, *oqp; HEADER *hp; char tmpdomain[MAXDNAME]; struct namebuf *np = NULL; struct databuf *nsp[NSMAX]; struct hashbuf *htp1; struct hashbuf *htp2; struct hashbuf *htp3; struct sockaddr_in *nsa; const char *fname; int n, count; int sendto_errno = 0; u_char *oldqbuf = NULL; int oldqlen = 0, has_tsig; u_char *smsg = NULL; int smsglen, smsgsize = 0, siglen; u_char sig[TSIG_SIG_SIZE]; DST_KEY *key; nsp[0] = NULL; ns_debug(ns_log_default, 3, "sysquery(%s, %d, %d, %p, %p, %d, %d)", dname, class, type, nss, keys, nsc, ntohs(port)); qp = qnew(dname, class, type, (nss != NULL && nsc != 0) ? 0 : 1); qp->q_distance = distance; if (nss != NULL && nsc != 0) np = NULL; else if (!NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) { htp1 = hashtab; htp2 = hashtab; htp3 = fcachetab; if (priming && dname[0] == '\0') { np = NULL; } else if (((np = nlookup(dname, &htp1, &fname, 0)) == NULL) && ((np = nlookup("", &htp2, &fname, 0)) == NULL) && ((np = nlookup("", &htp3, &fname, 0)) == NULL)) { ns_info(ns_log_default, "sysquery: nlookup error on %s?", dname); err1: ns_freeqry(qp); return (NULL); } n = findns(&np, class, nsp, &count, 0); switch (n) { case NXDOMAIN: case SERVFAIL: ns_info(ns_log_default, "sysquery: findns error (%s) on %s?", n == NXDOMAIN ? "NXDOMAIN" : "SERVFAIL", dname); err2: free_nsp(nsp); goto err1; } } /* Build new qinfo struct. */ qp->q_cmsg = qp->q_msg = NULL; qp->q_dfd = ds; if (nss == NULL || nsc == 0) nsfwdadd(qp, NS_ZFWDTAB(qp->q_fzone)); qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2; qp->q_flags |= Q_SYSTEM; getname(np, tmpdomain, sizeof tmpdomain); qp->q_domain = savestr(tmpdomain, 1); if ((qp->q_msg = (u_char *)memget(PACKETSZ)) == NULL) { ns_notice(ns_log_default, "sysquery: memget failed"); goto err2; } qp->q_msgsize = PACKETSZ; n = res_nmkquery(&res, opcode, dname, class, type, NULL, 0, NULL, qp->q_msg, PACKETSZ); if (n < 0) { ns_info(ns_log_default, "sysquery: res_mkquery(%s) failed", dname); goto err2; } qp->q_msglen = n; hp = (HEADER *) qp->q_msg; hp->id = qp->q_nsid = htons(nsid_next()); hp->rd = (qp->q_addr[qp->q_curaddr].forwarder ? 1 : 0); hp->aa = (opcode == NS_NOTIFY_OP); /* First check for an already pending query for this data. */ for (oqp = nsqhead; oqp != NULL; oqp = oqp->q_link) { if ((oqp != qp) && (oqp->q_msglen == qp->q_msglen) && memcmp(oqp->q_msg+2, qp->q_msg + 2, qp->q_msglen - 2) == 0 ) { #ifdef BIND_NOTIFY /* XXX - need fancier test to suppress duplicate * NOTIFYs to the same server (compare nss?) */ if (opcode != NS_NOTIFY_OP) #endif /*BIND_NOTIFY*/ { ns_debug(ns_log_default, 3, "sysquery: duplicate"); goto err2; } } } if (nss != NULL && nsc != 0) { int i; struct qserv *qs; for (i = 0, qs = qp->q_addr; i < nsc; i++, qs++) { qs->ns_addr.sin_family = AF_INET; qs->ns_addr.sin_addr = nss[i]; qs->ns_addr.sin_port = port; if (keys != NULL) qp->q_keys[i] = keys[i]; qs->ns = NULL; qs->nsdata = NULL; qs->stime = tt; qs->forwarder = 0; qs->noedns = 1; /* XXXMPA */ + qs->lame = 0; qs->nretry = 0; } qp->q_naddr = nsc; } else if (!NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) { fetch_a: count = nslookup(nsp, qp, dname, "sysquery"); if (count <= 0) { if (count < 0) { if (n == -1) ns_info(ns_log_default, "sysquery: nslookup reports danger (%s)", dname); goto err2; } else if (np && NAME(*np)[0] == '\0') { /* * It's not too serious if we don't have * the root server addresses if we have to * go through a forwarder anyway. Don't * bother to log it, since prime_cache() * won't do anything about it as currently * implemented. * * XXX - should we skip setting * needs_prime_cache as well? * * XXX - what happens when we implement * selective forwarding? */ if (!NS_OPTION_P(OPTION_FORWARD_ONLY)) ns_warning(ns_log_default, "sysquery: no addrs found for root NS (%s)", dname); if (class == C_IN && !priming) needs_prime_cache = 1; goto err2; } if (np) { free_nsp(nsp); nsp[0] = NULL; np = np_parent(np); n = findns(&np, class, nsp, &count, 0); switch (n) { case NXDOMAIN: /*FALLTHROUGH*/ case SERVFAIL: ns_info(ns_log_default, "sysquery: findns error (%d) on %s?", n, dname); goto err2; } getname(np, tmpdomain, sizeof tmpdomain); if (qp->q_domain != NULL) (void)freestr(qp->q_domain); qp->q_domain = savestr(tmpdomain, 1); goto fetch_a; } goto err2; } } schedretry(qp, retrytime(qp)); qp->q_addr[0].stime = tt; /* XXX - why not every? */ nsa = Q_NEXTADDR(qp, 0); if (ns_wouldlog(ns_log_default, 1)) { ns_debug(ns_log_default, 1, "sysquery: send -> %s dfd=%d nsid=%d id=%d retry=%ld", sin_ntoa(*nsa), qp->q_dfd, ntohs(qp->q_nsid), ntohs(qp->q_id), (long)qp->q_time); } #ifdef DEBUG if (debug >= 10) res_pquery(&res, qp->q_msg, qp->q_msglen, log_get_stream(packet_channel)); #endif key = qp->q_keys[0]; if (key == NULL) key = qp->q_keys[0] = tsig_key_from_addr(nsa->sin_addr); if (key != NULL || !qp->q_addr[0].noedns) { smsgsize = qp->q_msglen + TSIG_BUF_SIZE + 11; smsg = memget(smsgsize); smsglen = qp->q_msglen; siglen = sizeof(sig); memcpy(smsg, qp->q_msg, qp->q_msglen); } if (!qp->q_addr[0].noedns) smsglen += ns_add_opt(smsg, smsg + smsglen, smsgsize, 0, 0, - EDNS_MESSAGE_SZ, 0, NULL, 0); + server_options->edns_udp_size, + 0, NULL, 0); if (key != NULL) { n = ns_sign(smsg, &smsglen, smsgsize, NOERROR, key, NULL, 0, sig, &siglen, 0); if (n == 0) { has_tsig = 1; free_tsig(qp->q_nstsig); qp->q_nstsig = new_tsig(key, sig, siglen); } else { INSIST(0); has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; } } else { has_tsig = 0; free_tsig(qp->q_nstsig); qp->q_nstsig = NULL; } if (smsgsize != 0) { oldqbuf = qp->q_msg; oldqlen = qp->q_msglen; qp->q_msglen = smsglen; qp->q_msg = smsg; } if (sendto(qp->q_dfd, (char*)qp->q_msg, qp->q_msglen, 0, (struct sockaddr *)nsa, sizeof(struct sockaddr_in)) < 0) { sendto_errno = errno; if (!haveComplained(ina_ulong(nsa->sin_addr), (u_long)sendtoStr)) ns_info(ns_log_default, "sysquery: sendto(%s): %s", sin_ntoa(*nsa), strerror(errno)); nameserIncr(nsa->sin_addr, nssSendtoErr); } if (smsgsize != 0) { memput(smsg, smsgsize); qp->q_msg = oldqbuf; qp->q_msglen = oldqlen; } nameserIncr(nsa->sin_addr, nssSentSysQ); free_nsp(nsp); switch (sendto_errno) { case ENETDOWN: case ENETUNREACH: case EHOSTDOWN: case EHOSTUNREACH: unsched(qp); schedretry(qp, (time_t) 0); } return (qp); } /* * Check the list of root servers after receiving a response * to a query for the root servers. */ static int check_root() { struct databuf *dp, *pdp; struct namebuf *np; int count = 0; priming = 0; for (np = hashtab->h_tab[0]; np != NULL; np = np->n_next) if (NAME(*np)[0] == '\0') break; if (np == NULL) { ns_notice(ns_log_default, "check_root: Can't find root!"); return (0); } for (dp = np->n_data; dp != NULL; dp = dp->d_next) if (dp->d_type == T_NS) count++; ns_debug(ns_log_default, 1, "%d root servers", count); if (count < server_options->minroots) { ns_notice(ns_log_default, "check_root: %d root servers after query to root server < min", count); return (0); } pdp = NULL; dp = np->n_data; while (dp != NULL) { if (dp->d_type == T_NS && dp->d_zone == DB_Z_CACHE && dp->d_ttl < (u_int32_t)tt.tv_sec) { ns_debug(ns_log_default, 1, "deleting old root server '%s'", dp->d_data); dp = rm_datum(dp, np, pdp, NULL); /* SHOULD DELETE FROM HINTS ALSO */ continue; } pdp = dp; dp = dp->d_next; } if (check_ns()) return (1); else { priming = 1; return (0); } } /* * Check the root to make sure that for each NS record we have a A RR */ static int check_ns() { struct databuf *dp, *tdp; struct namebuf *np, *tnp; struct hashbuf *htp; char *dname; int found_arr; const char *fname; time_t curtime; int servers = 0, rrsets = 0; ns_debug(ns_log_default, 2, "check_ns()"); curtime = (u_int32_t) tt.tv_sec; for (np = hashtab->h_tab[0]; np != NULL; np = np->n_next) { if (NAME(*np)[0] != '\0') continue; for (dp = np->n_data; dp != NULL; dp = dp->d_next) { int cnames = 0; if (dp->d_rcode) continue; if (dp->d_type != T_NS) continue; servers++; /* look for A records */ dname = (caddr_t) dp->d_data; htp = hashtab; tnp = nlookup(dname, &htp, &fname, 0); if (tnp == NULL || fname != dname) { ns_debug(ns_log_default, 3, "check_ns: %s: not found %s %#lx", dname, fname, (u_long)tnp); sysquery(dname, dp->d_class, T_A, NULL, NULL, 0, ns_port, QUERY, 0); continue; } /* look for name server addresses */ found_arr = 0; (void)delete_stale(tnp); for (tdp = tnp->n_data; tdp != NULL; tdp = tdp->d_next) { if (tdp->d_rcode) continue; if (tdp->d_type == T_CNAME) cnames++; if (tdp->d_type != T_A || tdp->d_class != dp->d_class) continue; if ((tdp->d_zone == DB_Z_CACHE) && (tdp->d_ttl < (u_int32_t)curtime)) { ns_debug(ns_log_default, 3, "check_ns: stale entry '%s'", NAME(*tnp)); found_arr = 0; break; } found_arr++; } if (found_arr) rrsets++; else if (cnames > 0) ns_info(ns_log_default, "Root NS %s -> CNAME %s", NAME(*np), NAME(*tnp)); else sysquery(dname, dp->d_class, T_A, NULL, NULL, 0, ns_port, QUERY, 0); } } ns_debug(ns_log_default, 2, "check_ns: %d %d", servers, rrsets); return ((servers <= 2) ? (rrsets == servers) : ((rrsets * 2) >= servers) ); } /* int findns(npp, class, nsp, countp, flag) * Find NS's or an SOA * npp, class: * dname whose most enclosing NS is wanted * nsp, countp: * result array and count; array will also be NULL terminated * flag: * boolean: we're being called from ADDAUTH, bypass authority checks * return value: * NXDOMAIN: we are authoritative for this {dname,class} * *countp is bogus, but nsp[] has a single SOA returned in it. * SERVFAIL: we are auth but zone isn't loaded; or, no root servers found * *countp and nsp[] are bogus. * OK: we are not authoritative, and here are the NS records we found. * *countp and nsp[] return NS records of interest. */ int findns(struct namebuf **npp, int class, struct databuf **nsp, int *countp, int flag) { struct namebuf *np = *npp; struct databuf *dp; struct databuf **nspp; struct hashbuf *htp; nsp[0] = NULL; if (priming && (np == NULL || NAME(*np)[0] == '\0')) htp = fcachetab; else htp = hashtab; try_again: if (htp == fcachetab && class == C_IN && !priming) /* * XXX - do we want to set needs_prime_cache if * OPTION_FORWARD_ONLY? */ needs_prime_cache = 1; if (np == NULL) { /* find the root */ for (np = htp->h_tab[0]; np != NULL; np = np->n_next) if (NAME(*np)[0] == '\0') break; } while (np != NULL) { ns_debug(ns_log_default, 5, "findns: np %p '%s'", np, NAME(*np)); /* Look first for SOA records. */ #ifdef ADDAUTH if (!flag) #endif for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (dp->d_zone != DB_Z_CACHE && ((zones[dp->d_zone].z_type == Z_PRIMARY) || (zones[dp->d_zone].z_type == Z_SECONDARY)) && match(dp, class, T_SOA) && dp->d_type == T_SOA) { ns_debug(ns_log_default, 3, "findns: SOA found"); if (zones[dp->d_zone].z_flags & Z_AUTH) { *npp = np; nsp[0] = dp; nsp[1] = NULL; DRCNTINC(dp); return (NXDOMAIN); } else { /* XXX: zone isn't loaded but we're * primary or slave for it. * should we fwd this? */ return (SERVFAIL); } } } /* If no SOA records, look for NS records. */ nspp = &nsp[0]; *nspp = NULL; (void)delete_stale(np); for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!match(dp, class, T_NS)) continue; if (dp->d_rcode) continue; /* * Don't use records that may become invalid to * reference later when we do the rtt computation. * Never delete our safety-belt information! * * XXX: this is horribly bogus. */ if ((dp->d_zone == DB_Z_CACHE) && (dp->d_ttl < (u_int32_t)tt.tv_sec) && !(dp->d_flags & DB_F_HINT)) { ns_debug(ns_log_default, 1, "findns: stale entry '%s'", NAME(*np)); /* * We may have already added NS databufs * and are going to throw them away. Fix * reference counts. We don't need to free * them here as we just got them from the * cache. */ while (nspp > &nsp[0]) db_detach(--nspp); nsp[0] = NULL; goto try_parent; } if (nspp < &nsp[NSMAX-1]) { *nspp++ = dp; DRCNTINC(dp); } } *countp = nspp - nsp; if (*countp > 0) { ns_debug(ns_log_default, 3, "findns: %d NS's added for '%s'", *countp, NAME(*np)); *nspp = NULL; *npp = np; return (OK); /* Success, got some NS's */ } try_parent: np = np_parent(np); } if (htp == hashtab) { htp = fcachetab; goto try_again; } ns_debug(ns_log_default, 1, "findns: No root nameservers for class %s?", p_class(class)); - if ((unsigned)class < MAXCLASS && norootlogged[class] == 0) { + if (!NS_OPTION_P(OPTION_FORWARD_ONLY) && + (unsigned)class < MAXCLASS && norootlogged[class] == 0) { norootlogged[class] = 1; ns_info(ns_log_default, "No root nameservers for class %s", p_class(class)); } return (SERVFAIL); } /* * Extract RR's from the given node that match class and type. * Return number of bytes added to response. * If no matching data is found, then 0 is returned. */ int finddata(struct namebuf *np, int class, int type, - HEADER *hp, char **dnamep, int *lenp, int *countp) + HEADER *hp, char **dnamep, int *lenp, int *countp, int pass, + int glueok) { struct databuf *dp; char *cp; int buflen, n, count = 0; char *new_dnamep = NULL; int defer = 0, found_count = 0, choice, i; struct databuf **found = NULL; struct databuf **tmpfound = NULL; int foundcname; int stalecount; int ret = 0; stalecount = delete_stale(np); /* We don't want to return cached SIG records when asked for SIGs, * since we may have an incomplete set. */ if (type == T_SIG && findMyZone(np, class) == DB_Z_CACHE) return(0); if (type != T_ANY && type != T_PTR && type != T_NXT) { found = memget((stalecount + 1) * sizeof *found); tmpfound = memget((stalecount + 1) * sizeof *tmpfound); if (found == NULL || tmpfound == NULL) ns_panic(ns_log_default, 1, "finddata: out of memory"); defer = 1; } buflen = *lenp; #ifdef DEBUG if (buflen > PACKETSZ) ns_debug(ns_log_default, 1, "finddata(): buflen=%d", buflen); #endif cp = ((char *)hp) + *countp; foundcname = 0; for (dp = np->n_data; dp != NULL; dp = dp->d_next) { - if (!wanted(dp, class, type)) { - if (type == T_CNAME && class == dp->d_class) { - /* any data means no CNAME exists */ - if (dp->d_type != T_NXT && - dp->d_type != T_KEY && - dp->d_type != T_SIG) { - ret = 0; - goto done; - } - } + if (!wanted(dp, class, type)) continue; - } if (dp->d_cred == DB_C_ADDITIONAL) { #ifdef NOADDITIONAL continue; #else /* we want to expire additional data very * quickly. current strategy is to cut 5% * off each time it is accessed. this makes * stale(dp) true earlier when this datum is * used often. */ dp->d_ttl = tt.tv_sec + 0.95 * (int) (dp->d_ttl - tt.tv_sec); #endif } /* -ve $ing stuff, anant@isi.edu * if we have a -ve $ed record, change the rcode on the * header to reflect that */ if (dp->d_rcode == NOERROR_NODATA) { if (count != 0) { /* * This should not happen, yet it does... */ ns_info(ns_log_default, "NODATA & data for \"%s\" type %d class %d", *dnamep, type, class); continue; } - if (type == T_ANY) + if (type == T_ANY && dp->d_type != T_ANY) continue; hp->rcode = NOERROR_NODATA; if (dp->d_size == 0) { /* !RETURNSOA */ ret = 1; goto done; } } if (dp->d_rcode == NXDOMAIN) { if (count != 0) { /* * This should not happen, yet it might... */ ns_info(ns_log_default, "NXDOMAIN & data for \"%s\" type %d class %d", *dnamep, type, class); continue; } hp->rcode = NXDOMAIN; if (dp->d_size == 0) { /* !RETURNSOA */ ret = 1; goto done; } } #ifdef HITCOUNTS ++dp->d_hitcnt; ++db_total_hits; #endif /* HITCOUNTS */ /* Don't put anything but key or sig RR's in response to requests for key or sig */ if (((type == T_SIG) || (type == T_KEY)) && (!((dp->d_type == T_SIG) || (dp->d_type == T_KEY))) ) continue; + /* Don't return glue (NS/A/AAAA) */ + if (!glueok && findMyZone(np, class) == DB_Z_CACHE) + continue; + if (!defer) { if (foundcname != 0 && dp->d_type == T_CNAME) continue; if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; if (dp->d_type == T_CNAME) { foundcname = 1; + +#define SETAA(pass, class, dp) \ + (pass == 1 && class != C_ANY && dp->d_zone != DB_Z_CACHE && \ + (zones[dp->d_zone].z_type == z_master || \ + zones[dp->d_zone].z_type == z_slave) && \ + (zones[dp->d_zone].z_flags & Z_AUTH) != 0) + + if (SETAA(pass, class, dp)) + hp->aa = 1; + #define FOLLOWCNAME(type) \ (type != T_KEY) && (type != T_SIG) && (type != T_NXT) && (type != T_ANY) /* don't alias if querying for key, sig, nxt, or any */ if (FOLLOWCNAME(type)) new_dnamep = (char *)dp->d_data; } } else { - if (dp->d_type == T_CNAME) + if (dp->d_type == T_CNAME) { foundcname = 1; + + if (SETAA(pass, class, dp)) + hp->aa = 1; + + } found[found_count++] = dp; } } if (found_count == 0 && count == 0) { ret = 0; goto done; } /* * If the query type was SIG or ANY we will have returned the SIG * records already. */ if (type != T_SIG && type != T_ANY) { for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!wantedsig(dp, class, type)) continue; if (dp->d_cred == DB_C_ADDITIONAL) { #ifdef NOADDITIONAL continue; #else /* we want to expire additional data very * quickly. current strategy is to cut 5% * off each time it is accessed. this makes * stale(dp) true earlier when this datum is * used often. */ dp->d_ttl = tt.tv_sec + 0.95 * (int) (dp->d_ttl - tt.tv_sec); #endif } if (!defer) { if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; } else found[found_count++] = dp; } } if (defer && found_count > 0) { int first_sig; int non_sig_count; int sig_count; /* number of SIG records in found */ int idx, jdx; enum ordering order; order = match_order(np, class, foundcname ? T_CNAME : type); /* * shuffle the SIG records down to the bottom of the array * as we need to make sure they get packed last, no matter * what the ordering is. We're sure to maintain the * original ordering within the two sets of records (so * that fixed_order can work). * First we pack the non-SIG records into the temp array. */ for (idx = jdx = 0 ; idx < found_count ; idx++) { if (found[idx]->d_type != T_SIG) { tmpfound[jdx++] = found[idx]; } } non_sig_count = jdx; sig_count = found_count - jdx; first_sig = jdx ; /* * now shift the SIG records down to the end of the array * and copy in the non-SIG records */ for (i = idx = found_count - 1 ; i >= 0 ; idx--) { if (i < non_sig_count) { found[i] = tmpfound[i]; i--; } else if (found[idx]->d_type == T_SIG) { found[i--] = found[idx] ; } } foundcname = 0; switch (order) { case fixed_order: for (i = 0; i < found_count; i++) { dp = found[i]; if (foundcname != 0 && dp->d_type == T_CNAME) continue; if (dp->d_type == T_CNAME) { foundcname = 1; if (FOLLOWCNAME(type)) { new_dnamep = (char *)dp->d_data; } } if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; } break; case random_order: { /* first we shuffle the non-SIG records */ int iters = non_sig_count; for (i = 0; i < iters; i++) { choice = ((u_int)rand()>>3) % non_sig_count; non_sig_count--; dp = found[choice]; found[choice] = found[non_sig_count]; if (foundcname != 0 && dp->d_type == T_CNAME) continue; if (dp->d_type == T_CNAME) { foundcname = 1; if (FOLLOWCNAME(type)) { new_dnamep = (char *)dp->d_data; } } if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; } /* now shuffle the SIG records */ iters = sig_count; for (i = 0; i < iters; i++) { choice = ((u_int)rand()>>3) % sig_count; choice += first_sig; sig_count--; dp = found[choice]; found[choice] = found[sig_count + first_sig]; if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; } break; } case cyclic_order: /* first we do the non-SIG records */ if (non_sig_count > 0) choice = ((u_int)rand()>>3) % non_sig_count; else choice = 0; for (i = 0; i < non_sig_count ; i++) { dp = found[(i + choice) % non_sig_count]; if (foundcname != 0 && dp->d_type == T_CNAME) continue; if (dp->d_type == T_CNAME) { foundcname = 1; if (FOLLOWCNAME(type)) { new_dnamep = (char *)dp->d_data; } } if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; } /* now do the SIG record rotation. */ if (sig_count > 0) { choice = ((u_int)rand()>>3) % sig_count; choice += first_sig; i = choice; do { dp = found[i]; if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) { hp->tc = 1; ret = *lenp - buflen; goto done; } if (dp->d_secure != DB_S_SECURE) hp->ad = 0; cp += n; buflen -= n; count++; i++; if (i >= found_count) i = first_sig; } while (i != choice); } break; default: ns_warning(ns_log_default, "finddata: unknown ordering: %d", order); break; } } if (new_dnamep != NULL) *dnamep = new_dnamep; ns_debug(ns_log_default, 3, "finddata: added %d class %d type %d RRs", count, class, type); ret = *lenp - buflen; done: if (found != NULL) memput(found, (stalecount + 1) * sizeof *found); if (tmpfound != NULL) memput(tmpfound, (stalecount + 1) * sizeof *tmpfound); *countp = count; return (ret); } /* * Do we want this data record based on the class and type? */ static int wanted(const struct databuf *dp, int class, int type) { const u_char *cp; int coveredType; time_t expiration; #ifdef DEBUG char pclass[15], ptype[15]; #endif #ifdef DEBUG strcpy(pclass, p_class(class)); strcpy(ptype, p_type(type)); ns_debug(ns_log_default, 3, "wanted(%p, %s %s) [%s %s]", dp, pclass, ptype, p_class(dp->d_class), p_type(dp->d_type)); #endif if (dp->d_class != class && class != C_ANY) return (0); /* * Must check SIG for expiration below, other matches * return OK here. */ if (type == dp->d_type && (type != T_SIG)) return (1); /* For a T_ANY query, we do not want to return -ve $ed RRs. */ if (type == T_ANY && dp->d_rcode == NOERROR_NODATA) return (0); /* First, look at the type of RR. */ switch (dp->d_type) { /* Cases to deal with: T_ANY search, return all unexpired SIGs. T_SIG search, return all unexpired SIGs. T_ search, return all unexp SIG s. */ case T_SIG: cp = dp->d_data; GETSHORT(coveredType, cp); cp += INT16SZ + INT32SZ; /* skip alg, labels, & orig TTL */ GETLONG(expiration,cp); if (type == T_ANY || type == T_SIG) { if (expiration > time(0)) return (1); /* Unexpired matching SIG */ } return (0); /* We don't return this SIG. */ case T_ANY: return (1); case T_CNAME: if (dp->d_rcode != NOERROR_NODATA) return (1); else break; } /* OK, now look at the type of query. */ if (type == ns_t_any) return (1); else if (type == ns_t_mailb) switch (dp->d_type) { case T_MR: case T_MB: case T_MG: case T_MINFO: return (1); } else if (ns_t_xfr_p(type)) { /* * This is used to validate transfer requests, not * generate transfer responses. Is there an SOA? */ if (dp->d_type == ns_t_soa && dp->d_zone != DB_Z_CACHE && (zones[dp->d_zone].z_flags & Z_AUTH)) return (1); } return (0); } static int wantedsig(const struct databuf *dp, int class, int type) { const u_char *cp; int coveredType; time_t expiration; #ifdef DEBUG char pclass[15], ptype[15]; #endif #ifdef DEBUG strcpy(pclass, p_class(class)); strcpy(ptype, p_type(type)); ns_debug(ns_log_default, 3, "wantedtsig(%p, %s %s) [%s %s]", dp, pclass, ptype, p_class(dp->d_class), p_type(dp->d_type)); #endif if (dp->d_class != class && class != C_ANY) return (0); if (dp->d_type != T_SIG || dp->d_rcode != 0) return (0); cp = dp->d_data; GETSHORT(coveredType, cp); cp += INT16SZ + INT32SZ; /* skip alg, labels, & orig TTL */ GETLONG(expiration,cp); if (expiration < time(0)) return (0); if (type == T_ANY || type == T_SIG || type == coveredType) return (1); if (type == ns_t_mailb) { switch (coveredType) { case T_MR: case T_MB: case T_MG: case T_MINFO: return (1); } } return (0); } /* * Add RR entries from dpp array to a query/response. * Return the number of bytes added or negative the amount * added if truncation occured. Typically you are * adding NS records to a response. */ int add_data(struct namebuf *np, struct databuf **dpp, u_char *cp, int buflen, int *countp) { struct databuf *dp; char dname[MAXDNAME]; int n, bytes; bytes = *countp = 0; getname(np, dname, sizeof(dname)); for (dp = *dpp++; dp != NULL; dp = *dpp++) { if (stale(dp)) continue; /* ignore old cache entry */ if (dp->d_rcode) continue; if ((n = make_rr(dname, dp, cp, buflen, 1, dnptrs, dnptrs_end, 0)) < 0) return (-bytes); /* Truncation */ cp += n; buflen -= n; bytes += n; (*countp)++; } return (bytes); } static void rrsetadd(struct flush_set *flushset, const char *name, struct databuf *dp) { struct flush_set *fs = flushset; struct db_list *dbl; while (fs->fs_name && ( ns_samename(fs->fs_name,name) != 1 || (fs->fs_class != dp->d_class) || (fs->fs_type != dp->d_type) || (fs->fs_cred != dp->d_cred))) { fs++; } if (!fs->fs_name) { fs->fs_name = savestr(name, 1); fs->fs_class = dp->d_class; fs->fs_type = dp->d_type; fs->fs_cred = dp->d_cred; fs->fs_list = NULL; fs->fs_last = NULL; } dbl = (struct db_list *)memget(sizeof(struct db_list)); if (!dbl) panic("rrsetadd: out of memory", NULL); dbl->db_next = NULL; dbl->db_dp = dp; DRCNTINC(dbl->db_dp); if (fs->fs_last == NULL) fs->fs_list = dbl; else fs->fs_last->db_next = dbl; fs->fs_last = dbl; } static int ttlcheck(const char *name, struct db_list *dbl, int update) { int type = dbl->db_dp->d_type; int class = dbl->db_dp->d_class; struct hashbuf *htp = hashtab; const char *fname; struct namebuf *np; struct db_list *dbp = dbl; struct databuf *dp; u_int32_t ttl = 0; /* Make gcc happy. */ int first; np = nlookup(name, &htp, &fname, 0); if (np == NULL || fname != name || ns_wildcard(NAME(*np))) return (1); /* check that all the ttl's we have are the same, if not return 1 */ first = 1; for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!match(dp, class, type)) continue; if (first) { /* we can't update zone data so return early */ if (dp->d_zone != DB_Z_CACHE) return (0); ttl = dp->d_ttl; first = 0; } else if (ttl != dp->d_ttl) return (1); } /* there are no records of this type in the cache */ if (first) return(1); /* * the ttls of all records we have in the cache are the same * if the ttls differ in the new set we don't want it. */ /* check that all the ttl's we have are the same, if not return 0 */ first = 1; while (dbp) { if (first) { ttl = dbp->db_dp->d_ttl; first = 0; } else if (ttl != dbp->db_dp->d_ttl) { return(0); } dbp = dbp->db_next; } /* update ttl if required */ if (update) { for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!match(dp, class, type)) continue; if (dp->d_ttl > ttl) break; dp->d_ttl = ttl; fixttl(dp); } } return(1); } /* * lookup rrset in table and compare to dbl * tri state result * -1: lookup failed * 0: rrsets same * 1: rrsets differ */ static int rrsetcmp(char * name, struct db_list * dbl, struct hashbuf * table) { int type = dbl->db_dp->d_type; int class = dbl->db_dp->d_class; struct hashbuf *htp = table; const char *fname; struct namebuf *np; struct db_list *dbp = dbl; struct databuf *dp; int exists = 0; np = nlookup(name, &htp, &fname, 0); if (np == NULL || fname != name || ns_wildcard(NAME(*np))) { ns_debug(ns_log_default, 3, "rrsetcmp: name not in database"); return (-1); } /* check that all entries in dbl are in the cache */ while (dbp) { for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!match(dp, class, type)) continue; exists = 1; if (!db_cmp(dp, dbp->db_dp) #ifdef NOADDITIONAL && ((dp->d_cred == dbp->db_dp->d_cred) || (dp->d_cred != DB_C_ADDITIONAL)) #endif ) break; } if (!dp) { ns_debug(ns_log_default, 3, "rrsetcmp: %srecord%s in database", exists ? "" : "no ", exists ? " not" : "s"); return (exists ? 1 : -1); } dbp = dbp->db_next; } /* Check that all cache entries are in the list. */ for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!match(dp, class, type)) continue; #ifdef NCACHE if (dp->d_rcode) return (1); #endif dbp = dbl; while (dbp) { if (!db_cmp(dp, dbp->db_dp)) break; dbp = dbp->db_next; } if (!dbp) { ns_debug(ns_log_default, 3, "rrsetcmp: record not in rrset"); return (1); } } ns_debug(ns_log_default, 3, "rrsetcmp: rrsets matched"); return (0); } /* * verify incoming answer against what we already have in the hints * issue warnings / errors if differences detected. */ static void check_hints(struct flush_set * flushset) { struct zoneinfo *zp; struct flush_set *fs; struct db_list *dbp; /* We don't use hints when in forward only mode */ if (NS_OPTION_P(OPTION_FORWARD_ONLY)) return; /* find "." NS rrset and hence class */ for (fs = flushset; fs->fs_name != NULL; fs++) { if ((fs->fs_name[0] != '\0') || (fs->fs_type != ns_t_ns)) continue; /* see if we are a root server */ zp = find_zone(fs->fs_name, fs->fs_class); if (zp != NULL && (zp->z_type == z_master || zp->z_type == z_slave)) return; switch (rrsetcmp(fs->fs_name, fs->fs_list, fcachetab)) { case -1: ns_error(ns_log_default, "check_hints: no NS records for class %d in hints", fs->fs_class); break; case 1: ns_warning(ns_log_default, "check_hints: root NS list in hints for class %d does not match root NS list", fs->fs_class); break; case 0: break; default: ns_error(ns_log_default, "check_hints: unexpected response from rrsetcmp"); break; } break; } if (fs->fs_name == NULL) /* no root NS records */ return; dbp = fs->fs_list; while (dbp) { /* for each NS find A rrset in answer and check */ for (fs = flushset; fs->fs_name != NULL; fs++) { if (ns_samename(fs->fs_name, (char *)dbp->db_dp->d_data) != 1 || fs->fs_type != ns_t_a) continue; switch (rrsetcmp(fs->fs_name, fs->fs_list, fcachetab)) { case -1: ns_error(ns_log_default, "check_hints: no A records for %s class %d in hints", fs->fs_name[0] ? fs->fs_name : ".", fs->fs_class); break; case 1: ns_warning(ns_log_default, "check_hints: A records for %s class %d do not match hint records", fs->fs_name[0] ? fs->fs_name : ".", fs->fs_class); break; case 0: break; default: ns_error(ns_log_default, "check_hints: unexpected response from rrsetcmp"); break; } break; } if (fs->fs_name == NULL) ns_debug(ns_log_default, 2, "check_hints: no A records for %s", dbp->db_dp->d_data); dbp = dbp->db_next; } } static void rrsetupdate(struct flush_set * flushset, int flags, struct sockaddr_in from, int updatettl) { struct flush_set *fs = flushset; struct db_list *dbp, *odbp; int n; void *state = NULL; while (fs->fs_name) { ns_debug(ns_log_default, 2, "rrsetupdate: %s", fs->fs_name[0] ? fs->fs_name : "."); if ((n = rrsetcmp(fs->fs_name, fs->fs_list, hashtab)) && ttlcheck(fs->fs_name, fs->fs_list, 0)) { if (n > 0) flushrrset(fs, from); dbp = fs->fs_list; while (dbp) { n = db_set_update(fs->fs_name, dbp->db_dp, &state, flags, &hashtab, from, NULL, 0, NULL); ns_debug(ns_log_default, 3, "rrsetupdate: %s %d", fs->fs_name[0] ? fs->fs_name : ".", n); odbp = dbp; dbp = dbp->db_next; db_detach(&odbp->db_dp); memput(odbp, sizeof *odbp); } ns_debug(ns_log_default, 3, "rrsetupdate: %s %d", fs->fs_name[0] ? fs->fs_name : ".", n); } else { if ((n == 0) && updatettl) (void)ttlcheck(fs->fs_name,fs->fs_list, 1); dbp = fs->fs_list; while (dbp) { db_detach(&dbp->db_dp); odbp = dbp; dbp = dbp->db_next; memput(odbp, sizeof *odbp); } } fs->fs_list = NULL; fs++; } n = db_set_update(NULL, NULL, &state, flags, &hashtab, from, NULL, 0, NULL); } static void flushrrset(struct flush_set * fs, struct sockaddr_in from) { struct databuf *dp; int n; ns_debug(ns_log_default, 2, "flushrrset(%s, %s, %s, %d)", fs->fs_name[0]?fs->fs_name:".", p_type(fs->fs_type), p_class(fs->fs_class), fs->fs_cred); dp = savedata(fs->fs_class, fs->fs_type, 0, NULL, 0); dp->d_zone = DB_Z_CACHE; dp->d_cred = fs->fs_cred; dp->d_clev = 0; do { n = db_update(fs->fs_name, dp, NULL, NULL, DB_DELETE, hashtab, from); ns_debug(ns_log_default, 3, "flushrrset: %d", n); } while (n == OK); db_detach(&dp); } static void free_flushset(struct flush_set *flushset, int flushset_size) { struct flush_set *fs; struct db_list *dbl; for (fs = flushset; fs->fs_name != NULL; fs++) { fs->fs_name = freestr(fs->fs_name); while ((dbl = fs->fs_list) != NULL) { fs->fs_list = dbl->db_next; dbl->db_next = NULL; db_detach(&dbl->db_dp); memput(dbl, sizeof(*dbl)); } } memput(flushset, flushset_size); } /* * This is best thought of as a "cache invalidate" function. * It is called whenever a piece of data is determined to have * become invalid either through a timeout or a validation * failure. It is better to have no information, than to * have partial information you pass off as complete. */ void delete_all(struct namebuf *np, int class, int type) { struct databuf *dp, *pdp; ns_debug(ns_log_default, 3, "delete_all(%p:\"%s\" %s %s)", np, NAME(*np), p_class(class), p_type(type)); pdp = NULL; dp = np->n_data; while (dp != NULL) { if (dp->d_zone == DB_Z_CACHE && (dp->d_flags & DB_F_HINT) == 0 && match(dp, class, type)) { dp = rm_datum(dp, np, pdp, NULL); continue; } pdp = dp; dp = dp->d_next; } } /* delete_stale(np) * for all RRs associated with this name, check for staleness (& delete) * arguments: * np = pointer to namebuf to be cleaned. * returns: * number of RRs associated with this name. * side effects: * delete_all() can be called, freeing memory and relinking chains. */ int delete_stale(np) struct namebuf *np; { struct databuf *dp; int count; again: count = 0; for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (dp->d_zone == DB_Z_CACHE && stale(dp)) { delete_all(np, dp->d_class, dp->d_type); goto again; } count++; } return (count); } /* * Adjust answer message so that it fits in outlen. Set tc if required. * * If outlen = msglen, can be used to verify qdcount, ancount, nscount * and arcount. * * return new length */ int trunc_adjust(u_char *msg, int msglen, int outlen) { register HEADER *hp; u_int qdcount, ancount, nscount, arcount, dlen; u_char *cp = msg, *cp1, *eom_in, *eom_out; int n; eom_in = msg + msglen; eom_out = msg + outlen; hp = (HEADER *)msg; qdcount = ntohs(hp->qdcount); ancount = ntohs(hp->ancount); nscount = ntohs(hp->nscount); arcount = ntohs(hp->arcount); cp += HFIXEDSZ; while ((qdcount || ancount || nscount || arcount) && cp < eom_in && cp < eom_out) { cp1 = cp; /* use temporary in case we break */ n = dn_skipname(cp1, eom_in); if (n < 0) break; cp1 += n + 2 * INT16SZ; /* type, class */ if (!qdcount) { cp1 += INT32SZ; /* ttl */ if (cp1 + INT16SZ > eom_in) break; GETSHORT(dlen, cp1); cp1 += dlen; } if (cp1 > eom_in || cp1 > eom_out) break; cp = cp1; if (qdcount) qdcount--; else if (ancount) ancount--; else if (nscount) nscount--; else arcount--; } if (qdcount || ancount || nscount || arcount) { ns_debug(ns_log_default, 1, "trunc_adjust:%s %d %d %d %d %d, %d %d %d %d %d", hp->tc?" tc":"", msglen, ntohs(hp->qdcount), ntohs(hp->ancount), ntohs(hp->nscount), ntohs(hp->arcount), cp-msg, qdcount, ancount, nscount, arcount); hp->tc = 1; hp->qdcount = htons(ntohs(hp->qdcount) - qdcount); hp->ancount = htons(ntohs(hp->ancount) - ancount); hp->nscount = htons(ntohs(hp->nscount) - nscount); hp->arcount = htons(ntohs(hp->arcount) - arcount); } ENSURE(cp <= eom_out); return (cp - msg); } /* * mark the server "from" bad in the qp structure so it won't be retried. */ static int mark_noedns(struct qinfo *qp, struct sockaddr_in from, int cache) { int i; for (i = 0; i < (int)qp->q_naddr; i++) if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, from.sin_addr)) { if (qp->q_addr[i].noedns) return (1); if (qp->q_addr[i].nsdata && cache) qp->q_addr[i].nsdata->d_noedns = 1; qp->q_addr[i].noedns = 1; break; } return (0); } static void mark_bad(struct qinfo *qp, struct sockaddr_in from) { int i; for (i = 0; i < (int)qp->q_naddr; i++) if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, from.sin_addr)) qp->q_addr[i].nretry = MAXRETRY; } static void mark_lame(struct qinfo *qp, struct sockaddr_in from) { int i; for (i = 0; i < (int)qp->q_naddr; i++) if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, from.sin_addr) && qp->q_addr[i].ns != NULL) { qp->q_addr[i].ns->d_flags |= DB_F_LAME; db_lame_add(qp->q_domain, (char*)qp->q_addr[i].ns->d_data, tt.tv_sec + server_options->lame_ttl); } } /* * Retry the message if and only if from matches where the query was * last sent to. The code does not handle responses sent from the * wrong interface an a multihomed server. */ static void fast_retry(struct qinfo *qp, struct sockaddr_in from, int samehost) { if (ina_equal(qp->q_addr[qp->q_curaddr].ns_addr.sin_addr, from.sin_addr)) retry(qp, samehost); } static void add_related_additional(char *name) { int i; if (num_related >= MAX_RELATED - 1) return; for (i = 0; i < num_related; i++) if (ns_samename(name, related[i]) == 1) { (void)freestr(name); return; } related[num_related++] = name; } static void free_related_additional() { int i; for (i = 0; i < num_related; i++) related[i] = freestr(related[i]); num_related = 0; } static int related_additional(char *name) { int i; for (i = 0; i < num_related; i++) if (ns_samename(name, related[i]) == 1) return (1); return (0); } static void freestr_maybe(char **tname) { if (tname == NULL || *tname == NULL) return; *tname = freestr(*tname); } /* * Match a request namebuf against the configured rrset-order info. First * match wins. There is an implicit '*.' at the front to the ordering names. */ static enum ordering match_order(const struct namebuf *np, int class, int type) { rrset_order_list orders = server_options->ordering; rrset_order_element roe; if (orders == NULL) return (DEFAULT_ORDERING); for (roe = orders->first ; roe != NULL ; roe = roe->next) { if (roe->class != C_ANY && roe->class != class) continue; if (roe->type != T_ANY && roe->type != type) continue; if (match_name(np, roe->name, strlen(roe->name)) == 0) { return (roe->order); } } /* none matched so use default */ return (DEFAULT_ORDERING); } /* Do a simple compare of the NP data against the given NAME, recursively * looking at the NP parent if necessary. NAMELEN is the length of the NAME * that needs to be matched. Matching happen from right to left. Returns -1 * on failure, on success the index of the first character of the matched * portion of the string is returned. In the first level call a return * value of 0 is of interest. */ static int match_name(const struct namebuf *np, const char *name, size_t namelen) { int matched ; if (name[0] == '*' && name[1] == '\0') return 0; if (np->n_parent != NULL) { /* recurse to end of np list */ matched = match_name(np->n_parent,name,namelen); } else { matched = namelen; } if (matched > 0) { int labellen = NAMELEN(*np); char pch; const char *start; if (labellen > matched) { return -1; } else if (labellen < matched) { /* string is longer than this namebuf's data, so make sure there's a period before the end of the match so we don't just match a suffix. */ start = name + (matched - labellen); pch = start[-1]; if (pch != '.') { return -1; } } else { start = name ; } if (strncasecmp(start, NAME(*np), labellen) == 0) { /* looking good. tell our caller what portion of the tail of string has been matched */ if (start == name) return (0) ; else return (start - name - 1); /* matched '.' too */ } else { return (-1); } } return (matched); } diff --git a/contrib/bind/bin/named/ns_update.c b/contrib/bind/bin/named/ns_update.c index b2b5b9680ded..1c88e251a6fd 100644 --- a/contrib/bind/bin/named/ns_update.c +++ b/contrib/bind/bin/named/ns_update.c @@ -1,3064 +1,3066 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ns_update.c,v 8.104 2002/05/18 01:02:59 marka Exp $"; +static const char rcsid[] = "$Id: ns_update.c,v 8.106 2002/07/19 22:44:07 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1996-2000 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Check Point Software Technologies Incorporated not be used * in advertising or publicity pertaining to distribution of the document * or software without specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES * INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Based on the Dynamic DNS reference implementation by Viraj Bais * */ #include "port_before.h" #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 "port_after.h" #include "named.h" #define WRITEABLE_MASK (S_IWUSR | S_IWGRP | S_IWOTH) /* XXXRTH almost all funcs. in here should be static! map rdata_dump to db_to_textual map rdata_expand to wire_to_db make a textual_to_db and use it in merge_logs? replace all this "map" stuff with the new routines (from 4.9.5 I think) */ /* from ns_req.c */ static struct map m_opcode[] = { { "nxdomain", NXDOMAIN }, { "yxdomain", YXDOMAIN }, { "nxrrset", NXRRSET }, { "yxrrset", YXRRSET }, { "delete", DELETE }, { "add", ADD }, }; #define M_OPCODE_CNT (sizeof(m_opcode) / sizeof(struct map)) /* XXXRTH workaround map difficulties */ #define M_CLASS_CNT m_class_cnt #define M_TYPE_CNT m_type_cnt static const char *opcodes[] = { "delete", "add", "", "nxdomain", "", "", "yxdomain", "yxrrset", "nxrrset", "", "", }; /* from db_load.c */ static struct map m_section[] = { { "zone", S_ZONE }, { "prereq", S_PREREQ }, { "update", S_UPDATE }, { "reserved", S_ADDT }, }; #define M_SECTION_CNT (sizeof(m_section) / sizeof(struct map)) /* Forward. */ static int rdata_expand(const u_char *, const u_char *, const u_char *, u_int, size_t, u_char *, size_t); static FILE * open_transaction_log(struct zoneinfo *zp) { FILE *fp = fopen(zp->z_updatelog, "a+"); if (fp == NULL) { ns_error(ns_log_update, "can't open %s: %s", zp->z_updatelog, strerror(errno)); return (NULL); } (void) fchown(fileno(fp), user_id, group_id); if (fseek(fp, 0L, SEEK_END) != 0) { ns_error(ns_log_update, "can't fseek(%s, 0, SEEK_END)", zp->z_updatelog); fclose(fp); return (NULL); } if (ftell(fp) == 0L) { fprintf(fp, "%s", LogSignature); zp->z_serial_ixfr_start = get_serial(zp); } else zp->z_serial_ixfr_start = 0; return (fp); } static FILE * open_ixfr_log(struct zoneinfo *zp) { FILE *fp = fopen(zp->z_ixfr_base, "a+"); if (fp == NULL) { ns_error(ns_log_update, "can't open %s: %s", zp->z_ixfr_base, strerror(errno)); return (NULL); } (void) fchown(fileno(fp), user_id, group_id); if (fseek(fp, 0L, SEEK_END) != 0) { ns_error(ns_log_update, "can't fseek(%s, 0, SEEK_END)", zp->z_ixfr_base); fclose(fp); return (NULL); } if (ftell(fp) == 0L) { fprintf(fp, "%s", LogSignature); } return (fp); } static int close_transaction_log(struct zoneinfo *zp, FILE *fp) { if (fflush(fp) == EOF) { ns_error(ns_log_update, "fflush() of %s failed: %s", zp->z_updatelog, strerror(errno)); return (-1); } if (fsync(fileno(fp)) < 0) { ns_error(ns_log_update, "fsync() of %s failed: %s", zp->z_updatelog, strerror(errno)); return (-1); } if (fclose(fp) == EOF) { ns_error(ns_log_update, "fclose() of %s failed: %s", zp->z_updatelog, strerror(errno)); return (-1); } return (0); } static int close_ixfr_log(struct zoneinfo *zp, FILE *fp) { if (fflush(fp) == EOF) { ns_error(ns_log_update, "fflush() of %s failed: %s", zp->z_ixfr_base, strerror(errno)); fclose(fp); return (-1); } if (fsync(fileno(fp)) < 0) { ns_error(ns_log_update, "fsync() of %s failed: %s", zp->z_ixfr_base, strerror(errno)); fclose(fp); return (-1); } if (fclose(fp) == EOF) { ns_error(ns_log_update, "fclose() of %s failed: %s", zp->z_ixfr_base, strerror(errno)); return (-1); } return (0); } /* * return true if 'db' had been added. */ static int was_added(const ns_updque *updlist, struct databuf *dp) { ns_updrec *rrecp; for (rrecp = HEAD(*updlist); rrecp != NULL; rrecp = NEXT(rrecp, r_link)) if (rrecp->r_section == S_UPDATE && rrecp->r_dp == dp) return (1); return (0); } /* * return true if 'db' had been deleted. */ static int was_deleted(const ns_updque *updlist, struct databuf *dp) { ns_updrec *rrecp; struct databuf *adp; for (rrecp = HEAD(*updlist); rrecp != NULL; rrecp = NEXT(rrecp, r_link)) if (rrecp->r_section == S_UPDATE && rrecp->r_deldp != NULL) { adp = rrecp->r_deldp; do { if (adp == dp) return (1); } while ((adp = adp->d_next) != NULL); } return (0); } /* * printupdatelog(srcaddr, updlist, hp, zp, old_serial) * append an ascii form to the zone's transaction log file. */ static void printupdatelog(struct sockaddr_in srcaddr, const ns_updque *updlist, HEADER *hp, struct zoneinfo *zp, u_int32_t old_serial) { struct databuf *dp; ns_updrec *rrecp; int opcode; char time[25]; FILE *fp, *ifp; if (EMPTY(*updlist)) return; fp = open_transaction_log(zp); if (fp == NULL) return; if (zp->z_maintain_ixfr_base == 1) { ifp = open_ixfr_log(zp); if (ifp == NULL) { (void) close_transaction_log(zp, fp); return; } } else ifp = NULL; sprintf(time, "at %lu", (u_long)tt.tv_sec); fprintf(fp, "[DYNAMIC_UPDATE] id %u from %s %s (named pid %ld):\n", ntohs(hp->id), sin_ntoa(srcaddr), time, (long)getpid()); if (ifp) fprintf(ifp, "[DYNAMIC_UPDATE] id %u from %s %s (named pid %ld):\n", ntohs(hp->id), sin_ntoa(srcaddr), time, (long)getpid()); for (rrecp = HEAD(*updlist); rrecp != NULL; rrecp = NEXT(rrecp, r_link)) { INSIST(zp == &zones[rrecp->r_zone]); switch (rrecp->r_section) { case S_ZONE: fprintf(fp, "zone:\torigin %s class %s serial %u\n", zp->z_origin, p_class(zp->z_class), old_serial); if (ifp) fprintf(ifp, "zone:\torigin %s class %s serial %lu\n", zp->z_origin, p_class(zp->z_class), (u_long)old_serial); break; case S_PREREQ: opcode = rrecp->r_opcode; fprintf(fp, "prereq:\t{%s} %s. %s ", opcodes[opcode], rrecp->r_dname, p_class(zp->z_class)); if (opcode == NXRRSET || opcode == YXRRSET) { fprintf(fp, "%s ", p_type(rrecp->r_type)); if ((dp = rrecp->r_dp) && dp->d_size > 0) { dp->d_class = zp->z_class; (void) rdata_dump(dp, fp); } } fprintf(fp, "\n"); break; case S_UPDATE: opcode = rrecp->r_opcode; /* * Translate all deletes into explict actions by * looking at what was actually deleted from the * zone for the ixfr log. */ dp = rrecp->r_deldp; while (dp != NULL) { if (dp->d_rcode == 0 && !was_added(updlist, dp)) { if (ifp) { fprintf(ifp, "update:\t{%s} %s. %u %s %s ", "delete", rrecp->r_dname, dp->d_ttl, p_class(dp->d_class), p_type(dp->d_type)); (void) rdata_dump(dp, ifp); fprintf(ifp, "\n"); } } dp = dp->d_next; } /* * Only successful adds should be recorded. * Don't add changes that are undone later. * SOA additions performed later. */ if (opcode == ADD && (dp = rrecp->r_dp) != NULL && dp->d_type != T_SOA && (dp->d_mark & D_MARK_ADDED) != 0 && !was_deleted(updlist, dp)) { if (ifp) { fprintf(ifp, "update:\t{%s} %s. ", opcodes[opcode], rrecp->r_dname); fprintf(ifp, "%u ", rrecp->r_ttl); fprintf(ifp, "%s ", p_class(zp->z_class)); fprintf(ifp, "%s ", p_type(rrecp->r_type)); (void) rdata_dump(dp, ifp); fprintf(ifp, "\n"); } } /* Update log. */ fprintf(fp, "update:\t{%s} %s. ", opcodes[opcode], rrecp->r_dname); if (opcode == ADD) fprintf(fp, "%u ", rrecp->r_ttl); fprintf(fp, "%s ", p_class(zp->z_class)); if (rrecp->r_type != T_ANY) fprintf(fp, "%s ", p_type(rrecp->r_type)); if ((dp = rrecp->r_dp) && dp->d_size > 0) { dp->d_class = zp->z_class; (void) rdata_dump(dp, fp); } fprintf(fp, "\n"); break; case S_ADDT: break; default: ns_panic(ns_log_update, 1, "printupdatelog - impossible condition"); /*NOTREACHED*/ } } /* * SOA additions must be last in this update as they * (or [INCR_SERIAL]) terminate an IXFR chunk. Only the last SOA * addition will be emitted for any dynamic update regardless * of the number of SOA changes in the update. */ for (rrecp = HEAD(*updlist); rrecp != NULL; rrecp = NEXT(rrecp, r_link)) { INSIST(zp == &zones[rrecp->r_zone]); switch (rrecp->r_section) { case S_UPDATE: opcode = rrecp->r_opcode; if (opcode == ADD && (dp = rrecp->r_dp) != NULL && dp->d_type == T_SOA && (dp->d_mark & D_MARK_ADDED) != 0 && !was_deleted(updlist, dp)) { if (ifp) { fprintf(ifp, "update:\t{%s} %s. ", opcodes[opcode], rrecp->r_dname); fprintf(ifp, "%u ", rrecp->r_ttl); fprintf(ifp, "%s ", p_class(zp->z_class)); fprintf(ifp, "%s ", p_type(rrecp->r_type)); (void) rdata_dump(dp, ifp); fprintf(ifp, "\n[END_DELTA]\n"); } } break; default: break; } } fprintf(fp, "\n"); (void) close_transaction_log(zp, fp); if (ifp) (void) close_ixfr_log(zp, ifp); } static void cancel_soa_update(struct zoneinfo *zp) { ns_debug(ns_log_update, 3, "cancel_soa_update for %s", zp->z_origin); zp->z_flags &= ~Z_NEED_SOAUPDATE; zp->z_soaincrtime = 0; zp->z_updatecnt = 0; } /* * Figure out when a SOA serial number update should happen. * Returns non-zero if the caller should call sched_zone_maint(zp). */ int schedule_soa_update(struct zoneinfo *zp, int numupdated) { (void) gettime(&tt); zp->z_flags |= Z_NEED_SOAUPDATE; /* * Only z_deferupdcnt updates are allowed before we force * a serial update. */ zp->z_updatecnt += numupdated; if (zp->z_updatecnt >= zp->z_deferupdcnt) { if (zp->z_soaincrtime > tt.tv_sec) { zp->z_soaincrtime = tt.tv_sec; return (1); } } if (zp->z_soaincrintvl > 0) { /* We want automatic updates in this zone. */ if (zp->z_soaincrtime > 0) { /* Already scheduled. */ ns_debug(ns_log_update, 3, "schedule_soa_update('%s'): already scheduled", zp->z_origin); return (0); } else { /* First update since the soa was last incremented. */ zp->z_updatecnt = numupdated; zp->z_soaincrtime = tt.tv_sec + zp->z_soaincrintvl; /* * Never schedule soaincrtime to occur after * dumptime. */ if (zp->z_soaincrtime > zp->z_dumptime) zp->z_soaincrtime = zp->z_dumptime; ns_debug(ns_log_update, 3, "schedule_soa_update('%s'): scheduled for %lu", zp->z_origin, (u_long)zp->z_soaincrtime); return (1); } } return (0); } /* * Figure out when a zone dump should happen. * Returns non-zero if the caller should call sched_zone_maint(zp). */ int schedule_dump(struct zoneinfo *zp) { time_t half; (void) gettime(&tt); zp->z_flags |= Z_NEED_DUMP; if (zp->z_dumpintvl > 0) { /* We want automatic dumping in this zone. */ if (zp->z_dumptime > 0) { /* Already scheduled. */ ns_debug(ns_log_update, 3, "schedule_dump('%s'): already scheduled", zp->z_origin); return (0); } else { /* * Set new dump time for dynamic zone. Use a random * number in the last half of the dump limit; we want * it to be substantially correct while still * preventing dump synchronization among various * dynamic zones. */ half = (zp->z_dumpintvl + 1) / 2; zp->z_dumptime = tt.tv_sec + half + (rand() % half); /* * Never schedule soaincrtime to occur after * dumptime. */ if (zp->z_soaincrtime > zp->z_dumptime) zp->z_soaincrtime = zp->z_dumptime; ns_debug(ns_log_update, 3, "schedule_dump('%s'): scheduled for %lu", zp->z_origin, (u_long)zp->z_dumptime); return (1); } } return (0); } /* * int * process_prereq(rec, rcodep) * Process one prerequisite. * returns: * >0 prerequisite was satisfied. * =0 prerequisite was not satisfied, or an error occurred. * side effects: * sets *rcodep if an error occurs or prerequisite isn't satisfied. */ static int process_prereq(ns_updrec *ur, int *rcodep, u_int16_t zclass) { const char *dname = ur->r_dname; u_int16_t class = ur->r_class; u_int16_t type = ur->r_type; u_int32_t ttl = ur->r_ttl; struct databuf *rdp = ur->r_dp; const char *fname; struct hashbuf *htp; struct namebuf *np; struct databuf *dp; /* * An element in the list might have already been * processed if it is in the same RRset as a previous * RRset Exists (value dependent) prerequisite. */ if (rdp && (rdp->d_mark & D_MARK_FOUND) != 0) { /* Already processed. */ return (1); } if (ttl != 0) { ns_debug(ns_log_update, 1, "process_prereq: ttl!=0 in prereq section"); *rcodep = FORMERR; return (0); } htp = hashtab; np = nlookup(dname, &htp, &fname, 0); /* * Matching by wildcard not allowed here. * We need to post check for a wildcard match. */ if (fname != dname || (np != NULL && ns_wildcard(NAME(*np)) && (dname[0] != '*' || (dname[1] != '.' && dname[1] != '\0')))) np = NULL; if (class == C_ANY) { if (rdp->d_size) { ns_debug(ns_log_update, 1, "process_prereq: empty rdata required in prereq section with class=ANY"); *rcodep = FORMERR; return (0); } if (type == T_ANY) { /* Name is in use. */ ur->r_opcode = YXDOMAIN; if (np == NULL || np->n_data == NULL) { /* * Name does not exist or is * an empty nonterminal. */ ns_debug(ns_log_update, 1, "process_prereq: %s not in use", dname); *rcodep = NXDOMAIN; return (0); } } else { /* RRset exists (value independent). */ int found = 0; ur->r_opcode = YXRRSET; if (np != NULL) for (dp = np->n_data; dp && !found; dp = dp->d_next) if (match(dp, class, type) && dp->d_type == type) found = 1; if (!found) { ns_debug(ns_log_update, 1, "process_prereq: RRset (%s,%s,%s) does not exist", dname, p_type(type), p_class(zclass)); *rcodep = NXRRSET; return (0); } } } else if (class == C_NONE) { if (rdp->d_size) { ns_debug(ns_log_update, 1, "process_prereq: empty rdata required in prereq section with class=NONE"); *rcodep = FORMERR; return (0); } if (type == T_ANY) { /* Name is not in use. */ ur->r_opcode = NXDOMAIN; if (np != NULL && np->n_data != NULL) { /* * Name exists and is not an * empty nonterminal. */ ns_debug(ns_log_update, 1, "process_prereq: %s exists", dname); *rcodep = YXDOMAIN; return (0); } } else { /* RRset does not exist. */ int found = 0; ur->r_opcode = NXRRSET; class = zclass; if (np != NULL) for (dp = np->n_data; dp && !found; dp = dp->d_next) if (match(dp, class, type)) found = 1; if (found) { ns_debug(ns_log_update, 1, "process_prereq: RRset (%s,%s) exists", dname, p_type(type)); *rcodep = YXRRSET; return (0); } } } else if (class == zclass) { /* * RRset exists (value dependent). * * Check for RRset equality also. */ ns_updrec *tmp; ur->r_opcode = YXRRSET; if (!rdp) { ns_debug(ns_log_update, 1, "process_prereq: nonempty rdata required in prereq section with class=%s", p_class(class)); *rcodep = FORMERR; return (0); } if (np == NULL || fname != dname) { *rcodep = NXRRSET; return (0); } for (dp = np->n_data; dp; dp = dp->d_next) { if (match(dp, class, type) && dp->d_type == type) { int found = 0; for (tmp = ur; tmp != NULL && !found; tmp = NEXT(tmp, r_link)) { if (tmp->r_section != S_PREREQ) break; if (!db_cmp(dp, tmp->r_dp)) { tmp->r_dp->d_mark |= D_MARK_FOUND; found = 1; } } if (!found) { *rcodep = NXRRSET; return (0); } } } for (tmp = ur; tmp != NULL; tmp = NEXT(tmp, r_link)) if (tmp->r_section == S_PREREQ && ns_samename(dname, tmp->r_dname) == 1 && tmp->r_class == class && tmp->r_type == type && (ur->r_dp->d_mark & D_MARK_FOUND) == 0) { *rcodep = NXRRSET; return (0); } else { tmp->r_opcode = YXRRSET; } } else { ns_debug(ns_log_update, 1, "process_prereq: incorrect class %s", p_class(class)); *rcodep = FORMERR; return (0); } /* Through the gauntlet, and out. */ return (1); } static int prescan_nameok(ns_updrec *ur, int *rcodep, u_int16_t zclass, struct zoneinfo *zp) { const char *owner = ur->r_dname; u_int16_t class = ur->r_class; u_int16_t type = ur->r_type; char *cp = (char *)ur->r_dp->d_data; enum context context; enum transport transport; /* We don't care about deletes */ if (ur->r_class != zclass) return (1); if (zp->z_type == Z_PRIMARY) transport = primary_trans; else transport = secondary_trans; context = ns_ownercontext(type, transport); if (!ns_nameok(NULL, owner, class, zp, transport, context, owner, inaddr_any)) goto refused; switch (type) { case ns_t_soa: context = hostname_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; cp += strlen(cp) + 1; context = mailname_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; break; case ns_t_rp: context = mailname_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; cp += strlen(cp) + 1; context = domain_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; break; case ns_t_minfo: context = mailname_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; cp += strlen(cp) + 1; context = mailname_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; break; case ns_t_ns: context = hostname_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; break; case ns_t_cname: case ns_t_mb: case ns_t_mg: case ns_t_mr: context = domain_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; break; case ns_t_ptr: context = ns_ptrcontext(owner); if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; break; case ns_t_naptr: /* * Order (2) * Preference (2) */ cp += 4; /* Flags (txt) */ cp += (*cp&0xff) + 1; /* Service (txt) */ cp += (*cp&0xff) + 1; /* Pattern (txt) */ cp += (*cp&0xff) + 1; context = domain_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; break; case ns_t_srv: cp += 4; /* FALLTHROUGH */ case ns_t_mx: case ns_t_afsdb: case ns_t_rt: case ns_t_kx: cp += 2; context = hostname_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; break; case ns_t_px: cp += 2; context = domain_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; cp += strlen(cp) + 1; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; break; case ns_t_sig: /* * Type covered (2) * Alg (1) * * Labels (1) * ttl (4) * expires (4) * signed (4) * footprint (2) */ cp += 18; context = domain_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; break; case ns_t_nxt: context = domain_ctx; if (!ns_nameok(NULL, cp, class, zp, transport, context, owner, inaddr_any)) goto refused; break; default: break; } return (1); refused: *rcodep = REFUSED; return (0); } /* * int * prescan_update(ur, rcodep) * Process one prerequisite. * returns: * >0 update looks OK (format wise; who knows if it will succeed?) * =0 update has something wrong with it. * side effects: * sets *rcodep if an error occurs or prerequisite isn't satisfied. */ static int prescan_update(ns_updrec *ur, int *rcodep, u_int16_t zclass) { u_int16_t class = ur->r_class; u_int16_t type = ur->r_type; u_int32_t ttl = ur->r_ttl; struct databuf *rdp = ur->r_dp; if (class == zclass) { if (!ns_t_rr_p(type)) { ns_debug(ns_log_update, 1, "prescan_update: invalid type (%s)", p_type(type)); *rcodep = FORMERR; return (0); } if (ttl > MAXIMUM_TTL) { ns_debug(ns_log_update, 1, "prescan_update: invalid ttl (%u)", ttl); *rcodep = FORMERR; return (0); } } else if (class == C_ANY) { if (ttl != 0 || rdp->d_size || (!ns_t_rr_p(type) && type != T_ANY)) { ns_debug(ns_log_update, 1, "prescan_update: formerr(#2)"); *rcodep = FORMERR; return (0); } } else if (class == C_NONE) { if (ttl != 0 || !ns_t_rr_p(type)) { ns_debug(ns_log_update, 1, "prescan_update: formerr(#3) %d %s", ttl, p_type(type)); *rcodep = FORMERR; return (0); } } else { ns_debug(ns_log_update, 1, "prescan_update: invalid class (%s)", p_class(class)); *rcodep = FORMERR; return (0); } /* No format errors found. */ return (1); } /* * int * process_updates(updlist, rcodep, from) * Process prerequisites and apply updates from the list to the database. * returns: * number of successful updates, 0 if none were successful. * side effects: * *rcodep gets the transaction return code. * can schedule maintainance for zone dumps and soa.serial# increments. */ static int process_updates(const ns_updque *updlist, int *rcodep, struct sockaddr_in from) { int j, n, dbflags, matches, zonenum; int numupdated = 0, soaupdated = 0, schedmaint = 0; u_int16_t zclass; ns_updrec *ur; struct databuf *dp, *savedp; struct zoneinfo *zp; int zonelist[MAXDNAME]; *rcodep = SERVFAIL; if (EMPTY(*updlist)) return (0); ur = HEAD(*updlist); if (ur->r_section == S_ZONE) { zclass = ur->r_class; zonenum = ur->r_zone; zp = &zones[zonenum]; } else { ns_debug(ns_log_update, 1, "process_updates: missing zone record"); return (0); } /* Process prereq records and prescan update records. */ for (ur = HEAD(*updlist); ur != NULL; ur = NEXT(ur, r_link)) { const char * dname = ur->r_dname; u_int16_t class = ur->r_class; u_int16_t type = ur->r_type; u_int32_t ttl = ur->r_ttl; struct databuf *rdp = ur->r_dp; u_int section = ur->r_section; ns_debug(ns_log_update, 3, "process_update: record section=%s, dname=%s, \ class=%s, type=%s, ttl=%d, dp=%p", p_section(section, ns_o_update), dname, p_class(class), p_type(type), ttl, rdp); matches = findzone(dname, zclass, MAXDNAME, zonelist, MAXDNAME); ur->r_zone = 0; for (j = 0; j < matches && !ur->r_zone; j++) if (zonelist[j] == zonenum) ur->r_zone = zonelist[j]; if (!ur->r_zone || (section != S_ADDT && type == T_SOA && ns_samename(dname, zp->z_origin) != 1)) { ns_debug(ns_log_update, 1, "process_updates: record does not belong to the zone %s", zones[zonenum].z_origin); *rcodep = NOTZONE; return (0); } switch (section) { case S_ZONE: break; case S_PREREQ: if (!process_prereq(ur, rcodep, zclass)) return (0); /* *rcodep has been set. */ ns_debug(ns_log_update, 3, "prerequisite satisfied"); break; case S_UPDATE: if (!prescan_update(ur, rcodep, zclass)) return (0); /* *rcodep has been set. */ if (!prescan_nameok(ur, rcodep, zclass, zp)) return (0); /* *rcodep has been set. */ ns_debug(ns_log_update, 3, "update prescan succeeded"); break; case S_ADDT: break; default: ns_panic(ns_log_update, 1, "process_updates: impossible section"); /* NOTREACHED */ } } /* Now process the records in update section. */ for (ur = HEAD(*updlist); ur != NULL; ur = NEXT(ur, r_link)) { const char * dname = ur->r_dname; u_int16_t class = ur->r_class; if (ur->r_section != S_UPDATE) continue; dbflags = 0; savedp = NULL; dp = ur->r_dp; if (class == zp->z_class) { /* ADD databuf dp to hash table */ /* * Handling of various SOA/WKS/CNAME scenarios * is done in db_update(). */ ur->r_opcode = ADD; dbflags |= DB_NODATA | DB_REPLACE; n = db_update(dname, dp, dp, &savedp, dbflags, hashtab, from); if (!((n == OK) || ((zp->z_xferpid == XFER_ISIXFR) && (n == DATAEXISTS)))) { ns_debug(ns_log_update, 3, "process_updates: failed to add databuf (%d)", n); } else { ns_debug(ns_log_update, 3, "process_updates: added databuf %p", dp); dp->d_mark = D_MARK_ADDED; numupdated++; if (dp->d_type == T_SOA) soaupdated = 1; } } else if (class == C_ANY || class == C_NONE) { /* * DELETE databuf's matching dp from the hash table. * * handling of various SOA/NS scenarios done * in db_update(). */ ur->r_opcode = DELETE; /* * we know we're deleting now, and db_update won't * match with class==C_NONE, so we use the zone's * class. */ if (class == C_NONE) ur->r_dp->d_class = zp->z_class; dbflags |= DB_DELETE; n = db_update(dname, dp, NULL, &savedp, dbflags, hashtab, from); if (!((n == OK) || ((zp->z_xferpid == XFER_ISIXFR) && (n == NODATA)))) { ns_debug(ns_log_update, 3, "process_updates: delete failed"); } else { ns_debug(ns_log_update, 3, "process_updates: delete succeeded"); numupdated++; } } /* * Even an addition could have caused some deletions like * replacing old SOA or CNAME or WKS record or records of * lower cred/clev. * * We need to save the deleted databuf's in case we wish to * abort this update transaction and roll back all updates * applied from this packet. */ ur->r_deldp = savedp; } /* * If we got here, things are OK, so set rcodep to indicate so. */ *rcodep = NOERROR; if (!numupdated) return (0); /* * schedule maintenance for dumps and SOA.serial# increment * (this also sets Z_NEED_DUMP and Z_NEED_SOAUPDATE appropriately) */ schedmaint = 0; if (schedule_dump(zp)) schedmaint = 1; if (soaupdated) { /* * SOA updated by this update transaction, so * we need to set the zone serial number, stop any * automatic updates that may be pending, and send out * a NOTIFY message. */ zp->z_serial = get_serial_unchecked(zp); cancel_soa_update(zp); schedmaint = 1; #ifdef BIND_NOTIFY if (!loading) ns_notify(zp->z_origin, zp->z_class, ns_t_soa); #endif } else { if (schedule_soa_update(zp, numupdated)) schedmaint = 1; } if (schedmaint) sched_zone_maint(zp); return (numupdated); } static enum req_action req_update_private(HEADER *hp, u_char *cp, u_char *eom, u_char *msg, struct sockaddr_in from, struct tsig_record *in_tsig, ns_updque *curupd) { char dnbuf[MAXDNAME], *dname; u_int zocount, prcount, upcount, adcount, class, type, dlen; u_int32_t ttl; int i, n, matches, zonenum, numupdated = 0; int rcode = NOERROR; u_int section; u_char rdata[MAXDATA]; struct databuf *dp, *nsp[NSMAX]; struct zoneinfo *zp; ns_updrec *rrecp; int zonelist[MAXDNAME]; u_int32_t old_serial; DST_KEY *in_key = (in_tsig != NULL) ? in_tsig->key : NULL; nsp[0] = NULL; zocount = ntohs(hp->qdcount); prcount = ntohs(hp->ancount); upcount = ntohs(hp->nscount); adcount = ntohs(hp->arcount); /* Process zone section. */ ns_debug(ns_log_update, 3, "req_update: section ZONE, count %d", zocount); if ((n = dn_expand(msg, eom, cp, dnbuf, sizeof(dnbuf))) < 0) { ns_debug(ns_log_update, 1, "req_update: expand name failed"); hp->rcode = FORMERR; return (Finish); } dname = dnbuf; cp += n; if (cp + 2 * INT16SZ > eom) { ns_debug(ns_log_update, 1, "req_update: too short"); hp->rcode = FORMERR; return (Finish); } GETSHORT(type, cp); GETSHORT(class, cp); if (zocount != 1 || type != T_SOA) { ns_debug(ns_log_update, 1, "req_update: incorrect count or type for zone section: %d", zocount); hp->rcode = FORMERR; return (Finish); } matches = findzone(dname, class, 0, zonelist, MAXDNAME); if (matches == 1) { zonenum = zonelist[0]; zp = &zones[zonenum]; if (zp->z_class != (int)class || (zp->z_type != z_master && zp->z_type != z_slave)) matches = 0; } if (matches != 1) { ns_debug(ns_log_update, 1, "req_update: non-authoritative server for %s", dname); hp->rcode = NOTAUTH; return (Finish); } /* * Begin Access Control Point */ if (!ip_addr_or_key_allowed(zp->z_update_acl, from.sin_addr, in_key)) { - ns_notice(ns_log_security, + ns_notice(ns_log_update_security, "denied update from %s for \"%s\" %s", sin_ntoa(from), *dname ? dname : ".", p_class(class)); nameserIncr(from.sin_addr, nssRcvdUUpd); return (Refuse); } /* * End Access Control Point */ /* we should be authoritative */ if (!(zp->z_flags & Z_AUTH)) { ns_debug(ns_log_update, 1, "req_update: zone %s: Z_AUTH not set", dname); hp->rcode = NOTAUTH; return (Finish); } if (zp->z_type == Z_SECONDARY) { /* * XXX The code below is broken. * Until fixed, we just return NOTIMPL. */ #if 1 hp->rcode = ns_r_notimpl; return (Finish); #else /* We are a slave for this zone, forward it to the master. */ for (cnt = 0; cnt < zp->z_addrcnt; cnt++) *nspp++ = savedata(zp->z_class, T_A, USE_MINIMUM, (u_char *)&zp->z_addr[cnt].s_addr, INT32SZ); *nspp = NULL; /* * If the request came in over TCP, forward it over TCP */ should_use_tcp = (qsp != NULL); if (in_tsig != NULL) { tsig_len = ns_skiprr(eom, eom + TSIG_BUF_SIZE, ns_s_ar, 1); eom += tsig_len; } n = ns_forw(nsp, msg, eom-msg, from, qsp, dfd, &qp, dname, class, type, NULL, should_use_tcp, NULL); if (in_tsig != NULL) eom -= tsig_len; free_nsp(nsp); switch (n) { case FW_OK: case FW_DUP: return (Return); case FW_NOSERVER: /* should not happen */ case FW_SERVFAIL: hp->rcode = SERVFAIL; return (Finish); } #endif } /* * We are the primary master server for this zone, * proceed further and process update packet */ if (!(zp->z_flags & Z_DYNAMIC)) { ns_debug(ns_log_update, 1, "req_update: dynamic flag not set for zone %s", dname); return (Refuse); } old_serial = get_serial(zp); ns_debug(ns_log_update, 3, "req_update: update request for zone %s, class %s", zp->z_origin, p_class(class)); rrecp = res_mkupdrec(S_ZONE, dname, class, type, 0); rrecp->r_zone = zonenum; APPEND(*curupd, rrecp, r_link); /* * Parse the prerequisite and update sections for format errors. */ for (i = 0; (u_int)i < prcount + upcount; i++) { if ((n = dn_expand(msg, eom, cp, dnbuf, sizeof(dnbuf))) < 0) { ns_debug(ns_log_update, 1, "req_update: expand name failed"); hp->rcode = FORMERR; return (Finish); } dname = dnbuf; cp += n; if (cp + RRFIXEDSZ > eom) { ns_debug(ns_log_update, 1, "req_update: overrun in answer"); hp->rcode = FORMERR; return (Finish); } GETSHORT(type, cp); GETSHORT(class, cp); if (class > CLASS_MAX) { ns_debug(ns_log_update, 1, "req_update: bad class"); hp->rcode = FORMERR; return (Finish); } GETLONG(ttl, cp); GETSHORT(dlen, cp); n = 0; dp = NULL; if (dlen > 0) { if (cp + dlen > eom) { ns_debug(ns_log_update, 1, "req_update: bad dlen"); hp->rcode = FORMERR; return (Finish); } n = rdata_expand(msg, eom, cp, type, dlen, rdata, sizeof rdata); if (n == 0 || n > MAXDATA) { ns_debug(ns_log_update, 1, "req_update: failed to expand record"); hp->rcode = FORMERR; return (Finish); } cp += dlen; } section = ((u_int)i < prcount) ? S_PREREQ : S_UPDATE; rrecp = res_mkupdrec(section, dname, class, type, ttl); dp = savedata(class, type, ttl, rdata, n); dp->d_zone = zonenum; dp->d_cred = DB_C_ZONE; dp->d_secure = DB_S_INSECURE; /* should be UNCHECKED */ dp->d_clev = nlabels(zp->z_origin); /* XXX - also record in dp->d_ns, which host this came from */ rrecp->r_dp = dp; /* Append the current record to the end of list of records. */ APPEND(*curupd, rrecp, r_link); if (cp > eom) { ns_info(ns_log_update, "Malformed response from %s (overrun)", inet_ntoa(from.sin_addr)); hp->rcode = FORMERR; return (Finish); } } /* Now process all parsed records in the prereq and update sections. */ numupdated = process_updates(curupd, &rcode, from); hp->rcode = rcode; if (numupdated <= 0) { if (rcode != NOERROR) ns_error(ns_log_update, "error processing update packet (%s) id %d from %s", p_rcode(rcode), ntohs(hp->id), sin_ntoa(from)); return (Finish); } /* * Stop any outbound zone transfers. * (Eventlib is synchronous for this.) */ ns_stopxfrs(zp); /* Make a log of the update. */ (void) printupdatelog(from, curupd, hp, zp, old_serial); return (Finish); } void free_rrecp(ns_updque *updlist, int rcode, struct sockaddr_in from) { ns_updrec *rrecp, *first_rrecp, *next_rrecp; struct databuf *dp, *tmpdp; char *dname; const char *msg; if (rcode == NOERROR) { first_rrecp = HEAD(*updlist); msg = "free_rrecp: update transaction succeeded, cleaning up"; } else { first_rrecp = TAIL(*updlist); msg = "free_rrecp: update transaction aborted, rolling back"; } ns_debug(ns_log_update, 1, "%s", msg); for (rrecp = first_rrecp; rrecp != NULL; rrecp = next_rrecp) { if (rcode == NOERROR) next_rrecp = NEXT(rrecp, r_link); else next_rrecp = PREV(rrecp, r_link); UNLINK(*updlist, rrecp, r_link); if (rrecp->r_section != S_UPDATE) { if (rrecp->r_dp) db_detach(&rrecp->r_dp); INSIST(rrecp->r_deldp == NULL); res_freeupdrec(rrecp); continue; } dname = rrecp->r_dname; dp = rrecp->r_dp; rrecp->r_dp = NULL; if ((dp->d_mark & D_MARK_ADDED) != 0) { if (rcode == NOERROR) { /* * This databuf is now a part of hashtab, * or has been deleted by a subsequent update. * Either way, we must not free it. */ dp->d_mark &= ~D_MARK_ADDED; } else { /* Delete the databuf. */ if (db_update(dname, dp, NULL, NULL, DB_DELETE, hashtab, from) != OK) { ns_error(ns_log_update, "free_rrecp: failed to delete databuf: dname=%s, type=%s", dname, p_type(dp->d_type)); } else { ns_debug(ns_log_update, 3, "free_rrecp: deleted databuf %p", dp); } } } db_detach(&dp); /* Process deleted databuf's. */ dp = rrecp->r_deldp; rrecp->r_deldp = NULL; while (dp != NULL) { tmpdp = dp; dp = dp->d_next; tmpdp->d_next = NULL; if (rcode != NOERROR) { /* Add the databuf back. */ tmpdp->d_mark &= ~D_MARK_DELETED; if (db_update(dname, tmpdp, tmpdp, NULL, DB_REPLACE, hashtab, from) != OK) { ns_error(ns_log_update, "free_rrecp: failed to add back databuf: dname=%s, type=%s", dname, p_type(tmpdp->d_type)); } else { ns_debug(ns_log_update, 3, "free_rrecp: added back databuf %p", tmpdp); } } db_detach(&tmpdp); } res_freeupdrec(rrecp); } } enum req_action req_update(HEADER *hp, u_char *cp, u_char *eom, u_char *msg, struct sockaddr_in from, struct tsig_record *in_tsig) { enum req_action ret; ns_updque curupd; INIT_LIST(curupd); ret = req_update_private(hp, cp, eom, msg, from, in_tsig, &curupd); free_rrecp(&curupd, ret == Refuse ? ns_r_refused : hp->rcode, from); if (ret == Finish) { hp->qdcount = hp->ancount = hp->nscount = hp->arcount = 0; memset(msg + HFIXEDSZ, 0, (eom - msg) - HFIXEDSZ); } return (ret); } /* * expand rdata portion of a compressed resource record at cp into cp1 * and return the length of the expanded rdata (length of the compressed * rdata is "dlen"). */ static int rdata_expand(const u_char *msg, const u_char *eom, const u_char *cp, u_int type, size_t dlen, u_char *cp1, size_t size) { const u_char *cpinit = cp; const u_char *cp1init = cp1; int n, i, n1; switch (type) { case T_A: case T_AAAA: if ((type == T_A && dlen != INT32SZ) || (type == T_AAAA && dlen != NS_IN6ADDRSZ)) return (0); /*FALLTHROUGH*/ case T_WKS: case T_HINFO: case T_TXT: case T_X25: case T_ISDN: case T_NSAP: case T_LOC: case T_KEY: case ns_t_cert: if (size < dlen) return (0); memcpy(cp1, cp, dlen); return (dlen); case T_CNAME: case T_MB: case T_MG: case T_MR: case T_NS: case T_PTR: n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0 || (u_int)n != dlen) return (0); return (strlen((char *)cp1) + 1); case T_MINFO: case T_SOA: case T_RP: /* Get two compressed domain names. */ for (i = 0; i < 2; i++) { n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0) return (0); cp += n; n = strlen((char *)cp1) + 1; cp1 += n; size -= n; } if (type == T_SOA) { n = 5 * INT32SZ; if (size < (size_t)n || cp + n > eom) return(0); size -= n; memcpy(cp1, cp, n); cp += n; cp1 += n; } if (cp != cpinit + dlen) return (0); return (cp1 - cp1init); case T_MX: case T_AFSDB: case T_RT: case T_SRV: /* Grab preference. */ if (size < INT16SZ || cp + INT16SZ > eom) return (0); size -= INT16SZ; memcpy(cp1, cp, INT16SZ); cp += INT16SZ; cp1 += INT16SZ; if (type == T_SRV) { if (size < INT16SZ*2 || cp + INT16SZ*2 > eom) return (0); size -= INT16SZ*2; /* Grab weight and port. */ memcpy(cp1, cp, INT16SZ*2); cp1 += INT16SZ*2; cp += INT16SZ*2; } /* Get name. */ n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0) return (0); cp += n; n = strlen((char *)cp1) + 1; cp1 += n; if (cp != cpinit + dlen) return (0); return (cp1 - cp1init); case T_PX: /* Grab preference. */ if (size < INT16SZ || cp + INT16SZ > eom) return (0); size -= INT16SZ; memcpy(cp1, cp, INT16SZ); cp += INT16SZ; cp1 += INT16SZ; /* Get MAP822 name. */ n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0) return (0); cp += n; n = strlen((char *)cp1) + 1; cp1 += n; size -= n; n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0) return (0); cp += n; n = strlen((char *)cp1) + 1; cp1 += n; if (cp != cpinit + dlen) return (0); return (cp1 - cp1init); case T_SIG: if (dlen < SIG_HDR_SIZE || size < dlen) return (0); memcpy(cp1, cp, SIG_HDR_SIZE); size -= SIG_HDR_SIZE; cp += SIG_HDR_SIZE; cp1 += SIG_HDR_SIZE; n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0 || n + SIG_HDR_SIZE > (int)dlen) return (0); cp += n; n1 = dlen - n - SIG_HDR_SIZE; n = strlen((char *)cp1) + 1; cp1 += n; if ((int)size < n1) return (0); memcpy(cp1, cp, n1); cp1 += n1; return (cp1 - cp1init); case T_NXT: n = dn_expand(msg, eom, cp, (char *)cp1, size); if (n < 0 || (u_int)n >= dlen) return (0); size -= n; cp += n; n1 = dlen - n; n = strlen((char *)cp1) + 1; cp1 += n; /* * The first bit of the first octet determines the format * of the NXT record. A format for types >= 128 has not * yet been defined, so if bit zero is set, we just copy * what's there because we don't understand it. */ if ((*cp & 0x80) == 0) { /* * Bit zero is not set; this is an ordinary NXT * record. The bitmap must be at least 4 octets * because the NXT bit should be set. It should be * less than or equal to 16 octets because this NXT * format is only defined for types < 128. */ if (n1 < 4 || n1 > 16) return (0); } if (n1 > (int)size) return (0); memcpy(cp1, cp, n1); cp1 += n1; return (cp1 - cp1init); default: if (size < dlen) return (0); memcpy(cp1, cp, dlen); return (dlen); } } /* * Print out rdata portion of a resource record from a databuf into a file. * * XXX - similar code in db_dump() should be replaced by a call to this * function. */ void rdata_dump(struct databuf *dp, FILE *fp) { u_int32_t n, addr; u_char *cp, *end; int i, j; const char *proto; u_char *savecp; char temp_base64[NS_MD5RSA_MAX_BASE64]; u_int16_t keyflags; u_char *sigdata, *certdata; cp = (u_char *)dp->d_data; switch (dp->d_type) { case T_A: switch (dp->d_class) { case C_IN: case C_HS: GETLONG(n, cp); n = htonl(n); fputs(inet_ntoa(*(struct in_addr *)&n), fp); break; } if (dp->d_nstime) fprintf(fp, ";\tNT=%d", dp->d_nstime); break; case T_CNAME: case T_MB: case T_MG: case T_MR: case T_PTR: fprintf(fp, "%s.", cp); break; case T_NS: cp = (u_char *)dp->d_data; if (cp[0] == '\0') fprintf(fp, ".\t"); else fprintf(fp, "%s.", cp); break; case T_HINFO: case T_ISDN: if ((n = *cp++) != '\0') { fprintf(fp, "\"%.*s\"", (int)n, cp); cp += n; } else fprintf(fp, "\"\""); if ((n = *cp++) != '\0') fprintf(fp, " \"%.*s\"", (int)n, cp); else fprintf(fp, " \"\""); break; case T_SOA: fprintf(fp, "%s.", cp); cp += strlen((char *)cp) + 1; fprintf(fp, " %s. ( ", cp); #if defined(RETURNSOA) && defined(NCACHE) if (dp->d_rcode == NXDOMAIN) fputs(";", fp); #endif cp += strlen((char *)cp) + 1; GETLONG(n, cp); fprintf(fp, "%u", n); GETLONG(n, cp); fprintf(fp, " %u", n); GETLONG(n, cp); fprintf(fp, " %u", n); GETLONG(n, cp); fprintf(fp, " %u", n); GETLONG(n, cp); fprintf(fp, " %u )", n); #if defined(RETURNSOA) && defined(NCACHE) if (dp->d_rcode == NXDOMAIN) fprintf(fp, ";%s.;NXDOMAIN;\t-$", cp); #endif break; case T_MX: case T_AFSDB: case T_RT: GETSHORT(n, cp); fprintf(fp, "%u", n); fprintf(fp, " %s.", cp); break; case T_SRV: GETSHORT(n, cp); /* priority */ fprintf(fp, "%u ", n); GETSHORT(n, cp); /* weight */ fprintf(fp, "%u ", n); GETSHORT(n, cp); /* port */ fprintf(fp, "%u ", n); fprintf(fp, " %s.", cp); break; case T_PX: GETSHORT(n, cp); fprintf(fp, "%u", n); fprintf(fp, " %s.", cp); cp += strlen((char *)cp) + 1; fprintf(fp, " %s.", cp); break; case T_TXT: case T_X25: end = (u_char *)dp->d_data + dp->d_size; (void) putc('"', fp); while (cp < end) { if ((n = *cp++) != '\0') { for (j = n; j > 0 && cp < end; j--) if ((*cp < ' ') || (*cp > '~')) { fprintf(fp, "\\%03d", *cp++); } else if (*cp == '\\' || *cp =='"') { putc('\\', fp); putc(*cp++, fp); } else (void) putc(*cp++, fp); } if (cp != end) fputs("\" \"", fp); } /* XXXVIX need to keep the segmentation (see 4.9.5). */ (void) fputs("\"", fp); break; case T_NSAP: (void) fputs(inet_nsap_ntoa(dp->d_size, dp->d_data, NULL), fp); break; case T_LOC: (void) fputs(loc_ntoa(dp->d_data, NULL), fp); break; case T_WKS: GETLONG(addr, cp); addr = htonl(addr); fputs(inet_ntoa(*(struct in_addr *)&addr), fp); proto = protocolname((u_char)*cp); cp += sizeof(char); fprintf(fp, "%s ", proto); i = 0; while(cp < (u_char *)dp->d_data + dp->d_size) { j = *cp++; do { if (j & 0200) fprintf(fp, " %s", servicename(i, proto)); j <<= 1; } while (++i & 07); } break; case T_MINFO: case T_RP: fprintf(fp, "%s.", cp); cp += strlen((char *)cp) + 1; fprintf(fp, " %s.", cp); break; case T_KEY: savecp = cp; /* save the beginning */ /*>>> Flags (unsigned_16) */ NS_GET16(keyflags,cp); fprintf(fp, "0x%04x ", keyflags); /*>>> Protocol (8-bit decimal) */ fprintf(fp, "%3u ", *cp++); /*>>> Algorithm id (8-bit decimal) */ fprintf(fp, "%3u ", *cp++); /*>>> Public-Key Data (multidigit BASE64) */ /* containing ExponentLen, Exponent, and Modulus */ i = b64_ntop(cp, dp->d_size - (cp - savecp), temp_base64, sizeof temp_base64); if (i < 0) fprintf(fp, "; BAD BASE64"); else fprintf(fp, "%s", temp_base64); break; case T_SIG: sigdata = cp; /* RRtype (char *) */ NS_GET16(n,cp); fprintf(fp, "%s ", p_type(n)); /* Algorithm id (8-bit decimal) */ fprintf(fp, "%d ", *cp++); /* Labels (8-bit decimal) (not saved in file) */ /* XXXX FIXME -- check value and print err if bad */ cp++; /* OTTL (u_long) */ NS_GET32(n, cp); fprintf(fp, "%u ", n); /* Texp (u_long) */ NS_GET32(n, cp); fprintf(fp, "%s ", p_secstodate (n)); /* Tsig (u_long) */ NS_GET32(n, cp); fprintf(fp, "%s ", p_secstodate (n)); /* Kfootprint (unsigned_16) */ NS_GET16(n, cp); fprintf(fp, "%u ", n); /* Signer's Name (char *) */ fprintf(fp, "%s ", cp); cp += strlen((char *)cp) + 1; /* Signature (base64 of any length) */ i = b64_ntop(cp, dp->d_size - (cp - sigdata), temp_base64, sizeof temp_base64); if (i < 0) fprintf(fp, "; BAD BASE64"); else fprintf(fp, "%s", temp_base64); break; case T_NXT: fprintf(fp, "%s.", cp); n = strlen ((char *)cp) + 1; cp += n; i = 8 * (dp->d_size - n); /* How many bits? */ for (n = 0; n < (u_int32_t)i; n++) { if (NS_NXT_BIT_ISSET(n, cp)) fprintf(fp," %s",__p_type(n)); } break; case ns_t_cert: certdata = cp; NS_GET16(n,cp); fprintf(fp, "%d ", n); /* cert type */ NS_GET16(n,cp); fprintf(fp, "%d %d ", n, *cp++); /* tag & alg */ /* Certificate (base64 of any length) */ i = b64_ntop(cp, dp->d_size - (cp - certdata), temp_base64, sizeof(temp_base64)); if (i < 0) fprintf(fp, "; BAD BASE64"); else fprintf(fp, "%s", temp_base64); break; case ns_t_aaaa: { char t[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; (void) fputs(inet_ntop(AF_INET6, dp->d_data, t, sizeof t), fp); break; } default: fprintf(fp, "\\# %u", dp->d_size); if (dp->d_size) { fputs(" ( ", fp); isc_puthexstring(fp, dp->d_data, dp->d_size, 28, 48, "\n\t\t\t\t"); fputs(" )", fp); } } } /* * Return the number of authoritative zones that "dname" could belong to by * stripping up to "depth" labels from dname. Up to the first "maxzones" * authoritative zone numbers will be stored in "zonelist", ordered * deepest match first. */ int findzone(const char *dname, int class, int depth, int *zonelist, int maxzones){ char *tmpdname; char tmpdnamebuf[MAXDNAME]; char *zonename; int tmpdnamelen, zonenamelen, zonenum, i, j, c; int matches = 0; int escaped, found, done; ns_debug(ns_log_update, 4, "findzone(dname=%s, class=%d, depth=%d, \ zonelist=%p, maxzones=%d)", dname, class, depth, zonelist, maxzones); #ifdef DEBUG if (debug >= 5) { ns_debug(ns_log_update, 5, "zone dump:"); for (zonenum = 1; zonenum < nzones; zonenum++) printzoneinfo(zonenum, ns_log_update, 5); } #endif strcpy(tmpdnamebuf, dname); tmpdname = tmpdnamebuf; /* * The code to handle trailing dots and escapes is adapted * from ns_samedomain(). */ tmpdnamelen = strlen(tmpdname); /* * Ignore a trailing label separator (i.e. an unescaped dot) * in 'tmpdname'. */ if (tmpdnamelen && tmpdname[tmpdnamelen-1] == '.') { escaped = 0; /* note this loop doesn't get executed if tmpdnamelen==1 */ for (j = tmpdnamelen - 2; j >= 0; j--) if (tmpdname[j] == '\\') { if (escaped) escaped = 0; else escaped = 1; } else { break; } if (!escaped) { tmpdnamelen--; tmpdname[tmpdnamelen] = '\0'; } } for (done = i = 0; i <= depth && !done; i++) { for (zonenum = 1; zonenum < nzones; zonenum++) { if (zones[zonenum].z_type == z_nil) continue; if (zones[zonenum].z_class != class) continue; zonename = zones[zonenum].z_origin; zonenamelen = strlen(zonename); /* * Ignore a trailing label separator * (i.e. an unescaped dot) in 'zonename'. */ if (zonenamelen && zonename[zonenamelen-1] == '.') { escaped = 0; for (j = zonenamelen - 2; j >= 0; j--) if (zonename[j] == '\\') { if (escaped) escaped = 0; else escaped = 1; } else { break; } if (!escaped) zonenamelen--; } if (tmpdnamelen != zonenamelen) continue; ns_debug(ns_log_update, 5, "about to strncasecmp('%s', '%s', %d)", tmpdname, zonename, tmpdnamelen); /* XXXRTH I'm doing a special test for zonenamelen == 0 because I worry that some implementations of strncasecmp might not handle comparisions where n==0 correctly */ if (zonenamelen == 0 || !strncasecmp(tmpdname, zonename, tmpdnamelen)) { ns_debug(ns_log_update, 5, "match"); zonelist[matches++] = zonenum; if (matches == maxzones) { /* XXX should signal error */ return (matches); } } } /* * Strip off the first label if we're not already at * the root label. */ if (*tmpdname != '\0') { for (escaped = found = 0; (c = *tmpdname) && !found; tmpdname++) { if (!escaped && (c == '.')) /* * Note the loop increment will * make tmpdname point past the '.' * before the '!found' test causes * us to exit the loop. */ found = 1; if (escaped) escaped = 0; else if (c == '\\') escaped = 1; } } else done = 1; tmpdnamelen = strlen(tmpdname); } ns_debug(ns_log_update, 4, "findzone: returning %d match(es)", matches); return (matches); } /* * reapply lost updates from log file for the zone to the zone * * returns -1 on error, 0 on success, 1 if dump reload needed */ int merge_logs(struct zoneinfo *zp, char *logname) { char origin[MAXDNAME], data[MAXDATA], dnbuf[MAXDNAME], sclass[3]; char buf[BUFSIZ]; FILE *fp; u_int32_t serial, ttl, old_serial, new_serial; char *dname, *cp, *cp1; int type, class; int i, c, section, opcode, matches, zonenum = 0, err, multiline; int nonempty_lineno = -1, prev_pktdone = 0, cont = 0, inside_next = 0; int id, rcode = NOERROR; u_int32_t n; struct map *mp; ns_updrec *rrecp; struct databuf *dp; struct in_addr ina; int zonelist[MAXDNAME]; struct stat st; struct sockaddr_in empty_from; int datasize; unsigned long l; ns_updque curupd; unsigned long lutmp; empty_from.sin_family = AF_INET; empty_from.sin_addr.s_addr = htonl(INADDR_ANY); empty_from.sin_port = htons(0); /* XXX - much of this stuff is similar to that in nsupdate.c * getword_str() was used in nsupdate.c for reasons described there * getword() is used here just to be consistent with db_load() */ ns_debug(ns_log_update, 3, "merge_logs(%s)", logname); /* If there is no log file, just return. */ if (stat(logname, &st) < 0) { if (errno != ENOENT) ns_error(ns_log_update, "unexpected stat(%s) failure: %s", logname, strerror(errno)); return (-1); } fp = fopen(logname, "r"); if (fp == NULL) { ns_error(ns_log_update, "fopen(%s) failed: %s", logname, strerror(errno)); return (-1); } /* * See if we really have a log file -- it might be a zone dump * that was in the process of being isc_movefiled, or it might * be garbage! */ if (fgets(buf, sizeof(buf), fp)==NULL) { ns_error(ns_log_update, "fgets() from %s failed: %s", logname, strerror(errno)); fclose(fp); return (-1); } if (strcmp(buf, DumpSignature) == 0) { /* It's a dump; finish isc_movefile that was interrupted. */ ns_info(ns_log_update, "completing interrupted dump isc_movefile for %s", zp->z_source); fclose(fp); if (isc_movefile(logname, zp->z_source) < 0) { ns_error(ns_log_update, "isc_movefile(%s,%s) failed: %s :1", logname, zp->z_source, strerror(errno)); return (-1); } /* Finally, tell caller to reload zone. */ return (1); } if (strcmp(buf, LogSignature) != 0) { /* Not a dump and not a log; complain and then bail out. */ ns_error(ns_log_update, "invalid log file %s", logname); fclose(fp); return (-1); } ns_debug(ns_log_update, 3, "merging logs for %s from %s", zp->z_origin, logname); lineno = 1; INIT_LIST(curupd); for (;;) { err = 0; dname = NULL; if (!getword(buf, sizeof buf, fp, 0)) { if (lineno == (nonempty_lineno + 1) && !(feof(fp))) { /* * End of a nonempty line inside an update * packet or not inside an update packet. */ continue; } /* * Empty line or EOF. * * Marks completion of current update packet. */ inside_next = 0; prev_pktdone = 1; cont = 1; } else { nonempty_lineno = lineno; } if (!strcasecmp(buf, "[DYNAMIC_UPDATE]") || !strcasecmp(buf, "[IXFR_UPDATE]")) { err = 0; rcode = NOERROR; cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (cp == NULL || !sscanf((char *)cp, "id %d", &id)) id = -1; inside_next = 1; prev_pktdone = 1; cont = 1; } else if (!strcasecmp(buf, "[INCR_SERIAL]")) { /* XXXRTH not enough error checking here */ cp = fgets(buf, sizeof buf, fp); if (cp != NULL) lineno++; if (cp == NULL || !sscanf((char *)cp, "from %u to %u", &old_serial, &new_serial)) { ns_error(ns_log_update, "incr_serial problem with %s", logname); } else { serial = get_serial(zp); if (serial != old_serial) { ns_error(ns_log_update, "serial number mismatch (log=%u, zone=%u) in %s", old_serial, serial, logname); } else { set_serial(zp, new_serial); /* * The zone has changed; make sure * a dump is scheduled. */ (void)schedule_dump(zp); sched_zone_maint(zp); ns_info(ns_log_update, "set serial to %u (log file %s)", new_serial, logname); } } prev_pktdone = 1; cont = 1; } else if (!strcasecmp(buf, "[END_DELTA]")) { prev_pktdone = 1; cont = 1; } if (prev_pktdone) { if (!EMPTY(curupd)) { n = process_updates(&curupd, &rcode, empty_from); free_rrecp(&curupd, rcode, empty_from); if (n > 0) ns_info(ns_log_update, "successfully merged update id %d from log file %s", id, logname); else { ns_error(ns_log_update, "error merging update id %d from log file %s", id, logname); fclose(fp); return(-1); } } prev_pktdone = 0; if (feof(fp)) break; } if (cont) { cont = 0; continue; } if (!inside_next) continue; /* * inside the same update packet, * continue accumulating records. */ section = -1; n = strlen(buf); if (buf[n-1] == ':') buf[--n] = '\0'; for (mp = m_section; mp < m_section+M_SECTION_CNT; mp++) if (!strcasecmp(buf, mp->token)) { section = mp->val; break; } ttl = 0; type = -1; class = zp->z_class; n = 0; data[0] = '\0'; switch (section) { case S_ZONE: cp = fgets(buf, sizeof buf, fp); if (!cp) *buf = '\0'; n = sscanf(cp, "origin %s class %s serial %lu", origin, sclass, &lutmp); serial = lutmp; if (n != 3 || ns_samename(origin, zp->z_origin) != 1) err++; if (cp) lineno++; if (!err && serial != zp->z_serial) { ns_error(ns_log_update, "serial number mismatch in update id %d (log=%u, zone=%u) in %s", id, serial, zp->z_serial, logname); inside_next = 0; err++; } if (!err && inside_next) { int success; dname = origin; type = T_SOA; class = res_nametoclass(sclass, &success); if (!success) { err++; break; } matches = findzone(dname, class, 0, zonelist, MAXDNAME); if (matches) zonenum = zonelist[0]; else err++; } break; case S_PREREQ: case S_UPDATE: /* Operation code. */ if (!getword(buf, sizeof buf, fp, 0)) { err++; break; } opcode = -1; if (buf[0] == '{') { n = strlen(buf); for (i = 0; (u_int32_t)i < n; i++) buf[i] = buf[i+1]; if (buf[n-2] == '}') buf[n-2] = '\0'; } for (mp = m_opcode; mp < m_opcode+M_OPCODE_CNT; mp++) if (!strcasecmp(buf, mp->token)) { opcode = mp->val; break; } if (opcode == -1) { err++; break; } /* Owner's domain name. */ if (!getword((char *)dnbuf, sizeof dnbuf, fp, 1)) { err++; break; } n = strlen((char *)dnbuf) - 1; if (dnbuf[n] == '.') dnbuf[n] = '\0'; dname = dnbuf; ttl = 0; type = -1; class = zp->z_class; n = 0; data[0] = '\0'; (void) getword(buf, sizeof buf, fp, 1); if (isdigit(buf[0])) { /* ttl */ if (ns_parse_ttl(buf, &l) < 0) { err++; break; } ttl = l; (void) getword(buf, sizeof buf, fp, 1); } /* possibly class */ if (buf[0] != '\0') { int success; int maybe_class; maybe_class = res_nametoclass(buf, &success); if (success) { class = maybe_class; (void) getword(buf, sizeof buf, fp, 1); } } /* possibly type */ if (buf[0] != '\0') { int success; int maybe_type; maybe_type = res_nametotype(buf, &success); if (success) { type = maybe_type; (void) getword(buf, sizeof buf, fp, 1); } } if (buf[0] != '\0') /* possibly rdata */ /* * Convert the ascii data 'buf' to the proper * format based on the type and pack into * 'data'. * * XXX - same as in db_load(), * consolidation needed */ switch (type) { case T_A: if (!inet_aton(buf, &ina)) { err++; break; } n = ntohl(ina.s_addr); cp = data; PUTLONG(n, cp); n = INT32SZ; break; case T_HINFO: case T_ISDN: n = strlen(buf); data[0] = n; memcpy(data+1, buf, n); n++; if (!getword(buf, sizeof buf, fp, 0)) { i = 0; } else { endline(fp); i = strlen(buf); } data[n] = i; memcpy(data+n+1, buf, i); break; case T_SOA: case T_MINFO: case T_RP: (void) strcpy(data, buf); cp = data + strlen(data) -1; *(cp++) = 0; /* ditch dot */ if (!getword((char *)cp, sizeof data - (cp - data), fp, 1)) { err++; break; } cp += strlen((char *)cp) -1; *(cp++) = 0; /* ditch dot */ if (type != T_SOA) { n = cp - data; break; } else n = cp - data; if (class != zp->z_class || ns_samename(dname, zp->z_origin) != 1) { err++; break; } c = getnonblank(fp, logname, 1); if (c == '(') { multiline = 1; } else { multiline = 0; ungetc(c, fp); } n = getnum(fp, logname, GETNUM_SERIAL, &multiline); if (getnum_error) { err++; break; } PUTLONG(n, cp); for (i = 0; i < 4; i++) { if (getttl(fp, logname, lineno, &n, &multiline) <= 0) { err++; break; } PUTLONG(n, cp); } if (multiline) { c = getnonblank(fp, logname, 1); if (c != ')') { ungetc(c, fp); err++; break; } } n = cp - data; endline(fp); break; case T_WKS: if (!inet_aton(buf, &ina)) { err++; break; } n = ntohl(ina.s_addr); cp = data; PUTLONG(n, cp); *cp = (char)getprotocol(fp, logname ); n = INT32SZ + sizeof(char); n = getservices((int)n, data, fp, logname); break; case T_NS: case T_CNAME: case T_MB: case T_MG: case T_MR: case T_PTR: (void) strcpy(data, buf); if (makename(data, origin, sizeof(data)) == -1) { err++; break; } n = strlen(data) + 1; break; case T_MX: case T_AFSDB: case T_RT: n = 0; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); /* catch bad values */ cp = data; PUTSHORT((u_int16_t)n, cp); if (!getword(buf, sizeof(buf), fp, 1)) { err++; break; } (void) strcpy((char *)cp, buf); if (makename((char *)cp, origin, sizeof(data) - (cp-data)) == -1) { err++; break; } /* advance pointer to end of data */ cp += strlen((char *)cp) +1; /* now save length */ n = (cp - data); break; case T_PX: n = 0; data[0] = '\0'; cp = buf; while (isdigit(*cp)) n = n * 10 + (*cp++ - '0'); cp = data; PUTSHORT((u_int16_t)n, cp); for (i = 0; i < 2; i++) { if (!getword(buf, sizeof(buf), fp, 0)) { err++; break; } (void) strcpy((char *)cp, buf); cp += strlen((char *)cp) + 1; } n = cp - data; break; case T_TXT: case T_X25: i = strlen(buf); cp = data; datasize = sizeof data; cp1 = buf; while (i > MAXCHARSTRING) { if (datasize <= MAXCHARSTRING){ ns_error(ns_log_update, "record too big"); fclose(fp); return (-1); } datasize -= MAXCHARSTRING; *cp++ = (char)MAXCHARSTRING; memcpy(cp, cp1, MAXCHARSTRING); cp += MAXCHARSTRING; cp1 += MAXCHARSTRING; i -= MAXCHARSTRING; } if (datasize < i + 1) { ns_error(ns_log_update, "record too big"); fclose(fp); return (-1); } *cp++ = i; memcpy(cp, cp1, i); cp += i; n = cp - data; endline(fp); /* XXXVIX: segmented texts 4.9.5 */ break; case T_NSAP: n = inet_nsap_addr(buf, (u_char *)data, sizeof data); endline(fp); break; case T_LOC: cp = buf + (n = strlen(buf)); *cp = ' '; cp++; while ((i = getc(fp), *cp = i, i != EOF) && *cp != '\n' && (n < MAXDATA)) { cp++; n++; } if (*cp == '\n') ungetc(*cp, fp); *cp = '\0'; n = loc_aton(buf, (u_char *)data); if (n == 0) { err++; break; } endline(fp); break; case ns_t_sig: case ns_t_key: case ns_t_nxt: case ns_t_cert: { const char * errmsg = NULL; int s; s = parse_sec_rdata(buf, sizeof(buf), 1, (u_char *)data, sizeof(data), fp, zp, dnbuf, ttl, type, domain_ctx, primary_trans, &errmsg); if (s < 0) { err++; break; } break; } default: if (strcmp(buf, "\\#") != 0) { err++; break; } if (!getword(buf, sizeof buf, fp, 0) || !isdigit((unsigned char)buf[0])) { err++; break; } + errno = 0; n = strtoul(buf, &cp, 10); - if (n > 0xffff || *cp != '\0') { + if (errno != 0 || n > 0xffff || + *cp != '\0') { err++; break; } multiline = 0; i = isc_gethexstring((u_char *)data, sizeof(data), n, fp, &multiline); if (i == -1) { err++; break; } if (multiline) { c = getnonblank(fp, zp->z_updatelog, 1); if (c != ')') { ungetc(c, fp); err++; break; } multiline = 0; } endline(fp); } if (section == S_PREREQ) { ttl = 0; if (opcode == NXDOMAIN) { class = C_NONE; type = T_ANY; n = 0; } else if (opcode == YXDOMAIN) { class = C_ANY; type = T_ANY; n = 0; } else if (opcode == NXRRSET) { class = C_NONE; n = 0; } else if (opcode == YXRRSET) { if (n == 0) class = C_ANY; } } else { /* section == S_UPDATE */ if (opcode == DELETE) { ttl = 0; if (n == 0) { class = C_ANY; if (type == -1) type = T_ANY; /* WTF? C_NONE or C_ANY _must_ be the case if * we really are to delete this. If * C_NONE is used, according to process_updates(), * the class is gotten from the zone's class. * This still isn't perfect, but it will at least * work. * * Question: What is so special about the class * of the update while we are deleting?? */ } else /* if (zp->z_xferpid != XFER_ISIXFR) */ { class = C_NONE; } } } break; case S_ADDT: default: ns_debug(ns_log_update, 1, "cannot interpret section: %d", section); inside_next = 0; err++; } if (err) { ns_debug(ns_log_update, 1, "merge of update id %d failed due to error at line %d", id, lineno); free_rrecp(&curupd, FORMERR, empty_from); fclose(fp); return(-1); } rrecp = res_mkupdrec(section, dname, class, type, ttl); if (section != S_ZONE) { dp = savedata(class, type, ttl, (u_char *)data, n); dp->d_zone = zonenum; dp->d_cred = DB_C_ZONE; dp->d_clev = nlabels(zp->z_origin); dp->d_secure = DB_S_INSECURE; /* should be UNCHECKED */ rrecp->r_dp = dp; } else { rrecp->r_zone = zonenum; } APPEND(curupd, rrecp, r_link); } /* for (;;) */ INSIST(EMPTY(curupd)); fclose(fp); return (0); } /* * Create a disk database to back up zones */ int zonedump(struct zoneinfo *zp, int mode) { FILE *fp; const char *fname; struct hashbuf *htp; char *op; struct stat st; char tmp_name[MAXPATHLEN]; int escaped; char c; /* * We must check to see if Z_NEED_SOAUPDATE is set, and if so * we must do it. This won't be the case normally * (when called from ns_maint()), but it is possible if we're * exiting named. */ if (zp->z_flags & Z_NEED_SOAUPDATE) { u_int32_t serial, old_serial; old_serial = get_serial(zp); serial = old_serial + 1; if (serial == 0) serial = 1; set_serial(zp, serial); } /* Only dump zone if there is a cache specified */ if (zp->z_source && *(zp->z_source)) { ns_debug(ns_log_update, 1, "zonedump(%s)", zp->z_source); if (strlen(zp->z_source)+strlen(DumpSuffix) >= MAXPATHLEN) { ns_error(ns_log_update, "filename %s too long in zonedump", zp->z_source); /* * This problem won't ever get better, so we * clear the "need dump" flag. */ zp->z_flags &= ~Z_NEED_DUMP; return (-1); } (void)sprintf(tmp_name, "%s%s", zp->z_source, DumpSuffix); if ((fp = write_open(tmp_name)) == NULL) { ns_error(ns_log_update, "fopen() of %s failed: %s", tmp_name, strerror(errno)); return (-1); } fprintf(fp, "%s", DumpSignature); op = zp->z_origin; escaped = 0; while (*op && (((c = *op++) != '.') || escaped)) escaped = (c == '\\') && !escaped; gettime(&tt); htp = hashtab; if (nlookup(zp->z_origin, &htp, &fname, 0) != NULL) { if (db_dump(htp, fp, zp-zones, op) != OK) { ns_error(ns_log_update, "error dumping zone file %s", zp->z_source); (void)fclose(fp); return (-1); } } if (fflush(fp) == EOF) { ns_error(ns_log_update, "fflush() of %s failed: %s", tmp_name, strerror(errno)); fclose(fp); return (-1); } if (fsync(fileno(fp)) < 0) { ns_error(ns_log_update, "fsync() of %s failed: %s", tmp_name, strerror(errno)); fclose(fp); return (-1); } if (fclose(fp) == EOF) { ns_error(ns_log_update, "fclose() of %s failed: %s", tmp_name, strerror(errno)); return (-1); } /* * Try to make read only, so people will be less likely to * edit dynamic domains. */ if (stat(tmp_name, &st) < 0) { ns_error(ns_log_update, "stat(%s) failed, pressing on: %s", tmp_name, strerror(errno)); } else { zp->z_ftime = st.st_mtime; st.st_mode &= ~WRITEABLE_MASK; if (chmod(tmp_name, st.st_mode) < 0) ns_error(ns_log_update, "chmod(%s,%o) failed, pressing on: %s", tmp_name, st.st_mode, strerror(errno)); } if (mode == ISIXFR) { if (isc_movefile(tmp_name, zp->z_ixfr_tmp) < 0) { ns_error(ns_log_update, "isc_movefile(%s,%s) failed: %s :2", tmp_name, zp->z_ixfr_tmp, strerror(errno)); return (-1); } if (chmod(zp->z_source, 0644) < 0) ns_error(ns_log_update, "chmod(%s,%o) failed, pressing on: %s", zp->z_source, st.st_mode, strerror(errno)); if (isc_movefile(zp->z_ixfr_tmp, zp->z_source) < 0) { ns_error(ns_log_update, "isc_movefile(%s,%s) failed: %s :3", zp->z_ixfr_tmp, zp->z_source, strerror(errno)); return (-1); } st.st_mode &= ~WRITEABLE_MASK; if (chmod(zp->z_source, st.st_mode) < 0) ns_error(ns_log_update, "chmod(%s,%o) failed, pressing on: %s", zp->z_source, st.st_mode, strerror(errno)); } else if (mode == ISNOTIXFR) { if (isc_movefile(tmp_name, zp->z_updatelog) < 0) { ns_error(ns_log_update, "isc_movefile(%s,%s) failed: %s :4", tmp_name, zp->z_updatelog, strerror(errno)); return (-1); } if (isc_movefile(zp->z_updatelog, zp->z_source) < 0) { ns_error(ns_log_update, "isc_movefile(%s,%s) failed: %s :5", zp->z_updatelog, zp->z_source, strerror(errno)); return (-1); } } else { if (isc_movefile(tmp_name, zp->z_source) < 0) { ns_error(ns_log_update, "isc_movefile(%s,%s) failed: %s :6", tmp_name, zp->z_source, strerror(errno)); return (-1); } } } else ns_debug(ns_log_update, 1, "zonedump: no zone to dump"); zp->z_flags &= ~Z_NEED_DUMP; zp->z_dumptime = 0; return (0); } struct databuf * findzonesoa(struct zoneinfo *zp) { struct hashbuf *htp; struct namebuf *np; struct databuf *dp; const char *fname; htp = hashtab; np = nlookup(zp->z_origin, &htp, &fname, 0); if (np == NULL || fname != zp->z_origin) return (NULL); foreach_rr(dp, np, T_SOA, zp->z_class, zp - zones) return (dp); return (NULL); } u_char * findsoaserial(u_char *data) { char *cp = (char *)data; cp += strlen(cp) + 1; /* Nameserver. */ cp += strlen(cp) + 1; /* Mailbox. */ return ((u_char *)cp); } u_int32_t get_serial_unchecked(struct zoneinfo *zp) { struct databuf *dp; u_char *cp; u_int32_t ret; dp = findzonesoa(zp); if (!dp) ns_panic(ns_log_update, 1, "get_serial_unchecked(%s): can't locate zone SOA", zp->z_origin); cp = findsoaserial(dp->d_data); GETLONG(ret, cp); return (ret); } u_int32_t get_serial(struct zoneinfo *zp) { u_int32_t ret; ret = get_serial_unchecked(zp); if (ret != zp->z_serial) ns_panic(ns_log_update, 1, "get_serial(%s): db and zone serial numbers differ", zp->z_origin); return (ret); } void set_serial(struct zoneinfo *zp, u_int32_t serial) { struct databuf *dp; u_char *cp; dp = findzonesoa(zp); if (!dp) ns_panic(ns_log_update, 1, "set_serial(%s): can't locate zone SOA", zp->z_origin); cp = findsoaserial(dp->d_data); PUTLONG(serial, cp); zp->z_serial = serial; zp->z_flags &= ~Z_NEED_SOAUPDATE; zp->z_soaincrtime = 0; zp->z_updatecnt = 0; #ifdef BIND_NOTIFY if (!loading) ns_notify(zp->z_origin, zp->z_class, ns_t_soa); #endif /* * Note: caller is responsible for scheduling a dump. */ } /* * Increment serial number in zoneinfo structure and hash table SOA databuf */ int incr_serial(struct zoneinfo *zp) { u_int32_t serial, old_serial; FILE *fp, *ifp; time_t t; struct databuf *dp, *olddp; unsigned char *cp; old_serial = get_serial(zp); serial = old_serial + 1; if (serial == 0) serial = 1; set_serial(zp, serial); (void) gettime(&tt); t = (time_t)tt.tv_sec; fp = open_transaction_log(zp); if (fp == NULL) return (-1); fprintf(fp, "[INCR_SERIAL] from %u to %u %s\n", old_serial, serial, checked_ctime(&t)); if (close_transaction_log(zp, fp)<0) return (-1); if (zp->z_maintain_ixfr_base) { ifp = open_ixfr_log(zp); if (ifp == NULL) return (-1); dp = findzonesoa(zp); if (dp) { olddp = memget(BIND_DATASIZE(dp->d_size)); if (olddp != NULL) { memcpy(olddp, dp, BIND_DATASIZE(dp->d_size)); cp = findsoaserial(olddp->d_data); PUTLONG(old_serial, cp); fprintf(ifp, "update: {delete} %s. %u %s %s ", zp->z_origin, dp->d_ttl, p_class(dp->d_class), p_type(dp->d_type)); (void) rdata_dump(olddp, ifp); fprintf(ifp, "\n"); memput(olddp, BIND_DATASIZE(dp->d_size)); } fprintf(ifp, "update: {add} %s. %u %s %s ", zp->z_origin, dp->d_ttl, p_class(dp->d_class), p_type(dp->d_type)); (void) rdata_dump(dp, ifp); fprintf(ifp, "\n"); } fprintf(ifp, "[END_DELTA]\n"); if (close_ixfr_log(zp, ifp) < 0) return (-1); } /* * This shouldn't happen, but we check to be sure. */ if (!(zp->z_flags & Z_NEED_DUMP)) { ns_warning(ns_log_update, "incr_serial: Z_NEED_DUMP not set for zone '%s'", zp->z_origin); (void)schedule_dump(zp); } sched_zone_maint(zp); return (0); } void dynamic_about_to_exit(void) { struct zoneinfo *zp; ns_debug(ns_log_update, 1, "shutting down; dumping zones that need it"); for (zp = zones; zp < &zones[nzones]; zp++) { if ((zp->z_flags & Z_DYNAMIC) && ((zp->z_flags & Z_NEED_SOAUPDATE) || (zp->z_flags & Z_NEED_DUMP))) (void)zonedump(zp, ISNOTIXFR); } } diff --git a/contrib/bind/bin/ndc/ndc.c b/contrib/bind/bin/ndc/ndc.c index d0770429261d..1499184621f9 100644 --- a/contrib/bind/bin/ndc/ndc.c +++ b/contrib/bind/bin/ndc/ndc.c @@ -1,910 +1,913 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ndc.c,v 1.22 2002/06/24 07:28:55 marka Exp $"; +static const char rcsid[] = "$Id: ndc.c,v 1.25 2003/04/03 05:42:10 marka Exp $"; #endif /* not lint */ /* * Portions Copyright (c) 1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "pathnames.h" #ifndef PATH_SEP #define PATH_SEP '/' #endif typedef union { struct sockaddr_in in; #ifndef NO_SOCKADDR_UN struct sockaddr_un un; #endif } sockaddr_t; typedef void (*closure)(void *, const char *, int); static const char * program = "amnesia"; static enum { e_channel, e_signals } mode = e_channel; static const char * channel = _PATH_NDCSOCK; static const char helpfmt[] = "\t%-16s\t%s\n"; static const char * pidfile = _PATH_PIDFILE; static sockaddr_t client, server; static int quiet = 0, tracing = 0, silent = 0, client_set = 0; static int debug = 0, errors = 0, doneflag, exitflag; static int logger_show = 1; static evContext ev; static char cmd[1000]; static const char * named_path = _PATH_NAMED; static int slashcmd(void); static void slashhelp(void); static int builtincmd(void); static void command(void); static int running(int, pid_t *); static void command_channel(void); static void channel_loop(const char *, int, closure, void *); static void getpid_closure(void *, const char *, int); static void banner(struct ctl_cctx *, void *, const char *, u_int); static void done(struct ctl_cctx *, void *, const char *, u_int); static void logger(enum ctl_severity, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3); static void command_signals(void); static void stop_named(pid_t); static void start_named(const char *, int); static int fgetpid(const char *, pid_t *); static int get_sockaddr(const char *, sockaddr_t *); static size_t impute_addrlen(const struct sockaddr *); -static void vtrace(const char *, va_list); +static void vtrace(const char *, va_list) ISC_FORMAT_PRINTF(1, 0); static void trace(const char *, ...) ISC_FORMAT_PRINTF(1, 2); static void result(const char *, ...) ISC_FORMAT_PRINTF(1, 2); static void fatal(const char *, ...) ISC_FORMAT_PRINTF(1, 2); static void verror(const char *, va_list) ISC_FORMAT_PRINTF(1, 0); static void error(const char *, ...) ISC_FORMAT_PRINTF(1, 2); static void usage(const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "%s: usage error: ", program); vfprintf(stderr, fmt, args); fputc('\n', stderr); va_end(args); fatal("usage: %s \ [-l localsock] [-c channel] [-p pidfile] [-n namedpath] \ [-dqst] [command [args]]\n\ ", program); } /* Public. */ int main(int argc, char *argv[]) { char *p; int ch; if ((program = strrchr(argv[0], PATH_SEP)) != NULL) program++; else program = argv[0]; while ((ch = getopt(argc, argv, "c:p:l:n:dqst")) != -1) { switch (ch) { case 'c': channel = optarg; mode = e_channel; break; case 'p': pidfile = optarg; mode = e_signals; break; case 'l': if (!get_sockaddr(optarg, &client)) usage("bad local socket (%s)", optarg); client_set++; break; case 'n': named_path = optarg; break; case 'd': tracing++; debug++; break; case 'q': quiet++; break; case 's': silent++; break; case 't': tracing++; break; default: usage("unrecognized command option (%c)", ch); /* NOTREACHED */ } } if (mode != e_channel && client_set) usage("the -l flag is only valid for control channels"); if (mode == e_channel) { if (!get_sockaddr(channel, &server)) usage("bad channel name (%s)", channel); if (evCreate(&ev) < 0) fatal("evCreate - %s", strerror(errno)); } *(p = cmd) = '\0'; for (argc -= optind, argv += optind; argc > 0; argc--, argv++) { size_t t = strlen(*argv); if ((p - cmd) + t + 2 > sizeof cmd) usage("command too long"); strcpy(p, *argv); p += t; if (argv[1] != NULL) *p++ = ' '; *p = '\0'; } if (cmd[0] != '\0') { command(); } else { if (!quiet) result("Type help -or- /h if you need help."); for (exitflag = 0; !exitflag; (void)NULL) { if (!quiet) { printf("%s> ", program); fflush(stdout); } if (!fgets(cmd, sizeof cmd, stdin)) { if (!quiet) result("EOF"); exitflag++; continue; } if (cmd[strlen(cmd) - 1] == '\n') cmd[strlen(cmd) - 1] = '\0'; if (cmd[0] == '\0') continue; if (slashcmd()) continue; command(); } } if (mode == e_channel) evDestroy(ev); exit(errors != 0); } /* Private. */ static int slashcmd(void) { if (strncasecmp(cmd, "/help", strlen(cmd)) == 0) slashhelp(); else if (strncasecmp(cmd, "/exit", strlen(cmd)) == 0) exitflag++; else if (strncasecmp(cmd, "/trace", strlen(cmd)) == 0) result("tracing now %s", (tracing = !tracing) ? "on" : "off"); else if (strncasecmp(cmd, "/debug", strlen(cmd)) == 0) result("debugging now %s", (debug = !debug) ? "on" : "off"); else if (strncasecmp(cmd, "/quiet", strlen(cmd)) == 0) result("%s is now %s", program, (quiet = !quiet) ? "quiet" : "noisy"); else if (strncasecmp(cmd, "/silent", strlen(cmd)) == 0) result("%s is now %s", program, (silent = !silent) ? "silent" : "gregarious"); else return (0); return (1); } static void slashhelp(void) { printf(helpfmt, "/h(elp)", "this text"); printf(helpfmt, "/e(xit)", "leave this program"); printf(helpfmt, "/t(race)", "toggle tracing (protocol and system events)"); printf(helpfmt, "/d(ebug)", "toggle debugging (internal program events)"); printf(helpfmt, "/q(uiet)", "toggle quietude (prompts and results)"); printf(helpfmt, "/s(ilent)", "toggle silence (suppresses nonfatal errors)"); } struct argv { int argc; char **argv; int error; }; static char hexdigits[] = "0123456789abcdef"; static void getargs_closure(void *arg, const char *msg, int flags) { struct argv *argv = arg; int len; int i; const char *cp, *cp2; char *tp, c; UNUSED(flags); if (argv->error) return; if (argv->argc == -1) { i = atoi(msg + 4); if (i < 1) { argv->error = 1; return; } argv->argc = i; argv->argv = calloc((i+1), sizeof(char*)); return; } len = 0; cp = msg + 4; - while (*cp != NULL) { + while (*cp != '\0') { c = *cp; if (c == '%') { cp2 = strchr(hexdigits, cp[1]); if (cp2 == NULL) { argv->error = 1; return; } c = (cp2-hexdigits) << 4; cp2 = strchr(hexdigits, cp[2]); if (cp2 == NULL) { argv->error = 1; return; } c += (cp2-hexdigits); cp += 2; } if (!isalnum((unsigned)c)) { switch (c) { case '+': case '-': case '=': case '/': case '.': break; default: len++; } } len++; cp++; } i = 0; while (argv->argv[i] != NULL) i++; if (i >= argv->argc) { argv->error = 1; return; } argv->argv[i] = malloc(len + 1); if (argv->argv[i] == NULL) { argv->error = 1; return; } cp = msg + 4; tp = argv->argv[i]; - while (*cp != NULL) { + while (*cp != '\0') { c = *cp; if (c == '%') { cp2 = strchr(hexdigits, cp[1]); if (cp2 == NULL) { argv->error = 1; return; } c = (cp2-hexdigits) << 4; cp2 = strchr(hexdigits, cp[2]); if (cp2 == NULL) { argv->error = 1; return; } c += (cp2-hexdigits); cp += 2; } if (!isalnum((unsigned)c)) { switch (c) { case '+': case '-': case '=': case '/': case '.': break; default: *tp = '\\'; } } *tp++ = c; cp++; } } static int get_args(char **restp) { struct argv argv; int len, i; char *rest, *p; int result = 1; argv.argc = -1; argv.argv = NULL; argv.error = 0; channel_loop("args", 1, getargs_closure, &argv); if (argv.error) { result = 0; goto err; } len = 0; for (i = 1 ; i < argv.argc && argv.argv[i] != NULL; i++) len += strlen(argv.argv[i]) + 1; + if (len == 0) + len = 1; rest = malloc(len); if (rest == NULL) { result = 0; goto err; } p = rest; for (i = 1 ; i < argv.argc && argv.argv[i] != NULL; i++) { strcpy(p, argv.argv[i]); p += strlen(argv.argv[i]); *p++ = ' '; } if (p != rest) - p[-1] = '\0'; + p--; + p[0] = '\0'; *restp = rest; err: if (argv.argv) { for (i = 0 ; i < argv.argc && argv.argv[i] != NULL; i++) free(argv.argv[i]); free(argv.argv); } return (result); } static void exec_closure(void *arg, const char *msg, int flags) { int *result = arg; UNUSED(flags); if (atoi(msg) == 250) *result = 1; } static int try_exec(int local_quiet) { int good = 0; pid_t pid; channel_loop("exec", 1, exec_closure, &good); if (good) { sleep(3); if (!running(0, &pid)) error("name server has not restarted (yet?)"); else if (!local_quiet) result("new pid is %ld", (long)pid); } return (good); } static int builtincmd(void) { static const char spaces[] = " \t"; char *rest, *syscmd; pid_t pid; int save_quiet = quiet; int len; int freerest = 0; quiet = 1; len = strcspn(cmd, spaces); rest = cmd + len; if (*rest != '\0') rest += strspn(rest, spaces); if (*rest == '\0' && !strncasecmp(cmd, "restart", len)) { if (try_exec(save_quiet)) return (1); freerest = get_args(&rest); } syscmd = malloc(strlen(named_path) + sizeof " " + strlen(rest)); if (syscmd == NULL) fatal("malloc() failed - %s", strerror(errno)); strcpy(syscmd, named_path); if (*rest != '\0') { strcat(syscmd, " "); strcat(syscmd, rest); } if (freerest) free(rest); if (strncasecmp(cmd, "start", len) == 0) { if (running(debug, &pid)) error("name server already running? (pid %ld)", (long)pid); else start_named(syscmd, save_quiet); quiet = save_quiet; free(syscmd); return (1); } else if (strncasecmp(cmd, "restart", len) == 0) { if (!running(debug, &pid)) error("name server was not running (warning only)"); else stop_named(pid); start_named(syscmd, save_quiet); quiet = save_quiet; free(syscmd); return (1); } quiet = save_quiet; free(syscmd); return (0); } static void builtinhelp(void) { const char *fmt; switch (mode) { case e_channel: fmt = "(builtin) %s - %s\n"; break; case e_signals: fmt = helpfmt; break; default: abort(); } printf(fmt, "start", "start the server"); printf(fmt, "restart", "stop server if any, start a new one"); } static void command(void) { if (builtincmd()) return; switch (mode) { case e_channel: command_channel(); break; case e_signals: command_signals(); break; default: abort(); } } static int running(int show, pid_t *pidp) { pid_t pid; switch (mode) { case e_channel: pid = 0; channel_loop("getpid", show, getpid_closure, &pid); if (pid != 0) { if (tracing) result("pid %ld is running", (long)pid); *pidp = pid; return (1); } break; case e_signals: if (fgetpid(pidfile, pidp)) { if (tracing) result("pid %ld is running", (long)pid); return (1); } break; default: abort(); } if (show) error("pid not valid or server not running"); return (0); } static void getpid_closure(void *uap, const char *text, int flags) { pid_t *pidp = uap; const char *cp; flags = flags; if ((cp = strchr(text, '<')) != NULL) { long l = 0; char ch; while ((ch = *++cp) != '\0' && ch != '>' && isdigit(ch)) l *= 10, l += (ch - '0'); if (ch == '>') { *pidp = (pid_t)l; return; } } error("response does not contain pid (%s)", text); } static void command_channel(void) { int helping = (strcasecmp(cmd, "help") == 0); int save_quiet = quiet; if (helping) { quiet = 0; builtinhelp(); } channel_loop(cmd, !quiet, NULL, NULL); quiet = save_quiet; } struct args { const char *cmd; closure cl; void *ua; }; static void channel_loop(const char *cmdtext, int show, closure cl, void *ua) { struct ctl_cctx *ctl; struct sockaddr *client_addr; struct args a; evEvent e; int save_logger_show = logger_show; if (!client_set) client_addr = NULL; else client_addr = (struct sockaddr *)&client; a.cmd = cmdtext; a.cl = cl; a.ua = ua; logger_show = show; trace("command '%s'", cmdtext); ctl = ctl_client(ev, client_addr, impute_addrlen(client_addr), (struct sockaddr *)&server, impute_addrlen((struct sockaddr *)&server), banner, &a, 15, logger); if (ctl == NULL) { if (show) error("cannot connect to command channel (%s)", channel); } else { doneflag = 0; while (evGetNext(ev, &e, EV_WAIT) == 0) if (evDispatch(ev, e) < 0 || doneflag) break; ctl_endclient(ctl); } logger_show = save_logger_show; } static void banner(struct ctl_cctx *ctl, void *uap, const char *msg, u_int flags) { struct args *a = uap; if (msg == NULL) { trace("EOF"); doneflag = 1; return; } trace("%s", msg); if ((flags & CTL_MORE) != 0) return; if (ctl_command(ctl, a->cmd, strlen(a->cmd), done, a) < 0) { error("ctl_command failed - %s", strerror(errno)); doneflag = 1; } } static void done(struct ctl_cctx *ctl, void *uap, const char *msg, u_int flags) { struct args *a = uap; UNUSED(ctl); if (msg == NULL) { trace("EOF"); doneflag = 1; return; } if (!tracing && !quiet && strlen(msg) > 4) result("%s", msg + 4); trace("%s", msg); if (a->cl) (a->cl)(a->ua, msg, flags); if ((flags & CTL_MORE) == 0) doneflag = 1; } static void logger(enum ctl_severity ctlsev, const char *format, ...) { va_list args; va_start(args, format); switch (ctlsev) { case ctl_debug: /* FALLTHROUGH */ case ctl_warning: if (debug) vtrace(format, args); break; case ctl_error: if (logger_show) verror(format, args); break; default: va_end(args); abort(); } va_end(args); } static struct cmdsig { const char * cmd; int sig; const char * help; } cmdsigs[] = { { "dumpdb", SIGINT, "dump cache database to a file" }, { "reload", SIGHUP, "reload configuration file" }, { "stats", SIGILL, "dump statistics to a file" }, { "trace", SIGUSR1, "increment trace level" }, { "notrace", SIGUSR2, "turn off tracing" }, #ifdef SIGWINCH { "querylog", SIGWINCH, "toggle query logging" }, { "qrylog", SIGWINCH, "alias for querylog" }, #endif { NULL, 0, NULL } }; static void command_signals(void) { struct cmdsig *cmdsig; pid_t pid; if (strcasecmp(cmd, "help") == 0) { printf(helpfmt, "help", "this output"); printf(helpfmt, "status", "check for running server"); printf(helpfmt, "stop", "stop the server"); builtinhelp(); for (cmdsig = cmdsigs; cmdsig->cmd != NULL; cmdsig++) printf(helpfmt, cmdsig->cmd, cmdsig->help); } else if (strcasecmp(cmd, "status") == 0) { if (!fgetpid(pidfile, &pid)) error("pid not valid or server not running"); else result("pid %ld is running", (long)pid); } else if (strcasecmp(cmd, "stop") == 0) { if (!fgetpid(pidfile, &pid)) error("name server not running"); else stop_named(pid); } else { for (cmdsig = cmdsigs; cmdsig->cmd != NULL; cmdsig++) if (strcasecmp(cmd, cmdsig->cmd) == 0) break; if (cmdsig->cmd == NULL) error("unrecognized command (%s)", cmd); else if (!fgetpid(pidfile, &pid)) error("can't get pid (%s)", pidfile); else if (kill(pid, cmdsig->sig) < 0) error("kill() failed - %s", strerror(errno)); else trace("pid %ld sig %d OK", (long)pid, cmdsig->sig); } } static void stop_named(pid_t pid) { int n; trace("stopping named (pid %ld)", (long)pid); switch (mode) { case e_signals: if (kill(pid, SIGTERM) < 0) { error("kill(%ld, SIGTERM) failed - %s", (long)pid, strerror(errno)); return; } trace("SIGTERM ok, waiting for death"); break; case e_channel: channel_loop("stop", tracing, NULL, NULL); break; default: abort(); } for (n = 0; n < 10; n++) { if (kill(pid, 0) != 0) { trace("named (pid %ld) is dead", (long)pid); return; } sleep(1); } error("named (pid %ld) didn't die", (long)pid); } static void start_named(const char *syscmd, int local_quiet) { pid_t pid; if (system(syscmd) != 0) error("could not start new name server (%s)", syscmd); else { sleep(3); if (!running(0, &pid)) error("name server has not started (yet?)"); else if (!local_quiet) result("new pid is %ld", (long)pid); } } static int fgetpid(const char *f, pid_t *pid) { FILE *fp; int try; long t; for (try = 0; try < 5; try++) { trace("pidfile is \"%s\" (try #%d)", f, try + 1); if ((fp = fopen(f, "r")) == NULL) trace("pid file (%s) unavailable - %s", f, strerror(errno)); else if (fscanf(fp, "%ld\n", &t) != 1) trace("pid file (%s) format is bad", f); else if (*pid = (pid_t)t, fclose(fp), kill(*pid, 0) < 0) trace("pid file (%s) contains unusable pid (%d) - %s", f, *pid, strerror(errno)); else { trace("pid is %ld", (long)*pid); return (1); } sleep(1); } trace("pid not found"); return (0); } static int get_sockaddr(const char *name, sockaddr_t *addr) { char *slash; #ifndef NO_SOCKADDR_UN if (name[0] == '/') { memset(&addr->un, '\0', sizeof addr->un); addr->un.sun_family = AF_UNIX; strncpy(addr->un.sun_path, name, sizeof addr->un.sun_path - 1); addr->un.sun_path[sizeof addr->un.sun_path - 1] = '\0'; } else #endif if ((slash = strrchr(name, '/')) != NULL) { char *ibuf = malloc(slash - name + 1); if (!ibuf) usage("no memory for IP address (%s)", name); memcpy(ibuf, name, slash - name); ibuf[slash - name] = '\0'; memset(&addr->in, '\0', sizeof addr->in); if (!inet_pton(AF_INET, ibuf, &addr->in.sin_addr)) usage("bad ip address (%s)", name); if ((addr->in.sin_port = htons(atoi(slash+1))) == 0) usage("bad ip port (%s)", slash+1); addr->in.sin_family = AF_INET; free (ibuf); } else { return (0); } return (1); } static size_t impute_addrlen(const struct sockaddr *sa) { if (sa == NULL) return (0); switch (sa->sa_family) { case AF_INET: return (sizeof(struct sockaddr_in)); #ifndef NO_SOCKADDR_UN case AF_UNIX: return (sizeof(struct sockaddr_un)); #endif default: abort(); } /*NOTREACHED*/ } static void vtrace(const char *fmt, va_list ap) { if (tracing) { fprintf(stdout, "%s: [", program); vfprintf(stdout, fmt, ap); fputs("]\n", stdout); } } static void trace(const char *fmt, ...) { va_list args; va_start(args, fmt); vtrace(fmt, args); va_end(args); } static void result(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stdout, fmt, args); fputc('\n', stdout); va_end(args); } static void fatal(const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "%s: fatal error: ", program); vfprintf(stderr, fmt, args); fputc('\n', stderr); va_end(args); exit(1); } static void verror(const char *fmt, va_list ap) { fprintf(stderr, "%s: error: ", program); vfprintf(stderr, fmt, ap); fputc('\n', stderr); errors++; } static void error(const char *fmt, ...) { va_list args; va_start(args, fmt); if (silent) vtrace(fmt, args); else verror(fmt, args); va_end(args); } diff --git a/contrib/bind/bin/nslookup/getinfo.c b/contrib/bind/bin/nslookup/getinfo.c index 7189a8526aa6..3f64aa647a0a 100644 --- a/contrib/bind/bin/nslookup/getinfo.c +++ b/contrib/bind/bin/nslookup/getinfo.c @@ -1,1104 +1,1103 @@ /* * Copyright (c) 1985, 1989 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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 lint static const char sccsid[] = "@(#)getinfo.c 5.26 (Berkeley) 3/21/91"; -static const char rcsid[] = "$Id: getinfo.c,v 8.27 2002/05/22 04:06:57 marka Exp $"; +static const char rcsid[] = "$Id: getinfo.c,v 8.29.8.2 2003/06/02 09:24:39 marka Exp $"; #endif /* not lint */ /* ****************************************************************************** * * getinfo.c -- * * Routines to create requests to name servers * and interpret the answers. * * Adapted from 4.3BSD BIND gethostnamadr.c * ****************************************************************************** */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include "port_after.h" #include #include "res.h" static char *addr_list[MAXADDRS + 1]; static int addr_len[MAXADDRS + 1]; static int addr_type[MAXADDRS + 1]; static char *host_aliases[MAXALIASES]; static int host_aliases_len[MAXALIASES]; static u_char hostbuf[MAXDNAME]; typedef struct { char *name; char *domain[MAXDOMAINS]; int numDomains; char *address[MAXADDRS]; char len[MAXADDRS]; char type[MAXADDRS]; int numAddresses; } ServerTable; ServerTable server[MAXSERVERS]; typedef union { HEADER qb1; - u_char qb2[64*1024]; + u_char qb2[NS_MAXMSG]; } querybuf; typedef union { int32_t al; char ac; } align; #define GetShort(cp) ns_get16(cp); cp += INT16SZ; /* ****************************************************************************** * * GetAnswer -- * * Interprets an answer packet and retrieves the following * information: * * Results: * SUCCESS the info was retrieved. * NO_INFO the packet did not contain an answer. * NONAUTH non-authoritative information was found. * ERROR the answer was malformed. * Other errors returned in the packet header. * ****************************************************************************** */ static int GetAnswer(union res_sockaddr_union *nsAddrPtr, int queryType, char *msg, int msglen, Boolean iquery, HostInfo *hostPtr, Boolean isServer, Boolean merge) { register HEADER *headerPtr; register const u_char *cp; querybuf answer; char **aliasPtr; - u_char *eom, *bp; + u_char *eom, *bp, *ep; char **addrPtr; int *lenPtr; int *typePtr; char *namePtr; char *dnamePtr; int type, class; - int qdcount, ancount, arcount, nscount, buflen; + int qdcount, ancount, arcount, nscount; int origClass = 0; int numAliases = 0; int numAddresses = 0; int n, i, j, k, l, m; int dlen; int status; int numServers; size_t s; Boolean haveAnswer; Boolean printedAnswers = FALSE; int oldAliases; char **newAliases; int oldServers; ServerInfo **newServers; int oldAddresses; AddrInfo **newAddresses; /* * If the hostPtr was used before, free up the calloc'd areas. */ if (!merge) FreeHostInfoPtr(hostPtr); status = SendRequest(nsAddrPtr, (u_char *)msg, msglen, (u_char *) &answer, sizeof(answer), &n); if (status != SUCCESS) { if (res.options & RES_DEBUG2) printf("SendRequest failed\n"); return (status); } eom = (u_char *) &answer + n; headerPtr = (HEADER *) &answer; if (headerPtr->rcode != NOERROR) { return (headerPtr->rcode); } qdcount = ntohs(headerPtr->qdcount); ancount = ntohs(headerPtr->ancount); arcount = ntohs(headerPtr->arcount); nscount = ntohs(headerPtr->nscount); /* * If there are no answer, n.s. or additional records * then return with an error. */ if (ancount == 0 && nscount == 0 && arcount == 0) { return (NO_INFO); } - bp = hostbuf; - buflen = sizeof(hostbuf); - cp = (u_char *) &answer + HFIXEDSZ; + bp = hostbuf; + ep = hostbuf + sizeof(hostbuf); + cp = (u_char *) &answer + HFIXEDSZ; /* Skip over question section. */ while (qdcount-- > 0) { n = dn_skipname(cp, eom); if (n < 0) return (ERROR); cp += n + QFIXEDSZ; if (cp > eom) return (ERROR); } aliasPtr = host_aliases; addrPtr = addr_list; lenPtr = addr_len; typePtr = addr_type; haveAnswer = FALSE; /* * Scan through the answer resource records. * Answers for address query types are saved. * Other query type answers are just printed. */ if (ancount != 0) { if (headerPtr->ad) printf("Answer crypto-validated by server:\n"); if (!isServer && !headerPtr->aa) { printf("Non-authoritative answer:\n"); } if (queryType != T_A && queryType != T_AAAA && /* A6? */ !(iquery && queryType == T_PTR)) { while (--ancount >= 0 && cp < eom) { if ((cp = Print_rr(cp, (u_char *)&answer, eom, stdout)) == NULL) { return(ERROR); } } printedAnswers = TRUE; } else { while (--ancount >= 0 && cp < eom) { - n = dn_expand(answer.qb2, eom, cp, (char *)bp, buflen); + n = dn_expand(answer.qb2, eom, cp, (char *)bp, ep - bp); if (n < 0) return(ERROR); cp += n; if (cp + 3 * INT16SZ + INT32SZ > eom) return (ERROR); type = GetShort(cp); class = GetShort(cp); cp += INT32SZ; /* skip TTL */ dlen = GetShort(cp); if (cp + dlen > eom) return (ERROR); if (type == T_CNAME) { /* * Found an alias. */ cp += dlen; if (aliasPtr >= &host_aliases[MAXALIASES-1]) { continue; } *aliasPtr++ = (char *)bp; s = strlen((char *)bp) + 1; host_aliases_len[numAliases] = s; numAliases++; bp += s; - buflen -= s; continue; } else if (type == T_PTR) { /* * Found a "pointer" to the real name. */ - n = dn_expand(answer.qb2, eom, cp, (char *)bp, buflen); + n = dn_expand(answer.qb2, eom, cp, (char *)bp, ep - bp); if (n < 0) { cp += n; continue; } cp += n; s = strlen((char *)bp) + 1; hostPtr->name = Calloc(1, s); memcpy(hostPtr->name, bp, s); haveAnswer = TRUE; break; } else if (type != T_A && type != T_AAAA) { cp += dlen; continue; } if (type == T_A && dlen != INADDRSZ) return (ERROR); if (type == T_AAAA && dlen != 16) return (ERROR); if (haveAnswer) { /* * If we've already got 1 address, we aren't interested * in addresses with a different class. */ if (class != origClass) { cp += dlen; continue; } } else { /* * First address: record its class so we only save * additonal ones with the same attributes. */ origClass = class; if (hostPtr->name == NULL) { s = strlen((char *)bp) + 1; hostPtr->name = Calloc(1, s); memcpy(hostPtr->name, bp, s); } } - bp += (((u_int32_t)bp) % sizeof(align)); + bp += (((size_t)bp) % sizeof(align)); if (bp + dlen >= &hostbuf[sizeof(hostbuf)]) { if (res.options & RES_DEBUG) { printf("Size (%d) too big\n", dlen); } break; } if (numAddresses >= MAXADDRS) { printf("MAXADDRS exceeded: skipping address\n"); cp += dlen; continue; } memcpy(*addrPtr++ = (char *)bp, cp, dlen); *lenPtr++ = dlen; *typePtr++ = (class == C_IN) ? ((type == T_A) ? AF_INET : AF_INET6) : AF_UNSPEC; bp += dlen; cp += dlen; numAddresses++; haveAnswer = TRUE; } } } if ((queryType == T_A || queryType == T_AAAA || queryType == T_PTR) && haveAnswer) { /* * Go through the alias and address lists and return them * in the hostPtr variable. */ oldAliases = 0; if (merge && hostPtr->aliases != NULL) { while (hostPtr->aliases[oldAliases] != NULL) oldAliases++; } if (numAliases > 0) { newAliases = (char **) Calloc(1 + numAliases + oldAliases, sizeof(char *)); if (merge && hostPtr->aliases != NULL) { memcpy(newAliases, hostPtr->aliases, oldAliases * sizeof(char *)); free(hostPtr->aliases); } hostPtr->aliases = newAliases; k = oldAliases; for (i = 0; i < numAliases; i++) { for (l = 0; l < k; l++) if (!strcasecmp(hostPtr->aliases[l], host_aliases[i])) break; if (l < k) continue; hostPtr->aliases[k] = Calloc(1, host_aliases_len[i]); memcpy(hostPtr->aliases[k], host_aliases[i], host_aliases_len[i]); k++; } hostPtr->aliases[k] = NULL; } oldAddresses = 0; if (merge && hostPtr->addrList != NULL) { while (hostPtr->addrList[oldAddresses] != NULL) oldAddresses++; } if (numAddresses > 0) { newAddresses = (AddrInfo **)Calloc(1+numAddresses, sizeof(AddrInfo *)); if (merge && hostPtr->addrList != NULL) { memcpy(newAddresses, hostPtr->addrList, oldAddresses * sizeof(char *)); free(hostPtr->addrList); } hostPtr->addrList = newAddresses; k = oldAddresses; for (i = 0; i < numAddresses; i++) { for (l = 0; l < k; l++) if (hostPtr->addrList[l]->addrType == addr_type[i] && hostPtr->addrList[l]->addrLen == addr_len[i] && !memcmp(hostPtr->addrList[l]->addr, addr_list[i], addr_len[i])) break; if (l < k) continue; hostPtr->addrList[k] = (AddrInfo*)Calloc(1, sizeof(AddrInfo)); hostPtr->addrList[k]->addr = Calloc(1, addr_len[i]); hostPtr->addrList[k]->addrType = addr_type[i]; hostPtr->addrList[k]->addrLen = addr_len[i]; memcpy(hostPtr->addrList[k]->addr, addr_list[i], addr_len[i]); k++; } hostPtr->addrList[k] = NULL; } #ifdef verbose if (headerPtr->aa || nscount == 0) { hostPtr->servers = NULL; return (SUCCESS); } #else hostPtr->servers = NULL; return (SUCCESS); #endif } /* * At this point, for the T_A query type, only empty answers remain. * For other query types, additional information might be found * in the additional resource records part. */ if (!headerPtr->aa && (queryType != T_A) && (queryType != T_AAAA) && (nscount > 0 || arcount > 0)) { if (printedAnswers) { putchar('\n'); } printf("Authoritative answers can be found from:\n"); } cp = res_skip((u_char *)&answer, 2, eom); numServers = 0; if (queryType != T_A && queryType != T_AAAA) { /* * If we don't need to save the record, just print it. */ while (--nscount >= 0 && cp < eom) { if ((cp = Print_rr(cp, (u_char *) &answer, eom, stdout)) == NULL) { return(ERROR); } } } else { while (--nscount >= 0 && cp < eom) { /* * Go through the NS records and retrieve the names of hosts * that serve the requested domain. */ - n = dn_expand(answer.qb2, eom, cp, (char *)bp, buflen); + n = dn_expand(answer.qb2, eom, cp, (char *)bp, ep - bp); if (n < 0) { return(ERROR); } cp += n; s = strlen((char *)bp) + 1; dnamePtr = Calloc(1, s); /* domain name */ memcpy(dnamePtr, bp, s); if (cp + 3 * INT16SZ + INT32SZ > eom) return (ERROR); type = GetShort(cp); class = GetShort(cp); cp += INT32SZ; /* skip TTL */ dlen = GetShort(cp); if (cp + dlen > eom) return (ERROR); if (type != T_NS) { cp += dlen; } else { Boolean found; - n = dn_expand(answer.qb2, eom, cp, (char *)bp, buflen); + n = dn_expand(answer.qb2, eom, cp, (char *)bp, ep - bp); if (n < 0) { return(ERROR); } cp += n; s = strlen((char *)bp) + 1; namePtr = Calloc(1, s); /* server host name */ memcpy(namePtr, bp, s); /* * Store the information keyed by the server host name. */ found = FALSE; for (j = 0; j < numServers; j++) { if (strcasecmp(namePtr, server[j].name) == 0) { found = TRUE; free(namePtr); break; } } if (found) { server[j].numDomains++; if (server[j].numDomains <= MAXDOMAINS) { server[j].domain[server[j].numDomains-1] = dnamePtr; } } else { if (numServers >= MAXSERVERS) { break; } server[numServers].name = namePtr; server[numServers].domain[0] = dnamePtr; server[numServers].numDomains = 1; server[numServers].numAddresses = 0; numServers++; } } } } /* * Additional resource records contain addresses of servers. */ cp = res_skip((u_char*)&answer, 3, eom); if (queryType != T_A && queryType != T_AAAA) { /* * If we don't need to save the record, just print it. */ while (--arcount >= 0 && cp < eom) { if ((cp = Print_rr(cp, (u_char *) &answer, eom, stdout)) == NULL) { return(ERROR); } } } else { while (--arcount >= 0 && cp < eom) { - n = dn_expand(answer.qb2, eom, cp, (char *)bp, buflen); + n = dn_expand(answer.qb2, eom, cp, (char *)bp, ep - bp); if (n < 0) { break; } cp += n; if (cp + 3 * INT16SZ + INT32SZ > eom) return (ERROR); type = GetShort(cp); class = GetShort(cp); cp += INT32SZ; /* skip TTL */ dlen = GetShort(cp); if (cp + dlen > eom) return (ERROR); if (type != T_A && type != T_AAAA) { cp += dlen; continue; } else { if (type == T_A && dlen != INADDRSZ) return (ERROR); if (type == T_AAAA && dlen != 16) return (ERROR); for (j = 0; j < numServers; j++) { if (strcasecmp((char *)bp, server[j].name) == 0) { server[j].numAddresses++; if (server[j].numAddresses <= MAXADDRS) { server[j].address[server[j].numAddresses-1] = Calloc(1,dlen); memcpy(server[j].address[server[j].numAddresses-1], cp, dlen); server[j].len[server[j].numAddresses-1] = dlen; server[j].type[server[j].numAddresses-1] = (type == T_A) ? AF_INET : AF_INET6; break; } } } cp += dlen; } } } /* * If we are returning name server info, transfer it to the hostPtr. */ oldServers = 0; if (merge && hostPtr->servers != NULL) { while (hostPtr->servers[oldServers] != NULL) oldServers++; } if (numServers > 0) { newServers = (ServerInfo **) Calloc(numServers+oldServers+1, sizeof(ServerInfo *)); if (merge && hostPtr->servers != NULL) { memcpy(newServers, hostPtr->servers, oldServers * sizeof(ServerInfo *)); free(hostPtr->servers); } hostPtr->servers = newServers; k = oldServers; for (i = 0; i < numServers; i++) { for (l = 0; l < k; l++) if (!strcasecmp(hostPtr->servers[l]->name, server[i].name)) break; if (l < k) { free(server[i].name); for (j = 0; j < server[i].numDomains; j++) free(server[i].domain[j]); } else { hostPtr->servers[l] = (ServerInfo *) Calloc(1, sizeof(ServerInfo)); hostPtr->servers[l]->name = server[i].name; k++; hostPtr->servers[l]->domains = (char **) Calloc(server[i].numDomains+1,sizeof(char *)); for (j = 0; j < server[i].numDomains; j++) { hostPtr->servers[l]->domains[j] = server[i].domain[j]; } hostPtr->servers[l]->domains[j] = NULL; } oldAddresses = 0; if (merge && hostPtr->servers[l]->addrList != NULL) while (hostPtr->servers[l]->addrList[oldAddresses] != NULL) oldAddresses++; newAddresses = (AddrInfo **) Calloc(server[i].numAddresses+oldAddresses+1, sizeof(AddrInfo *)); if (merge && hostPtr->servers[l]->addrList != NULL) { memcpy(newAddresses, hostPtr->servers[l]->addrList, sizeof(AddrInfo *) * oldAddresses); free(hostPtr->servers[l]->addrList); } hostPtr->servers[l]->addrList = newAddresses; m = oldAddresses; for (j = 0; j < server[l].numAddresses; j++) { for (n = 0; n < m; n++) if (hostPtr->servers[l]->addrList[n]->addrType == server[i].type[j] && hostPtr->servers[l]->addrList[n]->addrLen == server[i].len[j] && !memcmp(hostPtr->servers[l]->addrList[n]->addr, server[i].address[j], server[i].len[j])) break; if (n < m) { free(server[i].address[j]); continue; } hostPtr->servers[l]->addrList[m] = (AddrInfo*)Calloc(1, sizeof(AddrInfo)); hostPtr->servers[l]->addrList[m]->addr = server[i].address[j]; hostPtr->servers[l]->addrList[m]->addrType = server[i].type[j]; hostPtr->servers[l]->addrList[m]->addrLen = server[i].len[j]; m++; } hostPtr->servers[l]->addrList[m] = NULL; } hostPtr->servers[k] = NULL; } switch (queryType) { case T_AAAA: case T_A: return NONAUTH; case T_PTR: if (iquery) return NO_INFO; /* fall through */ default: return SUCCESS; } } /* ******************************************************************************* * * GetHostInfo -- * * Retrieves host name, address and alias information * for a domain. * * Algorithm from res_nsearch(). * * Results: * ERROR - res_nmkquery failed. * + return values from GetAnswer() * ******************************************************************************* */ int GetHostInfoByName(nsAddrPtr, queryClass, queryType, name, hostPtr, isServer, merge) union res_sockaddr_union *nsAddrPtr; int queryClass; int queryType; const char *name; HostInfo *hostPtr; Boolean isServer; Boolean merge; { int n; register int result; register char **domain; const char *cp; Boolean got_nodata = FALSE; union res_sockaddr_union ina; Boolean tried_as_is = FALSE; char tmp[NS_MAXDNAME]; /* Catch explicit addresses */ if ((queryType == T_A) && IsAddr(name, &ina)) { hostPtr->name = Calloc(strlen(name)+3, 1); (void)sprintf(hostPtr->name,"[%s]",name); switch (ina.sin.sin_family) { case AF_INET: hostPtr->aliases = NULL; hostPtr->servers = NULL; hostPtr->addrList = (AddrInfo **)Calloc(2, sizeof(AddrInfo *)); hostPtr->addrList[0] = (AddrInfo *)Calloc(1, sizeof(AddrInfo)); hostPtr->addrList[0]->addr = Calloc(INT32SZ, sizeof(char)); memcpy(hostPtr->addrList[0]->addr, &ina.sin.sin_addr, INADDRSZ); hostPtr->addrList[0]->addrType = AF_INET; hostPtr->addrList[0]->addrLen = INADDRSZ; hostPtr->addrList[1] = NULL; break; case AF_INET6: hostPtr->aliases = NULL; hostPtr->servers = NULL; hostPtr->addrList = (AddrInfo **)Calloc(2, sizeof(AddrInfo *)); hostPtr->addrList[0] = (AddrInfo *)Calloc(1, sizeof(AddrInfo)); hostPtr->addrList[0]->addr = Calloc(1, 16); memcpy(hostPtr->addrList[0]->addr, &ina.sin6.sin6_addr, 16); hostPtr->addrList[0]->addrType = AF_INET6; hostPtr->addrList[0]->addrLen = 16; hostPtr->addrList[1] = NULL; break; } return(SUCCESS); } result = NXDOMAIN; for (cp = name, n = 0; *cp; cp++) if (*cp == '.') n++; if (n == 0 && (cp = res_hostalias(&res, name, tmp, sizeof tmp))) { printf("Aliased to \"%s\"\n\n", cp); return (GetHostDomain(nsAddrPtr, queryClass, queryType, cp, (char *)NULL, hostPtr, isServer, merge)); } /* * If there are dots in the name already, let's just give it a try * 'as is'. The threshold can be set with the "ndots" option. */ if (n >= (int)res.ndots) { result = GetHostDomain(nsAddrPtr, queryClass, queryType, name, (char *)NULL, hostPtr, isServer, merge); if (result == SUCCESS) return (result); if (result == NO_INFO) got_nodata++; tried_as_is++; } /* * We do at least one level of search if * - there is no dot and RES_DEFNAME is set, or * - there is at least one dot, there is no trailing dot, * and RES_DNSRCH is set. */ if ((n == 0 && (res.options & RES_DEFNAMES) != 0) || (n != 0 && *--cp != '.' && (res.options & RES_DNSRCH) != 0)) for (domain = res.dnsrch; *domain != NULL; domain++) { result = GetHostDomain(nsAddrPtr, queryClass, queryType, name, *domain, hostPtr, isServer, merge); /* * If no server present, give up. * If name isn't found in this domain, * keep trying higher domains in the search list * (if that's enabled). * On a NO_INFO error, keep trying, otherwise * a wildcard entry of another type could keep us * from finding this entry higher in the domain. * If we get some other error (negative answer or * server failure), then stop searching up, * but try the input name below in case it's fully-qualified. */ if (result == SUCCESS || result == NO_RESPONSE) return result; if (result == NO_INFO) got_nodata++; if ((result != NXDOMAIN && result != NO_INFO) || (res.options & RES_DNSRCH) == 0) break; } /* if we have not already tried the name "as is", do that now. * note that we do this regardless of how many dots were in the * name or whether it ends with a dot. */ if (!tried_as_is && (result = GetHostDomain(nsAddrPtr, queryClass, queryType, name, (char *)NULL, hostPtr, isServer, merge) ) == SUCCESS) return (result); if (got_nodata) result = NO_INFO; return (result); } /* * Perform a query on the concatenation of name and domain, * removing a trailing dot from name if domain is NULL. */ int GetHostDomain(nsAddrPtr, queryClass, queryType, name, domain, hostPtr, isServer, merge) union res_sockaddr_union *nsAddrPtr; int queryClass; int queryType; const char *name; char *domain; HostInfo *hostPtr; Boolean isServer; Boolean merge; { querybuf buf; char nbuf[2*MAXDNAME+2]; const char *longname = nbuf; int n; if (domain == NULL) { /* * Check for trailing '.'; * copy without '.' if present. */ n = strlen(name) - 1; if (name[n] == '.' && n < (int)sizeof(nbuf) - 1) { memcpy(nbuf, name, n); nbuf[n] = '\0'; } else longname = name; } else { (void)sprintf(nbuf, "%.*s.%.*s", MAXDNAME, name, MAXDNAME, domain); longname = nbuf; } n = res_nmkquery(&res, QUERY, longname, queryClass, queryType, NULL, 0, 0, buf.qb2, sizeof(buf)); if (n < 0) { if (res.options & RES_DEBUG) { printf("Res_nmkquery failed\n"); } return (ERROR); } n = GetAnswer(nsAddrPtr, queryType, (char *)&buf, n, 0, hostPtr, isServer, merge); /* * GetAnswer didn't find a name, so set it to the specified one. */ if (n == NONAUTH) { if (hostPtr->name == NULL) { size_t len = strlen(longname) + 1; hostPtr->name = Calloc(len, sizeof(char)); memcpy(hostPtr->name, longname, len); } } return(n); } /* ******************************************************************************* * * GetHostInfoByAddr -- * * Performs a PTR lookup in in-addr.arpa to find the host name * that corresponds to the given address. * * Results: * ERROR - res_nmkquery failed. * + return values from GetAnswer() * ******************************************************************************* */ int GetHostInfoByAddr(union res_sockaddr_union *nsAddrPtr, union res_sockaddr_union *address, HostInfo * hostPtr) { int n; querybuf buf; char qbuf[MAXDNAME]; char qbuf2[MAXDNAME]; char *p = NULL; int ismapped = 0; switch (address->sin.sin_family) { case AF_INET: p = (char *) &address->sin.sin_addr.s_addr; mapped: (void)sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa", ((unsigned)p[3 + (ismapped ? 12 : 0)] & 0xff), ((unsigned)p[2 + (ismapped ? 12 : 0)] & 0xff), ((unsigned)p[1 + (ismapped ? 12 : 0)] & 0xff), ((unsigned)p[0 + (ismapped ? 12 : 0)] & 0xff)); break; case AF_INET6: p = (char *)address->sin6.sin6_addr.s6_addr; if (IN6_IS_ADDR_V4MAPPED(&address->sin6.sin6_addr) || IN6_IS_ADDR_V4COMPAT(&address->sin6.sin6_addr)) { ismapped = 1; goto mapped; } (void)sprintf(qbuf, "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." "ip6.arpa", p[15] & 0xf, (p[15] >> 4) & 0xf, p[14] & 0xf, (p[14] >> 4) & 0xf, p[13] & 0xf, (p[13] >> 4) & 0xf, p[12] & 0xf, (p[12] >> 4) & 0xf, p[11] & 0xf, (p[11] >> 4) & 0xf, p[10] & 0xf, (p[10] >> 4) & 0xf, p[9] & 0xf, (p[9] >> 4) & 0xf, p[8] & 0xf, (p[8] >> 4) & 0xf, p[7] & 0xf, (p[7] >> 4) & 0xf, - p[6] & 0xf, (p[4] >> 4) & 0xf, + p[6] & 0xf, (p[6] >> 4) & 0xf, p[5] & 0xf, (p[5] >> 4) & 0xf, p[4] & 0xf, (p[4] >> 4) & 0xf, p[3] & 0xf, (p[3] >> 4) & 0xf, p[2] & 0xf, (p[2] >> 4) & 0xf, p[1] & 0xf, (p[1] >> 4) & 0xf, p[0] & 0xf, (p[0] >> 4) & 0xf); (void)sprintf(qbuf2, "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." "ip6.int", p[15] & 0xf, (p[15] >> 4) & 0xf, p[14] & 0xf, (p[14] >> 4) & 0xf, p[13] & 0xf, (p[13] >> 4) & 0xf, p[12] & 0xf, (p[12] >> 4) & 0xf, p[11] & 0xf, (p[11] >> 4) & 0xf, p[10] & 0xf, (p[10] >> 4) & 0xf, p[9] & 0xf, (p[9] >> 4) & 0xf, p[8] & 0xf, (p[8] >> 4) & 0xf, p[7] & 0xf, (p[7] >> 4) & 0xf, - p[6] & 0xf, (p[4] >> 4) & 0xf, + p[6] & 0xf, (p[6] >> 4) & 0xf, p[5] & 0xf, (p[5] >> 4) & 0xf, p[4] & 0xf, (p[4] >> 4) & 0xf, p[3] & 0xf, (p[3] >> 4) & 0xf, p[2] & 0xf, (p[2] >> 4) & 0xf, p[1] & 0xf, (p[1] >> 4) & 0xf, p[0] & 0xf, (p[0] >> 4) & 0xf); break; } n = res_nmkquery(&res, QUERY, qbuf, C_IN, T_PTR, NULL, 0, NULL, buf.qb2, sizeof buf); if (n < 0) { if (res.options & RES_DEBUG) { printf("res_nmkquery() failed\n"); } return (ERROR); } n = GetAnswer(nsAddrPtr, T_PTR, (char *) &buf, n, 1, hostPtr, 1, 0); if (n == SUCCESS) { switch (address->sin.sin_family) { case AF_INET: hostPtr->addrList = (AddrInfo **)Calloc(2, sizeof(AddrInfo *)); hostPtr->addrList[0] = (AddrInfo *)Calloc(1, sizeof(AddrInfo)); hostPtr->addrList[0]->addr = Calloc(INT32SZ, sizeof(char)); memcpy(hostPtr->addrList[0]->addr, p, INADDRSZ); hostPtr->addrList[0]->addrType = AF_INET; hostPtr->addrList[0]->addrLen = 4; hostPtr->addrList[1] = NULL; break; case AF_INET6: hostPtr->addrList = (AddrInfo **)Calloc(2, sizeof(AddrInfo *)); hostPtr->addrList[0] = (AddrInfo *)Calloc(1, sizeof(AddrInfo)); hostPtr->addrList[0]->addr = Calloc(16, sizeof(char)); memcpy(hostPtr->addrList[0]->addr, p, 16); hostPtr->addrList[0]->addrType = AF_INET6; hostPtr->addrList[0]->addrLen = 16; hostPtr->addrList[1] = NULL; break; } } if (n == SUCCESS || ismapped || address->sin.sin_family != AF_INET6) return n; n = res_nmkquery(&res, QUERY, qbuf2, C_IN, T_PTR, NULL, 0, NULL, buf.qb2, sizeof buf); if (n < 0) { if (res.options & RES_DEBUG) { printf("res_nmkquery() failed\n"); } return (ERROR); } n = GetAnswer(nsAddrPtr, T_PTR, (char *) &buf, n, 1, hostPtr, 1, 0); if (n == SUCCESS) { hostPtr->addrList = (AddrInfo **)Calloc(2, sizeof(AddrInfo *)); - hostPtr->addrList[0] = (AddrInfo *)Calloc(1, sizeof(AddrInfo *)); + hostPtr->addrList[0] = (AddrInfo *)Calloc(1, sizeof(AddrInfo)); hostPtr->addrList[0]->addr = Calloc(16, sizeof(char)); memcpy(hostPtr->addrList[0]->addr, p, 16); hostPtr->addrList[0]->addrType = AF_INET6; hostPtr->addrList[0]->addrLen = 16; hostPtr->addrList[1] = NULL; } return n; } /* ******************************************************************************* * * FreeHostInfoPtr -- * * Deallocates all the calloc'd areas for a HostInfo variable. * ******************************************************************************* */ void FreeHostInfoPtr(hostPtr) register HostInfo *hostPtr; { int i, j; if (hostPtr->name != NULL) { free(hostPtr->name); hostPtr->name = NULL; } if (hostPtr->aliases != NULL) { i = 0; while (hostPtr->aliases[i] != NULL) { free(hostPtr->aliases[i]); i++; } free((char *)hostPtr->aliases); hostPtr->aliases = NULL; } if (hostPtr->addrList != NULL) { i = 0; while (hostPtr->addrList[i] != NULL) { free(hostPtr->addrList[i]->addr); free(hostPtr->addrList[i]); i++; } free((char *)hostPtr->addrList); hostPtr->addrList = NULL; } if (hostPtr->servers != NULL) { i = 0; while (hostPtr->servers[i] != NULL) { if (hostPtr->servers[i]->name != NULL) { free(hostPtr->servers[i]->name); } if (hostPtr->servers[i]->domains != NULL) { j = 0; while (hostPtr->servers[i]->domains[j] != NULL) { free(hostPtr->servers[i]->domains[j]); j++; } free((char *)hostPtr->servers[i]->domains); } if (hostPtr->servers[i]->addrList != NULL) { j = 0; while (hostPtr->servers[i]->addrList[j] != NULL) { free(hostPtr->servers[i]->addrList[j]->addr); free(hostPtr->servers[i]->addrList[j]); j++; } free((char *)hostPtr->servers[i]->addrList); } free((char *)hostPtr->servers[i]); i++; } free((char *)hostPtr->servers); hostPtr->servers = NULL; } } diff --git a/contrib/bind/bin/nslookup/main.c b/contrib/bind/bin/nslookup/main.c index e992bb068213..7213c427cc02 100644 --- a/contrib/bind/bin/nslookup/main.c +++ b/contrib/bind/bin/nslookup/main.c @@ -1,1331 +1,1331 @@ /* * Copyright (c) 1985, 1989 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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 lint char copyright[] = "@(#) Copyright (c) 1985,1989 Regents of the University of California.\n\ All rights reserved.\n\ @(#) Portions Copyright (c) 1996-1999 Internet Software Consortium.\n"; #endif /* not lint */ #ifndef lint static const char sccsid[] = "@(#)main.c 5.42 (Berkeley) 3/3/91"; -static const char rcsid[] = "$Id: main.c,v 8.24 2002/05/26 03:12:20 marka Exp $"; +static const char rcsid[] = "$Id: main.c,v 8.25 2003/01/26 11:38:56 marka Exp $"; #endif /* not lint */ /* ****************************************************************************** * * main.c -- * * Main routine and some action routines for the name server * lookup program. * * Andrew Cherenson * U.C. Berkeley Computer Science Div. * CS298-26, Fall 1985 * ****************************************************************************** */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include #include "res.h" #include "pathnames.h" int yylex(void); /* * Name of a top-level name server. Can be changed with * the "set root" command. */ #ifndef ROOT_SERVER #define ROOT_SERVER "f.root-servers.net." #endif char rootServerName[NAME_LEN] = ROOT_SERVER; /* * Declare a resolver context. */ struct __res_state res; /* * Info about the most recently queried host. */ HostInfo curHostInfo; int curHostValid = FALSE; /* * Info about the default name server. */ HostInfo *defaultPtr = NULL; char defaultServer[NAME_LEN]; union res_sockaddr_union defaultAddr; /* * Initial name server query type is Address. */ int queryType = T_A; int queryClass = C_IN; /* * Stuff for Interrupt (control-C) signal handler. */ extern SIG_FN IntrHandler(int); FILE *filePtr; jmp_buf env; /* * Browser command for help. */ const char *pager; static void CvtAddrToPtr(char *name); static void ReadRC(void); /* * Forward declarations. */ static void LocalServer(HostInfo *defaultPtr); static void res_re_init(void); static void res_dnsrch(char *cp); static void Usage(void); static void ShowOptions(void); static void UnionFromAddr(union res_sockaddr_union *u, int family, void *addr) { memset(u, 0, sizeof *u); switch (family) { case AF_INET: u->sin.sin_family = AF_INET; u->sin.sin_port = htons(nsport); memcpy(&u->sin.sin_addr, addr, 4); #ifdef HAVE_SA_LEN u->sin.sin_len = sizeof(u->sin); #endif break; case AF_INET6: u->sin6.sin6_family = AF_INET6; u->sin6.sin6_port = htons(nsport); memcpy(&u->sin6.sin6_addr, addr, 16); #ifdef HAVE_SA_LEN u->sin6.sin6_len = sizeof(u->sin6); #endif break; default: abort(); } } /* ****************************************************************************** * * main -- * * Initializes the resolver library and determines the address * of the initial name server. The yylex routine is used to * read and perform commands. * ****************************************************************************** */ int main(int argc, char **argv) { char *wantedHost = NULL; Boolean useLocalServer; int result; int i; /* * Initialize the resolver library routines. */ if (res_ninit(&res) == -1) { fprintf(stderr,"*** Can't initialize resolver.\n"); exit(1); } /* * Allocate space for the default server's host info and * find the server's address and name. If the resolver library * already has some addresses for a potential name server, * then use them. Otherwise, see if the current host has a server. * Command line arguments may override the choice of initial server. */ defaultPtr = (HostInfo *) Calloc(1, sizeof(HostInfo)); /* * Parse the arguments: * no args = go into interactive mode, use default host as server * 1 arg = use as host name to be looked up, default host will be server * non-interactive mode * 2 args = 1st arg: * if it is '-', then * ignore but go into interactive mode * else * use as host name to be looked up, * go into non-interactive mode * 2nd arg: name or inet address of server * * "Set" options are specified with a leading - and must come before * any arguments. For example, to find the well-known services for * a host, type "nslookup -query=wks host" */ ReadRC(); /* look for options file */ ++argv; --argc; /* skip prog name */ while (argc && *argv[0] == '-' && argv[0][1]) { (void) SetOption (&(argv[0][1])); ++argv; --argc; } if (argc > 2) { Usage(); } if (argc && *argv[0] != '-') { wantedHost = *argv; /* name of host to be looked up */ } useLocalServer = FALSE; if (argc == 2) { int nscount = 0; union res_sockaddr_union u[MAXNS]; struct addrinfo *answer = NULL; struct addrinfo *cur = NULL; struct addrinfo hint; /* * Use an explicit name server. If the hostname lookup fails, * default to the server(s) in resolv.conf. */ memset(u, 0, sizeof(u)); memset(&hint, 0, sizeof(hint)); hint.ai_socktype = SOCK_DGRAM; if (!getaddrinfo(*++argv, NULL, &hint, &answer)) { for (cur = answer; cur != NULL; cur = cur->ai_next) { if (nscount == MAXNS) break; switch (cur->ai_addr->sa_family) { case AF_INET6: u[nscount].sin6 = *(struct sockaddr_in6*)cur->ai_addr; u[nscount++].sin6.sin6_port = htons(nsport); break; case AF_INET: u[nscount].sin = *(struct sockaddr_in*)cur->ai_addr; u[nscount++].sin.sin_port = htons(nsport); break; } } if (nscount != 0) res_setservers(&res, u, nscount); freeaddrinfo(answer);; } } if (res.nscount == 0 || useLocalServer) { LocalServer(defaultPtr); } else { int nscount = 0; union res_sockaddr_union u[MAXNS]; nscount = res_getservers(&res, u, MAXNS); for (i = 0; i < nscount; i++) { if (u[i].sin.sin_family == AF_INET && u[i].sin.sin_addr.s_addr == INADDR_ANY) { LocalServer(defaultPtr); break; } else { result = GetHostInfoByAddr(&u[i], &u[i], defaultPtr); if (result != SUCCESS) { char t[80]; switch (u[i].sin.sin_family) { case AF_INET: inet_ntop(AF_INET, &u[i].sin.sin_addr, t, sizeof(t)); break; case AF_INET6: inet_ntop(AF_INET6, &u[i].sin6.sin6_addr, t, sizeof(t)); break; default: strcpy(t, ""); break; } fprintf(stderr, "*** Can't find server name for address %s: %s\n", t, DecodeError(result)); } else { defaultAddr = u[i]; break; } } } /* * If we have exhausted the list, tell the user about the * command line argument to specify an address. */ if (i == res.nscount) { fprintf(stderr, "*** Default servers are not available\n"); exit(1); } } strcpy(defaultServer, defaultPtr->name); #ifdef DEBUG #ifdef DEBUG2 res.options |= RES_DEBUG2; #endif res.options |= RES_DEBUG; res.retry = 2; #endif /* DEBUG */ /* * If we're in non-interactive mode, look up the wanted host and quit. * Otherwise, print the initial server's name and continue with * the initialization. */ if (wantedHost != (char *) NULL) { LookupHost(wantedHost, 0); } else { PrintHostInfo(stdout, "Default Server:", defaultPtr); pager = getenv("PAGER"); if (pager == NULL) { pager = _PATH_PAGERCMD; } /* * Setup the environment to allow the interrupt handler to return here. */ (void) setjmp(env); /* * Return here after a longjmp. */ signal(SIGINT, IntrHandler); signal(SIGPIPE, SIG_IGN); /* * Read and evaluate commands. The commands are described in commands.l * Yylex returns 0 when ^D or 'exit' is typed. */ printf("> "); fflush(stdout); while(yylex()) { printf("> "); fflush(stdout); } } exit(0); } static void LocalServer(defaultPtr) HostInfo *defaultPtr; { char hostName[NAME_LEN]; (void) gethostname(hostName, sizeof(hostName)); memset(&defaultAddr, 0, sizeof(defaultAddr)); defaultAddr.sin.sin_addr.s_addr = htonl(INADDR_ANY); defaultAddr.sin.sin_family = AF_INET; defaultAddr.sin.sin_port = htons(nsport); #ifdef HAVE_SA_LEN defaultAddr.sin.sin_len = sizeof(defaultAddr.sin); #endif (void) GetHostInfoByName(&defaultAddr, C_IN, T_A, "0.0.0.0", defaultPtr, 1, 0); free(defaultPtr->name); defaultPtr->name = Calloc(1, sizeof(hostName)+1); strcpy(defaultPtr->name, hostName); } /* ****************************************************************************** * * Usage -- * * Lists the proper methods to run the program and exits. * ****************************************************************************** */ static void Usage(void) { fprintf(stderr, "Usage:\n"); fprintf(stderr, " nslookup [-opt ...] # interactive mode using default server\n"); fprintf(stderr, " nslookup [-opt ...] - server # interactive mode using 'server'\n"); fprintf(stderr, " nslookup [-opt ...] host # just look up 'host' using default server\n"); fprintf(stderr, " nslookup [-opt ...] host server # just look up 'host' using 'server'\n"); exit(1); } /* ****************************************************************************** * * IsAddr -- * * Returns TRUE if the string looks like an Internet address. * A string with a trailing dot is not an address, even if it looks * like one. * ****************************************************************************** */ Boolean IsAddr(host, addrPtr) const char *host; union res_sockaddr_union *addrPtr; /* If return TRUE, contains IP address */ { if (inet_pton(AF_INET6, host, &addrPtr->sin6.sin6_addr) == 1) { addrPtr->sin6.sin6_family = AF_INET6; addrPtr->sin6.sin6_port = htons(nsport); #ifdef HAVE_SA_LEN addrPtr->sin6.sin6_len = sizeof(addrPtr->sin6); #endif } else if (inet_pton(AF_INET, host, &addrPtr->sin.sin_addr) == 1) { addrPtr->sin.sin_family = AF_INET; addrPtr->sin.sin_port = htons(nsport); #ifdef HAVE_SA_LEN addrPtr->sin.sin_len = sizeof(addrPtr->sin); #endif } else return FALSE; return TRUE; } /* ****************************************************************************** * * SetDefaultServer -- * * Changes the default name server to the one specified by * the first argument. The command "server name" uses the current * default server to lookup the info for "name". The command * "lserver name" uses the original server to lookup "name". * * Side effects: * This routine will cause a core dump if the allocation requests fail. * * Results: * SUCCESS The default server was changed successfully. * NONAUTH The server was changed but addresses of * other servers who know about the requested server * were returned. * Errors No info about the new server was found or * requests to the current server timed-out. * ****************************************************************************** */ int SetDefaultServer(string, local) char *string; Boolean local; { register HostInfo *newDefPtr; union res_sockaddr_union servAddr; union res_sockaddr_union addr; char newServer[NAME_LEN]; int result; int tresult; int i; int j; /* * Parse the command line. It maybe of the form "server name", * "lserver name" or just "name". */ if (local) { i = matchString (" lserver ", string); if (i > 0) { j = pickString(string + i, newServer, sizeof newServer); if (j == 0) { /* value was too big for newServer variable */ fprintf(stderr, "SetDefaultServer: invalid name: %s\n", string + i); return(ERROR); } } } else { i = matchString(" server ", string); if (i > 0) { j = pickString(string + i, newServer, sizeof newServer); if (j == 0) { /* value was too big for newServer variable */ fprintf(stderr, "SetDefaultServer: invalid name: %s\n", string + i); return(ERROR); } } } if (i == 0) { i = pickString(string, newServer, sizeof newServer); if (i == 0) { /* value was too big for newServer variable */ fprintf(stderr,"SetDefaultServer: invalid name: %s\n", string); return(ERROR); } } /* * Allocate space for a HostInfo variable for the new server. Don't * overwrite the old HostInfo struct because info about the new server * might not be found and we need to have valid default server info. */ newDefPtr = (HostInfo *) Calloc(1, sizeof(HostInfo)); /* * A 'local' lookup uses the original server that the program was * initialized with. * * Check to see if we have the address of the server or the * address of a server who knows about this domain. * XXX For now, just use the first address in the list. */ if (local) { servAddr = defaultAddr; } else if (defaultPtr->addrList != NULL) { UnionFromAddr(&servAddr, defaultPtr->addrList[0]->addrType, defaultPtr->addrList[0]->addr); } else { - UnionFromAddr(&servAddr, defaultPtr->addrList[0]->addrType, + UnionFromAddr(&servAddr, defaultPtr->servers[0]->addrList[0]->addrType, defaultPtr->servers[0]->addrList[0]->addr); } result = ERROR; if (IsAddr(newServer, &addr)) { result = GetHostInfoByAddr(&servAddr, &addr, newDefPtr); /* If we can't get the name, fall through... */ } if (result != SUCCESS && result != NONAUTH) { result = GetHostInfoByName(&servAddr, C_IN, T_A, newServer, newDefPtr, 1, 0); if (result == SUCCESS || result == NONAUTH || result == NO_INFO) tresult = GetHostInfoByName(&servAddr, C_IN, T_AAAA, newServer, newDefPtr, 1, 1); if (result == NO_INFO) result = tresult; } /* If we ask for an A record and get none back, but get an NS record for the NS server, this is the NONAUTH case. We must check whether we got an IP address for the NS server or not. */ if ((result == SUCCESS || result == NONAUTH) && ((newDefPtr->addrList && newDefPtr->addrList[0] != 0) || (newDefPtr->servers && newDefPtr->servers[0] && newDefPtr->servers[0]->addrList[0] != 0))) { /* * Found info about the new server. Free the resources for * the old server. */ FreeHostInfoPtr(defaultPtr); free((char *)defaultPtr); defaultPtr = newDefPtr; strcpy(defaultServer, defaultPtr->name); PrintHostInfo(stdout, "Default Server:", defaultPtr); return(SUCCESS); } else { fprintf(stderr, "*** Can't find address for server %s: %s\n", newServer, DecodeError(result)); free((char *)newDefPtr); return(result); } } /* ****************************************************************************** * * DoLoookup -- * * Common subroutine for LookupHost and LookupHostWithServer. * * Results: * SUCCESS - the lookup was successful. * Misc. Errors - an error message is printed if the lookup failed. * ****************************************************************************** */ static int DoLookup(host, servPtr, serverName) char *host; HostInfo *servPtr; char *serverName; { int result; union res_sockaddr_union servAddr; union res_sockaddr_union addr; /* Skip escape character */ if (host[0] == '\\') host++; /* * If the user gives us an address for an address query, * silently treat it as a PTR query. If the query type is already * PTR, then convert the address into the in-addr.arpa format. * * Use the address of the server if it exists, otherwise use the * address of a server who knows about this domain. * XXX For now, just use the first address in the list. */ if (servPtr->addrList != NULL) { UnionFromAddr(&servAddr, servPtr->addrList[0]->addrType, servPtr->addrList[0]->addr); } else { UnionFromAddr(&servAddr, servPtr->servers[0]->addrList[0]->addrType, servPtr->servers[0]->addrList[0]->addr); } /* * RFC1123 says we "SHOULD check the string syntactically for a * dotted-decimal number before looking it up [...]" (p. 13). */ if ((queryType == T_A || queryType == T_AAAA) && IsAddr(host, &addr)) { result = GetHostInfoByAddr(&servAddr, &addr, &curHostInfo); } else { if (queryType == T_PTR) { CvtAddrToPtr(host); } result = GetHostInfoByName(&servAddr, queryClass, queryType, host, &curHostInfo, 0, 0); } switch (result) { case SUCCESS: /* * If the query was for an address, then the &curHostInfo * variable can be used by Finger. * There's no need to print anything for other query types * because the info has already been printed. */ if (queryType == T_A || queryType == T_AAAA) { curHostValid = TRUE; PrintHostInfo(filePtr, "Name:", &curHostInfo); } break; /* * No Authoritative answer was available but we got names * of servers who know about the host. */ case NONAUTH: PrintHostInfo(filePtr, "Name:", &curHostInfo); break; case NO_INFO: fprintf(stderr, "*** No %s (%s) records available for %s\n", DecodeType(queryType), p_type(queryType), host); break; case TIME_OUT: fprintf(stderr, "*** Request to %s timed-out\n", serverName); break; default: fprintf(stderr, "*** %s can't find %s: %s\n", serverName, host, DecodeError(result)); } return result; } /* ****************************************************************************** * * LookupHost -- * * Asks the default name server for information about the * specified host or domain. The information is printed * if the lookup was successful. * * Results: * ERROR - the output file could not be opened. * + results of DoLookup * ****************************************************************************** */ int LookupHost(string, putToFile) char *string; Boolean putToFile; { char host[NAME_LEN]; char file[PATH_MAX]; int result; int i; /* * Invalidate the current host information to prevent Finger * from using bogus info. */ curHostValid = FALSE; /* * Parse the command string into the host and * optional output file name. * */ i = pickString(string, host, sizeof host); if (i == 0) { /* string was too long for host variable */ fprintf(stderr, "*** invalid name: %s\n", string); return(ERROR); } if (!putToFile) { filePtr = stdout; } else { filePtr = OpenFile(string, file, sizeof file); if (filePtr == NULL) { fprintf(stderr, "*** Can't open %s for writing\n", file); return(ERROR); } fprintf(filePtr,"> %s\n", string); } PrintHostInfo(filePtr, "Server:", defaultPtr); result = DoLookup(host, defaultPtr, defaultServer); if (putToFile) { fclose(filePtr); filePtr = NULL; } return(result); } /* ****************************************************************************** * * LookupHostWithServer -- * * Asks the name server specified in the second argument for * information about the host or domain specified in the first * argument. The information is printed if the lookup was successful. * * Address info about the requested name server is obtained * from the default name server. This routine will return an * error if the default server doesn't have info about the * requested server. Thus an error return status might not * mean the requested name server doesn't have info about the * requested host. * * Comments from LookupHost apply here, too. * * Results: * ERROR - the output file could not be opened. * + results of DoLookup * ****************************************************************************** */ int LookupHostWithServer(char *string, Boolean putToFile) { char file[PATH_MAX]; char host[NAME_LEN]; char server[NAME_LEN]; int result; static HostInfo serverInfo; int i; int j; union res_sockaddr_union u; curHostValid = FALSE; i = pickString(string, host, sizeof host); if (i == 0) { /* value was too big for host variable */ fprintf(stderr, "*** invalid name: %s\n", string); return(ERROR); } j = pickString(string + i, server, sizeof server); if (j == 0) { /* value was too big for server variable */ fprintf(stderr, "*** invalid server name: %s\n", string + i); return(ERROR); } if (!putToFile) { filePtr = stdout; } else { filePtr = OpenFile(string, file, sizeof file); if (filePtr == NULL) { fprintf(stderr, "*** Can't open %s for writing\n", file); return(ERROR); } fprintf(filePtr,"> %s\n", string); } if (defaultPtr->addrList != NULL) UnionFromAddr(&u, defaultPtr->addrList[0]->addrType, defaultPtr->addrList[0]->addr); else UnionFromAddr(&u, defaultPtr->servers[0]->addrList[0]->addrType, defaultPtr->servers[0]->addrList[0]->addr); result = GetHostInfoByName(&u, C_IN, T_A, server, &serverInfo, 1, 0); if (result == NO_INFO) result = GetHostInfoByName(&u, C_IN, T_AAAA, server, &serverInfo, 1, 1); if (result != SUCCESS) { fprintf(stderr,"*** Can't find address for server %s: %s\n", server, DecodeError(result)); } else { PrintHostInfo(filePtr, "Server:", &serverInfo); result = DoLookup(host, &serverInfo, server); } if (putToFile) { fclose(filePtr); filePtr = NULL; } return(result); } /* ****************************************************************************** * * SetOption -- * * This routine is used to change the state information * that affect the lookups. The command format is * set keyword[=value] * Most keywords can be abbreviated. Parsing is very simplistic-- * A value must not be separated from its keyword by white space. * * Valid keywords: Meaning: * all lists current values of options. * ALL lists current values of options, including * hidden options. * [no]d2 turn on/off extra debugging mode. * [no]debug turn on/off debugging mode. * [no]defname use/don't use default domain name. * [no]search use/don't use domain search list. * domain=NAME set default domain name to NAME. * [no]ignore ignore/don't ignore trunc. errors. * query=value set default query type to value, * value is one of the query types in RFC883 * without the leading T_. (e.g., A, HINFO) * [no]recurse use/don't use recursive lookup. * retry=# set number of retries to #. * root=NAME change root server to NAME. * time=# set timeout length to #. * [no]vc use/don't use virtual circuit. * port TCP/UDP port to server. * * Deprecated: * [no]primary use/don't use primary server. * * Results: * SUCCESS the command was parsed correctly. * ERROR the command was not parsed correctly. * ****************************************************************************** */ int SetOption(option) register char *option; { char type[NAME_LEN]; char *ptr; int tmp; int i; while (isspace(*option)) ++option; if (strncmp (option, "set ", 4) == 0) option += 4; while (isspace(*option)) ++option; if (*option == 0) { fprintf(stderr, "*** Invalid set command\n"); return(ERROR); } else { if (strncmp(option, "all", 3) == 0) { ShowOptions(); } else if (strncmp(option, "ALL", 3) == 0) { ShowOptions(); } else if (strncmp(option, "d2", 2) == 0) { /* d2 (more debug) */ res.options |= (RES_DEBUG | RES_DEBUG2); } else if (strncmp(option, "nod2", 4) == 0) { res.options &= ~RES_DEBUG2; printf("d2 mode disabled; still in debug mode\n"); } else if (strncmp(option, "def", 3) == 0) { /* defname */ res.options |= RES_DEFNAMES; } else if (strncmp(option, "nodef", 5) == 0) { res.options &= ~RES_DEFNAMES; } else if (strncmp(option, "do", 2) == 0) { /* domain */ ptr = strchr(option, '='); if (ptr != NULL) { i = pickString(++ptr, res.defdname, sizeof res.defdname); if (i == 0) { /* name too long or nothing there */ fprintf(stderr, "** invalid 'domain' value: %s\n", ptr) ; return(ERROR); } res_re_init(); } } else if (strncmp(option, "deb", 1) == 0) { /* debug */ res.options |= RES_DEBUG; } else if (strncmp(option, "nodeb", 5) == 0) { res.options &= ~(RES_DEBUG | RES_DEBUG2); } else if (strncmp(option, "ig", 2) == 0) { /* ignore */ res.options |= RES_IGNTC; } else if (strncmp(option, "noig", 4) == 0) { res.options &= ~RES_IGNTC; } else if (strncmp(option, "po", 2) == 0) { /* port */ ptr = strchr(option, '='); if (ptr != NULL) { sscanf(++ptr, "%hu", &nsport); } #ifdef deprecated } else if (strncmp(option, "pri", 3) == 0) { /* primary */ res.options |= RES_PRIMARY; } else if (strncmp(option, "nopri", 5) == 0) { res.options &= ~RES_PRIMARY; #endif } else if (strncmp(option, "q", 1) == 0 || /* querytype */ strncmp(option, "ty", 2) == 0) { /* type */ ptr = strchr(option, '='); if (ptr != NULL) { i = pickString(++ptr, type, sizeof type); if (i == 0) { /* value too big or nothing there */ fprintf(stderr, "*** invalid type value: %s\n", ptr) ; return(ERROR); } i = StringToType(type, queryType, stderr); if (ns_t_xfr_p(i)) { fprintf(stderr, "*** qtype may not be a zone transfer\n"); return(ERROR); } queryType = i; } } else if (strncmp(option, "cl", 2) == 0) { /* query class */ ptr = strchr(option, '='); if (ptr != NULL) { i = pickString(++ptr, type, sizeof type); if (i == 0) { /* value too big or nothing there */ fprintf(stderr, "*** invalid class : %s\n", ptr) ; return(ERROR); } queryClass = StringToClass(type, queryClass, stderr); } } else if (strncmp(option, "rec", 3) == 0) { /* recurse */ res.options |= RES_RECURSE; } else if (strncmp(option, "norec", 5) == 0) { res.options &= ~RES_RECURSE; } else if (strncmp(option, "ret", 3) == 0) { /* retry */ ptr = strchr(option, '='); if (ptr != NULL) { sscanf(++ptr, "%d", &tmp); if (tmp >= 0) { res.retry = tmp; } } } else if (strncmp(option, "ro", 2) == 0) { /* root */ ptr = strchr(option, '='); if (ptr != NULL) { i = pickString(++ptr, rootServerName, sizeof rootServerName); if (i == 0) { /* value too big or nothing there */ fprintf(stderr, "*** invalid root server name : %s\n", ptr) ; return(ERROR) ; } } } else if (strncmp(option, "sea", 3) == 0) { /* search list */ res.options |= RES_DNSRCH; } else if (strncmp(option, "nosea", 5) == 0) { res.options &= ~RES_DNSRCH; } else if (strncmp(option, "srchl", 5) == 0) { /* domain search list */ ptr = strchr(option, '='); if (ptr != NULL) { res_dnsrch(++ptr); } } else if (strncmp(option, "ti", 2) == 0) { /* timeout */ ptr = strchr(option, '='); if (ptr != NULL) { sscanf(++ptr, "%d", &tmp); if (tmp >= 0) { res.retrans = tmp; } } } else if (strncmp(option, "v", 1) == 0) { /* vc */ res.options |= RES_USEVC; } else if (strncmp(option, "nov", 3) == 0) { res.options &= ~RES_USEVC; } else { fprintf(stderr, "*** Invalid option: %s\n", option); return(ERROR); } } return(SUCCESS); } /* * Fake a reinitialization when the domain is changed. */ static void res_re_init(void) { register char *cp, **pp; int n; /* find components of local domain that might be searched */ pp = res.dnsrch; *pp++ = res.defdname; for (cp = res.defdname, n = 0; *cp; cp++) if (*cp == '.') n++; cp = res.defdname; for (; n >= LOCALDOMAINPARTS && pp < res.dnsrch + MAXDFLSRCH; n--) { cp = strchr(cp, '.'); *pp++ = ++cp; } *pp = 0; res.options |= RES_INIT; } #define SRCHLIST_SEP '/' static void res_dnsrch(char *cp) { char **pp; int n; (void)strncpy(res.defdname, cp, sizeof(res.defdname) - 1); res.defdname[sizeof(res.defdname) - 1] = '\0'; if ((cp = strchr(res.defdname, '\n')) != NULL) *cp = '\0'; /* * Set search list to be blank-separated strings * on rest of line. */ cp = res.defdname; pp = res.dnsrch; *pp++ = cp; for (n = 0; *cp && pp < res.dnsrch + MAXDNSRCH; cp++) { if (*cp == SRCHLIST_SEP) { *cp = '\0'; n = 1; } else if (n) { *pp++ = cp; n = 0; } } if ((cp = strchr(pp[-1], SRCHLIST_SEP)) != NULL) { *cp = '\0'; } *pp = NULL; } /* ****************************************************************************** * * ShowOptions -- * * Prints out the state information used by the resolver * library and other options set by the user. * ****************************************************************************** */ static void ShowOptions(void) { register char **cp; PrintHostInfo(stdout, "Default Server:", defaultPtr); if (curHostValid) { PrintHostInfo(stdout, "Host:", &curHostInfo); } printf("Set options:\n"); printf(" %sdebug \t", (res.options & RES_DEBUG) ? "" : "no"); printf(" %sdefname\t", (res.options & RES_DEFNAMES) ? "" : "no"); printf(" %ssearch\t", (res.options & RES_DNSRCH) ? "" : "no"); printf(" %srecurse\n", (res.options & RES_RECURSE) ? "" : "no"); printf(" %sd2\t\t", (res.options & RES_DEBUG2) ? "" : "no"); printf(" %svc\t\t", (res.options & RES_USEVC) ? "" : "no"); printf(" %signoretc\t", (res.options & RES_IGNTC) ? "" : "no"); printf(" port=%u\n", nsport); printf(" querytype=%s\t", p_type(queryType)); printf(" class=%s\t", p_class(queryClass)); printf(" timeout=%d\t", res.retrans); printf(" retry=%d\n", res.retry); printf(" root=%s\n", rootServerName); printf(" domain=%s\n", res.defdname); cp = res.dnsrch; if (cp != NULL) { printf(" srchlist=%s", *cp); for (cp++; *cp; cp++) { printf("%c%s", SRCHLIST_SEP, *cp); } putchar('\n'); } putchar('\n'); } #undef SRCHLIST_SEP /* ****************************************************************************** * * PrintHelp -- * * Displays the help file. * ****************************************************************************** */ void PrintHelp(void) { char cmd[PATH_MAX]; sprintf(cmd, "%s %s", pager, _PATH_HELPFILE); system(cmd); } /* ****************************************************************************** * * CvtAddrToPtr -- * * Convert a dotted-decimal Internet address into the standard * PTR format (reversed address with .in-arpa. suffix). * * Assumes the argument buffer is large enougth to hold the result. * ****************************************************************************** */ static void CvtAddrToPtr(name) char *name; { const char *p; int ip[4]; union res_sockaddr_union addr; if (IsAddr(name, &addr)) { switch (addr.sin.sin_family) { case AF_INET: p = inet_ntoa(addr.sin.sin_addr); if (sscanf(p, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]) == 4) sprintf(name, "%d.%d.%d.%d.in-addr.arpa.", ip[3], ip[2], ip[1], ip[0]); break; case AF_INET6: sprintf(name, "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." "ip6.arpa", addr.sin6.sin6_addr.s6_addr[15] & 0xf, (addr.sin6.sin6_addr.s6_addr[15] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[14] & 0xf, (addr.sin6.sin6_addr.s6_addr[14] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[13] & 0xf, (addr.sin6.sin6_addr.s6_addr[13] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[12] & 0xf, (addr.sin6.sin6_addr.s6_addr[12] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[11] & 0xf, (addr.sin6.sin6_addr.s6_addr[11] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[10] & 0xf, (addr.sin6.sin6_addr.s6_addr[10] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[9] & 0xf, (addr.sin6.sin6_addr.s6_addr[9] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[8] & 0xf, (addr.sin6.sin6_addr.s6_addr[8] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[7] & 0xf, (addr.sin6.sin6_addr.s6_addr[7] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[6] & 0xf, (addr.sin6.sin6_addr.s6_addr[6] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[5] & 0xf, (addr.sin6.sin6_addr.s6_addr[5] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[4] & 0xf, (addr.sin6.sin6_addr.s6_addr[4] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[3] & 0xf, (addr.sin6.sin6_addr.s6_addr[3] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[2] & 0xf, (addr.sin6.sin6_addr.s6_addr[2] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[1] & 0xf, (addr.sin6.sin6_addr.s6_addr[1] >> 4) & 0xf, addr.sin6.sin6_addr.s6_addr[0] & 0xf, (addr.sin6.sin6_addr.s6_addr[0] >> 4) & 0xf); break; } } } /* ****************************************************************************** * * ReadRC -- * * Use the contents of ~/.nslookuprc as options. * ****************************************************************************** */ static void ReadRC(void) { register FILE *fp; register char *cp; char buf[PATH_MAX]; if ((cp = getenv("HOME")) != NULL && (strlen(cp) + strlen(_PATH_NSLOOKUPRC)) < sizeof(buf)) { (void) strcpy(buf, cp); (void) strcat(buf, _PATH_NSLOOKUPRC); if ((fp = fopen(buf, "r")) != NULL) { while (fgets(buf, sizeof(buf), fp) != NULL) { if ((cp = strchr(buf, '\n')) != NULL) { *cp = '\0'; } (void) SetOption(buf); } (void) fclose(fp); } } } diff --git a/contrib/bind/bin/nslookup/send.c b/contrib/bind/bin/nslookup/send.c index 738f66add051..4afcb568afae 100644 --- a/contrib/bind/bin/nslookup/send.c +++ b/contrib/bind/bin/nslookup/send.c @@ -1,404 +1,404 @@ /* * Copyright (c) 1985, 1989 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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 lint static const char sccsid[] = "@(#)send.c 5.18 (Berkeley) 3/2/91"; -static const char rcsid[] = "$Id: send.c,v 8.14 2002/05/10 04:35:09 marka Exp $"; +static const char rcsid[] = "$Id: send.c,v 8.14.10.1 2003/06/02 05:59:56 marka Exp $"; #endif /* not lint */ /* ****************************************************************************** * * send.c -- * * Routine to send request packets to a name server. * * Based on "@(#)res_send.c 6.25 (Berkeley) 6/1/90". * ****************************************************************************** */ /* * Send query to name server and wait for reply. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "res.h" static int s = -1; /* socket used for communications */ unsigned short nsport = NAMESERVER_PORT; /* ****************************************************************************** * * SendRequest -- * * Sends a request packet to a name server whose address * is specified by the first argument and returns with * the answer packet. * * Results: * SUCCESS - the request was sent and an answer * was received. * TIME_OUT - the virtual circuit connection timed-out * or a reply to a datagram wasn't received. * * ****************************************************************************** */ int SendRequest(union res_sockaddr_union *nsAddrPtr, const u_char *buf, int buflen, u_char *answer, u_int anslen, int *trueLenPtr) { int n, try, v_circuit, resplen; ISC_SOCKLEN_T salen; int gotsomewhere = 0, connected = 0; int connreset = 0; u_short id, len; u_char *cp; fd_set dsmask; struct timeval timeout; const HEADER *hp = (const HEADER *) buf; HEADER *anhp = (HEADER *) answer; struct iovec iov[2]; int terrno = ETIMEDOUT; char junk[512]; struct sockaddr_storage sa; int family = nsAddrPtr->sin.sin_family; int clen = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); if (res.options & RES_DEBUG2) { printf("------------\nSendRequest(), len %d\n", buflen); Print_query(buf, buf + buflen, 1); } v_circuit = (res.options & RES_USEVC) || buflen > PACKETSZ; id = hp->id; /* * Send request, RETRY times, or until successful */ for (try = 0; try < res.retry; try++) { usevc: if (v_circuit) { int truncated = 0; /* * Use virtual circuit; * at most one attempt per server. */ try = res.retry; if (s < 0) { s = socket(family, SOCK_STREAM, 0); if (s < 0) { terrno = errno; if (res.options & RES_DEBUG) perror("socket (vc) failed"); continue; } if (connect(s, (struct sockaddr *)nsAddrPtr, clen) < 0) { terrno = errno; if (res.options & RES_DEBUG) perror("connect failed"); (void) close(s); s = -1; continue; } } /* * Send length & message */ - __putshort(buflen, (u_char *)&len); + ns_put16(buflen, (u_char *)&len); iov[0].iov_base = (caddr_t)&len; iov[0].iov_len = INT16SZ; DE_CONST(buf, iov[1].iov_base); iov[1].iov_len = buflen; if (writev(s, iov, 2) != INT16SZ + buflen) { terrno = errno; if (res.options & RES_DEBUG) perror("write failed"); (void) close(s); s = -1; continue; } /* * Receive length & response */ cp = answer; len = INT16SZ; while ((n = read(s, (char *)cp, (int)len)) > 0) { cp += n; if ((len -= n) <= 0) break; } if (n <= 0) { terrno = errno; if (res.options & RES_DEBUG) perror("read failed"); (void) close(s); s = -1; /* * A long running process might get its TCP * connection reset if the remote server was * restarted. Requery the server instead of * trying a new one. When there is only one * server, this means that a query might work * instead of failing. We only allow one reset * per query to prevent looping. */ if (terrno == ECONNRESET && !connreset) { connreset = 1; } continue; } cp = answer; if ((resplen = ns_get16((u_char*)cp)) > (int)anslen) { if (res.options & RES_DEBUG) fprintf(stderr, "response truncated\n"); len = anslen; truncated = 1; } else len = resplen; while (len != 0 && (n = read(s, (char *)cp, (int)len)) > 0) { cp += n; len -= n; } if (n <= 0) { terrno = errno; if (res.options & RES_DEBUG) perror("read failed"); (void) close(s); s = -1; continue; } if (truncated) { /* * Flush rest of answer * so connection stays in synch. */ anhp->tc = 1; len = resplen - anslen; while (len != 0) { n = (len > sizeof(junk) ? sizeof(junk) : len); if ((n = read(s, junk, n)) > 0) len -= n; else break; } } } else { /* * Use datagrams. */ if (s < 0) { s = socket(family, SOCK_DGRAM, 0); if (s < 0) { terrno = errno; if (res.options & RES_DEBUG) perror("socket (dg) failed"); continue; } } #if BSD >= 43 if (connected == 0) { if (connect(s, (struct sockaddr *)nsAddrPtr, clen) < 0) { if (res.options & RES_DEBUG) perror("connect"); continue; } connected = 1; } if (send(s, buf, buflen, 0) != buflen) { if (res.options & RES_DEBUG) perror("send"); continue; } #else /* BSD */ if (sendto(s, (const char *)buf, buflen, 0, (struct sockaddr *) nsAddrPtr, clen) != buflen) { if (res.options & RES_DEBUG) perror("sendto"); continue; } #endif /* * Wait for reply */ timeout.tv_sec = (res.retrans << try); if (timeout.tv_sec <= 0) timeout.tv_sec = 1; timeout.tv_usec = 0; wait: FD_ZERO(&dsmask); FD_SET(s, &dsmask); n = select(s+1, &dsmask, (fd_set *)NULL, (fd_set *)NULL, &timeout); if (n < 0) { if (res.options & RES_DEBUG) perror("select"); continue; } if (n == 0) { /* * timeout */ if (res.options & RES_DEBUG) printf("timeout\n"); #if BSD >= 43 gotsomewhere = 1; #endif continue; } salen = sizeof sa; resplen = recvfrom(s, (char *)answer, anslen, 0, (struct sockaddr *)&sa, &salen); if (resplen <= 0) { if (res.options & RES_DEBUG) perror("recvfrom"); continue; } gotsomewhere = 1; if (id != anhp->id) { /* * response from old query, ignore it */ if (res.options & RES_DEBUG2) { printf("------------\nOld answer:\n"); Print_query(answer, answer+resplen, 1); } goto wait; } if (!(res.options & RES_IGNTC) && anhp->tc) { /* * get rest of answer; * use TCP with same server. */ if (res.options & RES_DEBUG) printf("truncated answer\n"); (void) close(s); s = -1; v_circuit = 1; goto usevc; } } if (res.options & RES_DEBUG) { if (res.options & RES_DEBUG2) printf("------------\nGot answer (%d bytes):\n", resplen); else printf("------------\nGot answer:\n"); Print_query(answer, answer+resplen, 1); } (void) close(s); s = -1; *trueLenPtr = resplen; return (SUCCESS); } if (s >= 0) { (void) close(s); s = -1; } if (v_circuit == 0) if (gotsomewhere == 0) return NO_RESPONSE; /* no nameservers found */ else return TIME_OUT; /* no answer obtained */ else if (errno == ECONNREFUSED) return NO_RESPONSE; else return ERROR; } /* * This routine is for closing the socket if a virtual circuit is used and * the program wants to close it. * * Called from the interrupt handler. */ void SendRequest_close(void) { if (s != -1) { (void) close(s); s = -1; } } diff --git a/contrib/bind/bin/nsupdate/nsupdate.c b/contrib/bind/bin/nsupdate/nsupdate.c index c8e8ce8df12d..3a452cc99d49 100644 --- a/contrib/bind/bin/nsupdate/nsupdate.c +++ b/contrib/bind/bin/nsupdate/nsupdate.c @@ -1,682 +1,683 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: nsupdate.c,v 8.27 2001/06/18 14:43:46 marka Exp $"; +static const char rcsid[] = "$Id: nsupdate.c,v 8.30 2003/04/03 05:51:07 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "../named/db_defs.h" /* XXX all of this stuff should come from libbind.a */ /* * Map class and type names to number */ struct map { char token[10]; int val; }; struct map class_strs[] = { { "in", C_IN }, { "chaos", C_CHAOS }, { "hs", C_HS }, }; #define M_CLASS_CNT (sizeof(class_strs) / sizeof(struct map)) struct map type_strs[] = { { "a", T_A }, { "ns", T_NS }, { "cname", T_CNAME }, { "soa", T_SOA }, { "mb", T_MB }, { "mg", T_MG }, { "mr", T_MR }, { "null", T_NULL }, { "wks", T_WKS }, { "ptr", T_PTR }, { "hinfo", T_HINFO }, { "minfo", T_MINFO }, { "mx", T_MX }, { "txt", T_TXT }, { "rp", T_RP }, { "afsdb", T_AFSDB }, { "x25", T_X25 }, { "isdn", T_ISDN }, { "rt", T_RT }, { "nsap", T_NSAP }, { "nsap_ptr", T_NSAP_PTR }, { "sig", T_SIG }, { "key", T_KEY }, { "px", T_PX }, { "loc", T_LOC }, { "nxt", T_NXT }, { "eid", T_EID }, { "nimloc", T_NIMLOC }, { "srv", T_SRV }, { "atma", T_ATMA }, { "naptr", T_NAPTR }, { "kx", ns_t_kx }, { "cert", ns_t_cert }, { "aaaa", ns_t_aaaa }, }; #define M_TYPE_CNT (sizeof(type_strs) / sizeof(struct map)) struct map section_strs[] = { { "zone", S_ZONE }, { "prereq", S_PREREQ }, { "update", S_UPDATE }, { "reserved", S_ADDT }, }; #define M_SECTION_CNT (sizeof(section_strs) / sizeof(struct map)) struct map opcode_strs[] = { { "nxdomain", NXDOMAIN }, { "yxdomain", YXDOMAIN }, { "nxrrset", NXRRSET }, { "yxrrset", YXRRSET }, { "delete", DELETE }, { "add", ADD }, }; #define M_OPCODE_CNT (sizeof(opcode_strs) / sizeof(struct map)) static int getcharstring(char *, char *, int, int, int); static char *progname; static void usage(void); static int getword_str(char *, int, char **, char *); static struct __res_state res; int dns_findprimary (res_state, char *, struct ns_tsig_key *, char *, int, struct in_addr *); /* * format of file read by nsupdate is kept the same as the log * file generated by updates, so that the log file can be fed * to nsupdate to reconstruct lost updates. * * file is read on line at a time using fgets() rather than * one word at a time using getword() so that it is easy to * adapt nsupdate to read piped input from other scripts * * overloading of class/type has to be deferred to res_update() * because class is needed by res_update() to determined the * zone to which a resource record belongs */ int main(int argc, char **argv) { FILE *fp = NULL; char buf[BUFSIZ], buf2[BUFSIZ]; char dnbuf[MAXDNAME], data[MAXDATA]; char *r_dname, *cp, *startp, *endp, *svstartp; char section[15], opcode[10]; int i, c, n, n1, inside, lineno = 0, vc = 0, debug = 0, r_size, r_section, r_opcode, prompt = 0, ret = 0, stringtobin = 0; int16_t r_class, r_type; u_int32_t r_ttl; struct map *mp; ns_updrec *rrecp; ns_updque listuprec; - extern int getopt(); - extern char *optarg; - extern int optind, opterr, optopt; ns_tsig_key key; char *keyfile=NULL, *keyname=NULL; progname = argv[0]; while ((c = getopt(argc, argv, "dsvk:n:")) != -1) { switch (c) { case 'v': vc = 1; break; case 'd': debug = 1; break; case 's': stringtobin = 1; break; case 'k': { /* -k keydir:keyname */ char *colon; if ((colon=strchr(optarg, ':'))==NULL) { fprintf(stderr, "key option argument should be keydir:keyname\n"); exit(1); } keyname=colon+1; keyfile=optarg; *colon='\0'; break; } case 'n': keyname=optarg; break; default: usage(); } } INIT_LIST(listuprec); if (keyfile) { #ifdef PARSE_KEYFILE if ((fp=fopen(keyfile, "r"))==NULL) { perror("open keyfile"); exit(1); } /* now read the header info from the file */ if ((i=fread(buf, 1, BUFSIZ, fp)) < 5) { fclose(fp); exit(1); } fclose(fp); fp=NULL; p=buf; n=strlen(p); /* get length of strings */ n1=strlen("Private-key-format: v"); if (n1 > n || strncmp(buf, "Private-key-format: v", n1)) { fprintf(stderr, "Invalid key file format\n"); exit(1); /* not a match */ } p+=n1; /* advance pointer */ sscanf((char *)p, "%d.%d", &file_major, &file_minor); /* should do some error checking with these someday */ while (*p++!='\n'); /* skip to end of line */ n=strlen(p); /* get length of strings */ n1=strlen("Algorithm: "); if (n1 > n || strncmp(p, "Algorithm: ", n1)) { fprintf(stderr, "Invalid key file format\n"); exit(1); /* not a match */ } p+=n1; /* advance pointer */ if (sscanf((char *)p, "%d", &alg)!=1) { fprintf(stderr, "Invalid key file format\n"); exit(1); } while (*p++!='\n'); /* skip to end of line */ n=strlen(p); /* get length of strings */ n1=strlen("Key: "); if (n1 > n || strncmp(p, "Key: ", n1)) { fprintf(stderr, "Invalid key file format\n"); exit(1); /* not a match */ } p+=n1; /* advance pointer */ pp=p; while (*pp++!='\n'); /* skip to end of line, terminate it */ *--pp='\0'; key.data=malloc(1024*sizeof(char)); key.len=b64_pton(p, key.data, 1024); strcpy(key.name, keyname); strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT"); #else /* use the dst* routines to parse the key files * * This requires that both the .key and the .private files * exist in your cwd, so the keyfile parmeter here is * assumed to be a path in which the K*.{key,private} files * exist. */ DST_KEY *dst_key; char cwd[PATH_MAX+1]; if (getcwd(cwd, PATH_MAX)==NULL) { perror("unable to get current directory"); exit(1); } if (chdir(keyfile)<0) { fprintf(stderr, "unable to chdir to %s: %s\n", keyfile, strerror(errno)); exit(1); } dst_init(); dst_key = dst_read_key(keyname, 0 /* not used for private keys */, KEY_HMAC_MD5, DST_PRIVATE); if (!dst_key) { fprintf(stderr, "dst_read_key: error reading key\n"); exit(1); } key.data=malloc(1024*sizeof(char)); dst_key_to_buffer(dst_key, key.data, 1024); key.len=dst_key->dk_key_size; strcpy(key.name, keyname); strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT"); if (chdir(cwd)<0) { fprintf(stderr, "unable to chdir to %s: %s\n", cwd, strerror(errno)); exit(1); } #endif } if ((argc - optind) == 0) { /* no file specified, read from stdin */ ret = system("tty -s"); if (ret == 0) /* terminal */ prompt = 1; else /* stdin redirect from a file or a pipe */ prompt = 0; } else { /* file specified, open it */ /* XXX - currently accepts only one filename */ if ((fp = fopen(argv[optind], "r")) == NULL) { fprintf(stderr, "error opening file: %s\n", argv[optind]); exit (1); } } for (;;) { inside = 1; if (prompt) fprintf(stdout, "> "); if (!fp) cp = fgets(buf, sizeof buf, stdin); else cp = fgets(buf, sizeof buf, fp); if (cp == NULL) /* EOF */ break; lineno++; /* get rid of the trailing newline */ n = strlen(buf); buf[--n] = '\0'; startp = cp; endp = strchr(cp, ';'); if (endp != NULL) endp--; else endp = cp + n - 1; /* verify section name */ if (!getword_str(section, sizeof section, &startp, endp)) { /* empty line */ inside = 0; } if (inside) { /* inside the same update packet, * continue accumulating records */ r_section = -1; n1 = strlen(section); if (section[n1-1] == ':') section[--n1] = '\0'; for (mp = section_strs; mp < section_strs+M_SECTION_CNT; mp++) if (!strcasecmp(section, mp->token)) { r_section = mp->val; break; } if (r_section == -1) { fprintf(stderr, "incorrect section name: %s\n", section); exit (1); } if (r_section == S_ZONE) { fprintf(stderr, "section ZONE not permitted\n"); exit (1); } /* read operation code */ if (!getword_str(opcode, sizeof opcode, &startp, endp)) { fprintf(stderr, "failed to read operation code\n"); exit (1); } r_opcode = -1; if (opcode[0] == '{') { n1 = strlen(opcode); for (i = 0; i < n1; i++) opcode[i] = opcode[i+1]; if (opcode[n1-2] == '}') opcode[n1-2] = '\0'; } for (mp = opcode_strs; mp < opcode_strs+M_OPCODE_CNT; mp++) { if (!strcasecmp(opcode, mp->token)) { r_opcode = mp->val; break; } } if (r_opcode == -1) { fprintf(stderr, "incorrect operation code: %s\n", opcode); exit (1); } /* read owner's domain name */ if (!getword_str(dnbuf, sizeof dnbuf, &startp, endp)) { fprintf(stderr, "failed to read owner name\n"); exit (1); } r_dname = dnbuf; r_ttl = (r_opcode == ADD) ? (~0U) : 0; r_type = -1; r_class = C_IN; /* default to IN */ r_size = 0; (void) getword_str(buf2, sizeof buf2, &startp, endp); if (isdigit(buf2[0])) { /* ttl */ - r_ttl = strtoul(buf2, 0, 10); - if (errno == ERANGE && r_ttl == ULONG_MAX) { + u_long tmp_ttl; + errno = 0; + tmp_ttl = strtoul(buf2, 0, 10); + if ((errno == ERANGE && tmp_ttl == ULONG_MAX) || + tmp_ttl > 0x7fffffffUL) { fprintf(stderr, "oversized ttl: %s\n", buf2); exit (1); } + r_ttl = tmp_ttl; (void) getword_str(buf2, sizeof buf2, &startp, endp); } if (buf2[0]) { /* possibly class */ for (mp = class_strs; mp < class_strs+M_CLASS_CNT; mp++) { if (!strcasecmp(buf2, mp->token)) { r_class = mp->val; (void) getword_str(buf2, sizeof buf2, &startp, endp); break; } } } /* * type and rdata field may or may not be required depending * on the section and operation */ switch (r_section) { case S_PREREQ: if (r_ttl) { fprintf(stderr, "nonzero ttl in prereq section: %lu\n", (u_long)r_ttl); r_ttl = 0; } switch (r_opcode) { case NXDOMAIN: case YXDOMAIN: if (buf2[0]) { fprintf (stderr, "invalid field: %s, ignored\n", buf2); exit (1); } break; case NXRRSET: case YXRRSET: if (buf2[0]) for (mp = type_strs; mp < type_strs+M_TYPE_CNT; mp++) if (!strcasecmp(buf2, mp->token)) { r_type = mp->val; break; } if (r_type == -1) { fprintf (stderr, "invalid type for RRset: %s\n", buf2); exit (1); } if (r_opcode == NXRRSET) break; /* * for RRset exists (value dependent) case, * nonempty rdata field will be present. * simply copy the whole string now and let * res_update() interpret the various fields * depending on type */ cp = startp; while (cp <= endp && isspace(*cp)) cp++; r_size = endp - cp + 1; break; default: fprintf (stderr, "unknown operation in prereq section\"%s\"\n", opcode); exit (1); } break; case S_UPDATE: switch (r_opcode) { case DELETE: r_ttl = 0; r_type = T_ANY; /* read type, if specified */ if (buf2[0]) for (mp = type_strs; mp < type_strs+M_TYPE_CNT; mp++) if (!strcasecmp(buf2, mp->token)) { r_type = mp->val; svstartp = startp; (void) getword_str(buf2, sizeof buf2, &startp, endp); if (buf2[0]) /* unget preference */ startp = svstartp; break; } /* read rdata portion, if specified */ cp = startp; while (cp <= endp && isspace(*cp)) cp++; r_size = endp - cp + 1; break; case ADD: if (r_ttl == ~0U) { fprintf (stderr, "ttl must be specified for record to be added: %s\n", buf); exit (1); } /* read type */ if (buf2[0]) for (mp = type_strs; mp < type_strs+M_TYPE_CNT; mp++) if (!strcasecmp(buf2, mp->token)) { r_type = mp->val; break; } if (r_type == -1) { fprintf(stderr, "invalid type for record to be added: %s\n", buf2); exit (1); } /* read rdata portion */ cp = startp; while (cp < endp && isspace(*cp)) cp++; r_size = endp - cp + 1; if (r_size <= 0) { fprintf(stderr, "nonempty rdata field needed to add the record at line %d\n", lineno); exit (1); } break; default: fprintf(stderr, "unknown operation in update section \"%s\"\n", opcode); exit (1); } break; default: fprintf(stderr, "unknown section identifier \"%s\"\n", section); exit (1); } if ( !(rrecp = res_mkupdrec(r_section, r_dname, r_class, r_type, r_ttl)) || (r_size > 0 && !(rrecp->r_data = (u_char *)malloc(r_size))) ) { if (rrecp) res_freeupdrec(rrecp); fprintf(stderr, "saverrec error\n"); exit (1); } if (stringtobin) { switch(r_opcode) { case T_HINFO: if (!getcharstring(buf,(char *)data,2,2,lineno)) exit(1); cp = data; break; case T_ISDN: if (!getcharstring(buf,(char *)data,1,2,lineno)) exit(1); cp = data; break; case T_TXT: if (!getcharstring(buf,(char *)data,1,0,lineno)) exit(1); cp = data; break; case T_X25: if (!getcharstring(buf,(char *)data,1,1,lineno)) exit(1); cp = data; break; default: break; } } rrecp->r_opcode = r_opcode; rrecp->r_size = r_size; (void) strncpy((char *)rrecp->r_data, cp, r_size); APPEND(listuprec, rrecp, r_link); } else { /* end of an update packet */ (void) res_ninit(&res); if (vc) res.options |= RES_USEVC | RES_STAYOPEN; if (debug) res.options |= RES_DEBUG; if (!EMPTY(listuprec)) { n = res_nupdate(&res, HEAD(listuprec), keyfile != NULL ? &key : NULL); if (n < 0) fprintf(stderr, "failed update packet\n"); while (!EMPTY(listuprec)) { ns_updrec *tmprrecp = HEAD(listuprec); UNLINK(listuprec, tmprrecp, r_link); if (tmprrecp->r_size != 0) free((char *)tmprrecp->r_data); res_freeupdrec(tmprrecp); } } } } /* for */ return (0); } static void usage() { fprintf(stderr, "Usage: %s [ -k keydir:keyname ] [-d] [-v] [file]\n", progname); exit(1); } /* * Get a whitespace delimited word from a string (not file) * into buf. modify the start pointer to point after the * word in the string. */ static int getword_str(char *buf, int size, char **startpp, char *endp) { char *cp; int c; for (cp = buf; *startpp <= endp; ) { c = **startpp; if (isspace(c) || c == '\0') { if (cp != buf) /* trailing whitespace */ break; else { /* leading whitespace */ (*startpp)++; continue; } } (*startpp)++; if (cp >= buf+size-1) break; *cp++ = (u_char)c; } *cp = '\0'; return (cp != buf); } #define MAXCHARSTRING 255 static int getcharstring(char *buf, char *data, int minfields, int maxfields, int lineno) { int nfield = 0, n = 0, i; do { nfield++; i = 0; if (*buf == '"') { buf++; while(buf[i] && buf[i] != '"') i++; } else { while(isspace(*buf)) i++; } if (i > MAXCHARSTRING) { fprintf(stderr, "%d: RDATA field %d too long", lineno, nfield); return(0); } if (n + i + 1 > MAXDATA) { fprintf(stderr, "%d: total RDATA too long", lineno); return(0); } data[n]=i; memmove(data + 1 + n, buf, i); buf += i + 1; n += i + 1; while(*buf && isspace(*buf)) buf++; } while (nfield < maxfields && *buf); if (nfield < minfields) { fprintf(stderr, "%d: expected %d RDATA fields, only saw %d", lineno, minfields, nfield); return (0); } return (n); } diff --git a/contrib/bind/doc/html/options.html b/contrib/bind/doc/html/options.html index f124ebaee567..9e3c0da9961e 100644 --- a/contrib/bind/doc/html/options.html +++ b/contrib/bind/doc/html/options.html @@ -1,853 +1,864 @@ BIND options Statement

BIND Configuration File Guide -- options Statement


Syntax

 options {
   [ hostname hostname_string; ]
   [ version version_string; ]
   [ directory path_name; ]
   [ named-xfer path_name; ]
   [ dump-file path_name; ]
   [ memstatistics-file path_name; ]
   [ pid-file path_name; ]
   [ statistics-file path_name; ]
   [ auth-nxdomain yes_or_no; ]
   [ deallocate-on-exit yes_or_no; ]
   [ dialup yes_or_no; ]
   [ fake-iquery yes_or_no; ]
   [ fetch-glue yes_or_no; ]
   [ has-old-clients yes_or_no; ]
   [ host-statistics yes_or_no; ]
   [ host-statistics-max number; ]
   [ multiple-cnames yes_or_no; ]
   [ notify ( yes_or_no | explicit ) <; ]
   [ suppress-initial-notify yes_or_no; ]
   [ recursion yes_or_no; ]
   [ rfc2308-type1 yes_or_no; ]
   [ use-id-pool yes_or_no; ]
   [ treat-cr-as-space yes_or_no; ]
   [ also-notify { ip_addr; [ ip_addr; ... ] }; ]
   [ forward ( only | first ); ]
   [ forwarders { [ in_addr ; [ in_addr ; ... ] ] }; ]
   [ check-names ( master | slave | response ) ( warn | fail | ignore); ]
   [ allow-query { address_match_list }; ]
   [ allow-transfer { address_match_list }; ]
   [ allow-recursion { address_match_list }; ]
   [ blackhole { address_match_list }; ]
   [ listen-on [ port ip_port ] { address_match_list }; ]
   [ query-source [ address ( ip_addr | * ) ] [ port ( ip_port | * ) ] ; ]
   [ lame-ttl number; ]
   [ max-transfer-time-in number; ]
   [ max-ncache-ttl number; ]
   [ min-roots number; ]
   [ serial-queries number; ]
   [ transfer-format ( one-answer | many-answers ); ]
   [ transfers-in  number; ]
   [ transfers-out number; ]
   [ transfers-per-ns number; ]
   [ transfer-source ip_addr; ]
   [ maintain-ixfr-base yes_or_no; ]
   [ max-ixfr-log-size number; ]
   [ coresize size_spec ; ]
   [ datasize size_spec ; ]
   [ files size_spec ; ]
   [ stacksize size_spec ; ]
   [ cleaning-interval number; ]
   [ heartbeat-interval number; ]
   [ interface-interval number; ]
   [ statistics-interval number; ]
   [ topology { address_match_list }; ]
   [ sortlist { address_match_list }; ]
   [ rrset-order { order_spec ; [ order_spec ; ... ] }; ]
   [ preferred-glue ( A | AAAA ); ]
+  [ edns-udp-size number; ]
 };
 

Definition and Usage

The options statement sets up global options to be used by BIND. This statement may appear at only once in a configuration file; if more than one occurrence is found, the first occurrence determines the actual options used, and a warning will be generated. If there is no options statement, an options block with each option set to its default will be used.

Server Information

hostname
This defaults to the hostname of the machine hosting the nameserver as found by gethostname(). Its prime purpose is to be able to identify which of a number of anycast servers is actually answering your queries by sending a txt query for hostname.bind in class chaos to the anycast server and getting back a unique name. Setting the hostname to a empty string ("") will disable processing of the queries.
version
The version the server should report via the ndc command or via a query of name version.bind in class chaos. The default is the real version number of the server, but some server operators prefer the string "surely you must be joking". Changing the value of this string will not prevent people from identifying what version you are running.

Pathnames

directory
The working directory of the server. Any non-absolute pathnames in the configuration file will be taken as relative to this directory. The default location for most server output files (e.g. "named.run") is this directory. If a directory is not specified, the working directory defaults to ".", the directory from which the server was started. The directory specified should be an absolute path.
named-xfer
The pathname to the named-xfer program that the server uses for inbound zone transfers. If not specified, the default is system dependent (e.g. "/usr/sbin/named-xfer").
dump-file
The pathname of the file the server dumps the database to when it receives SIGINT signal (ndc dumpdb). If not specified, the default is "named_dump.db".
memstatistics-file
The pathname of the file the server writes memory usage statistics to, on exit, if deallocate-on-exit is yes. If not specified, the default is "named.memstats".
pid-file
The pathname of the file the server writes its process ID in. If not specified, the default is operating system dependent, but is usually "/var/run/named.pid" or "/etc/named.pid". The pid-file is used by programs like "ndc" that want to send signals to the running nameserver.
statistics-file
The pathname of the file the server appends statistics to when it receives SIGILL signal (ndc stats). If not specified, the default is "named.stats".

Boolean Options

auth-nxdomain
If yes, the AA bit is always set on NXDOMAIN responses, even if the server is not actually authoritative. The default is no. Turning auth-nxdomain will allow older clients that require AA to be set to accept NXDOMAIN responses to work.
deallocate-on-exit
If yes, the server will painstakingly deallocate every object it it allocated, when it exits, and then write a memory usage report to the memstatistics-file. The default is no, because it is faster to let the operating system clean up. deallocate-on-exit is handy for detecting memory leaks.
dialup
If yes, the server treats all zones as if they are doing zone transfers across a dial on demand dialup link, which can be brought up by traffic originating from this server. This has different effects according to zone type and concentrates the zone maintenance so that it all happens in a short interval, once every heartbeat-interval and hopefully during the one call. It also suppresses some of the normal zone maintainance traffic. The default is no. The dialup option may also be specified in the zone statement, in which case it overrides the options dialup statement.

If the zone is a master zone, the server will send out NOTIFY request to all the slaves. This will trigger the "zone up to date checking" in the slave (providing it supports NOTIFY), allowing the slave to verify the zone while the call us up.

If the zone is a slave or stub zone, the server will suppress the regular "zone up to date" queries and only perform them when the heartbeat-interval expires.

fake-iquery
If yes, the server will simulate the obsolete DNS query type IQUERY. The default is no.
fetch-glue
If yes (the default), the server will fetch "glue" resource records it doesn't have when constructing the additional data section of a response. fetch-glue no can be used in conjunction with recursion no to prevent the server's cache from growing or becoming corrupted (at the cost of requiring more work from the client).
has-old-clients
Setting the option to yes is equivalent to setting the following options: auth-nxdomain yes; and rfc2308-type1 no;. The use of has-old-clients with auth-nxdomain and rfc2308-type1 is order dependent.
host-statistics
If yes, statistics are kept for every host that the the nameserver interacts with. The default is no. Note: turning on host-statistics can consume huge amounts of memory.
host-statistics-max
The maximum number of host records that will be kept. When this limit is reached no new hosts will be added to the host statistics. If the set to zero then there is no limit set. The default value is zero.
maintain-ixfr-base
If yes, a transaction log is kept for Incremental Zone Transfer. The default is no.
multiple-cnames
If yes, multiple CNAME resource records will be allowed for a domain name. The default is no. Allowing multiple CNAME records is against standards and is not recommended. Multiple CNAME support is available because previous versions of BIND allowed multiple CNAME records, and these records have been used for load balancing by a number of sites.
notify
If yes (the default), DNS NOTIFY messages are sent when a zone the server is authoritative for changes. The use of NOTIFY speeds convergence between the master and its slaves. Slave servers that receive a NOTIFY message, and understand it, will contact the master server for the zone to see if they need to do a zone transfer. If they do, they will initiate it immediately. If explicit, the NOTIFY messages will only be sent to the addresses in the also-notify list. The notify option may also be specified in the zone statement, in which case it overrides the options notify statement.
suppress-initial-notify
If yes, suppress the initial notify messages when the server first loads. The default is no.
recursion
If yes, and a DNS query requests recursion, the server will attempt to do all the work required to answer the query. If recursion is not on, the server will return a referral to the client if it doesn't know the answer. The default is yes. See also fetch-glue above.
rfc2308-type1
If yes, the server will send NS records along with the SOA record for negative answers from the cache. You need to set this to no if you have an old BIND server using you as a forwarder that does not understand negative answers which contain both SOA and NS records or you have an old version of sendmail. The correct fix is to upgrade the broken server or sendmail. The default is no.
use-id-pool
If yes, the server will keep track of its own outstanding query ID's to avoid duplication and increase randomness. This will result in 128KB more memory being consumed by the server. The default is no.
treat-cr-as-space
If yes, the server will treat '\r' characters the same way it treats a ' ' or '\t'. This may be necessary when loading zone files on a UNIX system that were generated on an NT or DOS machine. The default is no.

Also-Notify

also-notify

Defines a global list of IP addresses that also get sent NOTIFY messages whenever a fresh copy of the zone is loaded. This helps to ensure that copies of the zones will quickly converge on ``stealth'' servers. If an also-notify list is given in a zone statement, it will override the options also-notify statement. When a zone notify statement is set to no, the IP addresses in the global also-notify list will not get sent NOTIFY messages for that zone. The default is the empty list (no global notification list).

Forwarding

The forwarding facility can be used to create a large site-wide cache on a few servers, reducing traffic over links to external nameservers. It can also be used to allow queries by servers that do not have direct access to the Internet, but wish to look up exterior names anyway. Forwarding occurs only on those queries for which the server is not authoritative and does not have the answer in its cache.

forward
This option is only meaningful if the forwarders list is not empty. A value of first, the default, causes the server to query the forwarders first, and if that doesn't answer the question the server will then look for the answer itself. If only is specified, the server will only query the forwarders.
forwarders
Specifies the IP addresses to be used for forwarding. The default is the empty list (no forwarding).

Forwarding can also be configured on a per-zone basis, allowing for the global forwarding options to be overridden in a variety of ways. You can set particular zones to use different forwarders, or have different forward only/first behavior, or to not forward at all. See the zone statement for more information.

Future versions of BIND 8 will provide a more powerful forwarding system. The syntax described above will continue to be supported.

Name Checking

The server can check domain names based upon their expected client contexts. For example, a domain name used as a hostname can be checked for compliance with the RFCs defining valid hostnames.

Three checking methods are available:

ignore
No checking is done.
warn
Names are checked against their expected client contexts. Invalid names are logged, but processing continues normally.
fail
Names are checked against their expected client contexts. Invalid names are logged, and the offending data is rejected.

The server can check names three areas: master zone files, slave zone files, and in responses to queries the server has initiated. If check-names response fail has been specified, and answering the client's question would require sending an invalid name to the client, the server will send a REFUSED response code to the client.

The defaults are:

     check-names master fail;
     check-names slave warn;
     check-names response ignore;
 

check-names may also be specified in the zone statement, in which case it overrides the options check-names statement. When used in a zone statement, the area is not specified (because it can be deduced from the zone type).

Access Control

Access to the server can be restricted based on the IP address of the requesting system. See address_match_list for details on how to specify IP address lists.

allow-query
Specifies which hosts are allowed to ask ordinary questions. allow-query may also be specified in the zone statement, in which case it overrides the options allow-query statement. If not specified, the default is to allow queries from all hosts.
allow-transfer
Specifies which hosts are allowed to receive zone transfers from the server. allow-transfer may also be specified in the zone statement, in which case it overrides the options allow-transfer statement. If not specified, the default is to allow transfers from all hosts.
allow-recursion
Specifies which hosts are allowed to make recursive queries through this server. If not specified, the default is to allow recursive queries from all hosts.
blackhole
Specifies a list of addresses that the server will not accept queries from or use to resolve a query. Queries from these addresses will not be responded to.

Interfaces

The interfaces and ports that the server will answer queries from may be specified using the listen-on option. listen-on takes an optional port, and an address_match_list. The server will listen on all interfaces allowed by the address match list. If a port is not specified, port 53 will be used.

Multiple listen-on statements are allowed. For example,

     listen-on { 5.6.7.8; };
     listen-on port 1234 { !1.2.3.4; 1.2/16; };
 
will enable the nameserver on port 53 for the IP address 5.6.7.8, and on port 1234 of an address on the machine in net 1.2 that is not 1.2.3.4.

If no listen-on is specified, the server will listen on port 53 on all interfaces.

Query Address

If the server doesn't know the answer to a question, it will query other nameservers. query-source specifies the address and port used for such queries. If address is * or is omitted, a wildcard IP address (INADDR_ANY) will be used. If port is * or is omitted, a random unprivileged port will be used. The default is

     query-source address * port *;
 

Note: query-source port applies only to UDP queries, TCP queries always use a random unprivileged port.

Zone Transfers

max-transfer-time-in
Inbound zone transfers (named-xfer processes) running longer than this many minutes will be terminated. The default is 120 minutes (2 hours).
transfer-format
The server supports two zone transfer methods. one-answer uses one DNS message per resource record transferred. many-answers packs as many resource records as possible into a message. many-answers is more efficient, but is only known to be understood by BIND 8.1+ and patched versions of BIND 4.9.5. The default is one-answer. transfer-format may be overridden on a per-server basis by using the server statement.
transfers-in
The maximum number of inbound zone transfers that can be running concurrently. The default value is 10. Increasing transfers-in may speed up the convergence of slave zones, but it also may increase the load on the local system.
transfers-out
This option will be used in the future to limit the number of concurrent outbound zone transfers. It is checked for syntax, but is otherwise ignored.
transfers-per-ns
The maximum number of inbound zone transfers (named-xfer processes) that can be concurrently transferring from a given remote nameserver. The default value is 2. Increasing transfers-per-ns may speed up the convergence of slave zones, but it also may increase the load on the remote nameserver. transfers-per-ns may be overridden on a per-server basis by using the transfers phrase of the server statement.
transfer-source
transfer-source determines which local address will be bound to the TCP connection used to fetch all zones transferred inbound by the server. If not set, it defaults to a system controlled value which will usually be the address of the interface ``closest to'' the remote end. This address must appear in the remote end's allow-transfer option for the zone being transferred, if one is specified. This statement sets the transfer-source for all zones, but can be overridden on a per-zone basis by including a transfer-source statement within the zone block in the configuration file.
serial-queries
Slave servers will periodically query master servers to find out if zone serial numbers have changed. Each such query uses a minute amount of the slave server's network bandwidth, but more importantly each query uses a small amount of memory in the slave server while waiting for the master server to respond. The serial-queries option sets the maximum number of concurrent serial-number queries allowed to be outstanding at any given time. The default is four (4). Note: If a server loads a large (tens or hundreds of thousands) number of slave zones, this limit should be raised to the high hundreds or low thousands -- otherwise the slave server may never actually become aware of zone changes in the master servers. Beware, though, that setting this limit arbitrarily high can spend a considerable amount of your slave server's network, CPU, and memory resources. As with all tunable limits, this one should be changed gently and monitored for its effects.

Resource Limits

The server's usage of many system resources can be limited. Some operating systems don't support some of the limits. On such systems, a warning will be issued if the unsupported limit is used. Some operating systems don't support limiting resources, and on these systems a cannot set resource limits on this system message will be logged.

Scaled values are allowed when specifying resource limits. For example, 1G can be used instead of 1073741824 to specify a limit of one gigabyte. unlimited requests unlimited use, or the maximum available amount. default uses the limit that was in force when the server was started. See size_spec for more details.

coresize
The maximum size of a core dump. The default is default.
datasize
The maximum amount of data memory the server may use. The default is default.
files
The maximum number of files the server may have open concurrently. The default is unlimited. Note: on some operating systems the server cannot set an unlimited value and cannot determine the maximum number of open files the kernel can support. On such systems, choosing unlimited will cause the server to use the larger of the rlim_max for RLIMIT_NOFILE and the value returned by sysconf(_SC_OPEN_MAX). If the actual kernel limit is larger than this value, use limit files to specify the limit explicitly.
max-ixfr-log-size
Limit the size of the transaction log kept for Incremental Zone Transfer. Default 0 (unlimited).
stacksize
The maximum amount of stack memory the server may use. The default is default.

Periodic Task Intervals

cleaning-interval
The server will remove expired resource records from the cache every cleaning-interval minutes. The default is 60 minutes. If set to 0, no periodic cleaning will occur.
heartbeat-interval
The server will perform zone maintenance tasks for all zones marked dialup yes whenever this interval expires. The default is 60 minutes. Reasonable values are up to 1 day (1440 minutes). If set to 0, no zone maintenance for these zones will occur.
interface-interval
The server will scan the network interface list every interface-interval minutes. The default is 60 minutes. If set to 0, interface scanning will only occur when the configuration file is loaded. After the scan, listeners will be started on any new interfaces (provided they are allowed by the listen-on configuration). Listeners on interfaces that have gone away will be cleaned up.
statistics-interval
Nameserver statistics will be logged every statistics-interval minutes. The default is 60. If set to 0, no statistics will be logged.

Topology

All other things being equal, when the server chooses a nameserver to query from a list of nameservers, it prefers the one that is topologically closest to itself. The topology statement takes an address_match_list and interprets it in a special way. Each top-level list element is assigned a distance. Non-negated elements get a distance based on their position in the list, where the closer the match is to the start of the list, the shorter the distance is between it and the server. A negated match will be assigned the maximum distance from the server. If there is no match, the address will get a distance which is further than any non-negated list element, and closer than any negated element. For example,

     topology {
         10/8;
         !1.2.3/24;
         { 1.2/16; 3/8; };
     };
 

will prefer servers on network 10 the most, followed by hosts on network 1.2.0.0 (netmask 255.255.0.0) and network 3, with the exception of hosts on network 1.2.3 (netmask 255.255.255.0), which is preferred least of all.

The default topology is

     topology { localhost; localnets; };
 

Resource Record sorting

When returning multiple RRs, the nameserver will normally return them in Round Robin, i.e. after each request, the first RR is put to the end of the list. As the order of RRs is not defined, this should not cause any problems.

The client resolver code should re-arrange the RRs as appropriate, i.e. using any addresses on the local net in preference to other addresses. However, not all resolvers can do this, or are not correctly configured.

When a client is using a local server, the sorting can be performed in the server, based on the client's address. This only requires configuring the nameservers, not all the clients.

The sortlist statement takes an address match list and interprets it even more specially than the topology statement does.

Each top level statement in the sortlist must itself be an explicit address match list with one or two elements. The first element (which may be an IP address, an IP prefix, an ACL name or nested address match list) of each top level list is checked against the source address of the query until a match is found.

Once the source address of the query has been matched, if the top level statement contains only one element, the actual primitive element that matched the source address is used to select the address in the response to move to the beginning of the response. If the statement is a list of two elements, the second element is treated like the address match list in a topology statement. Each top level element is assigned a distance and the address in the response with the minimum distance is moved to the beginning of the response.

In the following example, any queries received from any of the addresses of the host itself will get responses preferring addresses on any of the locally connected networks. Next most preferred are addresses on the 192.168.1/24 network, and after that either the 192.168.2/24 or 192.168.3/24 network with no preference shown between these two networks. Queries received from a host on the 192.168.1/24 network will prefer other addresses on that network to the 192.168.2/24 and 192.168.3/24 networks. Queries received from a host on the 192.168.4/24 or the 192.168.5/24 network will only prefer other addresses on their directly connected networks.

 sortlist {
            { localhost;         // IF   the local host
              { localnets;       // THEN first fit on the
                192.168.1/24;    //      following nets
                { 192,168.2/24; 192.168.3/24; }; }; };
            { 192.168.1/24;      // IF   on class C 192.168.1
              { 192.168.1/24;    // THEN use .1, or .2 or .3
                { 192.168.2/24; 192.168.3/24; }; }; };
            { 192.168.2/24;      // IF   on class C 192.168.2
              { 192.168.2/24;    // THEN use .2, or .1 or .3
                { 192.168.1/24; 192.168.3/24; }; }; };
            { 192.168.3/24;      // IF   on class C 192.168.3
              { 192.168.3/24;    // THEN use .3, or .1 or .2
                { 192.168.1/24; 192.168.2/24; }; }; };
            { { 192.168.4/24; 192.168.5/24; }; // if .4 or .5, prefer that net
            };
 };
 
The following example will give reasonable behaviour for the local host and hosts on directly connected networks. It is similar to the behavior of the address sort in BIND 4.9.x. Responses sent to queries from the local host will favor any of the directly connected networks. Responses sent to queries from any other hosts on a directly connected network will prefer addresses on that same network. Responses to other queries will not be sorted.
 sortlist {
             { localhost; localnets; };
             { localnets; };
 };
 

RRset Ordering

When multiple records are returned in an answer it may be useful to configure the order the records are placed into the response. For example the records for a zone might be configured to always be returned in the order they are defined in the zone file. Or perhaps a random shuffle of the records as they are returned is wanted. The rrset-order statement permits configuration of the ordering made of the records in a multiple record response. The default, if no ordering is defined, is a cyclic ordering (round robin).

An order_spec is defined as follows:

   [ class class_name ][ type type_name ][ name "FQDN" ] order ordering
 

If no class is specified, the default is ANY. If no type is specified, the default is ANY. If no name is specified, the default is "*".

The legal values for ordering are:

fixed
Records are returned in the order they are defined in the zone file.
random
Records are returned in some random order.
cyclic
Records are returned in a round-robin order.

For example:

     rrset-order {
 	class IN type A name "rc.vix.com" order random;
         order cyclic;
     };
 

will cause any responses for type A records in class IN that have "rc.vix.com" as a suffix, to always be returned in random order. All other records are returned in cyclic order.

If multiple rrset-order statements appear, they are not combined--the last one applies.

If no rrset-order statement is specified, a default one of:

     rrset-order { class ANY type ANY name "*" order cyclic ; };
 

is used.

Glue Ordering

When running a root nameserver it is sometimes necessary to ensure that other nameservers that are priming are successful. This requires that glue A records for at least of the nameservers are returned in the answer to a priming query. This can be achieved by setting preferred-glue A; which will add A records before other types in the additional section. +

EDNS

+ +Some firewalls fail to pass EDNS/UDP messages that are larger than +certain size, 512 or the UDP reassembly buffer. To allow EDNS to +work across such firewalls it is necessary to advertise a EDNS +buffer size that is small enough to not trigger failures. +edns-udp-size can be use to adjust the advertised size. +Values less than 512 will be increased to 512 and values greater than +4096 will be truncated to 4096. +

Tuning

lame-ttl
Sets the number of seconds to cache a lame server indication. 0 disables caching. Default is 600 (10 minutes). Maximum value is 1800 (30 minutes).
max-ncache-ttl
To reduce network traffic and increase performance the server stores negative answers. max-ncache-ttl is used to set a maximum retention time for these answers in the server is seconds. The default max-ncache-ttl is 10800 seconds (3 hours). max-ncache-ttl cannot exceed the maximum retention time for ordinary (positive) answers (7 days) and will be silently truncated to 7 days if set to a value which is greater that 7 days.
min-roots
The minimum number of root servers that is required for a request for the root servers to be accepted. Default 2.

[ BIND Config. File | BIND Home | ISC ]


-Last Updated: $Id: options.html,v 1.49 2002/09/09 00:19:17 marka Exp $ +Last Updated: $Id: options.html,v 1.49.6.1 2003/06/02 09:56:33 marka Exp $
diff --git a/contrib/bind/doc/man/dig.1 b/contrib/bind/doc/man/dig.1 index 67995591452c..40da7a0fa7d4 100644 --- a/contrib/bind/doc/man/dig.1 +++ b/contrib/bind/doc/man/dig.1 @@ -1,704 +1,704 @@ -.\" $Id: dig.1,v 8.9 2002/06/18 01:53:43 marka Exp $ +.\" $Id: dig.1,v 8.11 2003/04/03 05:52:34 marka Exp $ .\" .\" ++Copyright++ 1993 .\" - .\" Copyright (c) 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" - .\" Portions Copyright (c) 1993 by Digital Equipment Corporation. .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies, and that .\" the name of Digital Equipment Corporation not be used in advertising or .\" publicity pertaining to distribution of the document or software without .\" specific, written prior permission. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL .\" WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT .\" CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL .\" DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR .\" PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS .\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS .\" SOFTWARE. .\" - .\" --Copyright-- .\" .\" Distributed with 'dig' version 2.0 from University of Southern .\" California Information Sciences Institute (USC-ISI). .\" .\" dig.1 2.0 (USC-ISI) 8/30/90 .\" .Dd August 30, 1990 .Dt DIG @CMD_EXT_U@ .Os BSD 4 .Sh NAME .Nm dig .Nd send domain name query packets to name servers .Sh SYNOPSIS .Nm dig .Op Ic @ Ns Ar server .Ar domain .Op Aq Ar query-type .Op Aq Ar query-class .Op Ic + Ns Aq Ar query-option .Op Fl Aq Ar dig-option .Op Ar %comment .Sh DESCRIPTION .Ic Dig (domain information groper) is a flexible command line tool which can be used to gather information from the Domain Name System servers. .Ic Dig has two modes: simple interactive mode for a single query, and batch mode which executes a query for each in a list of several query lines. All query options are accessible from the command line. .Pp The usual simple use of .Ic dig will take the form: .Pp .Bd -ragged -offset indent-two .Ic dig @ Ns Ar server domain query-type query-class .Ed .Pp where: .Bl -tag -width Fl .It Ar server may be either a domain name or a raw (IPv4 / IPv6) Internet address. If this optional field is omitted, .Ic dig will attempt to use the default name server for your machine. .sp 1 .Em Note : If a domain name is specified, this will be resolved using the domain name system resolver (i.e., BIND). If your system does not support DNS, you may .Em have to specify a dot-notation address. Alternatively, if there is a server at your disposal somewhere, all that is required is that .Pa /etc/resolv.conf be present and indicate where the default name servers reside, so that .Ar server itself can be resolved. See .Xr resolver @FORMAT_EXT@ for information on .Pa /etc/resolv.conf . .Sy WARNING : Changing .Pa /etc/resolv.conf will affect both the standard resolver library and .Pq potentially several programs which use it. As an option, the user may set the environment variable .Ev LOCALRES to name a file which is to be used instead of .Pa /etc/resolv.conf .Po Ns Ev LOCALRES is specific to the .Ic dig resolver and is not referenced by the standard resolver .Pc . If the .Ev LOCALRES variable is not set or the specified file is not readable, then .Pa /etc/resolv.conf will be used. .It Ar domain is the domain name for which you are requesting information. See the .Fl x option (documented in the .Sx OTHER OPTIONS subsection of this section) for convenient way to specify reverse address query. .It Ar query-type is the type of information (DNS query type) that you are requesting. If omitted, the default is .Dq Ar a .Pq Dv T_A = Ar address . The following types are recognized: .Pp .Bl -hang -width "hinfo T_HINFO " -compact .It Ar a\ \ \ \ \ \ Dv T_A network address .It Ar any\ \ \ \ Dv T_ANY all/any information about specified domain .It Ar mx\ \ \ \ \ Dv T_MX mail exchanger for the domain .It Ar ns\ \ \ \ \ Dv T_NS name servers .It Ar soa\ \ \ \ Dv T_SOA zone of authority record .It Ar hinfo\ \ Dv T_HINFO host information .It Ar axfr\ \ \ Dv T_AXFR zone transfer (must ask an authoritative server) .It Ar txt\ \ \ \ Dv T_TXT arbitrary number of strings .El .Pp (See RFC 1035 for the complete list.) .It Ar query-class is the network class requested in the query. If omitted, the default is .Dq Ar in .Pq Dv C_IN = Ar Internet . The following classes are recognized: .Pp .Bl -tag -width "hinfo T_HINFO " -compact .It Ar in\ \ \ \ \ Dv C_IN Internet class domain .It Ar any\ \ \ \ Dv C_ANY all/any class information .El .Pp (See RFC 1035 for the complete list.) .Pp .Em Note : .Dq Ar Any can be used to specify a .Em class and/or a .Em type of query. .Ic Dig will parse the first occurrence of .Dq Ar any to mean .Ar query-type = Dv T_ANY . To specify .Ar query-class = Dv C_ANY , you must either specify .Dq any twice, or set .Ar query-class using the .Fl c option (see below). .El .Ss OTHER OPTIONS .Bl -tag -width Fl .It % Ns Ar ignored-comment .Dq % is used to included an argument that is simply not parsed. This may be useful if running .Ic dig in batch mode. Instead of resolving every .Ar @server-domain-name in a list of queries, you can avoid the overhead of doing so, and still have the domain name on the command line as a reference. Example: .Pp .Bd -ragged -offset indent-two .Ic dig @128.9.0.32 %venera.isi.edu mx isi.edu .Ed .Pp .It Fl Aq Ar dig option .Dq Fl is used to specify an option which affects the operation of .Ic dig . The following options are currently available (although not guaranteed to be useful): .Bl -tag -width Fl .It Fl x Ar dot-notation-address Convenient form to specify inverse address mapping. Instead of .Dq Ic dig 32.0.9.28.in-addr.arpa , one can simply .Dq Ic dig -x 28.9.0.32 . .It Fl x Ar IPv6-address Convenient form to specify inverse address mapping. Instead of -.Dq Ic dig 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 , +.Dq Ic dig 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 , one can simply .Dq Ic dig -x ::1 . .It Fl f Ar file File for .Ic dig batch mode. The file contains a list of query specifications ( .Ns Ic dig command lines) which are to be executed successively. Lines beginning with .Sq \&; , .Sq # , or .Sq \en are ignored. Other options may still appear on command line, and will be in effect for each batch query. .It Fl T Ar time Time in seconds between start of successive queries when running in batch mode. Can be used to keep two or more batch .Ic dig commands running roughly in sync. Default is zero. .It Fl p Ar port Port number. Query a name server listening to a non-standard port number. Default is 53. .It Fl P Ns Bq Ar ping-string After query returns, execute a .Xr ping @SYS_OPS_EXT@ command for response time comparison. This rather unelegantly makes a call to the shell. The last three lines of statistics is printed for the command: .Pp .Bd -ragged -offset indent-two .Ic ping Fl s server_name 56 3 .Ed .Pp If the optional .Dq Ar ping_string is present, it replaces .Dq Ic ping Fl s in the shell command. .It Fl t Ar query-type Specify type of query. May specify either an integer value to be included in the type field or use the abbreviated mnemonic as discussed above (i.e., .Ar mx = Dv T_MX ) . .It Fl c Ar query-class Specify class of query. May specify either an integer value to be included in the class field or use the abbreviated mnemonic as discussed above (i.e., in = C_IN). .It Fl k Ar keydir:keyname Sign the query with the TSIG key named keyname that is in the directory keydir. .It Fl envsav This flag specifies that the .Ic dig environment (defaults, print options, etc.), after all of the arguments are parsed, should be saved to a file to become the default environment. This is useful if you do not like the standard set of defaults and do not desire to include a large number of options each time .Ic dig is used. The environment consists of resolver state variable flags, timeout, and retries as well as the flags detailing .Ic dig output (see below). If the shell environment variable .Ev LOCALDEF is set to the name of a file, this is where the default .Ic dig environment is saved. If not, the file .Dq Pa DiG.env is created in the current working directory. .Pp .Em Note : .Ev LOCALDEF is specific to the .Ic dig resolver, and will not affect operation of the standard resolver library. .Pp Each time .Ic dig is executed, it looks for .Dq Pa ./DiG.env or the file specified by the shell environment variable .Ev LOCALDEF . If such file exists and is readable, then the environment is restored from this file before any arguments are parsed. .It Fl envset This flag only affects batch query runs. When .Dq Fl envset is specified on a line in a .Ic dig batch file, the .Ic dig environment after the arguments are parsed becomes the default environment for the duration of the batch file, or until the next line which specifies .Dq Fl envset . .It Xo .Fl Op Cm no .Ns Cm stick .Xc This flag only affects batch query runs. It specifies that the .Ic dig environment (as read initially or set by .Dq Fl envset switch) is to be restored before each query (line) in a .Ic dig batch file. The default .Dq Fl nostick means that the .Ic dig environment does not stick, hence options specified on a single line in a .Ic dig batch file will remain in effect for subsequent lines (i.e. they are not restored to the .Dq sticky default). .El .It Ic + Ns Aq Ar query-option .Dq + is used to specify an option to be changed in the query packet or to change .Ic dig output specifics. Many of these are the same parameters accepted by .Xr nslookup @SYS_OPS_EXT@ . If an option requires a parameter, the form is as follows: .Pp .Bd -ragged -offset indent-two .Ic + .Ns Ar keyword .Ns Op = Ns Ar value .Ed .Pp Most keywords can be abbreviated. Parsing of the .Dq + options is very simplistic \(em a value must not be separated from its keyword by white space. The following keywords are currently available: .Pp Keyword Abbrev. Meaning [default] .Pp .Bl -tag -width "[no]primary (ret) " -compact .It Xo .Op Cm no .Ns Cm debug\ \ \ \ .Pq Cm deb .Xc turn on/off debugging mode .Bq Cm deb .It Xo .Op Cm no .Ns Cm d2\ \ \ \ \ \ \ \ \ \ .Xc turn on/off extra debugging mode .Bq Cm nod2 .It Xo .Op Cm no .Ns Cm recurse\ \ .Pq Cm rec .Xc use/don't use recursive lookup .Bq Cm rec .It Xo .Cm retry= Ns Ar # .Cm \ \ \ \ \ .Pq Cm ret .Xc set number of retries to # .Bq 4 .It Xo .Cm time= Ns Ar # .Cm \ \ \ \ \ \ .Pq Cm ti .Xc set timeout length to # seconds .Bq 4 .It Xo .Op Cm no .Ns Cm ko .Xc keep open option (implies vc) .Bq Cm noko .It Xo .Op Cm no .Ns Cm vc .Xc use/don't use virtual circuit .Bq Cm novc .It Xo .Op Cm no .Ns Cm defname\ \ .Pq Cm def .Xc use/don't use default domain name .Bq Cm def .It Xo .Op Cm no .Ns Cm search\ \ \ .Pq Cm sea .Xc use/don't use domain search list .Bq Cm sea .It Xo .Cm domain= Ns Ar NAME\ \ .Pq Cm do .Xc set default domain name to .Ar NAME .It Xo .Op Cm no .Ns Cm ignore\ \ \ .Pq Cm i .Xc ignore/don't ignore trunc. errors .Bq Cm noi .It Xo .Op Cm no .Ns Cm primary\ \ .Pq Cm pr .Xc use/don't use primary server .Bq Cm nopr .It Xo .Op Cm no .Ns Cm aaonly\ \ \ .Pq Cm aa .Xc authoritative query only flag .Bq Cm noaa .It Xo .Op Cm no .Ns Cm cmd .Xc echo parsed arguments .Bq Cm cmd .It Xo .Op Cm no .Ns Cm stats\ \ \ \ .Pq Cm st .Xc print query statistics .Bq Cm st .It Xo .Op Cm no .Ns Cm Header\ \ \ .Pq Cm H .Xc print basic header .Bq Cm H .It Xo .Op Cm no .Ns Cm header\ \ \ .Pq Cm he .Xc print header flags .Bq Cm he .It Xo .Op Cm no .Ns Cm ttlid\ \ \ \ .Pq Cm tt .Xc print TTLs .Bq Cm tt .It Xo .Op Cm no .Ns Cm trunc\ \ \ \ .Pq Cm tr .Xc truncate origin from names .Bq Cm tr .It Xo .Op Cm no .Ns Cm cl .Xc print class info .Bq Cm nocl .It Xo .Op Cm no .Ns Cm qr .Xc print outgoing query .Bq Cm noqr .It Xo .Op Cm no .Ns Cm reply\ \ \ \ .Pq Cm rep .Xc print reply .Bq Cm rep .It Xo .Op Cm no .Ns Cm ques\ \ \ \ \ .Pq Cm qu .Xc print question section .Bq Cm qu .It Xo .Op Cm no .Ns Cm answer\ \ \ .Pq Cm an .Xc print answer section .Bq Cm an .It Xo .Op Cm no .Ns Cm author\ \ \ .Pq Cm au .Xc print authoritative section .Bq Cm au .It Xo .Op Cm no .Ns Cm addit\ \ \ \ .Pq Cm ad .Xc print additional section .Bq Cm ad .It Xo .Op Cm no .Ns Cm dnssec\ \ \ .Pq Cm \ddn .Xc set the DNSSEC OK bit in the OPT pseudo record .Bq Cm nodn .It Cm pfdef set to default print flags .It Cm pfmin set to minimal default print flags .It Cm pfset= Ns Ar # set print flags to # (# can be hex/octal/decimal) .It Cm pfand= Ns Ar # bitwise and print flags with # .It Cm pfor= Ns Ar # bitwise or print flags with # .El .Pp The .Cm retry and .Cm time options affect the retransmission strategy used by the resolver library when sending datagram queries. The algorithm is as follows: .Pp .Bd -literal -offset indent for i = 0 to retry - 1 for j = 1 to num_servers send_query wait((time * (2**i)) / num_servers) end end .Ed .Pp (Note: .Ic dig always uses a value of 1 for .Dq Li num_servers . ) .El .Ss DETAILS .Ic Dig once required a slightly modified version of the BIND .Xr resolver @LIB_NETWORK_EXT@ library. As of BIND 4.9, BIND's resolver has been augmented to work properly with .Ic dig . Essentially, .Ic dig is a straight-forward (albeit not pretty) effort of parsing arguments and setting appropriate parameters. .Ic Dig uses .Xr resolver @LIB_NETWORK_EXT@ routines .Fn res_init , .Fn res_mkquery , .Fn res_send as well as accessing the .Ft _res structure. .Sh ENVIRONMENT .Bl -tag -width "LOCALRES " -compact .It Ev LOCALRES file to use in place of Pa /etc/resolv.conf .It Ev LOCALDEF default environment file .El .Pp See also the explanation of the .Fl envsav , .Fl envset , and .Xo .Fl Op Cm no .Ns Cm stick .Xc options, above. .Sh FILES .Bl -tag -width "/etc/resolv.conf " -compact .It Pa /etc/resolv.conf initial domain name and name server addresses .It Pa \./DiG.env default save file for default options .El .Sh SEE ALSO .Xr @INDOT@named @SYS_OPS_EXT@ , .Xr resolver @LIB_NETWORK_EXT@ , .Xr resolver @FORMAT_EXT@ , .Xr nslookup @SYS_OPS_EXT@ . .Sh STANDARDS RFC 1035. .Sh AUTHOR Steve Hotz hotz@isi.edu .Sh ACKNOWLEDGMENTS .Ic Dig uses functions from .Xr nslookup @SYS_OPS_EXT@ authored by Andrew Cherenson. .Sh BUGS .Ic Dig has a serious case of "creeping featurism" -- the result of -considering several potential uses during it's development. It would +considering several potential uses during its development. It would probably benefit from a rigorous diet. Similarly, the print flags and granularity of the items they specify make evident their rather ad hoc genesis. .Pp .Ic Dig does not consistently exit nicely (with appropriate status) when a problem occurs somewhere in the resolver .Po .Sy NOTE : most of the common exit cases are handled .Pc . This is particularly annoying when running in batch mode. If it exits abnormally (and is not caught), the entire batch aborts; when such an event is trapped, .Ic dig simply continues with the next query. diff --git a/contrib/bind/doc/man/named-xfer.8 b/contrib/bind/doc/man/named-xfer.8 index 7d73b0f757fd..05ccb5a586a1 100644 --- a/contrib/bind/doc/man/named-xfer.8 +++ b/contrib/bind/doc/man/named-xfer.8 @@ -1,183 +1,210 @@ .\" ++Copyright++ 1985 .\" - .\" Copyright (c) 1985 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" - .\" Portions Copyright (c) 1993 by Digital Equipment Corporation. .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies, and that .\" the name of Digital Equipment Corporation not be used in advertising or .\" publicity pertaining to distribution of the document or software without .\" specific, written prior permission. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL .\" WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT .\" CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL .\" DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR .\" PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS .\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS .\" SOFTWARE. .\" - .\" Portions Copyright (c) 1999 by Check Point Software Technologies, Inc. .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies, and that .\" the name of Check Point Software Technologies Incorporated not be used .\" in advertising or publicity pertaining to distribution of the document .\" or software without specific, written prior permission. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND CHECK POINT SOFTWARE TECHNOLOGIES .\" INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, .\" INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. -.\" IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPRATED +.\" IN NO EVENT SHALL CHECK POINT SOFTWARE TECHNOLOGIES INCORPORATED .\" BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR .\" ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" --Copyright-- .\" .\" from named.8 6.6 (Berkeley) 2/14/89 .\" .Dd June 26, 1993 .Dt @XFER_INDOT_U@NAMED-XFER @SYS_OPS_EXT_U@ .Os BSD 4 .Sh NAME .Nm @XFER_INDOT@named-xfer .Nd ancillary agent for inbound zone transfers .Sh SYNOPSIS .Nm named-xfer .Fl z Ar zone_to_transfer .Fl f Ar db_file .Fl s Ar serial_no .Op Fl d Ar debuglevel .Op Fl l Ar debug_log_file .Op Fl i Ar ixfr_file .Op Fl t Ar trace_file .Op Fl p Ar port# +.Op Fl C Ar class .Op Fl S +.Op Fl q +.Op Fl x Ar src_address +.Op Fl T Ar tsig_file .Ar nameserver .Op Sy axfr | ixfr .Sh DESCRIPTION .Ic Named-xfer is an ancillary program executed by .Xr @INDOT@named @SYS_OPS_EXT@ to perform an inbound zone transfer. It is rarely executed directly, and then only by system administrators who are trying to debug a zone transfer problem. See RFC's 1033, 1034, and 1035 for more information on the Internet name-domain system. .Pp Options are: .Bl -tag -width Fl .It Fl z Ar zone_to_transfer specifies the name of the zone to be transferred. .It Fl f Ar db_file specifies the name of the .Ar db_file into which the zone should be dumped when it is received from the primary server. .It Fl s Ar serial_no specifies the serial number of our current copy of this zone. If the .Sy SOA RR we get from the primary server does not have a serial number higher than this, the transfer will be aborted. .It Fl d Ar debuglevel Print debugging information. The .Ar debuglevel is a number determines the level of messages printed. .It Fl l Ar debug_log_file Specifies a log file for debugging messages. The default is system- dependent but is usually in .Pa /var/tmp or .Pa /usr/tmp . Note that this only applies if .Dq Fl d is also specified. .It Fl i Ar ixfr_file Specifies the name of the .Ar ixfr_file into which the zone changes from Incremental Zone Transfer (IXFR) should be dumped when it is received from the primary server. .It Fl t Ar trace_file Specifies a .Ar trace_file which will contain a protocol trace of the zone transfer. This is probably only of interest to people debugging the name server itself. .It Fl p Ar port# Use a different port number. The default is the standard port number as returned by .Xr getservbyname @LIB_NETWORK_EXT@ for the service .Dq Li domain . +.It Fl C Ar class +Defines which class to use. +Defaults to 'IN'. .It Fl S Perform a restricted transfer of only the SOA, NS records and glue A records for the zone. The SOA record will not be loaded by .Xr @INDOT@named @SYS_OPS_EXT@ but will be used to determine when to verify the NS records. See the .Dq Li stubs directive in .Xr @INDOT@named @SYS_OPS_EXT@ for more information. +.It Fl q +Tells @INDOT@named-xfer to be quiet. +.It Fl x Ar src_address +Specifies the source address to use for this query. +.It Fl T Ar tsig_file +Specifies a file to transfer TSIG information to @INDOT@named-xfer. +Multiple entries of the following format: +.Pp +.Bl -hang -width "IP address" -compact +.It IP address +When connecting to this address use this TSIG key. +.It key name +.It algorithm +157 (HMAC-MD5) is the only algorithm supported. +.It key data +base64 +.El +.Pp +@INDOT@named-xfer expects this file to be temporary in nature and +will explicitly delete this file after its use. .El .Pp Additional arguments are taken as name server addresses in so-called .Dq dotted-quad syntax .Em only ; no host name are allowed here. At least one address must be specified. Any additional addresses will be tried, in order, if the first one fails to transfer to us successfully. The .Sy axfr or .Sy ixfr after name server address designates the type of zone transfer to perform. Use .Sy axfr for a full zone transfer or .Sy ixfr for an incremental zone transfer. .Sh SEE ALSO .Xr hostname @DESC_EXT@ , .Xr @INDOT@named @SYS_OPS_EXT@ , .Xr resolver @LIB_NETWORK_EXT@ , .Xr resolver @FORMAT_EXT@ , RFC 882, RFC 883, RFC 973, RFC 974, RFC 1033, RFC 1034, RFC 1035, RFC 1123, RFC 1995 .Dq Name Server Operations Guide for Sy BIND . diff --git a/contrib/bind/doc/man/named.8 b/contrib/bind/doc/man/named.8 index 882cea14d1ce..1c31c37a73d5 100644 --- a/contrib/bind/doc/man/named.8 +++ b/contrib/bind/doc/man/named.8 @@ -1,445 +1,445 @@ .\" ++Copyright++ 1985, 1996 .\" - .\" Copyright (c) 1985, 1996 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" - .\" Portions Copyright (c) 1993 by Digital Equipment Corporation. .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies, and that .\" the name of Digital Equipment Corporation not be used in advertising or .\" publicity pertaining to distribution of the document or software without .\" specific, written prior permission. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL .\" WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT .\" CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL .\" DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR .\" PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS .\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS .\" SOFTWARE. .\" - .\" --Copyright-- .\" .\" @(#)named.8 6.6 (Berkeley) 2/14/89 .\" .Dd February 1, 1996 .Dt @INDOT_U@named @SYS_OPS_EXT_U@ .Os BSD 4 .Sh NAME .Nm @INDOT@named .Nd Internet domain name server (DNS) .Sh SYNOPSIS .Nm @INDOT@named .Op Fl d Ar debuglevel .Op Fl p Ar port# .Oo Fl Po .Cm b Ns \&| Ns Cm c .Pc .Ar config_file .Oc .Op Fl f q r v .Op Fl u Ar user_name .Op Fl g Ar group_name .Op Fl t Ar directory .Op Fl w Ar directory .Op Ar config_file .Sh DESCRIPTION .Ic Named is the Internet domain name server. See RFC's 1033, 1034, and 1035 for more information on the Internet name-domain system. Without any arguments, .Ic named will read the default configuration file .Pa /etc/named.conf , read any initial data, and listen for queries. A .Ar config_file argument given at the end of the command line will override any .Ar config_file specified by using the .Dq Fl b or .Dq Fl c flags. .Pp .Sy NOTE : Several of .Nm named Ns 's options, and much more of its behaviour, can be controlled in the configuration file. Please refer to the configuration file guide included with this .Sy BIND distribution for further information. .Pp Options are: .Bl -tag -width Fl .It Fl d Ar debuglevel Print debugging information. The .Ar debuglevel is a number determines the level of messages printed. If negative, .Ar debuglevel is set to .Dq 1 . .Pp .Sy NOTE : The new debugging framework is considerably more sophisticated than it was in older versions of .Nm @INDOT@named . The configuration file's .Dq Li logging statement allows for multiple, distinct levels of debugging for each of a large set of categories of events (such as queries, transfers in or out, etc.). Please refer to the configuration file guide included with this .Sy BIND distribution for further information about these extensive new capabilities. .It Fl p Ar port# Use the specified remote port number; this is the port number to which .Nm @INDOT@named will send queries. The default value is the standard port number, i.e., the port number returned by .Xr getservbyname @LIB_NETWORK_EXT@ for service .Dq Li domain . .Pp .Sy NOTE : Previously, the syntax .Dq Fl p Ar port# Ns Op Ar \&/localport# was supported; the first port was that used when contacting .Em remote servers, and the second one was the service port bound by the .Em local instance of .Nm @INDOT_U@named . The current usage is equivalent to the old usage without the .Ar localport# specified; this functionality can be specified with the .Dq Li listen-on clause of the configuration file's .Dq Li options statement. .It Xo Fl Po .Cm b Ns \&| Ns Cm c .Pc Ar config_file .Xc Use an alternate .Ar config_file ; this argument is overridden by any .Ar config_file which is specified at the end of the command line. The default value is .Pa /etc/named.conf . .It Fl f Run this process in the foreground; don't .Xr fork @SYSCALL_EXT@ and daemonize. (The default is to daemonize.) .It Fl q Trace all incoming queries if .Nm @INDOT_U@named has been compiled with .Li QRYLOG defined. .Pp .Sy NOTE : This option is deprecated in favor of the .Dq Li queries .Em logging category of the configuration file's .Dq Li logging statement; for more information, please refer to the configuration file guide included with this distribution of .Sy BIND . .It Fl r Turns recursion off in the server. Answers can come only from local (primary or secondary) zones. This can be used on root servers. The default is to use recursion. .Pp .Sy NOTE : This option can be overridden by and is deprecated in favor of the .Dq Li recursion clause of the configuration file's .Dq Li options statement. .It Fl v Report the version and exit. .It Fl u Ar user_name Specifies the user the server should run as after it initializes. The value specified may be either a username or a numeric user id. If the .Dq Fl g flag is not specified, then the group id used will be the primary group of the user specified (initgroups() is called, so all of the user's groups will be available to the server). .Pp .It Fl g Ar group_name Specifies the group the server should run as after it initializes. The value specified may be either a groupname or a numeric group id. .Pp .It Fl t Ar directory Specifies the directory the server should chroot() into as soon as it is -finshed processing command line arguments. +finished processing command line arguments. .Pp .It Fl w Ar directory Sets the working directory of the server. The .Dq Li directory clause of the configuration file's .Dq Li options statement overrides any value specified on the command line. The default working directory is the current directory .Pq Dq \&. . .El .Pp Any additional argument is taken as the name of the configuration file, for compatibility with older implementations; as noted above, this argument overrides any .Ar config_file specified by the use of the .Dq Fl b or .Dq Fl c flags. If no further argument is given, then the default configuration file is used .Pq Pa /etc/named.conf . .Ss Master File Format The master file consists of control information and a list of resource records for objects in the zone of the forms: .Bd -literal -offset indent $INCLUDE $ORIGIN $TTL .Ed .Pp where: .Bl -tag -width "opt_domain " .It Ar domain is .Dq Li .\& for root, .Dq Li @ for the current origin, or a standard domain name. If .Ar domain is a standard domain name that does .Em not end with .Dq Li \&. , the current origin is appended to the domain. Domain names ending with .Dq Li .\& are unmodified. .It Ar opt_domain This field is used to define an origin for the data in an included file. It is equivalent to placing an .Li $ORIGIN statement before the first line of the included file. The field is optional. Neither the .Ar opt_domain field nor .Li $ORIGIN statements in the included file modify the current origin for this file. .It Ar ttl A integer number that sets the default time-to-live for future records without an explicit ttl. .It Ar opt_ttl An optional integer number for the time-to-live field. If not set the ttl is taken from the last $TTL statement. If no $TTL statement has occurred then the SOA minimum value is used and a warning is generated. .It Ar opt_class The object address type; currently only one type is supported, .Dv IN , for objects connected to the DARPA Internet. .It Ar type This field contains one of the following tokens; the data expected in the .Ar resource_record_data field is in parentheses: .Bl -tag -width "HINFO " -offset indent .It Dv A a host address (dotted-quad IP address) .It Dv NS an authoritative name server (domain) .It Dv MX a mail exchanger (domain), preceded by a preference value (0..32767), with lower numeric values representing higher logical preferences. .It Dv CNAME the canonical name for an alias (domain) .It Dv SOA marks the start of a zone of authority (domain of originating host, domain address of maintainer, a serial number and the following parameters in seconds: refresh, retry, expire and minimum TTL (see RFC 883 and RFC 2308)). .It Dv NULL a null resource record (no format or data) .It Dv RP a Responsible Person for some domain name (mailbox, TXT-referral) .It Dv PTR a domain name pointer (domain) .It Dv HINFO host information (cpu_type OS_type) .El .El .Pp Resource records normally end at the end of a line, but may be continued across lines between opening and closing parentheses. Comments are introduced by semicolons and continue to the end of the line. .Pp .Sy NOTE : There are other resource record types not shown here. You should consult the .Sy BIND Operations Guide .Pq Dq BOG for the complete list. Some resource record types may have been standardized in newer RFC's but not yet implemented in this version of .Sy BIND . .Ss SOA Record Format Each master zone file should begin with an SOA record for the zone. An example SOA record is as follows: .Bd -literal @ IN SOA ucbvax.Berkeley.EDU. rwh.ucbvax.Berkeley.EDU. ( 1989020501 ; serial 10800 ; refresh 3600 ; retry 3600000 ; expire 86400 ) ; minimum .Ed .Pp The SOA specifies a serial number, which should be incremented each time the master file is changed. Note that the serial number can be given as a dotted number, but this is a .Em very unwise thing to do since the translation to normal integers is via concatenation rather than multiplication and addition. You can spell out the year, month, day of month, and 0..99 version number and still fit inside the unsigned 32-bit size of this field. (It's true that we will have to rethink this strategy in the year 4294, but we're not worried about it.) .Pp Secondary servers check the serial number at intervals specified by the refresh time in seconds; if the serial number changes, a zone transfer will be done to load the new data. If a master server cannot be contacted when a refresh is due, the retry time specifies the interval at which refreshes should be attempted. If a master server cannot be contacted within the interval given by the expire time, all data from the zone is discarded by secondary servers. The minimum value is the cache time-to-live for negative answers (RFC 2308). .Sh NOTES The boot file directives .Dq Li domain and .Dq Li suffixes have been obsoleted by a more useful, resolver-based implementation of suffixing for partially-qualified domain names. The prior mechanisms could fail under a number of situations, especially when then local nameserver did not have complete information. .Pp The following signals have the specified effect when sent to the server process using the .Xr kill @CMD_EXT@ command: .Pp .Bl -tag -width "SIGWINCH" .It Dv SIGHUP Causes server to read .Pa named.conf and reload the database. If the server is built with the .Li FORCED_RELOAD compile-time option, then .Dv SIGHUP will also cause the server to check the serial number on all secondary zones; normally, the serial numbers are only checked at the SOA-specified intervals. .It Dv SIGINT Dumps the current data base and cache to .Dq Pa /var/tmp/named_dump.db or the value of .Dv _PATH_DUMPFILE . .It Dv SIGILL Dumps statistics data into .Pa named.stats if the server is compiled with .Li -DSTATS . Statistics data is appended to the file. .It Dv SIGSYS Dumps the profiling data in .Pa /var/tmp if the server is compiled with profiling (server forks, chdirs and exits). .It Dv SIGTERM Saves any modified dynamic zones to the file system, and shuts down the server. .It Dv SIGUSR1 Turns on debugging; each .Dv SIGUSR1 increments debug level. .Po .Dv SIGEMT on older systems without .Dv SIGUSR1 . .Pc .It Dv SIGUSR2 Turns off debugging completely. .Po .Dv SIGFPE on older systems without .Dv SIGUSR2 . .Pc .It Dv SIGWINCH Toggles logging of all incoming queries via .Xr syslog @LIB_C_EXT@ (requires server to have been built with the .Li QRYLOG option). .El .Sh FILES .Bl -tag -width "/var/tmp/named_dump.db (_PATH_DUMPFILE) " -compact .It Pa /etc/named.conf default name server configuration file .It Pa /var/run/named.pid Pq Dv _PATH_PIDFILE the process id .It Pa /var/tmp/named_dump.db Pq Dv _PATH_DUMPFILE dump of the name server database .It Pa /var/tmp/named.run Pq file: Dv _PATH_DEBUG debug output .It Pa /var/tmp/named.stats Pq file: Dv _PATH_STATS nameserver statistics data .El .Sh SEE ALSO .Xr named.conf @FORMAT_EXT@ , .Xr gethostbyname @LIB_NETWORK_EXT@ , .Xr hostname @DESC_EXT@ , .Xr kill @CMD_EXT@ , .Xr resolver @LIB_NETWORK_EXT@ , .Xr resolver @FORMAT_EXT@ , .Xr signal @LIB_C_EXT@ , RFC 882, RFC 883, RFC 973, RFC 974, RFC 1033, RFC 1034, RFC 1035, RFC 1123, RFC 2308 .Dq Name Server Operations Guide for Sy BIND diff --git a/contrib/bind/doc/man/named.conf.5 b/contrib/bind/doc/man/named.conf.5 index 4f5896c8dcec..2d7e627a9fab 100644 --- a/contrib/bind/doc/man/named.conf.5 +++ b/contrib/bind/doc/man/named.conf.5 @@ -1,2134 +1,2145 @@ .\" Copyright (c) 1999-2000 by Internet Software Consortium .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS .\" ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE .\" CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL .\" DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR .\" PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS .\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS .\" SOFTWARE. .Dd January 7, 1999 .Dt NAMED.CONF 5 .Os BSD 4 .Sh NAME .Nm named.conf .Nd configuration file for .Xr named 8 .Sh OVERVIEW BIND 8 is much more configurable than previous release of BIND. There are entirely new areas of configuration, such as access control lists and categorized logging. Many options that previously applied to all zones can now be used selectively. These features, plus a consideration of future configuration needs led to the creation of a new configuration file format. .Ss General Syntax A BIND 8 configuration consists of two general features, statements and comments. All statements end with a semicolon. Many statements can contain substatements, which are each also terminated with a semicolon. .Pp The following statements are supported: .Bl -tag -width 0n .It Ic logging specifies what the server logs, and where the log messages are sent .It Ic options controls global server configuration options and sets defaults for other statements .It Ic zone defines a zone .It Ic acl defines a named IP address matching list, for access control and other uses .It Ic key specifies key information for use in authentication and authorization .It Ic trusted-keys defines DNSSEC keys that are preconfigured into the server and implicitly trusted .It Ic server sets certain configuration options for individual remote servers .It Ic controls declares control channels to be used by the .Nm ndc utility .It Ic include includes another file .El .Pp The .Ic logging and .Ic options statements may only occur once per configuration, while the rest may appear numerous times. Further detail on each statement is provided in individual sections below. .Pp Comments may appear anywhere that whitespace may appear in a BIND configuration file. To appeal to programmers of all kinds, they can be written in C, C++, or shell/perl constructs. .Pp C-style comments start with the two characters .Li /* (slash, star) and end with .Li */ (star, slash). Because they are completely delimited with these characters, they can be used to comment only a portion of a line or to span multiple lines. .Pp C-style comments cannot be nested. For example, the following is not valid because the entire comment ends with the first .Li */ : .Bd -literal -offset indent /* This is the start of a comment. This is still part of the comment. /* This is an incorrect attempt at nesting a comment. */ This is no longer in any comment. */ .Ed .Pp C++-style comments start with the two characters .Li // (slash, slash) and continue to the end of the physical line. They cannot be continued across multiple physical lines; to have one logical comment span multiple lines, each line must use the .Li // pair. For example: .Bd -literal -offset indent // This is the start of a comment. The next line // is a new comment, even though it is logically // part of the previous comment. .Ed .Pp Shell-style (or perl-style, if you prefer) comments start with the character .Li # (hash or pound or number or octothorpe or whatever) and continue to the end of the physical line, like C++ comments. For example: .Bd -literal -offset indent # This is the start of a comment. The next line # is a new comment, even though it is logically # part of the previous comment. .Ed .Pp .Em WARNING : you cannot use the .Li ; (semicolon) character to start a comment such as you would in a zone file. The semicolon indicates the end of a configuration statement, so whatever follows it will be interpreted as the start of the next statement. .Ss Converting from BIND 4.9.x BIND 4.9.x configuration files can be converted to the new format by using .Pa src/bin/named/named-bootconf , a shell script that is part of the BIND 8.2.x source kit. .Sh DOCUMENTATION DEFINITIONS Described below are elements used throughout the BIND configuration file documentation. Elements which are only associated with one statement are described only in the section describing that statement. .Bl -tag -width 0n .It Va acl_name The name of an .Va address_match_list as defined by the .Ic acl statement. .It Va address_match_list A list of one or more .Va ip_addr , .Va ip_prefix , .Va key_id , or .Va acl_name elements, as described in the .Sx ADDRESS MATCH LISTS section. .It Va dotted-decimal One or more integers valued 0 through 255 separated only by dots (``.''), such as .Li 123 , .Li 45.67 or .Li 89.123.45.67 . .It Va domain_name A quoted string which will be used as a DNS name, for example .Qq Li my.test.domain . .It Va path_name A quoted string which will be used as a pathname, such as .Qq Li zones/master/my.test.domain . .It Va ip_addr An IP address with exactly four elements in .Va dotted-decimal notation. .It Va ip_port An IP port .Va number . .Va number is limited to .Li 0 through .Li 65535 , with values below 1024 typically restricted to root-owned processes. In some cases an asterisk (``*'') character can be used as a placeholder to select a random high-numbered port. .It Va ip_prefix An IP network specified in .Va dotted-decimal form, followed by ``/'' and then the number of bits in the netmask. E.g. .Li 127/8 is the network .Li 127.0.0.0 with netmask .Li 255.0.0.0 . .Li 1.2.3.0/28 is network .Li 1.2.3.0 with netmask .Li 255.255.255.240. .It Va key_name A string representing the name of a shared key, to be used for transaction security. .It Va number A non-negative integer with an entire range limited by the range of a C language signed integer (2,147,483,647 on a machine with 32 bit integers). Its acceptable value might further be limited by the context in which it is used. .It Va size_spec A .Va number , the word .Li unlimited , or the word .Li default . .Pp The maximum value of .Va size_spec is that of unsigned long integers on the machine. .Li unlimited requests unlimited use, or the maximum available amount. .Li default uses the limit that was in force when the server was started. .Pp A .Va number can optionally be followed by a scaling factor: .Li K or .Li k for kilobytes, .Li M or .Li m for megabytes, and .Li G or .Li g for gigabytes, which scale by 1024, 1024*1024, and 1024*1024*1024 respectively. .Pp Integer storage overflow is currently silently ignored during conversion of scaled values, resulting in values less than intended, possibly even negative. Using .Li unlimited is the best way to safely set a really large number. .It Va yes_or_no Either .Li yes or .Li no . The words .Li true and .Li false are also accepted, as are the numbers .Li 1 and .Li 0 . .El .Sh ADDRESS MATCH LISTS .Ss Syntax .Bd -literal \fIaddress_match_list\fR = 1\&*\fIaddress_match_element\fR .Pp \fIaddress_match_element\fR = [ \&"!\&" ] ( \fIaddress_match_list\fR / \fIip_address\fR / \fIip_prefix\fR / \fIacl_name\fR / \&"key \&" \fIkey_id\fR ) \&";\&" .Ed .Ss Definition and Usage Address match lists are primarily used to determine access control for various server operations. They are also used to define priorities for querying other nameservers and to set the addresses on which .Nm named will listen for queries. The elements which constitute an address match list can be any of the following: .Bl -bullet .It an .Va ip-address (in .Va dotted-decimal notation, .It an .Va ip-prefix (in the '/'-notation), .It A .Va key_id , as defined by the .Ic key statement, .It the name of an address match list previously defined with the .Ic acl statement, or .It another .Va address_match_list . .El .Pp Elements can be negated with a leading exclamation mark (``!''), and the match list names .Li any , .Li none , .Li localhost and .Li localnets are predefined. More information on those names can be found in the description of the .Ic acl statement. .Pp The addition of the .Ic key clause made the name of this syntactic element something of a misnomer, since security keys can be used to validate access without regard to a host or network address. Nonetheless, the term ``address match list'' is still used throughout the documentation. .Pp When a given IP address or prefix is compared to an address match list, the list is traversed in order until an element matches. The interpretation of a match depends on whether the list is being used for access control, defining .Ic listen-on ports, or as a topology, and whether the element was negated. .Pp When used as an access control list, a non-negated match allows access and a negated match denies access. If there is no match at all in the list, access is denied. The clauses .Ic allow-query , .Ic allow-transfer , .Ic allow-update , .Ic allow-recursion , and .Ic blackhole all use address match lists like this. Similarly, the .Ic listen-on option will cause the server to not accept queries on any of the machine's addresses which do not match the list. .Pp When used with the .Ic topology option, a non-negated match returns a distance based on its position on the list (the closer the match is to the start of the list, the shorter the distance is between it and the server). A negated match will be assigned the maximum distance from the server. If there is no match, the address will get a distance which is further than any non-negated list element, and closer than any negated element. .Pp Because of the first-match aspect of the algorithm, an element that defines a subset of another element in the list should come before the broader element, regardless of whether either is negated. For example, in .Dl 1.2.3/24; !1.2.3.13 the 1.2.3.13 element is completely useless, because the algorithm will match any lookup for 1.2.3.13 to the 1.2.3/24 element. Using .Dl !1.2.3.13; 1.2.3/24 fixes that problem by having 1.2.3.13 blocked by the negation but all other 1.2.3.* hosts fall through. .Sh THE LOGGING STATEMENT .Ss Syntax .Bd -literal logging { [ channel \fIchannel_name\fR { ( file \fIpath_name\fR [ versions ( \fInumber\fR | unlimited ) ] [ size \fIsize_spec\fR ] | syslog ( kern | user | mail | daemon | auth | syslog | lpr | news | uucp | cron | authpriv | ftp | local0 | local1 | local2 | local3 | local4 | local5 | local6 | local7 ) | null ); .Pp [ severity ( critical | error | warning | notice | info | debug [ \fIlevel\fR ] | dynamic ); ] [ print-category \fIyes_or_no\fR; ] [ print-severity \fIyes_or_no\fR; ] [ print-time \fIyes_or_no\fR; ] }; ] .Pp [ category \fIcategory_name\fR { \fIchannel_name\fR; [ \fIchannel_name\fR; ... ] }; ] ... }; .Ed .Ss Definition and Usage The .Ic logging statement configures a wide variety of logging options for the nameserver. Its .Ic channel phrase associates output methods, format options and severity levels with a name that can then be used with the .Ic category phrase to select how various classes of messages are logged. .Pp Only one .Ic logging statement is used to define as many channels and categories as are wanted. If there are multiple logging statements in a configuration, the first defined determines the logging, and warnings are issued for the others. If there is no logging statement, the logging configuration will be: .Bd -literal logging { category default { default_syslog; default_debug; }; category panic { default_syslog; default_stderr; }; category packet { default_debug; }; category eventlib { default_debug; }; }; .Ed .Pp The logging configuration is established as soon as the .Ic logging statement is parsed. If you want to redirect messages about processing of the entire configuration file, the .Ic logging statement must appear first. Even if you do not redirect configuration file parsing messages, we recommend always putting the .Ic logging statement first so that this rule need not be consciously recalled if you ever do want the parser's messages relocated. .Ss The channel phrase All log output goes to one or more ``channels''; you can make as many of them as you want. .Pp Every channel definition must include a clause that says whether messages selected for the channel go to a file, to a particular syslog facility, or are discarded. It can optionally also limit the message severity level that will be accepted by the channel (default is .Li info ) , and whether to include a time stamp generated by .Nm named , the category name, or severity level. The default is not to include any of those three. .Pp The word .Li null as the destination option for the channel will cause all messages sent to it to be discarded; other options for the channel are meaningless. .Pp The .Ic file clause can include limitations both on how large the file is allowed to become, and how many versions of the file will be saved each time the file is opened. .Pp The .Ic size option for files is simply a hard ceiling on log growth. If the file ever exceeds the size, then .Nm named will just not write anything more to it until the file is reopened; exceeding the size does not automatically trigger a reopen. The default behavior is to not limit the size of the file. .Pp If you use the .Ic version logfile option, then .Nm named will retain that many backup versions of the file by renaming them when opening. For example, if you choose to keep 3 old versions of the file lamers.log then just before it is opened lamers.log.1 is renamed to lames.log.2, lamers.log.0 is renamed to lamers.log.1, and lamers.log is renamed to lamers.log.0. No rolled versions are kept by default; any existing log file is simply appended. The .Li unlimited keyword is synonymous with .Li 99 in current BIND releases. Example usage of size and versions options: .Bd -literal channel an_example_level { file "lamers.log" versions 3 size 20m; print-time yes; print-category yes; }; .Ed .Pp The argument for the .Ic syslog clause is a syslog facility as described in the .Xr syslog 3 manual page. How .Nm syslogd will handle messages sent to this facility is described in the .Xr syslog.conf 5 manual page. If you have a system which uses a very old version of syslog that only uses two arguments to the .Fn openlog function, then this clause is silently ignored. .Pp The .Ic severity clause works like syslog's ``priorities'', except that they can also be used if you are writing straight to a file rather than using syslog. Messages which are not at least of the severity level given will not be selected for the channel; messages of higher severity levels will be accepted. .Pp If you are using syslog, then the .Pa syslog.conf priorities will also determine what eventually passes through. For example, defining a channel facility and severity as .Li daemon and .Li debug but only logging .Li daemon.warning via .Pa syslog.conf will cause messages of severity .Li info and .Li notice to be dropped. If the situation were reversed, with .Nm named writing messages of only .Li warning or higher, then .Nm syslogd would print all messages it received from the channel. .Pp The server can supply extensive debugging information when it is in debugging mode. If the server's global debug level is greater than zero, then debugging mode will be active. The global debug level is set either by starting the .Nm named server with the .Fl d flag followed by a positive integer, or by sending the running server the .Dv SIGUSR1 signal (for example, by using .Ic ndc trace ) . The global debug level can be set to zero, and debugging mode turned off, by sending the server the .Dv SIGUSR2 signal (as with .Ic ndc notrace ) . All debugging messages in the server have a debug level, and higher debug levels give more more detailed output. Channels that specify a specific debug severity, e.g. .Bd -literal channel specific_debug_level { file \&"foo\&"; severity debug 3; }; .Ed .Pp will get debugging output of level 3 or less any time the server is in debugging mode, regardless of the global debugging level. Channels with .Li dynamic severity use the server's global level to determine what messages to print. .Pp If .Ic print-time has been turned on, then the date and time will be logged. .Ic print-time may be specified for a syslog channel, but is usually pointless since syslog also prints the date and time. If .Ic print-category is requested, then the category of the message will be logged as well. Finally, if .Ic print-severity is on, then the severity level of the message will be logged. The .Ic print- options may be used in any combination, and will always be printed in the following order: time, category, severity. Here is an example where all three .Ic print- options are on: .Bd -literal 28-Apr-1997 15:05:32.863 default: notice: Ready to answer queries. .Ed .Pp There are four predefined channels that are used for default logging as follows. How they are used used is described in the next section, .Sx The category phrase . .Bd -literal channel default_syslog { syslog daemon; # send to syslog's daemon facility severity info; # only send priority info and higher }; .Pp channel default_debug { file \&"named.run\&"; # write to named.run in the working directory # Note: stderr is used instead of \&"named.run\&" # if the server is started with the -f option. severity dynamic; # log at the server's current debug level }; .Pp channel default_stderr { # writes to stderr file \&"\&"; # this is illustrative only; there's currently # no way of specifying an internal file # descriptor in the configuration language. severity info; # only send priority info and higher }; .Pp channel null { null; # toss anything sent to this channel }; .Ed .Pp Once a channel is defined, it cannot be redefined. Thus you cannot alter the built-in channels directly, but you can modify the default logging by pointing categories at channels you have defined. .Ss The category phrase There are many categories, so you can send the logs you want to see wherever you want, without seeing logs you don't want. If you don't specify a list of channels for a category, then log messages in that category will be sent to the .Li default category instead. If you don't specify a default category, the following ``default default'' is used: .Bd -literal category default { default_syslog; default_debug; }; .Ed .Pp As an example, let's say you want to log security events to a file, but you also want keep the default logging behavior. You'd specify the following: .Bd -literal channel my_security_channel { file \&"my_security_file\&"; severity info; }; category security { my_security_channel; default_syslog; default_debug; }; .Ed .Pp To discard all messages in a category, specify the .Li null channel: .Bd -literal category lame-servers { null; }; category cname { null; }; .Ed .Pp The following categories are available: .Bl -tag -width 0n .It Ic default The catch-all. Many things still aren't classified into categories, and they all end up here. Also, if you don't specify any channels for a category, the default category is used instead. If you do not define the default category, the following definition is used: .Dl category default { default_syslog; default_debug; }; .It Ic config High-level configuration file processing. .It Ic parser Low-level configuration file processing. .It Ic queries A short log message is generated for every query the server receives. .It Ic lame-servers Messages like ``Lame server on ...'' .It Ic statistics Statistics. .It Ic panic If the server has to shut itself down due to an internal problem, it will log the problem in this category as well as in the problem's native category. If you do not define the panic category, the following definition is used: .Dl category panic { default_syslog; default_stderr; }; .It Ic update Dynamic updates. .It Ic update-security Denied dynamic updates due to access controls. .It Ic ncache Negative caching. .It Ic xfer-in Zone transfers the server is receiving. .It Ic xfer-out Zone transfers the server is sending. .It Ic db All database operations. .It Ic eventlib Debugging info from the event system. Only one channel may be specified for this category, and it must be a file channel. If you do not define the eventlib category, the following definition is used: .Dl category eventlib { default_debug; }; .It Ic packet Dumps of packets received and sent. Only one channel may be specified for this category, and it must be a file channel. If you do not define the packet category, the following definition is used: .Dl category packet { default_debug; }; .It Ic notify The NOTIFY protocol. .It Ic cname Messages like ``... points to a CNAME''. .It Ic security Approved/unapproved requests. .It Ic os Operating system problems. .It Ic insist Internal consistency check failures. .It Ic maintenance Periodic maintenance events. .It Ic load Zone loading messages. .It Ic response-checks Messages arising from response checking, such as ``Malformed response ...'', ``wrong ans. name ...'', ``unrelated additional info ...'', ``invalid RR type ...'', and ``bad referral ...''. .El .Sh THE OPTIONS STATEMENT .Ss Syntax .Bd -literal options { [ hostname \fIhostname_string\fR; ] [ version \fIversion_string\fR; ] [ directory \fIpath_name\fR; ] [ named-xfer \fIpath_name\fR; ] [ dump-file \fIpath_name\fR; ] [ memstatistics-file \fIpath_name\fR; ] [ pid-file \fIpath_name\fR; ] [ statistics-file \fIpath_name\fR; ] [ auth-nxdomain \fIyes_or_no\fR; ] [ deallocate-on-exit \fIyes_or_no\fR; ] [ dialup \fIyes_or_no\fR; ] [ fake-iquery \fIyes_or_no\fR; ] [ fetch-glue \fIyes_or_no\fR; ] [ has-old-clients \fIyes_or_no\fR; ] [ host-statistics \fIyes_or_no\fR; ] [ host-statistics-max \fInumber\fR; ] [ multiple-cnames \fIyes_or_no\fR; ] [ notify ( \fIyes_or_no\fR | explicit ); ] [ suppress-initial-notify \fIyes_or_no\fR; ] [ recursion \fIyes_or_no\fR; ] [ rfc2308-type1 \fIyes_or_no\fR; ] [ use-id-pool \fIyes_or_no\fR; ] [ treat-cr-as-space \fIyes_or_no\fR; ] [ also-notify \fIyes_or_no\fR; ] [ forward ( only | first ); ] [ forwarders { [ \fIin_addr\fR ; [ \fIin_addr\fR ; ... ] ] }; ] [ check-names ( master | slave | response ) ( warn | fail | ignore ); ] [ allow-query { \fIaddress_match_list\fR }; ] [ allow-recursion { \fIaddress_match_list\fR }; ] [ allow-transfer { \fIaddress_match_list\fR }; ] [ blackhole { \fIaddress_match_list\fR }; ] [ listen-on [ port \fIip_port\fR ] { \fIaddress_match_list\fR }; ] [ query-source [ address ( \fIip_addr\fR | * ) ] [ port ( \fIip_port\fR | * ) ] ; ] [ lame-ttl \fInumber\fR; ] [ max-transfer-time-in \fInumber\fR; ] [ max-ncache-ttl \fInumber\fR; ] [ min-roots \fInumber\fR; ] [ serial-queries \fInumber\fR; ] [ transfer-format ( one-answer | many-answers ); ] [ transfers-in \fInumber\fR; ] [ transfers-out \fInumber\fR; ] [ transfers-per-ns \fInumber\fR; ] [ transfer-source \fIip_addr\fR; ] [ maintain-ixfr-base \fIyes_or_no\fR; ] [ max-ixfr-log-size \fInumber\fR; ] [ coresize \fIsize_spec\fR ; ] [ datasize \fIsize_spec\fR ; ] [ files \fIsize_spec\fR ; ] [ stacksize \fIsize_spec\fR ; ] [ cleaning-interval \fInumber\fR; ] [ heartbeat-interval \fInumber\fR; ] [ interface-interval \fInumber\fR; ] [ statistics-interval \fInumber\fR; ] [ topology { \fIaddress_match_list\fR }; ] [ sortlist { \fIaddress_match_list\fR }; ] [ rrset-order { \fIorder_spec\fR ; [ \fIorder_spec\fR ; ... ] }; ] [ preferred-glue ( A | AAAA ); ] + [ edns-udp-size \fInumber\fR; ] }; .Ed .Ss Definition and Usage The options statement sets up global options to be used by BIND. This statement may appear at only once in a configuration file; if more than one occurrence is found, the first occurrence determines the actual options used, and a warning will be generated. If there is no options statement, an options block with each option set to its default will be used. .Ss Server Information .Bl -tag -width 0n .It Ic hostname This defaults to the hostname of the machine hosting the nameserver as found by gethostname(). Its prime purpose is to be able to identify which of a number of anycast servers is actually answering your queries by sending a txt query for .Pa hostname.bind in class chaos to the anycast server and geting back a unique name. Setting the hostname to a empty string ("") will disable processing of the queries. .It Ic version The version the server should report via the ndc command or via a query of name .Pa version.bind in class chaos. The default is the real version number of the server, but some server operators prefer the string ( .Ic surely you must be joking ). .El .Ss Pathnames .Bl -tag -width 0n .It Ic directory The working directory of the server. Any non-absolute pathnames in the configuration file will be taken as relative to this directory. The default location for most server output files (e.g. .Pa named.run ) is this directory. If a directory is not specified, the working directory defaults to .Pa \&. , the directory from which the server was started. The directory specified should be an absolute path. .It Ic named-xfer The pathname to the named-xfer program that the server uses for inbound zone transfers. If not specified, the default is system dependent (e.g. .Pa /usr/sbin/named-xfer ). .It Ic dump-file The pathname of the file the server dumps the database to when it receives .Dv SIGINT signal (as sent by .Ic ndc dumpdb ). If not specified, the default is .Pa named_dump.db . .It Ic memstatistics-file The pathname of the file the server writes memory usage statistics to on exit, if .Ic deallocate-on-exit is .Li yes . If not specified, the default is .Pa named.memstats . .It Ic pid-file The pathname of the file the server writes its process ID in. If not specified, the default is operating system dependent, but is usually .Pa /var/run/named.pid or .Pa /etc/named.pid . The pid-file is used by programs like .Nm ndc that want to send signals to the running nameserver. .It Ic statistics-file The pathname of the file the server appends statistics to when it receives .Dv SIGILL signal (from .Ic ndc stats ) . If not specified, the default is .Pa named.stats . .El .Ss Boolean Options .Bl -tag -width 0n .It Ic auth-nxdomain If .Li yes , then the .Li AA bit is always set on .Dv NXDOMAIN responses, even if the server is not actually authoritative. The default is .Li no . Turning .Lc auth-nxdomain will allow older clients that require .Li AA to be set to accept .Dv NXDOMAIN responses to work. .It Ic deallocate-on-exit If .Li yes , then when the server exits it will painstakingly deallocate every object it allocated, and then write a memory usage report to the .Ic memstatistics-file . The default is .Li no , because it is faster to let the operating system clean up. .Ic deallocate-on-exit is handy for detecting memory leaks. .It Ic dialup If .Li yes , then the server treats all zones as if they are doing zone transfers across a dial on demand dialup link, which can be brought up by traffic originating from this server. This has different effects according to zone type and concentrates the zone maintenance so that it all happens in a short interval, once every .Ic heartbeat-interval and hopefully during the one call. It also suppresses some of the normal zone maintenance traffic. The default is .Li no . The .Ic dialup option may also be specified in the .Ic zone statement, in which case it overrides the .Ic options dialup statement. .Pp If the zone is a .Ic master then the server will send out .Dv NOTIFY request to all the slaves. This will trigger the zone up to date checking in the slave (providing it supports .Dv NOTIFY ) allowing the slave to verify the zone while the call us up. .Pp If the zone is a .Ic slave or .Ic stub then the server will suppress the zone regular zone up to date queries and only perform the when the .Ic heartbeat-interval expires. .It Ic fake-iquery If .Li yes , the server will simulate the obsolete DNS query type .Dv IQUERY . The default is .Li no . .It Ic fetch-glue If .Li yes (the default), the server will fetch ``glue'' resource records it doesn't have when constructing the additional data section of a response. .Ic fetch-glue no can be used in conjunction with .Ic recursion no to prevent the server's cache from growing or becoming corrupted (at the cost of requiring more work from the client). .It Ic has-old-clients Setting the option to .Li yes , is equivalent to setting the following three options: .Ic auth-nxdomain yes ; , .Ic maintain-ixfr-base yes ; , and .Ic rfc2308-type1 no ; .Pp The use of .Ic has-old-clients with .Ic auth-nxdomain , .Ic maintain-ixfr-base , and .Ic rfc2308-type1 -is order dependant. +is order dependent. .It Ic host-statistics If .Li yes , then statistics are kept for every host that the the nameserver interacts with. The default is .Li no . .Em Note : turning on .Ic host-statistics can consume huge amounts of memory. .It Ic maintain-ixfr-base If .Li yes , -a IXFR database file is kept for all dynamicaly updated zones. +a IXFR database file is kept for all dynamically updated zones. This enables the server to answer IXFR queries which can speed up zone transfers enormously. The default is .Li no . .It Ic multiple-cnames If .Li yes , then multiple CNAME resource records will be allowed for a domain name. The default is .Li no . Allowing multiple CNAME records is against standards and is not recommended. Multiple CNAME support is available because previous versions of BIND allowed multiple CNAME records, and these records have been used for load balancing by a number of sites. .It Ic notify If .Li yes (the default), DNS NOTIFY messages are sent when a zone the server is authoritative for changes. The use of NOTIFY speeds convergence between the master and its slaves. Slave servers that receive a NOTIFY message and understand it will contact the master server for the zone and see if they need to do a zone transfer, and if they do, they will initiate it immediately. If .Li explicit , the DNS NOTIFY messages will only be sent to the addresses in the .Ic also-notify list. The .Ic notify option may also be specified in the .Ic zone statement, in which case it overrides the .Ic options notify statement. .It Ic suppress-initial-notify If .Li yes , suppress the initial notify messages when the server first loads. The default is .Li no . .It Ic recursion If .Li yes , and a DNS query requests recursion, then the server will attempt to do all the work required to answer the query. If recursion is not on, the server will return a referral to the client if it doesn't know the answer. The default is .Li yes . See also .Ic fetch-glue above. .It Ic rfc2308-type1 If .Li yes, the server will send NS records along with the SOA record for negative answers. You need to set this to no if you have an old BIND server using you as a forwarder that does not understand negative answers which contain both SOA and NS records or you have an old version of sendmail. The correct fix is to upgrade the broken server or sendmail. The default is .Li no . .It Ic use-id-pool If .Li yes, the server will keep track of its own outstanding query ID's to avoid duplication and increase randomness. This will result in 128KB more memory being consumed by the server. The default is .Li no . .It Ic treat-cr-as-space If .Li yes, the server will treat CR characters the same way it treats a space or tab. This may be necessary when loading zone files on a UNIX system that were generated on an NT or DOS machine. The default is .Li no . .El .Ss Also-Notify .Ic also-notify .Pp Defines a global list of IP addresses that also get sent NOTIFY messages whenever a fresh copy of the zone is loaded. This helps to ensure that copies of the zones will quickly converge on ``stealth'' servers. If an .Ic also-notify list is given in a .Ic zone statement, it will override the .Ic options also-notify statement. When a .Ic zone notify statement is set to .Ic no , the IP addresses in the global .Ic also-notify list will not get sent NOTIFY messages for that zone. The default is the empty list (no global notification list). .Ss Forwarding The forwarding facility can be used to create a large site-wide cache on a few servers, reducing traffic over links to external nameservers. It can also be used to allow queries by servers that do not have direct access to the Internet, but wish to look up exterior names anyway. Forwarding occurs only on those queries for which the server is not authoritative and does not have the answer in its cache. .Bl -tag -width 0n .It Ic forward This option is only meaningful if the .Ic forwarders list is not empty. A value of .Li first , the default, causes the server to query the forwarders first, and if that doesn't answer the question the server will then look for the answer itself. If .Li only is specified, the server will only query the forwarders. .It Ic forwarders Specifies the IP addresses to be used for forwarding. The default is the empty list (no forwarding). .El .Pp Forwarding can also be configured on a per-zone basis, allowing for the global forwarding options to be overridden in a variety of ways. You can set particular zones to use different forwarders, or have different .Ic forward only/first behavior, or to not forward at all. See .Sx THE ZONE STATEMENT section for more information. .Pp Future versions of BIND 8 will provide a more powerful forwarding system. The syntax described above will continue to be supported. .Ss Name Checking The server can check domain names based upon their expected client contexts. For example, a domain name used as a hostname can be checked for compliance with the RFCs defining valid hostnames. .Pp Three checking methods are available: .Bl -tag -width 0n .It Ic ignore No checking is done. .It Ic warn Names are checked against their expected client contexts. Invalid names are logged, but processing continues normally. .It Ic fail Names are checked against their expected client contexts. Invalid names are logged, and the offending data is rejected. .El .Pp The server can check names three areas: master zone files, slave zone files, and in responses to queries the server has initiated. If .Ic check-names response fail has been specified, and answering the client's question would require sending an invalid name to the client, the server will send a .Dv REFUSED response code to the client. .Pp The defaults are: .Bd -literal check-names master fail; check-names slave warn; check-names response ignore; .Ed .Pp .Ic check-names may also be specified in the .Ic zone statement, in which case it overrides the .Ic options check-names statement. When used in a .Ic zone statement, the area is not specified (because it can be deduced from the zone type). .Ss Access Control Access to the server can be restricted based on the IP address of the requesting system or via shared secret keys. See .Sx ADDRESS MATCH LISTS for details on how to specify access criteria. .Bl -tag -width 0n .It Ic allow-query Specifies which hosts are allowed to ask ordinary questions. .Ic allow-query may also be specified in the .Ic zone statement, in which case it overrides the .Ic options allow-query statement. If not specified, the default is to allow queries from all hosts. .Bl -tag -width 0n .It Ic allow-recursion Specifies which hosts are allowed to ask recursive questions. If not specified, the default is to allow recursive queries from all hosts. .It Ic allow-transfer Specifies which hosts are allowed to receive zone transfers from the server. .Ic allow-transfer may also be specified in the .Ic zone statement, in which case it overrides the .Ic options allow-transfer statement. If not specified, the default is to allow transfers from all hosts. .It Ic blackhole Specifies a list of addresses that the server will not accept queries from or use to resolve a query. Queries from these addresses will not be responded to. .El .El .Ss Interfaces The interfaces and ports that the server will answer queries from may be specified using the .Ic listen-on option. .Ic listen-on takes an optional port, and an address match list. The server will listen on all interfaces allowed by the address match list. If a port is not specified, port 53 will be used. .Pp Multiple .Ic listen-on statements are allowed. For example, .Bd -literal listen-on { 5.6.7.8; }; listen-on port 1234 { !1.2.3.4; 1.2/16; }; .Ed .Pp will enable the nameserver on port 53 for the IP address 5.6.7.8, and on port 1234 of an address on the machine in net 1.2 that is not 1.2.3.4. .Pp If no .Ic listen-on is specified, the server will listen on port 53 on all interfaces. .Ss Query Address If the server doesn't know the answer to a question, it will query other nameservers. .Ic query-source specifies the address and port used for such queries. If .Ic address is .Li * or is omitted, a wildcard IP address ( .Dv INADDR_ANY ) will be used. If .Va port is .Li * or is omitted, a random unprivileged port will be used. The default is .Dl query-source address * port *; .Pp Note: .Ic query-source currently applies only to UDP queries; TCP queries always use a wildcard IP address and a random unprivileged port. .Ss Zone Transfers .Bl -tag -width 0n .It Ic max-transfer-time-in Inbound zone transfers ( .Nm named-xfer processes) running longer than this many minutes will be terminated. The default is 120 minutes (2 hours). .It Ic transfer-format The server supports two zone transfer methods. .Li one-answer uses one DNS message per resource record transferred. .Li many-answers packs as many resource records as possible into a message. .Li many-answers is more efficient, but is only known to be understood by BIND 8.1 and patched versions of BIND 4.9.5. The default is .Li one-answer . .Ic transfer-format may be overridden on a per-server basis by using the .Ic server statement. .It Ic transfers-in The maximum number of inbound zone transfers that can be running concurrently. The default value is 10. Increasing .Ic transfers-in may speed up the convergence of slave zones, but it also may increase the load on the local system. .It Ic transfers-out This option will be used in the future to limit the number of concurrent outbound zone transfers. It is checked for syntax, but is otherwise ignored. .It Ic transfers-per-ns The maximum number of inbound zone transfers ( .Nm named-xfer processes) that can be concurrently transferring from a given remote nameserver. The default value is 2. Increasing .Ic transfers-per-ns may speed up the convergence of slave zones, but it also may increase the load on the remote nameserver. .Ic transfers-per-ns may be overridden on a per-server basis by using the .Ic transfers phrase of the .Ic server statement. .It Ic transfer-source .Nm transfer-source determines which local address will be bound to the TCP connection used to fetch all zones transferred inbound by the server. If not set, it defaults to a system controlled value which will usually be the address of the interface ``closest to`` the remote end. This address must appear in the remote end's .Nm allow-transfer option for the zones being transferred, if one is specified. This statement sets the .Nm transfer-source -for all zones, but can be overriden on a per-zone basis by includinga +for all zones, but can be overridden on a per-zone basis by including a .Nm transfer-source statement within the zone block in the configuration file. .El .Ss Resource Limits The server's usage of many system resources can be limited. Some operating systems don't support some of the limits. On such systems, a warning will be issued if the unsupported limit is used. Some operating systems don't support limiting resources, and on these systems a .D1 cannot set resource limits on this system message will be logged. .Pp Scaled values are allowed when specifying resource limits. For example, .Li 1G can be used instead of .Li 1073741824 to specify a limit of one gigabyte. .Li unlimited requests unlimited use, or the maximum available amount. .Li default uses the limit that was in force when the server was started. See the definition of .Va size_spec in the .Sx DOCUMENTATION DEFINITIONS section for more details. .Bl -tag -width 0n .It Ic coresize The maximum size of a core dump. The default value is .Li default . .It Ic datasize The maximum amount of data memory the server may use. The default value is .Li default . .It Ic files The maximum number of files the server may have open concurrently. The default value is .Li unlimited . Note that on some operating systems the server cannot set an unlimited value and cannot determine the maximum number of open files the kernel can support. On such systems, choosing .Li unlimited will cause the server to use the larger of the .Va rlim_max from .Fn getrlimit RLIMIT_NOFILE and the value returned by .Fn sysconf _SC_OPEN_MAX . If the actual kernel limit is larger than this value, use .Ic limit files to specify the limit explicitly. .It Ic max-ixfr-log-size The .Li max-ixfr-log-size will be used in a future release of the server to limit the size of the transaction log kept for Incremental Zone Transfer. .It Ic stacksize The maximum amount of stack memory the server may use. The default value is .Li default . .El .Ss Periodic Task Intervals .Bl -tag -width 0n .It Ic cleaning-interval The server will remove expired resource records from the cache every .Ic cleaning-interval minutes. The default is 60 minutes. If set to 0, no periodic cleaning will occur. .It Ic heartbeat-interval The server will perform zone maintenance tasks for all zones marked .Ic dialup yes whenever this interval expires. The default is 60 minutes. Reasonable values are up to 1 day (1440 minutes). If set to 0, no zone maintenance for these zones will occur. .It Ic interface-interval The server will scan the network interface list every .Ic interface-interval minutes. The default is 60 minutes. If set to 0, interface scanning will only occur when the configuration file is loaded. After the scan, listeners will be started on any new interfaces (provided they are allowed by the .Ic listen-on configuration). Listeners on interfaces that have gone away will be cleaned up. .It Ic statistics-interval Nameserver statistics will be logged every .Ic statistics-interval minutes. The default is 60. If set to 0, no statistics will be logged. .El .Ss Topology All other things being equal, when the server chooses a nameserver to query from a list of nameservers, it prefers the one that is topologically closest to itself. The .Ic topology statement takes an address match list and interprets it in a special way. Each top-level list element is assigned a distance. Non-negated elements get a distance based on their position in the list, where the closer the match is to the start of the list, the shorter the distance is between it and the server. A negated match will be assigned the maximum distance from the server. If there is no match, the address will get a distance which is further than any non-negated list element, and closer than any negated element. For example, .Bd -literal topology { 10/8; !1.2.3/24; { 1.2/16; 3/8; }; }; .Ed .Pp will prefer servers on network 10 the most, followed by hosts on network 1.2.0.0 (netmask 255.255.0.0) and network 3, with the exception of hosts on network 1.2.3 (netmask 255.255.255.0), which is preferred least of all. .Pp The default topology is .Dl topology { localhost; localnets; }; .Ss Resource Record sorting When returning multiple RRs, the nameserver will normally return them in .Ic Round Robin , i.e. after each request, the first RR is put to the end of the list. As the order of RRs is not defined, this should not cause any problems. .Pp The client resolver code should re-arrange the RRs as appropriate, i.e. using any addresses on the local net in preference to other addresses. However, not all resolvers can do this, or are not correctly configured. .Pp When a client is using a local server, the sorting can be performed in the server, based on the client's address. This only requires configuring the nameservers, not all the clients. .Pp The .Ic sortlist statement takes an address match list and interprets it even more specially than the .Ic topology statement does. .Pp Each top level statement in the sortlist must itself be an explicit address match list with one or two elements. The first element (which may be an IP address, an IP prefix, an ACL name or nested address match list) of each top level list is checked against the source address of the query until a match is found. .Pp Once the source address of the query has been matched, if the top level statement contains only one element, the actual primitive element that matched the source address is used to select the address in the response to move to the beginning of the response. If the statement is a list of two elements, the second element is treated like the address match list in a topology statement. Each top level element is assigned a distance and the address in the response with the minimum distance is moved to the beginning of the response. .Pp In the following example, any queries received from any of the addresses of the host itself will get responses preferring addresses on any of the locally connected networks. Next most preferred are addresses on the 192.168.1/24 network, and after that either the 192.168.2/24 or 192.168.3/24 network with no preference shown between these two networks. Queries received from a host on the 192.168.1/24 network will prefer other addresses on that network to the 192.168.2/24 and 192.168.3/24 networks. Queries received from a host on the 192.168.4/24 or the 192.168.5/24 network will only prefer other addresses on their directly connected networks. .Bd -literal sortlist { { localhost; // IF the local host { localnets; // THEN first fit on the 192.168.1/24; // following nets { 192,168.2/24; 192.168.3/24; }; }; }; { 192.168.1/24; // IF on class C 192.168.1 { 192.168.1/24; // THEN use .1, or .2 or .3 { 192.168.2/24; 192.168.3/24; }; }; }; { 192.168.2/24; // IF on class C 192.168.2 { 192.168.2/24; // THEN use .2, or .1 or .3 { 192.168.1/24; 192.168.3/24; }; }; }; { 192.168.3/24; // IF on class C 192.168.3 { 192.168.3/24; // THEN use .3, or .1 or .2 { 192.168.1/24; 192.168.2/24; }; }; }; { { 192.168.4/24; 192.168.5/24; }; // if .4 or .5, prefer that net }; }; .Ed .Pp The following example will give reasonable behaviour for the local host and hosts on directly connected networks. It is similar to the behavior of the address sort in BIND 4.9.x. Responses sent to queries from the local host will favor any of the directly connected networks. Responses sent to queries from any other hosts on a directly connected network will prefer addresses on that same network. Responses to other queries will not be sorted. .Bd -literal sortlist { { localhost; localnets; }; { localnets; }; }; .Ed .Ss RRset Ordering When multiple records are returned in an answer it may be useful to configure the order the records are placed into the response. For example the records for a zone might be configured to always be returned in the order they are defined in the zone file. Or perhaps a random shuffle of the records as they are returned is wanted. The rrset-order statement permits configuration of the ordering made of the records in a multiple record response. The default, if no ordering is defined, is a cyclic ordering (round robin). .Pp An .Ic order_spec is defined as follows: .Bd -literal [ \fIclass class_name\fR ][ \fItype type_name\fR ][ \fIname\fR "FQDN" ] \fIorder\fR ordering .Ed .Pp If no class is specified, the default is .Ic ANY . If no .Li Ictype is specified, the default is .Ic ANY . If no name is specified, the default is "*". .Pp The legal values for .Ic ordering are: .Bl -tag -width indent .It Ic fixed Records are returned in the order they are defined in the zone file. .It Ic random Records are returned in some random order. .It Ic cyclic Records are returned in a round-robin order. .El .Pp For example: .Bd -literal rrset-order { class IN type A name "rc.vix.com" order random; order cyclic; }; .Ed .Pp will cause any responses for type A records in class IN that have "rc.vix.com" as a suffix, to always be returned in random order. All other records are returned in cyclic order. .Pp If multiple .Ic rrset-order statements appear, they are not combined--the last one applies. .Pp If no .Ic rrset-order statement is specified, a default one of: .Bd -literal rrset-order { class ANY type ANY name "*" order cyclic ; }; .Ed .Pp is used. .Ss Glue Ordering When running a root nameserver it is sometimes necessary to ensure that other nameservers that are priming are successful. This requires that glue A records for at least of the nameservers are returned in the answer to a priming query. This can be achieved by setting .Ic preferred-glue A; which will add A records before other types in the additional section. +.Ss EDNS +Some firewalls fail to pass EDNS/UDP messages that are larger than +certain size, 512 or the UDP reassembly buffer. +To allow EDNS to +work across such firewalls it is necessary to advertise a EDNS +buffer size that is small enough to not trigger failures. +.Ic edns-udp-size +can be use to adjust the advertised size. +Values less than 512 will be increased to 512 and values greater than +4096 will be truncated to 4096. .Ss Tuning .Bl -tag -width 0n .It Ic lame-ttl Sets the number of seconds to cache a lame server indication. 0 disables caching. Default is 600 (10 minutes). Maximum value is 1800 (30 minutes) .It Ic max-ncache-ttl To reduce network traffic and increase performance the server store negative answers. .Ic max-ncache-ttl is used to set a maximum retention time for these answers in the server is seconds. The default .Ic max-ncache-ttl is 10800 seconds (3 hours). .Ic max-ncache-ttl cannot exceed the maximum retention time for ordinary (positive) answers (7 days) and will be silently truncated to 7 days if set to a value which is greater that 7 days. .It Ic min-roots The minimum number of root servers that is required for a request for the root servers to be accepted. Default is 2. .El .Sh THE ZONE STATEMENT .Ss Syntax .Bd -literal zone \fIdomain_name\fR [ ( in | hs | hesiod | chaos ) ] { type master; file \fIpath_name\fR; [ check-names ( warn | fail | ignore ); ] [ allow-update { \fIaddress_match_list\fR }; ] [ allow-query { \fIaddress_match_list\fR }; ] [ allow-transfer { \fIaddress_match_list\fR }; ] [ forward ( only | first ); ] [ forwarders { [ \fIip_addr\fR ; [ \fIip_addr\fR ; ... ] ] }; ] [ dialup \fIyes_or_no\fR; ] [ notify ( \fIyes_or_no\fR | explicit ); ] [ also-notify { \fIip_addr\fR; [ \fIip_addr\fR; ... ] }; [ pubkey \fInumber\fR \fInumber\fR \fInumber\fR \fIstring\fR; ] }; .Pp zone \fIdomain_name\fR [ ( in | hs | hesiod | chaos ) ] { type ( slave | stub ); [ file \fIpath_name\fR; ] masters [ port \fIip_port\fR ] { \fIip_addr\fR [ key \fIkey_id\fR ]; [ ... ] }; [ check-names ( warn | fail | ignore ); ] [ allow-update { \fIaddress_match_list\fR }; ] [ allow-query { \fIaddress_match_list\fR }; ] [ allow-transfer { \fIaddress_match_list\fR }; ] [ forward ( only | first ); ] [ forwarders { [ \fIip_addr\fR ; [ \fIip_addr\fR ; ... ] ] }; ] [ transfer-source \fIip_addr\fR; ] [ max-transfer-time-in \fInumber\fR; ] [ notify \fIyes_or_no\fR; ] [ also-notify { \fIip_addr\fR; [ \fIip_addr\fR; ... ] }; [ pubkey \fInumber\fR \fInumber\fR \fInumber\fR \fIstring\fR; ] }; .Pp zone \fIdomain_name\fR [ ( in | hs | hesiod | chaos ) ] { type forward; [ forward ( only | first ); ] [ forwarders { [ \fIip_addr\fR ; [ \fIip_addr\fR ; ... ] ] }; ] [ check-names ( warn | fail | ignore ); ] }; .Pp zone \&".\&" [ ( in | hs | hesiod | chaos ) ] { type hint; file \fIpath_name\fR; [ check-names ( warn | fail | ignore ); ] }; .Ed .Ss Definition and Usage The .Ic zone statement is used to define how information about particular DNS zones is managed by the server. There are five different zone types. .Bl -tag -width 0n .It Ic master The server has a master copy of the data for the zone and will be able to provide authoritative answers for it. .It Ic slave A .Ic slave zone is a replica of a master zone. The .Ic masters list specifies one or more IP addresses that the slave contacts to update its copy of the zone. If a .Ic port is specified then checks to see if the zone is current and zone transfers will be done to the port given. If .Ic file is specified, then the replica will be written to the named file. Use of the .Ic file clause is highly recommended, since it often speeds server startup and eliminates a needless waste of bandwidth. .It Ic stub A .Ic stub zone is like a slave zone, except that it replicates only the NS records of a master zone instead of the entire zone. .It Ic forward A .Ic forward zone is used to direct all queries in it to other servers, as described in .Sx THE OPTIONS STATEMENT section. The specification of options in such a zone will override any global options declared in the .Ic options statement. .Pp If either no .Ic forwarders clause is present in the zone or an empty list for .Ic forwarders is given, then no forwarding will be done for the zone, cancelling the effects of any .Ic forwarders in the .Ic options statement. Thus if you want to use this type of zone to change only the behavior of the global .Ic forward option, and not the servers used, then you also need to respecify the global forwarders. .It Ic hint The initial set of root nameservers is specified using a .Ic hint zone. When the server starts up, it uses the root hints to find a root nameserver and get the most recent list of root nameservers. .El .Pp Note: previous releases of BIND used the term .Ic primary for a master zone, .Ic secondary for a slave zone, and .Ic cache for a hint zone. .Ss Classes The zone's name may optionally be followed by a class. If a class is not specified, class .Ic in (for "internet"), is assumed. This is correct for the vast majority of cases. .Pp The .Ic hesiod class is for an information service from MIT's Project Athena. It is used to share information about various systems databases, such as users, groups, printers and so on. More information can be found at ftp://athena-dist.mit.edu/pub/ATHENA/usenix/athena_changes.PS. The keyword .Ic hs is a synonym for .Ic hesiod . .Pp Another MIT development was CHAOSnet, a LAN protocol created in the mid-1970s. It is still sometimes seen on LISP stations and other hardware in the AI community, and zone data for it can be specified with the .Ic chaos class. .Ss Options .Bl -tag -width 0n .It Ic check-names See the subsection on .Sx Name Checking in .Sx THE OPTIONS STATEMENT . .It Ic allow-query See the description of .Ic allow-query in the .Sx Access Control subsection of .Sx THE OPTIONS STATEMENT . .It Ic allow-update Specifies which hosts are allowed to submit Dynamic DNS updates to the server. The default is to deny updates from all hosts. .It Ic allow-transfer See the description of .Ic allow-transfer in the .Sx Access Control subsection of .Sx THE OPTIONS STATEMENT . .It Ic transfer-source .Ic transfer-source determines which local address will be bound to the TCP connection used to fetch this zone. If not set, it defaults to a system controlled value which will usually be the address of the interface ``closest to'' the remote end. This address must appear in the remote end's .Ic allow-transfer option for this zone if one is specified. .It Ic max-transfer-time-in See the description of .Ic max-transfer-time-in in the .Sx Zone Transfers subsection of .Sx THE OPTIONS STATEMENT . .It Ic dialup See the description of .Ic dialup in the .Sx Boolean Options subsection of .Sx THE OPTIONS STATEMENT . .It Ic notify See the description of .Sx notify in the .Sx Boolean Options subsection of the .Sx THE OPTIONS STATEMENT . .It Ic also-notify .Ic also-notify is only meaningful if .Ic notify is active for this zone. The set of machines that will receive a DNS NOTIFY message for this zone is made up of all the listed nameservers for the zone (other than the primary master) plus any IP addresses specified with .Ic also-notify . .Ic also-notify is not meaningful for .Ic stub zones. The default is the empty list. .It Ic forward .Ic forward is only meaningful if the zone has a .Ic forwarders list. The .Ic only value causes the lookup to fail after trying the .Ic forwarders and getting no answer, while .Ic first would allow a normal lookup to be tried. .It Ic forwarders The .Ic forwarders option in a zone is used to override the list of global forwarders. If it is not specified in a zone of type .Ic forward , .Em no forwarding is done for the zone; the global options are not used. .It Ic pubkey The DNSSEC flags, protocol, and algorithm are specified, as well as a base-64 encoded string representing the key. .El .Sh THE ACL STATEMENT .Ss Syntax .Bd -literal acl \fIname\fR { \fIaddress_match_list\fR }; .Ed .Ss Definition and Usage The .Ic acl statement creates a named address match list. It gets its name from a primary use of address match lists: Access Control Lists (ACLs). .Pp Note that an address match list's name must be defined with .Ic acl before it can be used elsewhere; no forward references are allowed. .Pp The following ACLs are built-in: .Bl -tag -width 0n .It Ic any Allows all hosts. .It Ic none Denies all hosts. .It Ic localhost Allows the IP addresses of all interfaces on the system. .It Ic localnets Allows any host on a network for which the system has an interface. .El .Sh THE KEY STATEMENT .Ss Syntax .Bd -literal key \fIkey_id\fR { algorithm \fIalgorithm_id\fR; secret \fIsecret_string\fR; }; .Ed .Ss Definition and Usage The .Ic key statement defines a key ID which can be used in a .Ic server statement to associate a method of authentication with a particular name server that is more rigorous than simple IP address matching. A key ID must be created with the .Ic key statement before it can be used in a .Ic server definition or an address match list. .Pp The .Va algorithm_id is a string that specifies a security/authentication algorithm. .Va secret_string is the secret to be used by the algorithm, and is treated as a base-64 encoded string. It should go without saying, but probably can't, that if you have .Va secret_string 's in your .Pa named.conf , then it should not be readable by anyone but the superuser. .Sh THE TRUSTED-KEYS STATEMENT .Ss Syntax .Bd -literal trusted-keys { [ \fIdomain_name\fR \fIflags\fR \fIprotocol\fR \fIalgorithm\fR \fIkey\fR; ] }; .Ed .Ss Definition and Usage The .Ic trusted-keys statement is for use with DNSSEC-style security, originally specified in RFC 2065. DNSSEC is meant to provide three distinct services: key distribution, data origin authentication, and transaction and request authentication. A complete description of DNSSEC and its use is beyond the scope of this document, and readers interested in more information should start with RFC 2065 and then continue with the Internet Drafts available at http://www.ietf.org/ids.by.wg/dnssec.html. .Pp Each trusted key is associated with a domain name. Its attributes are the non-negative integral .Va flags , .Va protocol , and .Va algorithm , as well as a base-64 encoded string representing the .Va key . .Pp Any number of trusted keys can be specified. .Sh THE SERVER STATEMENT .Ss Syntax .Bd -literal server \fIip_addr\fR { [ edns \fIyes_or_no\fR; ] [ bogus \fIyes_or_no\fR; ] [ support-ixfr \fIyes_or_no\fR; ] [ transfers \fInumber\fR; ] [ transfer-format ( one-answer | many-answers ); ] [ keys { \fIkey_id\fR [ \fIkey_id\fR ... ] }; ] }; .Ed .Ss Definition and Usage The server statement defines the characteristics to be associated with a remote name server. .Pp If you discover that a server does not support EDNS you can prevent named making EDNS queries to it by specifying .Ic edns .Ic no; . The default value of .Ic edns is .Ic yes . .Pp If you discover that a server is giving out bad data, marking it as .Ic bogus will prevent further queries to it. The default value of .Ic bogus is .Li no . .Pp If the server supports IXFR you can tell named to attempt to perform a IXFR style zone transfer by specifing .Ic support-ixfr .Li yes . The default value of .Ic support-ixfr is .Li no . .Pp The server supports two zone transfer methods. The first, .Ic one-answer , uses one DNS message per resource record transferred. .Ic many-answers packs as many resource records as possible into a message. .Ic many-answers is more efficient, but is only known to be understood by BIND 8.1 and patched versions of BIND 4.9.5. You can specify which method to use for a server with the .Ic transfer-format option. If .Ic transfer-format is not specified, the .Ic transfer-format specified by the .Ic options statement will be used. .Pp The .Ic transfers will be used in a future release of the server to limit the number of concurrent in-bound zone transfers from the specified server. It is checked for syntax but is otherwise ignored. .Pp The .Ic keys clause is used to identify a .Va key_id defined by the .Ic key statement, to be used for transaction security when talking to the remote server. The .Ic key -statememnt must come before the +statement must come before the .Ic server statement that references it. .Pp The .Ic keys statement is intended for future use by the server. It is checked for syntax but is otherwise ignored. .Sh THE CONTROLS STATEMENT .Ss Syntax .Bd -literal controls { [ inet \fIip_addr\fR port \fIip_port\fR allow { \fIaddress_match_list\fR; }; ] [ unix \fIpath_name\fR perm \fInumber\fR owner \fInumber\fR group \fInumber\fR; ] }; .Ed .Ss Definition and Usage The .Ic controls statement declares control channels to be used by system administrators to affect the operation of the local name server. These control channels are used by the .Nm ndc utility to send commands to and retrieve non-DNS results from a name server. .Pp A .Ic unix control channel is a FIFO in the file system, and access to it is controlled by normal file system permissions. It is created by .Nm named with the specified file mode bits (see .Xr chmod 1 ) , user and group owner. Note that, unlike .Nm chmod , the mode bits specified for .Ic perm will normally have a leading .Li 0 so the number is interpreted as octal. Also note that the user and group ownership specified as .Ic owner and .Ic group must be given as numbers, not names. It is recommended that the permissions be restricted to administrative personnel only, or else any user on the system might be able to manage the local name server. .Pp An .Ic inet control channel is a TCP/IP socket accessible to the Internet, created at the specified .Va ip_port on the specified .Va ip_addr . Modern .Nm telnet clients are capable of speaking directly to these sockets, and the control protocol is ARPAnet-style text. It is recommended that 127.0.0.1 be the only .Va ip_addr used, and this only if you trust all non-privileged users on the local host to manage your name server. .Sh THE INCLUDE STATEMENT .Ss Syntax .Bd -literal include \fIpath_name\fR; .Ed .Ss Definition and Usage The .Ic include statement inserts the specified file at the point that the .Ic include statement is encountered. It cannot be used within another statement, though, so a line such as .Dl acl internal_hosts { include "internal_hosts.acl"; }; is not allowed. .Pp Use .Ic include to break the configuration up into easily-managed chunks. For example: .Bd -literal include "/etc/security/keys.bind"; include "/etc/acls.bind"; .Ed .Pp could be used at the top of a BIND configuration file in order to include any ACL or key information. .Pp Be careful not to type ``#include'', like you would in a C program, because ``#'' is used to start a comment. .Sh EXAMPLES The simplest configuration file that is still realistically useful is one which simply defines a hint zone that has a full path to the root servers file. .Bd -literal zone \&".\&" in { type hint; file \&"/var/named/root.cache\&"; }; .Ed .Pp Here's a more typical real-world example. .Bd -literal /* * A simple BIND 8 configuration */ .Pp logging { category lame-servers { null; }; category cname { null; }; }; .Pp options { directory \&"/var/named\&"; }; .Pp controls { inet * port 52 allow { any; }; // a bad idea unix \&"/var/run/ndc\&" perm 0600 owner 0 group 0; // the default }; .Pp zone \&"isc.org\&" in { type master; file \&"master/isc.org\&"; }; .Pp zone \&"vix.com\&" in { type slave; file \&"slave/vix.com\&"; masters { 10.0.0.53; }; }; .Pp zone \&"0.0.127.in-addr.arpa\&" in { type master; file \&"master/127.0.0\&"; }; .Pp zone \&".\&" in { type hint; file \&"root.cache\&"; }; .Ed .Sh FILES .Bl -tag -width 0n -compact .It Pa /etc/named.conf The BIND 8 .Nm named configuration file. .El .Sh SEE ALSO .Xr named 8 , .Xr ndc 8 diff --git a/contrib/bind/doc/man/resolver.3 b/contrib/bind/doc/man/resolver.3 index c2cd91598d18..f7c5424289aa 100644 --- a/contrib/bind/doc/man/resolver.3 +++ b/contrib/bind/doc/man/resolver.3 @@ -1,608 +1,653 @@ .\" Copyright (c) 1985, 1995 The Regents of the University of California. .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms are permitted provided .\" that: (1) source distributions retain this entire copyright notice and .\" comment, and (2) distributions including binaries display the following .\" acknowledgement: ``This product includes software developed by the .\" University of California, Berkeley and its contributors'' in the .\" documentation or other materials provided with the distribution and in .\" all advertising materials mentioning features or use of this software. .\" Neither the name of the University nor the names of its contributors may .\" be used to endorse or promote products derived from this software without .\" specific prior written permission. .\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" .\" @(#)resolver.3 6.5 (Berkeley) 6/23/90 -.\" $Id: resolver.3,v 8.17 2002/08/07 03:47:33 marka Exp $ +.\" $Id: resolver.3,v 8.17.6.1 2003/06/02 09:11:27 marka Exp $ .\" .Dd July 4, 2000 .Dt RESOLVER @LIB_NETWORK_EXT_U@ .Os BSD 4 .Sh NAME .Nm res_ninit , .Nm res_ourserver_p , .Nm fp_resstat , .Nm res_hostalias , .Nm res_pquery , .Nm res_nquery , .Nm res_nsearch , .Nm res_nquerydomain , .Nm res_nmkquery , .Nm res_nsend , .Nm res_nupdate , .Nm res_nmkupdate , .Nm res_nclose , .Nm res_nsendsigned , .Nm res_findzonecut , +.Nm res_getservers , +.Nm res_setservers , .Nm dn_comp , .Nm dn_expand , .Nm hstrerror , .Nm res_init , .Nm res_isourserver , .Nm fp_nquery , .Nm p_query , .Nm hostalias , .Nm res_query , .Nm res_search , .Nm res_querydomain , .Nm res_mkquery , .Nm res_send , .Nm res_update , .Nm res_close , .Nm herror .Nd resolver routines .Sh SYNOPSIS .Fd #include .Fd #include .Fd #include .Fd #include +.Fd #include .Vt typedef struct __res_state *res_state ; .Pp +.Ft int .Fn res_ninit "res_state statp" +.Ft int .Fn res_ourserver_p "const res_state statp" "const struct sockaddr_in *addr" +.Ft void .Fn fp_resstat "const res_state statp" "FILE *fp" +.Ft "const char *" .Fn res_hostalias "const res_state statp" "const char *name" "char *buf" "size_t buflen" +.Ft int .Fn res_pquery "const res_state statp" "const u_char *msg" "int msglen" "FILE *fp" +.Ft int .Fn res_nquery "res_state statp" "const char *dname" "int class" "int type" "u_char *answer" "int anslen" +.Ft int .Fn res_nsearch "res_state statp" "const char *dname" "int class" "int type" "u_char * answer" "int anslen" +.Ft int .Fn res_nquerydomain "res_state statp" "const char *name" "const char *domain" "int class" "int type" "u_char *answer" "int anslen" +.Ft int .Fo res_nmkquery .Fa "res_state statp" .Fa "int op" .Fa "const char *dname" .Fa "int class" .Fa "int type" .Fa "const u_char *data" .Fa "int datalen" .Fa "const u_char *newrr" .Fa "u_char *buf" .Fa "int buflen" .Fc +.Ft int .Fn res_nsend "res_state statp" "const u_char *msg" "int msglen" "u_char *answer" "int anslen" +.Ft int .Fn res_nupdate "res_state statp" "ns_updrec *rrecp_in" +.Ft int .Fn res_nmkupdate "res_state statp" "ns_updrec *rrecp_in" "u_char *buf" "int buflen" +.Ft void .Fn res_nclose "res_state statp" +.Ft int .Fn res_nsendsigned "res_state statp" "const u_char *msg" "int msglen" "ns_tsig_key *key" "u_char *answer" "int anslen" +.Ft int .Fn res_findzonecut "res_state statp" "const char *dname" "ns_class class" "int options" "char *zname" "size_t zsize" "struct in_addr *addrs" "int naddrs" +.Ft int +.Fn res_getservers "res_state statp" "union res_sockaddr_union *set" "int cnt" +.Ft void +.Fn res_setservers "res_state statp" "const union res_sockaddr_union *set" "int cnt" +.Ft int .Fn dn_comp "const char *exp_dn" "u_char *comp_dn" "int length" "u_char **dnptrs" "u_char **lastdnptr" +.Ft int .Fn dn_expand "const u_char *msg" "const u_char *eomorig" "const u_char *comp_dn" "char *exp_dn" "int length" +.Ft "const char *" .Fn hstrerror "int err" .Ss DEPRECATED .Fd #include .Fd #include .Fd #include .Fd #include +.Fd #include +.Ft int .Fn res_init "void" +.Ft int .Fn res_isourserver "const struct sockaddr_in *addr" +.Ft int .Fn fp_nquery "const u_char *msg" "int msglen" "FILE *fp" +.Ft void .Fn p_query "const u_char *msg" "FILE *fp" +.Ft "const char *" .Fn hostalias "const char *name" +.Ft int .Fn res_query "const char *dname" "int class" "int type" "u_char *answer" "int anslen" +.Ft int .Fn res_search "const char *dname" "int class" "int type" "u_char *answer" "int anslen" +.Ft int .Fn res_querydomain "const char *name" "const char *domain" "int class" "int type" "u_char *answer" "int anslen" +.Ft int .Fo res_mkquery .Fa "int op" .Fa "const char *dname" .Fa "int class" .Fa "int type" .Fa "const char *data" .Fa "int datalen" .Fa "struct rrec *newrr" .Fa "u_char *buf" .Fa "int buflen" .Fc +.Ft int .Fn res_send "const u_char *msg" "int msglen" "u_char *answer" "int anslen" +.Ft int .Fn res_update "ns_updrec *rrecp_in" +.Ft void .Fn res_close "void" +.Ft void .Fn herror "const char *s" .Sh DESCRIPTION These routines are used for making, sending and interpreting query and reply messages with Internet domain name servers. .Pp State information is kept in .Fa statp and is used to control the behavior of these functions. .Fa statp should be set to all zeros prior to the first call to any of these functions. .Pp The functions .Fn res_init , .Fn res_isourserver , .Fn fp_nquery , .Fn p_query , .Fn hostalias , .Fn res_query , .Fn res_search , .Fn res_querydomain , .Fn res_mkquery , .Fn res_send , .Fn res_update , .Fn res_close and .Fn herror are deprecated and are supplied for compatability with old source code. They use global configuration and state information that is kept in the structure .Ft _res rather than that referenced through .Ft statp . .Pp Most of the values in .Ft statp and .Ft _res are initialized on the first call to .Fn res_ninit / .Fn res_init to reasonable defaults and can be ignored. Options stored in .Ft statp->options / .Ft _res.options are defined in .Pa resolv.h and are as follows. Options are stored as a simple bit mask containing the bitwise .Dq OR of the options enabled. .Bl -tag -width "RES_DEB" .It Dv RES_INIT True if the initial name server address and default domain name are initialized (i.e., .Fn res_ninit / .Fn res_init has been called). .It Dv RES_DEBUG Print debugging messages. .It Dv RES_AAONLY Accept authoritative answers only. Should continue until it finds an authoritative answer or finds an error. Currently this is not implemented. .It Dv RES_USEVC Use TCP connections for queries instead of UDP datagrams. .It Dv RES_STAYOPEN Used with .Dv RES_USEVC to keep the TCP connection open between queries. This is useful only in programs that regularly do many queries. UDP should be the normal mode used. .It Dv RES_IGNTC Ignore truncation errors, i.e., don't retry with TCP. .It Dv RES_RECURSE Set the recursion-desired bit in queries. This is the default. (\c .Fn res_nsend / .Fn res_send does not do iterative queries and expects the name server to handle recursion.) .It Dv RES_DEFNAMES If set, .Fn res_nsearch / .Fn res_search will append the default domain name to single-component names (those that do not contain a dot). This option is enabled by default. .It Dv RES_DNSRCH If this option is set, .Fn res_nsearch / .Fn res_search will search for host names in the current domain and in parent domains; see .Xr hostname @DESC_EXT@ . This is used by the standard host lookup routine .Xr gethostbyname @LIB_NETWORK_EXT@ . This option is enabled by default. .It Dv RES_NOALIASES This option turns off the user level aliasing feature controlled by the .Ev HOSTALIASES environment variable. Network daemons should set this option. .It Dv RES_USE_INET6 This option causes .Xr gethostbyname @LIB_NETWORK_EXT@ to look for AAAA records before looking for A records if none are found. .It Dv RES_ROTATE This options causes the .Fn res_nsend / .Fn res_send to rotate the list of nameservers in .Fa statp->nsaddr_list / .Fa _res.nsaddr_list . .It Dv RES_KEEPTSIG This option causes .Fn res_nsendsigned to leave the message unchanged after TSIG verification; otherwise the TSIG record would be removed and the header updated. .It Dv RES_NOTLDQUERY This option causes .Fn res_nsearch to not attempt to resolve a unqualified name as if it were a top level domain (TLD). This option can cause problems if the site has "localhost" as a TLD rather than having localhost on one or more elements of the search list. This option has no effect if neither .Dv RES_DEFNAMES or .Dv RES_DNSRCH is set. .El .Pp The .Fn res_ninit / .Fn res_init routine reads the configuration file (if any; see .Xr resolver @FORMAT_EXT@ ) to get the default domain name, search list and the Internet address of the local name server(s). If no server is configured, the host running the resolver is tried. The current domain name is defined by the hostname if not specified in the configuration file; it can be overridden by the environment variable .Ev LOCALDOMAIN . This environment variable may contain several blank-separated tokens if you wish to override the .Dq search list on a per-process basis. This is similar to the .Ic search command in the configuration file. Another environment variable .Pq Dq Ev RES_OPTIONS can be set to override certain internal resolver options which are otherwise set by changing fields in the .Ft statp / .Ft _res structure or are inherited from the configuration file's .Ic options command. The syntax of the .Dq Ev RES_OPTIONS environment variable is explained in .Xr resolver @FORMAT_EXT@ . Initialization normally occurs on the first call to one of the other resolver routines. .Pp The .Fn res_nquery / .Fn res_query functions provides interfaces to the server query mechanism. They constructs a query, sends it to the local server, awaits a response, and makes preliminary checks on the reply. The query requests information of the specified .Fa type and .Fa class for the specified fully-qualified domain name .Fa dname . The reply message is left in the .Fa answer buffer with length .Fa anslen supplied by the caller. .Fn res_nquery / .Fn res_query return -1 on error or the length of the answer. .Pp The .Fn res_nsearch / .Fn res_search routines make a query and awaits a response like .Fn res_nquery / .Fn res_query , but in addition, it implements the default and search rules controlled by the .Dv RES_DEFNAMES and .Dv RES_DNSRCH options. It returns the length of the first successful reply which is stored in .Ft answer or -1 on error. .Pp The remaining routines are lower-level routines used by .Fn res_nquery / .Fn res_query . The .Fn res_nmkquery / .Fn res_mkquery functions constructs a standard query message and places it in .Fa buf . It returns the size of the query, or \-1 if the query is larger than .Fa buflen . The query type .Fa op is usually .Dv QUERY , but can be any of the query types defined in .Pa . The domain name for the query is given by .Fa dname . .Fa Newrr is currently unused but is intended for making update messages. .Pp The .Fn res_nsend / .Fn res_send / .Fn res_nsendsigned routines sends a pre-formatted query and returns an answer. It will call .Fn res_ninit / .Fn res_init if .Dv RES_INIT is not set, send the query to the local name server, and handle timeouts and retries. Additionally, .Fn res_nsendsigned will use TSIG signatures to add authentication to the query and verify the response. In this case, only one nameserver will be contacted. The length of the reply message is returned, or \-1 if there were errors. .Pp .Fn res_nquery / .Fn res_query , .Fn res_nsearch / .Fn res_search and .Fn res_nsend / .Fn res_send return a length that may be bigger than .Fa anslen . In that case the query should be retried with a bigger buffer. NOTE the answer to the second query may be larger still so supplying a buffer that bigger that the answer returned by the previous query is recommended. .Pp .Fa answer MUST be big enough to receive a maximum UDP response from the server or parts of the answer will be silently discarded. The default maximum UDP response size is 512 bytes. .Pp The function .Fn res_ourserver_p returns true when .Fa inp is one of the servers in .Fa statp->nsaddr_list / .Fa _res.nsaddr_list . .Pp The functions .Fn fp_nquery / .Fn p_query print out the query and any answer in .Fa msg on .Fa fp . .Fn p_query is equivalent to .Fn fp_nquery with .Fa msglen set to 512. .Pp The function .Fn fp_resstat prints out the active flag bits in .Fa statp->options preceeded by the text ";; res options:" on .Fa file . .Pp The functions .Fn res_hostalias / .Fn hostalias lookup up name in the file referred to by the .Ev HOSTALIASES files return a fully qualified hostname if found or NULL if not found or an error occurred. .Fn res_hostalias uses .Fa buf to store the result in, .Fn hostalias uses a static buffer. .Pp The functions +.Fn res_getservers +and +.Fn res_setservers +are used to get and set the list of server to be queried. +.Pp +The functions .Fn res_nupdate / .Fn res_update take a list of ns_updrec .Fa rrecp_in . Identifies the containing zone for each record and groups the records according to containing zone maintaining in zone order then sends and update request to the servers for these zones. The number of zones updated is returned or -1 on error. Note that .Fn res_nupdate will perform TSIG authenticated dynamic update operations if the key is not NULL. .Pp The function .Fn res_findzonecut discovers the closest enclosing zone cut for a specified domain name, and finds the IP addresses of the zone's master servers. .Pp The functions .Fn res_nmkupdate / .Fn res_mkupdate take a linked list of ns_updrec .Fa rrecp_in and construct a UPDATE message in .Fa buf . .Fn res_nmkupdate / .Fn res_mkupdate return the length of the constructed message on no error or one of the following error values. .Bl -inset -width "-5" .It -1 An error occurred parsing .Fa rrecp_in . .It -2 The buffer .Fa buf was too small. .It -3 The first record was not a zone section or there was a section order problem. The section order is S_ZONE, S_PREREQ and S_UPDATE. .It -4 A number overflow occurred. .It -5 Unknown operation or no records. .El .Pp The functions .Fn res_nclose / .Fn res_close close any open files referenced through .Fa statp / .Fa _res . .Pp The .Fn dn_comp function compresses the domain name .Fa exp_dn and stores it in .Fa comp_dn . The size of the compressed name is returned or \-1 if there were errors. The size of the array pointed to by .Fa comp_dn is given by .Fa length . The compression uses an array of pointers .Fa dnptrs to previously-compressed names in the current message. The first pointer points to to the beginning of the message and the list ends with .Dv NULL . The limit to the array is specified by .Fa lastdnptr . A side effect of .Fn dn_comp is to update the list of pointers for labels inserted into the message as the name is compressed. If .Fa dnptr is .Dv NULL , names are not compressed. If .Fa lastdnptr is .Dv NULL , the list of labels is not updated. .Pp The .Fn dn_expand entry expands the compressed domain name .Fa comp_dn to a full domain name. The compressed name is contained in a query or reply message; .Fa msg is a pointer to the beginning of the message. .Fa eomorig is a pointer to the first location after the message. The uncompressed name is placed in the buffer indicated by .Fa exp_dn which is of size .Fa length . The size of compressed name is returned or \-1 if there was an error. .Pp The variables .Ft statp->res_h_errno / .Ft _res.res_h_errno and external variable .Ft h_errno is set whenever an error occurs during resolver operation. The following definitions are given in .Pa : .Bd -literal #define NETDB_INTERNAL -1 /* see errno */ #define NETDB_SUCCESS 0 /* no problem */ #define HOST_NOT_FOUND 1 /* Authoritative Answer Host not found */ #define TRY_AGAIN 2 /* Non-Authoritative not found, or SERVFAIL */ #define NO_RECOVERY 3 /* Non-Recoverable: FORMERR, REFUSED, NOTIMP */ #define NO_DATA 4 /* Valid name, no data for requested type */ .Ed .Pp The .Fn herror function writes a message to the diagnostic output consisting of the string parameter .Fa s , the constant string ": ", and a message corresponding to the value of .Ft h_errno . .Pp The .Fn hstrerror function returns a string which is the message text corresponding to the value of the .Fa err parameter. .Sh FILES .Bl -tag -width "/etc/resolv.conf " .It Pa /etc/resolv.conf See .Xr resolver @FORMAT_EXT@ . .El .Sh SEE ALSO .Xr gethostbyname @LIB_NETWORK_EXT@ , .Xr hostname @DESC_EXT@ , .Xr @INDOT@named @SYS_OPS_EXT@ , .Xr resolver @FORMAT_EXT@ ; RFC1032, RFC1033, RFC1034, RFC1035, RFC974; SMM:11, -.Dq Name Server Operations Guide for Sy BIND +.Dq Name Server Operations Guide for BIND diff --git a/contrib/bind/include/arpa/nameser.h b/contrib/bind/include/arpa/nameser.h index 58bc702db969..a8c62fdf5674 100644 --- a/contrib/bind/include/arpa/nameser.h +++ b/contrib/bind/include/arpa/nameser.h @@ -1,574 +1,576 @@ /* * Copyright (c) 1983, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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: nameser.h,v 8.47 2002/04/30 03:43:53 marka Exp $ + * $Id: nameser.h,v 8.48.8.2 2003/06/02 09:24:40 marka Exp $ */ #ifndef _ARPA_NAMESER_H_ #define _ARPA_NAMESER_H_ #define BIND_4_COMPAT #include #if (!defined(BSD)) || (BSD < 199306) # include #else # include #endif #include /* * Revision information. This is the release date in YYYYMMDD format. * It can change every day so the right thing to do with it is use it * in preprocessor commands such as "#if (__NAMESER > 19931104)". Do not * compare for equality; rather, use it to determine whether your libbind.a * contains a new enough lib/nameser/ to support the feature you need. */ #define __NAMESER 19991006 /* New interface version stamp. */ /* * Define constants based on RFC 883, RFC 1034, RFC 1035 */ -#define NS_PACKETSZ 512 /* maximum packet size */ +#define NS_PACKETSZ 512 /* default UDP packet size */ #define NS_MAXDNAME 1025 /* maximum domain name */ +#define NS_MAXMSG 65535 /* maximum message size */ #define NS_MAXCDNAME 255 /* maximum compressed domain name */ #define NS_MAXLABEL 63 /* maximum length of domain label */ #define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */ #define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */ #define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */ #define NS_INT32SZ 4 /* #/bytes of data in a u_int32_t */ #define NS_INT16SZ 2 /* #/bytes of data in a u_int16_t */ #define NS_INT8SZ 1 /* #/bytes of data in a u_int8_t */ #define NS_INADDRSZ 4 /* IPv4 T_A */ #define NS_IN6ADDRSZ 16 /* IPv6 T_AAAA */ #define NS_CMPRSFLGS 0xc0 /* Flag bits indicating name compression. */ #define NS_DEFAULTPORT 53 /* For both TCP and UDP. */ /* * These can be expanded with synonyms, just keep ns_parse.c:ns_parserecord() * in synch with it. */ typedef enum __ns_sect { ns_s_qd = 0, /* Query: Question. */ ns_s_zn = 0, /* Update: Zone. */ ns_s_an = 1, /* Query: Answer. */ ns_s_pr = 1, /* Update: Prerequisites. */ ns_s_ns = 2, /* Query: Name servers. */ ns_s_ud = 2, /* Update: Update. */ ns_s_ar = 3, /* Query|Update: Additional records. */ ns_s_max = 4 } ns_sect; /* * This is a message handle. It is caller allocated and has no dynamic data. * This structure is intended to be opaque to all but ns_parse.c, thus the * leading _'s on the member names. Use the accessor functions, not the _'s. */ typedef struct __ns_msg { const u_char *_msg, *_eom; u_int16_t _id, _flags, _counts[ns_s_max]; const u_char *_sections[ns_s_max]; ns_sect _sect; int _rrnum; const u_char *_msg_ptr; } ns_msg; /* Private data structure - do not use from outside library. */ struct _ns_flagdata { int mask, shift; }; extern struct _ns_flagdata _ns_flagdata[]; /* Accessor macros - this is part of the public interface. */ #define ns_msg_id(handle) ((handle)._id + 0) #define ns_msg_base(handle) ((handle)._msg + 0) #define ns_msg_end(handle) ((handle)._eom + 0) #define ns_msg_size(handle) ((handle)._eom - (handle)._msg) #define ns_msg_count(handle, section) ((handle)._counts[section] + 0) /* * This is a parsed record. It is caller allocated and has no dynamic data. */ typedef struct __ns_rr { char name[NS_MAXDNAME]; u_int16_t type; u_int16_t rr_class; u_int32_t ttl; u_int16_t rdlength; const u_char * rdata; } ns_rr; /* Accessor macros - this is part of the public interface. */ #define ns_rr_name(rr) (((rr).name[0] != '\0') ? (rr).name : ".") #define ns_rr_type(rr) ((ns_type)((rr).type + 0)) #define ns_rr_class(rr) ((ns_class)((rr).rr_class + 0)) #define ns_rr_ttl(rr) ((rr).ttl + 0) #define ns_rr_rdlen(rr) ((rr).rdlength + 0) #define ns_rr_rdata(rr) ((rr).rdata + 0) /* * These don't have to be in the same order as in the packet flags word, * and they can even overlap in some cases, but they will need to be kept * in synch with ns_parse.c:ns_flagdata[]. */ typedef enum __ns_flag { ns_f_qr, /* Question/Response. */ ns_f_opcode, /* Operation code. */ ns_f_aa, /* Authoritative Answer. */ ns_f_tc, /* Truncation occurred. */ ns_f_rd, /* Recursion Desired. */ ns_f_ra, /* Recursion Available. */ ns_f_z, /* MBZ. */ ns_f_ad, /* Authentic Data (DNSSEC). */ ns_f_cd, /* Checking Disabled (DNSSEC). */ ns_f_rcode, /* Response code. */ ns_f_max } ns_flag; /* * Currently defined opcodes. */ typedef enum __ns_opcode { ns_o_query = 0, /* Standard query. */ ns_o_iquery = 1, /* Inverse query (deprecated/unsupported). */ ns_o_status = 2, /* Name server status query (unsupported). */ /* Opcode 3 is undefined/reserved. */ ns_o_notify = 4, /* Zone change notification. */ ns_o_update = 5, /* Zone update message. */ ns_o_max = 6 } ns_opcode; /* * Currently defined response codes. */ typedef enum __ns_rcode { ns_r_noerror = 0, /* No error occurred. */ ns_r_formerr = 1, /* Format error. */ ns_r_servfail = 2, /* Server failure. */ ns_r_nxdomain = 3, /* Name error. */ ns_r_notimpl = 4, /* Unimplemented. */ ns_r_refused = 5, /* Operation refused. */ /* these are for BIND_UPDATE */ ns_r_yxdomain = 6, /* Name exists */ ns_r_yxrrset = 7, /* RRset exists */ ns_r_nxrrset = 8, /* RRset does not exist */ ns_r_notauth = 9, /* Not authoritative for zone */ ns_r_notzone = 10, /* Zone of record different from zone section */ ns_r_max = 11, /* The following are EDNS extended rcodes */ ns_r_badvers = 16, /* The following are TSIG errors */ ns_r_badsig = 16, ns_r_badkey = 17, ns_r_badtime = 18 } ns_rcode; /* BIND_UPDATE */ typedef enum __ns_update_operation { ns_uop_delete = 0, ns_uop_add = 1, ns_uop_max = 2 } ns_update_operation; /* * This structure is used for TSIG authenticated messages */ struct ns_tsig_key { char name[NS_MAXDNAME], alg[NS_MAXDNAME]; unsigned char *data; int len; }; typedef struct ns_tsig_key ns_tsig_key; /* * This structure is used for TSIG authenticated TCP messages */ struct ns_tcp_tsig_state { int counter; struct dst_key *key; void *ctx; unsigned char sig[NS_PACKETSZ]; int siglen; }; typedef struct ns_tcp_tsig_state ns_tcp_tsig_state; #define NS_TSIG_FUDGE 300 #define NS_TSIG_TCP_COUNT 100 #define NS_TSIG_ALG_HMAC_MD5 "HMAC-MD5.SIG-ALG.REG.INT" #define NS_TSIG_ERROR_NO_TSIG -10 #define NS_TSIG_ERROR_NO_SPACE -11 #define NS_TSIG_ERROR_FORMERR -12 /* * Currently defined type values for resources and queries. */ typedef enum __ns_type { ns_t_invalid = 0, /* Cookie. */ ns_t_a = 1, /* Host address. */ ns_t_ns = 2, /* Authoritative server. */ ns_t_md = 3, /* Mail destination. */ ns_t_mf = 4, /* Mail forwarder. */ ns_t_cname = 5, /* Canonical name. */ ns_t_soa = 6, /* Start of authority zone. */ ns_t_mb = 7, /* Mailbox domain name. */ ns_t_mg = 8, /* Mail group member. */ ns_t_mr = 9, /* Mail rename name. */ ns_t_null = 10, /* Null resource record. */ ns_t_wks = 11, /* Well known service. */ ns_t_ptr = 12, /* Domain name pointer. */ ns_t_hinfo = 13, /* Host information. */ ns_t_minfo = 14, /* Mailbox information. */ ns_t_mx = 15, /* Mail routing information. */ ns_t_txt = 16, /* Text strings. */ ns_t_rp = 17, /* Responsible person. */ ns_t_afsdb = 18, /* AFS cell database. */ ns_t_x25 = 19, /* X_25 calling address. */ ns_t_isdn = 20, /* ISDN calling address. */ ns_t_rt = 21, /* Router. */ ns_t_nsap = 22, /* NSAP address. */ ns_t_nsap_ptr = 23, /* Reverse NSAP lookup (deprecated). */ ns_t_sig = 24, /* Security signature. */ ns_t_key = 25, /* Security key. */ ns_t_px = 26, /* X.400 mail mapping. */ ns_t_gpos = 27, /* Geographical position (withdrawn). */ ns_t_aaaa = 28, /* Ip6 Address. */ ns_t_loc = 29, /* Location Information. */ ns_t_nxt = 30, /* Next domain (security). */ ns_t_eid = 31, /* Endpoint identifier. */ ns_t_nimloc = 32, /* Nimrod Locator. */ ns_t_srv = 33, /* Server Selection. */ ns_t_atma = 34, /* ATM Address */ ns_t_naptr = 35, /* Naming Authority PoinTeR */ ns_t_kx = 36, /* Key Exchange */ ns_t_cert = 37, /* Certification record */ ns_t_a6 = 38, /* IPv6 address (deprecates AAAA) */ ns_t_dname = 39, /* Non-terminal DNAME (for IPv6) */ ns_t_sink = 40, /* Kitchen sink (experimentatl) */ ns_t_opt = 41, /* EDNS0 option (meta-RR) */ + ns_t_apl = 42, /* Address prefix list (RFC 3123) */ ns_t_tkey = 249, /* Transaction key */ ns_t_tsig = 250, /* Transaction signature. */ ns_t_ixfr = 251, /* Incremental zone transfer. */ ns_t_axfr = 252, /* Transfer zone of authority. */ ns_t_mailb = 253, /* Transfer mailbox records. */ ns_t_maila = 254, /* Transfer mail agent records. */ ns_t_any = 255, /* Wildcard match. */ ns_t_zxfr = 256, /* BIND-specific, nonstandard. */ ns_t_max = 65536 } ns_type; /* Exclusively a QTYPE? (not also an RTYPE) */ #define ns_t_qt_p(t) (ns_t_xfr_p(t) || (t) == ns_t_any || \ (t) == ns_t_mailb || (t) == ns_t_maila) /* Some kind of meta-RR? (not a QTYPE, but also not an RTYPE) */ #define ns_t_mrr_p(t) ((t) == ns_t_tsig || (t) == ns_t_opt) /* Exclusively an RTYPE? (not also a QTYPE or a meta-RR) */ #define ns_t_rr_p(t) (!ns_t_qt_p(t) && !ns_t_mrr_p(t)) #define ns_t_udp_p(t) ((t) != ns_t_axfr && (t) != ns_t_zxfr) #define ns_t_xfr_p(t) ((t) == ns_t_axfr || (t) == ns_t_ixfr || \ (t) == ns_t_zxfr) /* * Values for class field */ typedef enum __ns_class { ns_c_invalid = 0, /* Cookie. */ ns_c_in = 1, /* Internet. */ ns_c_2 = 2, /* unallocated/unsupported. */ ns_c_chaos = 3, /* MIT Chaos-net. */ ns_c_hs = 4, /* MIT Hesiod. */ /* Query class values which do not appear in resource records */ ns_c_none = 254, /* for prereq. sections in update requests */ ns_c_any = 255, /* Wildcard match. */ ns_c_max = 65536 } ns_class; /* DNSSEC constants. */ typedef enum __ns_key_types { ns_kt_rsa = 1, /* key type RSA/MD5 */ ns_kt_dh = 2, /* Diffie Hellman */ ns_kt_dsa = 3, /* Digital Signature Standard (MANDATORY) */ ns_kt_private = 254 /* Private key type starts with OID */ } ns_key_types; typedef enum __ns_cert_types { cert_t_pkix = 1, /* PKIX (X.509v3) */ cert_t_spki = 2, /* SPKI */ cert_t_pgp = 3, /* PGP */ cert_t_url = 253, /* URL private type */ cert_t_oid = 254 /* OID private type */ } ns_cert_types; /* Flags field of the KEY RR rdata. */ #define NS_KEY_TYPEMASK 0xC000 /* Mask for "type" bits */ #define NS_KEY_TYPE_AUTH_CONF 0x0000 /* Key usable for both */ #define NS_KEY_TYPE_CONF_ONLY 0x8000 /* Key usable for confidentiality */ #define NS_KEY_TYPE_AUTH_ONLY 0x4000 /* Key usable for authentication */ #define NS_KEY_TYPE_NO_KEY 0xC000 /* No key usable for either; no key */ /* The type bits can also be interpreted independently, as single bits: */ #define NS_KEY_NO_AUTH 0x8000 /* Key unusable for authentication */ #define NS_KEY_NO_CONF 0x4000 /* Key unusable for confidentiality */ #define NS_KEY_RESERVED2 0x2000 /* Security is *mandatory* if bit=0 */ #define NS_KEY_EXTENDED_FLAGS 0x1000 /* reserved - must be zero */ #define NS_KEY_RESERVED4 0x0800 /* reserved - must be zero */ #define NS_KEY_RESERVED5 0x0400 /* reserved - must be zero */ #define NS_KEY_NAME_TYPE 0x0300 /* these bits determine the type */ #define NS_KEY_NAME_USER 0x0000 /* key is assoc. with user */ #define NS_KEY_NAME_ENTITY 0x0200 /* key is assoc. with entity eg host */ #define NS_KEY_NAME_ZONE 0x0100 /* key is zone key */ #define NS_KEY_NAME_RESERVED 0x0300 /* reserved meaning */ #define NS_KEY_RESERVED8 0x0080 /* reserved - must be zero */ #define NS_KEY_RESERVED9 0x0040 /* reserved - must be zero */ #define NS_KEY_RESERVED10 0x0020 /* reserved - must be zero */ #define NS_KEY_RESERVED11 0x0010 /* reserved - must be zero */ #define NS_KEY_SIGNATORYMASK 0x000F /* key can sign RR's of same name */ #define NS_KEY_RESERVED_BITMASK ( NS_KEY_RESERVED2 | \ NS_KEY_RESERVED4 | \ NS_KEY_RESERVED5 | \ NS_KEY_RESERVED8 | \ NS_KEY_RESERVED9 | \ NS_KEY_RESERVED10 | \ NS_KEY_RESERVED11 ) #define NS_KEY_RESERVED_BITMASK2 0xFFFF /* no bits defined here */ /* The Algorithm field of the KEY and SIG RR's is an integer, {1..254} */ #define NS_ALG_MD5RSA 1 /* MD5 with RSA */ #define NS_ALG_DH 2 /* Diffie Hellman KEY */ #define NS_ALG_DSA 3 /* DSA KEY */ #define NS_ALG_DSS NS_ALG_DSA #define NS_ALG_EXPIRE_ONLY 253 /* No alg, no security */ #define NS_ALG_PRIVATE_OID 254 /* Key begins with OID giving alg */ /* Protocol values */ /* value 0 is reserved */ #define NS_KEY_PROT_TLS 1 #define NS_KEY_PROT_EMAIL 2 #define NS_KEY_PROT_DNSSEC 3 #define NS_KEY_PROT_IPSEC 4 #define NS_KEY_PROT_ANY 255 /* Signatures */ #define NS_MD5RSA_MIN_BITS 512 /* Size of a mod or exp in bits */ -#define NS_MD5RSA_MAX_BITS 2552 +#define NS_MD5RSA_MAX_BITS 4096 /* Total of binary mod and exp */ #define NS_MD5RSA_MAX_BYTES ((NS_MD5RSA_MAX_BITS+7/8)*2+3) /* Max length of text sig block */ #define NS_MD5RSA_MAX_BASE64 (((NS_MD5RSA_MAX_BYTES+2)/3)*4) #define NS_MD5RSA_MIN_SIZE ((NS_MD5RSA_MIN_BITS+7)/8) #define NS_MD5RSA_MAX_SIZE ((NS_MD5RSA_MAX_BITS+7)/8) #define NS_DSA_SIG_SIZE 41 #define NS_DSA_MIN_SIZE 213 #define NS_DSA_MAX_BYTES 405 /* Offsets into SIG record rdata to find various values */ #define NS_SIG_TYPE 0 /* Type flags */ #define NS_SIG_ALG 2 /* Algorithm */ #define NS_SIG_LABELS 3 /* How many labels in name */ #define NS_SIG_OTTL 4 /* Original TTL */ #define NS_SIG_EXPIR 8 /* Expiration time */ #define NS_SIG_SIGNED 12 /* Signature time */ #define NS_SIG_FOOT 16 /* Key footprint */ #define NS_SIG_SIGNER 18 /* Domain name of who signed it */ /* How RR types are represented as bit-flags in NXT records */ #define NS_NXT_BITS 8 #define NS_NXT_BIT_SET( n,p) (p[(n)/NS_NXT_BITS] |= (0x80>>((n)%NS_NXT_BITS))) #define NS_NXT_BIT_CLEAR(n,p) (p[(n)/NS_NXT_BITS] &= ~(0x80>>((n)%NS_NXT_BITS))) #define NS_NXT_BIT_ISSET(n,p) (p[(n)/NS_NXT_BITS] & (0x80>>((n)%NS_NXT_BITS))) #define NS_NXT_MAX 127 /* * EDNS0 extended flags, host order. */ #define NS_OPT_DNSSEC_OK 0x8000U /* * Inline versions of get/put short/long. Pointer is advanced. */ #define NS_GET16(s, cp) do { \ register const u_char *t_cp = (const u_char *)(cp); \ (s) = ((u_int16_t)t_cp[0] << 8) \ | ((u_int16_t)t_cp[1]) \ ; \ (cp) += NS_INT16SZ; \ } while (0) #define NS_GET32(l, cp) do { \ register const u_char *t_cp = (const u_char *)(cp); \ (l) = ((u_int32_t)t_cp[0] << 24) \ | ((u_int32_t)t_cp[1] << 16) \ | ((u_int32_t)t_cp[2] << 8) \ | ((u_int32_t)t_cp[3]) \ ; \ (cp) += NS_INT32SZ; \ } while (0) #define NS_PUT16(s, cp) do { \ register u_int16_t t_s = (u_int16_t)(s); \ register u_char *t_cp = (u_char *)(cp); \ *t_cp++ = t_s >> 8; \ *t_cp = t_s; \ (cp) += NS_INT16SZ; \ } while (0) #define NS_PUT32(l, cp) do { \ register u_int32_t t_l = (u_int32_t)(l); \ register u_char *t_cp = (u_char *)(cp); \ *t_cp++ = t_l >> 24; \ *t_cp++ = t_l >> 16; \ *t_cp++ = t_l >> 8; \ *t_cp = t_l; \ (cp) += NS_INT32SZ; \ } while (0) /* * ANSI C identifier hiding for bind's lib/nameser. */ #define ns_msg_getflag __ns_msg_getflag #define ns_get16 __ns_get16 #define ns_get32 __ns_get32 #define ns_put16 __ns_put16 #define ns_put32 __ns_put32 #define ns_initparse __ns_initparse #define ns_skiprr __ns_skiprr #define ns_parserr __ns_parserr #define ns_sprintrr __ns_sprintrr #define ns_sprintrrf __ns_sprintrrf #define ns_format_ttl __ns_format_ttl #define ns_parse_ttl __ns_parse_ttl #define ns_datetosecs __ns_datetosecs #define ns_name_ntol __ns_name_ntol #define ns_name_ntop __ns_name_ntop #define ns_name_pton __ns_name_pton #define ns_name_unpack __ns_name_unpack #define ns_name_pack __ns_name_pack #define ns_name_compress __ns_name_compress #define ns_name_uncompress __ns_name_uncompress #define ns_name_skip __ns_name_skip #define ns_name_rollback __ns_name_rollback #define ns_sign __ns_sign #define ns_sign2 __ns_sign2 #define ns_sign_tcp __ns_sign_tcp #define ns_sign_tcp2 __ns_sign_tcp2 #define ns_sign_tcp_init __ns_sign_tcp_init #define ns_find_tsig __ns_find_tsig #define ns_verify __ns_verify #define ns_verify_tcp __ns_verify_tcp #define ns_verify_tcp_init __ns_verify_tcp_init #define ns_samedomain __ns_samedomain #define ns_subdomain __ns_subdomain #define ns_makecanon __ns_makecanon #define ns_samename __ns_samename __BEGIN_DECLS int ns_msg_getflag __P((ns_msg, int)); u_int ns_get16 __P((const u_char *)); u_long ns_get32 __P((const u_char *)); void ns_put16 __P((u_int, u_char *)); void ns_put32 __P((u_long, u_char *)); int ns_initparse __P((const u_char *, int, ns_msg *)); int ns_skiprr __P((const u_char *, const u_char *, ns_sect, int)); int ns_parserr __P((ns_msg *, ns_sect, int, ns_rr *)); int ns_sprintrr __P((const ns_msg *, const ns_rr *, const char *, const char *, char *, size_t)); int ns_sprintrrf __P((const u_char *, size_t, const char *, ns_class, ns_type, u_long, const u_char *, size_t, const char *, const char *, char *, size_t)); int ns_format_ttl __P((u_long, char *, size_t)); int ns_parse_ttl __P((const char *, u_long *)); u_int32_t ns_datetosecs __P((const char *cp, int *errp)); int ns_name_ntol __P((const u_char *, u_char *, size_t)); int ns_name_ntop __P((const u_char *, char *, size_t)); int ns_name_pton __P((const char *, u_char *, size_t)); int ns_name_unpack __P((const u_char *, const u_char *, const u_char *, u_char *, size_t)); int ns_name_pack __P((const u_char *, u_char *, int, const u_char **, const u_char **)); int ns_name_uncompress __P((const u_char *, const u_char *, const u_char *, char *, size_t)); int ns_name_compress __P((const char *, u_char *, size_t, const u_char **, const u_char **)); int ns_name_skip __P((const u_char **, const u_char *)); void ns_name_rollback __P((const u_char *, const u_char **, const u_char **)); int ns_sign __P((u_char *, int *, int, int, void *, const u_char *, int, u_char *, int *, time_t)); int ns_sign2 __P((u_char *, int *, int, int, void *, const u_char *, int, u_char *, int *, time_t, u_char **, u_char **)); int ns_sign_tcp __P((u_char *, int *, int, int, ns_tcp_tsig_state *, int)); int ns_sign_tcp2 __P((u_char *, int *, int, int, ns_tcp_tsig_state *, int, u_char **, u_char **)); int ns_sign_tcp_init __P((void *, const u_char *, int, ns_tcp_tsig_state *)); u_char *ns_find_tsig __P((u_char *, u_char *)); int ns_verify __P((u_char *, int *, void *, const u_char *, int, u_char *, int *, time_t *, int)); int ns_verify_tcp __P((u_char *, int *, ns_tcp_tsig_state *, int)); int ns_verify_tcp_init __P((void *, const u_char *, int, ns_tcp_tsig_state *)); int ns_samedomain __P((const char *, const char *)); int ns_subdomain __P((const char *, const char *)); int ns_makecanon __P((const char *, char *, size_t)); int ns_samename __P((const char *, const char *)); __END_DECLS #ifdef BIND_4_COMPAT #include #endif #endif /* !_ARPA_NAMESER_H_ */ diff --git a/contrib/bind/include/arpa/nameser_compat.h b/contrib/bind/include/arpa/nameser_compat.h index cbceb6ed2421..273933a711fd 100644 --- a/contrib/bind/include/arpa/nameser_compat.h +++ b/contrib/bind/include/arpa/nameser_compat.h @@ -1,231 +1,232 @@ /* Copyright (c) 1983, 1989 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * from nameser.h 8.1 (Berkeley) 6/2/93 - * $Id: nameser_compat.h,v 8.14 2002/05/18 01:39:11 marka Exp $ + * $Id: nameser_compat.h,v 8.15 2002/07/17 07:01:02 marka Exp $ */ #ifndef _ARPA_NAMESER_COMPAT_ #define _ARPA_NAMESER_COMPAT_ #define __BIND 19950621 /* (DEAD) interface version stamp. */ #ifndef BYTE_ORDER #if (BSD >= 199103) # include #else #ifdef linux # include #else #define LITTLE_ENDIAN 1234 /* least-significant byte first (vax, pc) */ #define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */ #define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp)*/ #if defined(vax) || defined(ns32000) || defined(sun386) || defined(i386) || \ defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \ defined(__alpha__) || defined(__alpha) || \ (defined(__Lynx__) && defined(__x86__)) #define BYTE_ORDER LITTLE_ENDIAN #endif #if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \ defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \ defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\ defined(apollo) || defined(__convex__) || defined(_CRAY) || \ defined(__hppa) || defined(__hp9000) || \ defined(__hp9000s300) || defined(__hp9000s700) || \ defined(__hp3000s900) || defined(__hpux) || defined(MPE) || \ defined (BIT_ZERO_ON_LEFT) || defined(m68k) || \ (defined(__Lynx__) && \ (defined(__68k__) || defined(__sparc__) || defined(__powerpc__))) #define BYTE_ORDER BIG_ENDIAN #endif #endif /* linux */ #endif /* BSD */ #endif /* BYTE_ORDER */ #if !defined(BYTE_ORDER) || \ (BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN && \ BYTE_ORDER != PDP_ENDIAN) /* you must determine what the correct bit order is for * your compiler - the next line is an intentional error * which will force your compiles to bomb until you fix * the above macros. */ error "Undefined or invalid BYTE_ORDER"; #endif /* * Structure for query header. The order of the fields is machine- and * compiler-dependent, depending on the byte/bit order and the layout * of bit fields. We use bit fields only in int variables, as this * is all ANSI requires. This requires a somewhat confusing rearrangement. */ typedef struct { unsigned id :16; /* query identification number */ #if BYTE_ORDER == BIG_ENDIAN /* fields in third byte */ unsigned qr: 1; /* response flag */ unsigned opcode: 4; /* purpose of message */ unsigned aa: 1; /* authoritive answer */ unsigned tc: 1; /* truncated message */ unsigned rd: 1; /* recursion desired */ /* fields in fourth byte */ unsigned ra: 1; /* recursion available */ unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ unsigned ad: 1; /* authentic data from named */ unsigned cd: 1; /* checking disabled by resolver */ unsigned rcode :4; /* response code */ #endif #if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN /* fields in third byte */ unsigned rd :1; /* recursion desired */ unsigned tc :1; /* truncated message */ unsigned aa :1; /* authoritive answer */ unsigned opcode :4; /* purpose of message */ unsigned qr :1; /* response flag */ /* fields in fourth byte */ unsigned rcode :4; /* response code */ unsigned cd: 1; /* checking disabled by resolver */ unsigned ad: 1; /* authentic data from named */ unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ unsigned ra :1; /* recursion available */ #endif /* remaining bytes */ unsigned qdcount :16; /* number of question entries */ unsigned ancount :16; /* number of answer entries */ unsigned nscount :16; /* number of authority entries */ unsigned arcount :16; /* number of resource entries */ } HEADER; #define PACKETSZ NS_PACKETSZ #define MAXDNAME NS_MAXDNAME #define MAXCDNAME NS_MAXCDNAME #define MAXLABEL NS_MAXLABEL #define HFIXEDSZ NS_HFIXEDSZ #define QFIXEDSZ NS_QFIXEDSZ #define RRFIXEDSZ NS_RRFIXEDSZ #define INT32SZ NS_INT32SZ #define INT16SZ NS_INT16SZ +#define INT8SZ NS_INT8SZ #define INADDRSZ NS_INADDRSZ #define IN6ADDRSZ NS_IN6ADDRSZ #define INDIR_MASK NS_CMPRSFLGS #define NAMESERVER_PORT NS_DEFAULTPORT #define S_ZONE ns_s_zn #define S_PREREQ ns_s_pr #define S_UPDATE ns_s_ud #define S_ADDT ns_s_ar #define QUERY ns_o_query #define IQUERY ns_o_iquery #define STATUS ns_o_status #define NS_NOTIFY_OP ns_o_notify #define NS_UPDATE_OP ns_o_update #define NOERROR ns_r_noerror #define FORMERR ns_r_formerr #define SERVFAIL ns_r_servfail #define NXDOMAIN ns_r_nxdomain #define NOTIMP ns_r_notimpl #define REFUSED ns_r_refused #define YXDOMAIN ns_r_yxdomain #define YXRRSET ns_r_yxrrset #define NXRRSET ns_r_nxrrset #define NOTAUTH ns_r_notauth #define NOTZONE ns_r_notzone /*#define BADSIG ns_r_badsig*/ /*#define BADKEY ns_r_badkey*/ /*#define BADTIME ns_r_badtime*/ #define DELETE ns_uop_delete #define ADD ns_uop_add #define T_A ns_t_a #define T_NS ns_t_ns #define T_MD ns_t_md #define T_MF ns_t_mf #define T_CNAME ns_t_cname #define T_SOA ns_t_soa #define T_MB ns_t_mb #define T_MG ns_t_mg #define T_MR ns_t_mr #define T_NULL ns_t_null #define T_WKS ns_t_wks #define T_PTR ns_t_ptr #define T_HINFO ns_t_hinfo #define T_MINFO ns_t_minfo #define T_MX ns_t_mx #define T_TXT ns_t_txt #define T_RP ns_t_rp #define T_AFSDB ns_t_afsdb #define T_X25 ns_t_x25 #define T_ISDN ns_t_isdn #define T_RT ns_t_rt #define T_NSAP ns_t_nsap #define T_NSAP_PTR ns_t_nsap_ptr #define T_SIG ns_t_sig #define T_KEY ns_t_key #define T_PX ns_t_px #define T_GPOS ns_t_gpos #define T_AAAA ns_t_aaaa #define T_LOC ns_t_loc #define T_NXT ns_t_nxt #define T_EID ns_t_eid #define T_NIMLOC ns_t_nimloc #define T_SRV ns_t_srv #define T_ATMA ns_t_atma #define T_NAPTR ns_t_naptr #define T_A6 ns_t_a6 #define T_TSIG ns_t_tsig #define T_IXFR ns_t_ixfr #define T_AXFR ns_t_axfr #define T_MAILB ns_t_mailb #define T_MAILA ns_t_maila #define T_ANY ns_t_any #define C_IN ns_c_in #define C_CHAOS ns_c_chaos #define C_HS ns_c_hs /* BIND_UPDATE */ #define C_NONE ns_c_none #define C_ANY ns_c_any #define GETSHORT NS_GET16 #define GETLONG NS_GET32 #define PUTSHORT NS_PUT16 #define PUTLONG NS_PUT32 #endif /* _ARPA_NAMESER_COMPAT_ */ diff --git a/contrib/bind/include/hesiod.h b/contrib/bind/include/hesiod.h index a110bd0d347a..6072de9e0386 100644 --- a/contrib/bind/include/hesiod.h +++ b/contrib/bind/include/hesiod.h @@ -1,40 +1,38 @@ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * This file is primarily maintained by and . */ /* - * $Id: hesiod.h,v 1.7 1999/01/08 19:22:45 vixie Exp $ + * $Id: hesiod.h,v 1.7.20.1 2003/06/02 05:48:04 marka Exp $ */ #ifndef _HESIOD_H_INCLUDED #define _HESIOD_H_INCLUDED -int hesiod_init __P((void **context)); -void hesiod_end __P((void *context)); -char * hesiod_to_bind __P((void *context, const char *name, - const char *type)); -char ** hesiod_resolve __P((void *context, const char *name, - const char *type)); -void hesiod_free_list __P((void *context, char **list)); -struct __res_state * __hesiod_res_get __P((void *context)); -void __hesiod_res_set __P((void *context, struct __res_state *, +int hesiod_init __P((void **)); +void hesiod_end __P((void *)); +char * hesiod_to_bind __P((void *, const char *, const char *)); +char ** hesiod_resolve __P((void *, const char *, const char *)); +void hesiod_free_list __P((void *, char **)); +struct __res_state * __hesiod_res_get __P((void *)); +void __hesiod_res_set __P((void *, struct __res_state *, void (*)(void *))); #endif /*_HESIOD_H_INCLUDED*/ diff --git a/contrib/bind/include/irp.h b/contrib/bind/include/irp.h index 240238304280..a366292ff63f 100644 --- a/contrib/bind/include/irp.h +++ b/contrib/bind/include/irp.h @@ -1,99 +1,103 @@ /* * Copyright (c) 1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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: irp.h,v 8.1 1999/01/18 07:46:46 vixie Exp $ + * $Id: irp.h,v 8.1.20.3 2003/06/02 08:20:37 marka Exp $ */ #ifndef _IRP_H_INCLUDED #define _IRP_H_INCLUDED #define IRPD_TIMEOUT 30 /* seconds */ #define IRPD_MAXSESS 50 /* number of simultaneous sessions. */ #define IRPD_PORT 6660 /* 10 times the number of the beast. */ #define IRPD_PATH "/var/run/irpd" /* af_unix socket path */ /* If sets the environment variable IRPDSERVER to an IP address (e.g. "192.5.5.1"), then that's the host the client expects irpd to be running on. */ #define IRPD_HOST_ENV "IRPDSERVER" /* Protocol response codes. */ #define IRPD_WELCOME_CODE 200 #define IRPD_NOT_WELCOME_CODE 500 #define IRPD_GETHOST_ERROR 510 #define IRPD_GETHOST_NONE 210 #define IRPD_GETHOST_OK 211 #define IRPD_GETHOST_SETOK 212 #define IRPD_GETNET_ERROR 520 #define IRPD_GETNET_NONE 220 #define IRPD_GETNET_OK 221 #define IRPD_GETNET_SETOK 222 #define IRPD_GETUSER_ERROR 530 #define IRPD_GETUSER_NONE 230 #define IRPD_GETUSER_OK 231 #define IRPD_GETUSER_SETOK 232 #define IRPD_GETGROUP_ERROR 540 #define IRPD_GETGROUP_NONE 240 #define IRPD_GETGROUP_OK 241 #define IRPD_GETGROUP_SETOK 242 #define IRPD_GETSERVICE_ERROR 550 #define IRPD_GETSERVICE_NONE 250 #define IRPD_GETSERVICE_OK 251 #define IRPD_GETSERVICE_SETOK 252 #define IRPD_GETPROTO_ERROR 560 #define IRPD_GETPROTO_NONE 260 #define IRPD_GETPROTO_OK 261 #define IRPD_GETPROTO_SETOK 262 #define IRPD_GETNETGR_ERROR 570 #define IRPD_GETNETGR_NONE 270 #define IRPD_GETNETGR_OK 271 #define IRPD_GETNETGR_NOMORE 272 #define IRPD_GETNETGR_MATCHES 273 #define IRPD_GETNETGR_NOMATCH 274 #define IRPD_GETNETGR_SETOK 275 #define IRPD_GETNETGR_SETERR 276 #define irs_irp_read_body __irs_irp_read_body #define irs_irp_read_response __irs_irp_read_response #define irs_irp_disconnect __irs_irp_disconnect #define irs_irp_connect __irs_irp_connect #define irs_irp_connection_setup __irs_irp_connection_setup #define irs_irp_send_command __irs_irp_send_command struct irp_p; -char *irs_irp_read_body(struct irp_p *pvt, size_t *size); -int irs_irp_read_response(struct irp_p *pvt, char *text, size_t len); -void irs_irp_disconnect(struct irp_p *pvt); -int irs_irp_connect(struct irp_p *pvt); -int irs_irp_is_connected(struct irp_p *pvt); -int irs_irp_connection_setup(struct irp_p *cxndata, int *warned); -int irs_irp_send_command(struct irp_p *pvt, const char *fmt, ...); -int irs_irp_get_full_response(struct irp_p *pvt, int *code, char *text, - size_t textlen, char **body, - size_t *bodylen); -int irs_irp_read_line(struct irp_p *pvt, char *buffer, int len); +char *irs_irp_read_body(struct irp_p *, size_t *); +int irs_irp_read_response(struct irp_p *, char *, size_t); +void irs_irp_disconnect(struct irp_p *); +int irs_irp_connect(struct irp_p *); +int irs_irp_is_connected(struct irp_p *); +int irs_irp_connection_setup(struct irp_p *, int *); +#ifdef __GNUC__ +int irs_irp_send_command(struct irp_p *, const char *, ...) + __attribute__((__format__(__printf__, 2, 3))); +#else +int irs_irp_send_command(struct irp_p *, const char *, ...); +#endif +int irs_irp_get_full_response(struct irp_p *, int *, char *, size_t, + char **, size_t *); +int irs_irp_read_line(struct irp_p *, char *, int); #endif diff --git a/contrib/bind/include/irs.h b/contrib/bind/include/irs.h index 8e232adc2f49..6b09b59b7ff7 100644 --- a/contrib/bind/include/irs.h +++ b/contrib/bind/include/irs.h @@ -1,330 +1,345 @@ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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: irs.h,v 8.6 2001/05/29 05:47:00 marka Exp $ + * $Id: irs.h,v 8.7.6.2 2003/06/02 06:06:58 marka Exp $ */ #ifndef _IRS_H_INCLUDED #define _IRS_H_INCLUDED #include #include #include #include #include #include -#include /* * This is the group map class. */ struct irs_gr { void * private; void (*close) __P((struct irs_gr *)); struct group * (*next) __P((struct irs_gr *)); struct group * (*byname) __P((struct irs_gr *, const char *)); struct group * (*bygid) __P((struct irs_gr *, gid_t)); int (*list) __P((struct irs_gr *, const char *, gid_t, gid_t *, int *)); void (*rewind) __P((struct irs_gr *)); void (*minimize) __P((struct irs_gr *)); struct __res_state * (*res_get) __P((struct irs_gr *)); void (*res_set) __P((struct irs_gr *, res_state, void (*)(void *))); }; /* * This is the password map class. */ struct irs_pw { void * private; void (*close) __P((struct irs_pw *)); struct passwd * (*next) __P((struct irs_pw *)); struct passwd * (*byname) __P((struct irs_pw *, const char *)); struct passwd * (*byuid) __P((struct irs_pw *, uid_t)); void (*rewind) __P((struct irs_pw *)); void (*minimize) __P((struct irs_pw *)); struct __res_state * (*res_get) __P((struct irs_pw *)); void (*res_set) __P((struct irs_pw *, res_state, void (*)(void *))); }; /* * This is the service map class. */ struct irs_sv { void * private; void (*close) __P((struct irs_sv *)); struct servent *(*byname) __P((struct irs_sv *, const char *, const char *)); struct servent *(*byport) __P((struct irs_sv *, int, const char *)); struct servent *(*next) __P((struct irs_sv *)); void (*rewind) __P((struct irs_sv *)); void (*minimize) __P((struct irs_sv *)); struct __res_state * (*res_get) __P((struct irs_sv *)); void (*res_set) __P((struct irs_sv *, res_state, void (*)(void *))); }; /* * This is the protocols map class. */ struct irs_pr { void * private; void (*close) __P((struct irs_pr *)); struct protoent *(*byname) __P((struct irs_pr *, const char *)); struct protoent *(*bynumber) __P((struct irs_pr *, int)); struct protoent *(*next) __P((struct irs_pr *)); void (*rewind) __P((struct irs_pr *)); void (*minimize) __P((struct irs_pr *)); struct __res_state * (*res_get) __P((struct irs_pr *)); void (*res_set) __P((struct irs_pr *, res_state, void (*)(void *))); }; /* * This is the hosts map class. */ struct irs_ho { void * private; void (*close) __P((struct irs_ho *)); struct hostent *(*byname) __P((struct irs_ho *, const char *)); struct hostent *(*byname2) __P((struct irs_ho *, const char *, int)); struct hostent *(*byaddr) __P((struct irs_ho *, const void *, int, int)); struct hostent *(*next) __P((struct irs_ho *)); void (*rewind) __P((struct irs_ho *)); void (*minimize) __P((struct irs_ho *)); struct __res_state * (*res_get) __P((struct irs_ho *)); void (*res_set) __P((struct irs_ho *, res_state, void (*)(void *))); struct addrinfo *(*addrinfo) __P((struct irs_ho *, const char *, const struct addrinfo *)); }; /* * This is the networks map class. */ struct irs_nw { void * private; void (*close) __P((struct irs_nw *)); struct nwent * (*byname) __P((struct irs_nw *, const char *, int)); struct nwent * (*byaddr) __P((struct irs_nw *, void *, int, int)); struct nwent * (*next) __P((struct irs_nw *)); void (*rewind) __P((struct irs_nw *)); void (*minimize) __P((struct irs_nw *)); struct __res_state * (*res_get) __P((struct irs_nw *)); void (*res_set) __P((struct irs_nw *, res_state, void (*)(void *))); }; /* * This is the netgroups map class. */ struct irs_ng { void * private; void (*close) __P((struct irs_ng *)); int (*next) __P((struct irs_ng *, const char **, const char **, const char **)); int (*test) __P((struct irs_ng *, const char *, const char *, const char *, const char *)); void (*rewind) __P((struct irs_ng *, const char *)); void (*minimize) __P((struct irs_ng *)); }; /* * This is the generic map class, which copies the front of all others. */ struct irs_map { void * private; void (*close) __P((void *)); }; /* * This is the accessor class. It contains pointers to all of the * initializers for the map classes for a particular accessor. */ struct irs_acc { void * private; void (*close) __P((struct irs_acc *)); struct irs_gr * (*gr_map) __P((struct irs_acc *)); struct irs_pw * (*pw_map) __P((struct irs_acc *)); struct irs_sv * (*sv_map) __P((struct irs_acc *)); struct irs_pr * (*pr_map) __P((struct irs_acc *)); struct irs_ho * (*ho_map) __P((struct irs_acc *)); struct irs_nw * (*nw_map) __P((struct irs_acc *)); struct irs_ng * (*ng_map) __P((struct irs_acc *)); struct __res_state * (*res_get) __P((struct irs_acc *)); void (*res_set) __P((struct irs_acc *, res_state, void (*)(void *))); }; /* * This is because the official definition of "struct netent" has no * concept of CIDR even though it allows variant address families (on * output but not input). The compatibility stubs convert the structs * below into "struct netent"'s. */ struct nwent { char *n_name; /* official name of net */ char **n_aliases; /* alias list */ int n_addrtype; /* net address type */ void *n_addr; /* network address */ int n_length; /* address length, in bits */ }; /* * Hide external function names from POSIX. */ #define irs_gen_acc __irs_gen_acc #define irs_lcl_acc __irs_lcl_acc #define irs_dns_acc __irs_dns_acc #define irs_nis_acc __irs_nis_acc #define irs_irp_acc __irs_irp_acc +#define irs_destroy __irs_destroy +#define irs_dns_gr __irs_dns_gr +#define irs_dns_ho __irs_dns_ho +#define irs_dns_nw __irs_dns_nw +#define irs_dns_pr __irs_dns_pr +#define irs_dns_pw __irs_dns_pw +#define irs_dns_sv __irs_dns_sv +#define irs_gen_gr __irs_gen_gr +#define irs_gen_ho __irs_gen_ho +#define irs_gen_ng __irs_gen_ng +#define irs_gen_nw __irs_gen_nw +#define irs_gen_pr __irs_gen_pr +#define irs_gen_pw __irs_gen_pw +#define irs_gen_sv __irs_gen_sv +#define irs_irp_get_full_response __irs_irp_get_full_response +#define irs_irp_gr __irs_irp_gr +#define irs_irp_ho __irs_irp_ho +#define irs_irp_is_connected __irs_irp_is_connected +#define irs_irp_ng __irs_irp_ng +#define irs_irp_nw __irs_irp_nw +#define irs_irp_pr __irs_irp_pr +#define irs_irp_pw __irs_irp_pw +#define irs_irp_read_line __irs_irp_read_line +#define irs_irp_sv __irs_irp_sv +#define irs_lcl_gr __irs_lcl_gr +#define irs_lcl_ho __irs_lcl_ho +#define irs_lcl_ng __irs_lcl_ng +#define irs_lcl_nw __irs_lcl_nw +#define irs_lcl_pr __irs_lcl_pr +#define irs_lcl_pw __irs_lcl_pw +#define irs_lcl_sv __irs_lcl_sv +#define irs_nis_gr __irs_nis_gr +#define irs_nis_ho __irs_nis_ho +#define irs_nis_ng __irs_nis_ng +#define irs_nis_nw __irs_nis_nw +#define irs_nis_pr __irs_nis_pr +#define irs_nis_pw __irs_nis_pw +#define irs_nis_sv __irs_nis_sv +#define net_data_create __net_data_create +#define net_data_destroy __net_data_destroy +#define net_data_minimize __net_data_minimize /* * Externs. */ -extern struct irs_acc * irs_gen_acc __P((const char *options, - const char *conf_file)); -extern struct irs_acc * irs_lcl_acc __P((const char *options)); -extern struct irs_acc * irs_dns_acc __P((const char *options)); -extern struct irs_acc * irs_nis_acc __P((const char *options)); -extern struct irs_acc * irs_irp_acc __P((const char *options)); +extern struct irs_acc * irs_gen_acc __P((const char *, const char *)); +extern struct irs_acc * irs_lcl_acc __P((const char *)); +extern struct irs_acc * irs_dns_acc __P((const char *)); +extern struct irs_acc * irs_nis_acc __P((const char *)); +extern struct irs_acc * irs_irp_acc __P((const char *)); -extern void irs_destroy(void); +extern void irs_destroy __P((void)); /* * These forward declarations are for the semi-private functions in * the get*.c files. Each of these funcs implements the real get* * functionality and the standard versions are just wrappers that * call these. Apart from the wrappers, only irpd is expected to * call these directly, hence these decls are put here and not in * the /usr/include replacements. */ struct net_data; /* forward */ /* * net_data_create gets a singleton net_data object. net_data_init * creates as many net_data objects as times it is called. Clients using * the default interface will use net_data_create by default. Servers will * probably want net_data_init (one call per client) */ -struct net_data *net_data_create(const char *conf_file); -struct net_data *net_data_init(const char *conf_file); -void net_data_destroy(void *p); +struct net_data *net_data_create __P((const char *)); +struct net_data *net_data_init __P((const char *)); +void net_data_destroy __P((void *)); -extern struct group *getgrent_p __P((struct net_data *net_data)); -extern struct group *getgrnam_p __P((const char *name, - struct net_data *net_data)); -extern struct group *getgrgid_p __P((gid_t gid, - struct net_data *net_data)); -extern int setgroupent_p __P((int stayopen, - struct net_data *net_data)); -extern void endgrent_p __P((struct net_data *net_data)); -extern int getgrouplist_p __P((const char *name, - gid_t basegid, - gid_t *groups, - int *ngroups, - struct net_data *net_data)); +extern struct group *getgrent_p __P((struct net_data *)); +extern struct group *getgrnam_p __P((const char *, struct net_data *)); +extern struct group *getgrgid_p __P((gid_t, struct net_data *)); +extern int setgroupent_p __P((int, struct net_data *)); +extern void endgrent_p __P((struct net_data *)); +extern int getgrouplist_p __P((const char *, gid_t, gid_t *, int *, + struct net_data *)); #ifdef SETGRENT_VOID -extern void setgrent_p __P((struct net_data *net_data)); +extern void setgrent_p __P((struct net_data *)); #else -extern int setgrent_p __P((struct net_data *net_data)); +extern int setgrent_p __P((struct net_data *)); #endif -extern struct hostent *gethostbyname_p __P((const char *name, - struct net_data *net_data)); -extern struct hostent *gethostbyname2_p __P((const char *name, int af, - struct net_data *net_data)); -extern struct hostent *gethostbyaddr_p __P((const char *addr, int len, - int af, - struct net_data *net_data)); -extern struct hostent *gethostent_p __P((struct net_data *net_data)); -extern void sethostent_p __P((int stayopen, - struct net_data *net_data)); -extern void endhostent_p __P((struct net_data *net_data)); -extern struct hostent *getipnodebyname_p __P((const char *name, int af, - int flags, int *errp, - struct net_data *net_data)); -extern struct hostent *getipnodebyaddr_p __P((const void *addr, size_t len, - int af, int *errp, - struct net_data *net_data)); +extern struct hostent *gethostbyname_p __P((const char *, + struct net_data *)); +extern struct hostent *gethostbyname2_p __P((const char *, int, + struct net_data *)); +extern struct hostent *gethostbyaddr_p __P((const char *, int, int, + struct net_data *)); +extern struct hostent *gethostent_p __P((struct net_data *)); +extern void sethostent_p __P((int, struct net_data *)); +extern void endhostent_p __P((struct net_data *)); +extern struct hostent *getipnodebyname_p __P((const char *, int, int, int *, + struct net_data *)); +extern struct hostent *getipnodebyaddr_p __P((const void *, size_t, + int, int *, struct net_data *)); -extern struct netent *getnetent_p __P((struct net_data *net_data)); -extern struct netent *getnetbyname_p __P((const char *name, - struct net_data *net_data)); -extern struct netent *getnetbyaddr_p __P((unsigned long net, int type, - struct net_data *net_data)); -extern void setnetent_p __P((int stayopen, - struct net_data *net_data)); -extern void endnetent_p __P((struct net_data *net_data)); +extern struct netent *getnetent_p __P((struct net_data *)); +extern struct netent *getnetbyname_p __P((const char *, struct net_data *)); +extern struct netent *getnetbyaddr_p __P((unsigned long, int, + struct net_data *)); +extern void setnetent_p __P((int, struct net_data *)); +extern void endnetent_p __P((struct net_data *)); -extern void setnetgrent_p __P((const char *netgroup, - struct net_data *net_data)); -extern void endnetgrent_p __P((struct net_data *net_data)); -extern int innetgr_p __P((const char *netgroup, - const char *host, - const char *user, - const char *domain, - struct net_data *net_data)); -extern int getnetgrent_p __P((const char **host, const char **user, - const char **domain, - struct net_data *net_data)); +extern void setnetgrent_p __P((const char *, struct net_data *)); +extern void endnetgrent_p __P((struct net_data *)); +extern int innetgr_p __P((const char *, const char *, const char *, + const char *, struct net_data *)); +extern int getnetgrent_p __P((const char **, const char **, + const char **, struct net_data *)); -extern struct protoent *getprotoent_p __P((struct net_data *net_data)); -extern struct protoent *getprotobyname_p __P((const char *name, - struct net_data *net_data)); -extern struct protoent *getprotobynumber_p __P((int proto, - struct net_data *net_data)); -extern void setprotoent_p __P((int stayopen, - struct net_data *net_data)); -extern void endprotoent_p __P((struct net_data *net_data)); +extern struct protoent *getprotoent_p __P((struct net_data *)); +extern struct protoent *getprotobyname_p __P((const char *, + struct net_data *)); +extern struct protoent *getprotobynumber_p __P((int, struct net_data *)); +extern void setprotoent_p __P((int, struct net_data *)); +extern void endprotoent_p __P((struct net_data *)); -extern struct passwd *getpwent_p __P((struct net_data *net_data)); -extern struct passwd *getpwnam_p __P((const char *name, - struct net_data *net_data)); -extern struct passwd *getpwuid_p __P((uid_t uid, - struct net_data *net_data)); -extern int setpassent_p __P((int stayopen, - struct net_data *net_data)); -extern void endpwent_p __P((struct net_data *net_data)); +extern struct passwd *getpwent_p __P((struct net_data *)); +extern struct passwd *getpwnam_p __P((const char *, struct net_data *)); +extern struct passwd *getpwuid_p __P((uid_t, struct net_data *)); +extern int setpassent_p __P((int, struct net_data *)); +extern void endpwent_p __P((struct net_data *)); #ifdef SETPWENT_VOID -extern void setpwent_p __P((struct net_data *net_data)); +extern void setpwent_p __P((struct net_data *)); #else -extern int setpwent_p __P((struct net_data *net_data)); +extern int setpwent_p __P((struct net_data *)); #endif -extern struct servent *getservent_p __P((struct net_data *net_data)); -extern struct servent *getservbyname_p __P((const char *name, - const char *proto, - struct net_data *net_data)); -extern struct servent *getservbyport_p __P((int port, const char *proto, - struct net_data *net_data)); -extern void setservent_p __P((int stayopen, - struct net_data *net_data)); -extern void endservent_p __P((struct net_data *net_data)); +extern struct servent *getservent_p __P((struct net_data *)); +extern struct servent *getservbyname_p __P((const char *, const char *, + struct net_data *)); +extern struct servent *getservbyport_p __P((int, const char *, + struct net_data *)); +extern void setservent_p __P((int, struct net_data *)); +extern void endservent_p __P((struct net_data *)); #endif /*_IRS_H_INCLUDED*/ diff --git a/contrib/bind/include/isc/ctl.h b/contrib/bind/include/isc/ctl.h index 008e7a62e753..fec78b196dd1 100644 --- a/contrib/bind/include/isc/ctl.h +++ b/contrib/bind/include/isc/ctl.h @@ -1,109 +1,109 @@ #ifndef ISC_CTL_H #define ISC_CTL_H /* * Copyright (c) 1998,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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: ctl.h,v 8.11 2001/08/10 02:40:49 marka Exp $ + * $Id: ctl.h,v 8.11.10.1 2003/06/02 06:04:03 marka Exp $ */ #include #include #include /* Macros. */ #define CTL_MORE 0x0001 /* More will be / should be sent. */ #define CTL_EXIT 0x0002 /* Close connection after this. */ #define CTL_DATA 0x0004 /* Go into / this is DATA mode. */ /* Types. */ struct ctl_cctx; struct ctl_sctx; struct ctl_sess; struct ctl_verb; enum ctl_severity { ctl_debug, ctl_warning, ctl_error }; -typedef void (*ctl_logfunc)(enum ctl_severity, const char *fmt, ...); +typedef void (*ctl_logfunc)(enum ctl_severity, const char *, ...); typedef void (*ctl_verbfunc)(struct ctl_sctx *, struct ctl_sess *, - const struct ctl_verb *, const char *rest, - u_int respflags, const void *respctx, void *uctx); + const struct ctl_verb *, const char *, + u_int, const void *, void *); typedef void (*ctl_srvrdone)(struct ctl_sctx *, struct ctl_sess *, void *); typedef void (*ctl_clntdone)(struct ctl_cctx *, void *, const char *, u_int); struct ctl_verb { const char * name; ctl_verbfunc func; const char * help; }; /* General symbols. */ #define ctl_logger __ctl_logger #ifdef __GNUC__ void ctl_logger(enum ctl_severity, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); #else void ctl_logger(enum ctl_severity, const char *, ...); #endif /* Client symbols. */ #define ctl_client __ctl_client #define ctl_endclient __ctl_endclient #define ctl_command __ctl_command struct ctl_cctx * ctl_client(evContext, const struct sockaddr *, size_t, const struct sockaddr *, size_t, ctl_clntdone, void *, u_int, ctl_logfunc); void ctl_endclient(struct ctl_cctx *); int ctl_command(struct ctl_cctx *, const char *, size_t, ctl_clntdone, void *); /* Server symbols. */ #define ctl_server __ctl_server #define ctl_endserver __ctl_endserver #define ctl_response __ctl_response #define ctl_sendhelp __ctl_sendhelp #define ctl_getcsctx __ctl_getcsctx #define ctl_setcsctx __ctl_setcsctx struct ctl_sctx * ctl_server(evContext, const struct sockaddr *, size_t, const struct ctl_verb *, u_int, u_int, u_int, int, int, ctl_logfunc, void *); void ctl_endserver(struct ctl_sctx *); void ctl_response(struct ctl_sess *, u_int, const char *, u_int, const void *, ctl_srvrdone, void *, const char *, size_t); void ctl_sendhelp(struct ctl_sess *, u_int); void * ctl_getcsctx(struct ctl_sess *); void * ctl_setcsctx(struct ctl_sess *, void *); #endif /*ISC_CTL_H*/ diff --git a/contrib/bind/include/isc/dst.h b/contrib/bind/include/isc/dst.h index 9e68a103a7c1..46707b06b9ee 100644 --- a/contrib/bind/include/isc/dst.h +++ b/contrib/bind/include/isc/dst.h @@ -1,141 +1,183 @@ #ifndef DST_H #define DST_H #ifndef HAS_DST_KEY typedef struct dst_key { char *dk_key_name; /* name of the key */ int dk_key_size; /* this is the size of the key in bits */ int dk_proto; /* what protocols this key can be used for */ int dk_alg; /* algorithm number from key record */ u_int32_t dk_flags; /* and the flags of the public key */ u_int16_t dk_id; /* identifier of the key */ } DST_KEY; #endif /* HAS_DST_KEY */ +/* + * do not taint namespace + */ +#define dst_bsafe_init __dst_bsafe_init +#define dst_buffer_to_key __dst_buffer_to_key +#define dst_check_algorithm __dst_check_algorithm +#define dst_compare_keys __dst_compare_keys +#define dst_cylink_init __dst_cylink_init +#define dst_dnskey_to_key __dst_dnskey_to_key +#define dst_eay_dss_init __dst_eay_dss_init +#define dst_free_key __dst_free_key +#define dst_generate_key __dst_generate_key +#define dst_hmac_md5_init __dst_hmac_md5_init +#define dst_init __dst_init +#define dst_key_to_buffer __dst_key_to_buffer +#define dst_key_to_dnskey __dst_key_to_dnskey +#define dst_random __dst_random +#define dst_read_key __dst_read_key +#define dst_rsaref_init __dst_rsaref_init +#define dst_s_build_filename __dst_s_build_filename +#define dst_s_calculate_bits __dst_s_calculate_bits +#define dst_s_conv_bignum_b64_to_u8 __dst_s_conv_bignum_b64_to_u8 +#define dst_s_conv_bignum_u8_to_b64 __dst_s_conv_bignum_u8_to_b64 +#define dst_s_dns_key_id __dst_s_dns_key_id +#define dst_s_dump __dst_s_dump +#define dst_s_filename_length __dst_s_filename_length +#define dst_s_fopen __dst_s_fopen +#define dst_s_get_int16 __dst_s_get_int16 +#define dst_s_get_int32 __dst_s_get_int32 +#define dst_s_id_calc __dst_s_id_calc +#define dst_s_put_int16 __dst_s_put_int16 +#define dst_s_put_int32 __dst_s_put_int32 +#define dst_s_quick_random __dst_s_quick_random +#define dst_s_quick_random_set __dst_s_quick_random_set +#define dst_s_random __dst_s_random +#define dst_s_semi_random __dst_s_semi_random +#define dst_s_verify_str __dst_s_verify_str +#define dst_sig_size __dst_sig_size +#define dst_sign_data __dst_sign_data +#define dst_verify_data __dst_verify_data +#define dst_write_key __dst_write_key + /* * DST Crypto API defintions */ void dst_init(void); int dst_check_algorithm(const int); -int dst_sign_data(const int mode, /* specifies INIT/UPDATE/FINAL/ALL */ - DST_KEY *in_key, /* the key to use */ - void **context, /* pointer to state structure */ - const u_char *data, /* data to be signed */ - const int len, /* length of input data */ - u_char *signature, /* buffer to write signature to */ - const int sig_len); /* size of output buffer */ +int dst_sign_data(const int, /* specifies INIT/UPDATE/FINAL/ALL */ + DST_KEY *, /* the key to use */ + void **, /* pointer to state structure */ + const u_char *, /* data to be signed */ + const int, /* length of input data */ + u_char *, /* buffer to write signature to */ + const int); /* size of output buffer */ -int dst_verify_data(const int mode, /* specifies INIT/UPDATE/FINAL/ALL */ - DST_KEY *in_key, /* the key to use */ - void **context, /* pointer to state structure */ - const u_char *data, /* data to be verified */ - const int len, /* length of input data */ - const u_char *signature,/* buffer containing signature */ - const int sig_len); /* length of signature */ +int dst_verify_data(const int, /* specifies INIT/UPDATE/FINAL/ALL */ + DST_KEY *, /* the key to use */ + void **, /* pointer to state structure */ + const u_char *, /* data to be verified */ + const int, /* length of input data */ + const u_char *, /* buffer containing signature */ + const int); /* length of signature */ -DST_KEY *dst_read_key(const char *in_name, /* name of key */ - const u_int16_t in_id, /* key tag identifier */ - const int in_alg, /* key algorithm */ - const int key_type); /* Private/PublicKey wanted*/ +DST_KEY *dst_read_key(const char *, /* name of key */ + const u_int16_t, /* key tag identifier */ + const int, /* key algorithm */ + const int); /* Private/PublicKey wanted*/ -int dst_write_key(const DST_KEY *key, /* key to write out */ - const int key_type); /* Public/Private */ +int dst_write_key(const DST_KEY *, /* key to write out */ + const int); /* Public/Private */ -DST_KEY *dst_dnskey_to_key(const char *in_name, /* KEY record name */ - const u_char *key, /* KEY RDATA */ - const int len); /* size of input buffer*/ +DST_KEY *dst_dnskey_to_key(const char *, /* KEY record name */ + const u_char *, /* KEY RDATA */ + const int); /* size of input buffer*/ -int dst_key_to_dnskey(const DST_KEY *key, /* key to translate */ - u_char *out_storage, /* output buffer */ - const int out_len); /* size of out_storage*/ +int dst_key_to_dnskey(const DST_KEY *, /* key to translate */ + u_char *, /* output buffer */ + const int); /* size of out_storage*/ -DST_KEY *dst_buffer_to_key(const char *key_name, /* name of the key */ - const int alg, /* algorithm */ - const int flags, /* dns flags */ - const int protocol, /* dns protocol */ - const u_char *key_buf, /* key in dns wire fmt */ - const int key_len); /* size of key */ +DST_KEY *dst_buffer_to_key(const char *, /* name of the key */ + const int, /* algorithm */ + const int, /* dns flags */ + const int, /* dns protocol */ + const u_char *, /* key in dns wire fmt */ + const int); /* size of key */ -int dst_key_to_buffer(DST_KEY *key, u_char *out_buff, int buf_len); +int dst_key_to_buffer(DST_KEY *, u_char *, int); -DST_KEY *dst_generate_key(const char *name, /* name of new key */ - const int alg, /* key algorithm to generate */ - const int bits, /* size of new key */ - const int exp, /* alg dependent parameter*/ - const int flags, /* key DNS flags */ - const int protocol); /* key DNS protocol */ +DST_KEY *dst_generate_key(const char *, /* name of new key */ + const int, /* key algorithm to generate */ + const int, /* size of new key */ + const int, /* alg dependent parameter*/ + const int, /* key DNS flags */ + const int); /* key DNS protocol */ -DST_KEY *dst_free_key(DST_KEY *f_key); -int dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2); +DST_KEY *dst_free_key(DST_KEY *); +int dst_compare_keys(const DST_KEY *, const DST_KEY *); -int dst_sig_size(DST_KEY *key); +int dst_sig_size(DST_KEY *); -int dst_random(const int mode, int wanted, u_char *outran); +int dst_random(const int, int wanted, u_char *); /* support for dns key tags/ids */ -u_int16_t dst_s_dns_key_id(const u_char *dns_key_rdata, const int rdata_len); -u_int16_t dst_s_id_calc(const u_char *key_data, const int key_len); +u_int16_t dst_s_dns_key_id(const u_char *, const int); +u_int16_t dst_s_id_calc(const u_char *, const int); /* Used by callers as well as by the library. */ #define RAW_KEY_SIZE 8192 /* large enough to store any key */ /* DST_API control flags */ /* These are used used in functions dst_sign_data and dst_verify_data */ -#define SIG_MODE_INIT 1 /* initalize digest */ +#define SIG_MODE_INIT 1 /* initialize digest */ #define SIG_MODE_UPDATE 2 /* add data to digest */ #define SIG_MODE_FINAL 4 /* generate/verify signature */ #define SIG_MODE_ALL (SIG_MODE_INIT|SIG_MODE_UPDATE|SIG_MODE_FINAL) /* Flags for dst_read_private_key() */ #define DST_FORCE_READ 0x1000000 #define DST_CAN_SIGN 0x010F #define DST_NO_AUTHEN 0x8000 #define DST_EXTEND_FLAG 0x1000 #define DST_STANDARD 0 #define DST_PRIVATE 0x2000000 #define DST_PUBLIC 0x4000000 #define DST_RAND_SEMI 1 #define DST_RAND_STD 2 #define DST_RAND_KEY 3 #define DST_RAND_DSS 4 /* DST algorithm codes */ #define KEY_RSA 1 #define KEY_DH 2 #define KEY_DSA 3 #define KEY_PRIVATE 254 #define KEY_EXPAND 255 #define KEY_HMAC_MD5 157 #define KEY_HMAC_SHA1 158 #define UNKNOWN_KEYALG 0 #define DST_MAX_ALGS KEY_HMAC_SHA1 /* DST constants to locations in KEY record changes in new KEY record */ #define DST_FLAGS_SIZE 2 #define DST_KEY_PROT 2 #define DST_KEY_ALG 3 #define DST_EXT_FLAG 4 #define DST_KEY_START 4 #ifndef SIGN_F_NOKEY #define SIGN_F_NOKEY 0xC000 #endif /* error codes from dst routines */ #define SIGN_INIT_FAILURE (-23) #define SIGN_UPDATE_FAILURE (-24) #define SIGN_FINAL_FAILURE (-25) #define VERIFY_INIT_FAILURE (-26) #define VERIFY_UPDATE_FAILURE (-27) #define VERIFY_FINAL_FAILURE (-28) #define MISSING_KEY_OR_SIGNATURE (-30) #define UNSUPPORTED_KEYALG (-31) #endif /* DST_H */ diff --git a/contrib/bind/include/isc/eventlib.h b/contrib/bind/include/isc/eventlib.h index 5b98516f0f7f..e5d8f207ba4e 100644 --- a/contrib/bind/include/isc/eventlib.h +++ b/contrib/bind/include/isc/eventlib.h @@ -1,196 +1,191 @@ /* * Copyright (c) 1995-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* eventlib.h - exported interfaces for eventlib * vix 09sep95 [initial] * - * $Id: eventlib.h,v 1.23 2001/05/29 05:47:09 marka Exp $ + * $Id: eventlib.h,v 1.23.10.1 2003/06/02 06:04:03 marka Exp $ */ #ifndef _EVENTLIB_H #define _EVENTLIB_H #include #include #include #include #ifndef __P # define __EVENTLIB_P_DEFINED # ifdef __STDC__ # define __P(x) x # else # define __P(x) () # endif #endif /* In the absence of branded types... */ typedef struct { void *opaque; } evConnID; typedef struct { void *opaque; } evFileID; typedef struct { void *opaque; } evStreamID; typedef struct { void *opaque; } evTimerID; typedef struct { void *opaque; } evWaitID; typedef struct { void *opaque; } evContext; typedef struct { void *opaque; } evEvent; #define evInitID(id) ((id)->opaque = NULL) #define evTestID(id) ((id).opaque != NULL) -typedef void (*evConnFunc)__P((evContext ctx, void *uap, int fd, - const void *la, int lalen, - const void *ra, int ralen)); -typedef void (*evFileFunc)__P((evContext ctx, void *uap, int fd, int evmask)); -typedef void (*evStreamFunc)__P((evContext ctx, void *uap, int fd, int bytes)); -typedef void (*evTimerFunc)__P((evContext ctx, void *uap, - struct timespec due, struct timespec inter)); -typedef void (*evWaitFunc)__P((evContext ctx, void *uap, const void *tag)); +typedef void (*evConnFunc)__P((evContext, void *, int, const void *, int, + const void *, int)); +typedef void (*evFileFunc)__P((evContext, void *, int, int)); +typedef void (*evStreamFunc)__P((evContext, void *, int, int)); +typedef void (*evTimerFunc)__P((evContext, void *, + struct timespec, struct timespec)); +typedef void (*evWaitFunc)__P((evContext, void *, const void *)); typedef struct { unsigned char mask[256/8]; } evByteMask; #define EV_BYTEMASK_BYTE(b) ((b) / 8) #define EV_BYTEMASK_MASK(b) (1 << ((b) % 8)) #define EV_BYTEMASK_SET(bm, b) \ ((bm).mask[EV_BYTEMASK_BYTE(b)] |= EV_BYTEMASK_MASK(b)) #define EV_BYTEMASK_CLR(bm, b) \ ((bm).mask[EV_BYTEMASK_BYTE(b)] &= ~EV_BYTEMASK_MASK(b)) #define EV_BYTEMASK_TST(bm, b) \ ((bm).mask[EV_BYTEMASK_BYTE(b)] & EV_BYTEMASK_MASK(b)) #define EV_POLL 1 #define EV_WAIT 2 #define EV_NULL 4 #define EV_READ 1 #define EV_WRITE 2 #define EV_EXCEPT 4 /* eventlib.c */ #define evCreate __evCreate #define evSetDebug __evSetDebug #define evDestroy __evDestroy #define evGetNext __evGetNext #define evDispatch __evDispatch #define evDrop __evDrop #define evMainLoop __evMainLoop #define evHighestFD __evHighestFD -int evCreate __P((evContext *ctx)); -void evSetDebug __P((evContext ctx, int lev, FILE *out)); -int evDestroy __P((evContext ctx)); -int evGetNext __P((evContext ctx, evEvent *ev, int options)); -int evDispatch __P((evContext ctx, evEvent ev)); -void evDrop __P((evContext ctx, evEvent ev)); -int evMainLoop __P((evContext ctx)); -int evHighestFD __P((evContext ctx)); +int evCreate __P((evContext *)); +void evSetDebug __P((evContext, int, FILE *)); +int evDestroy __P((evContext)); +int evGetNext __P((evContext, evEvent *, int)); +int evDispatch __P((evContext, evEvent)); +void evDrop __P((evContext, evEvent)); +int evMainLoop __P((evContext)); +int evHighestFD __P((evContext)); /* ev_connects.c */ #define evListen __evListen #define evConnect __evConnect #define evCancelConn __evCancelConn #define evHold __evHold #define evUnhold __evUnhold #define evTryAccept __evTryAccept -int evListen __P((evContext ctx, int fd, int maxconn, - evConnFunc func, void *uap, evConnID *id)); -int evConnect __P((evContext ctx, int fd, const void *ra, int ralen, - evConnFunc func, void *uap, evConnID *id)); -int evCancelConn __P((evContext ctx, evConnID id)); +int evListen __P((evContext, int, int, evConnFunc, void *, evConnID *)); +int evConnect __P((evContext, int, const void *, int, + evConnFunc, void *, evConnID *)); +int evCancelConn __P((evContext, evConnID)); int evHold __P((evContext, evConnID)); int evUnhold __P((evContext, evConnID)); int evTryAccept __P((evContext, evConnID, int *)); /* ev_files.c */ #define evSelectFD __evSelectFD #define evDeselectFD __evDeselectFD -int evSelectFD __P((evContext ctx, int fd, int eventmask, - evFileFunc func, void *uap, evFileID *id)); -int evDeselectFD __P((evContext ctx, evFileID id)); +int evSelectFD __P((evContext, int, int, evFileFunc, void *, evFileID *)); +int evDeselectFD __P((evContext, evFileID)); /* ev_streams.c */ #define evConsIovec __evConsIovec #define evWrite __evWrite #define evRead __evRead #define evTimeRW __evTimeRW #define evUntimeRW __evUntimeRW #define evCancelRW __evCancelRW -struct iovec evConsIovec __P((void *buf, size_t cnt)); -int evWrite __P((evContext ctx, int fd, const struct iovec *iov, int cnt, - evStreamFunc func, void *uap, evStreamID *id)); -int evRead __P((evContext ctx, int fd, const struct iovec *iov, int cnt, - evStreamFunc func, void *uap, evStreamID *id)); -int evTimeRW __P((evContext ctx, evStreamID id, evTimerID timer)); -int evUntimeRW __P((evContext ctx, evStreamID id)); -int evCancelRW __P((evContext ctx, evStreamID id)); +struct iovec evConsIovec __P((void *, size_t)); +int evWrite __P((evContext, int, const struct iovec *, int, + evStreamFunc func, void *, evStreamID *)); +int evRead __P((evContext, int, const struct iovec *, int, + evStreamFunc func, void *, evStreamID *)); +int evTimeRW __P((evContext, evStreamID, evTimerID timer)); +int evUntimeRW __P((evContext, evStreamID)); +int evCancelRW __P((evContext, evStreamID)); /* ev_timers.c */ #define evConsTime __evConsTime #define evAddTime __evAddTime #define evSubTime __evSubTime #define evCmpTime __evCmpTime #define evTimeSpec __evTimeSpec #define evTimeVal __evTimeVal #define evNowTime __evNowTime #define evLastEventTime __evLastEventTime #define evSetTimer __evSetTimer #define evClearTimer __evClearTimer #define evResetTimer __evResetTimer #define evSetIdleTimer __evSetIdleTimer #define evClearIdleTimer __evClearIdleTimer #define evResetIdleTimer __evResetIdleTimer #define evTouchIdleTimer __evTouchIdleTimer struct timespec evConsTime __P((time_t sec, long nsec)); -struct timespec evAddTime __P((struct timespec add1, struct timespec add2)); -struct timespec evSubTime __P((struct timespec minu, struct timespec subtra)); +struct timespec evAddTime __P((struct timespec, struct timespec)); +struct timespec evSubTime __P((struct timespec, struct timespec)); struct timespec evNowTime __P((void)); struct timespec evLastEventTime __P((evContext)); struct timespec evTimeSpec __P((struct timeval)); struct timeval evTimeVal __P((struct timespec)); -int evCmpTime __P((struct timespec a, struct timespec b)); -int evSetTimer __P((evContext ctx, evTimerFunc func, void *uap, - struct timespec due, struct timespec inter, - evTimerID *id)); -int evClearTimer __P((evContext ctx, evTimerID id)); +int evCmpTime __P((struct timespec, struct timespec)); +int evSetTimer __P((evContext, evTimerFunc, void *, struct timespec, + struct timespec, evTimerID *)); +int evClearTimer __P((evContext, evTimerID)); int evResetTimer __P((evContext, evTimerID, evTimerFunc, void *, struct timespec, struct timespec)); int evSetIdleTimer __P((evContext, evTimerFunc, void *, struct timespec, evTimerID *)); int evClearIdleTimer __P((evContext, evTimerID)); int evResetIdleTimer __P((evContext, evTimerID, evTimerFunc, void *, struct timespec)); int evTouchIdleTimer __P((evContext, evTimerID)); /* ev_waits.c */ #define evWaitFor __evWaitFor #define evDo __evDo #define evUnwait __evUnwait #define evDefer __evDefer -int evWaitFor __P((evContext ctx, const void *tag, evWaitFunc func, void *uap, - evWaitID *id)); -int evDo __P((evContext ctx, const void *tag)); -int evUnwait __P((evContext ctx, evWaitID id)); +int evWaitFor __P((evContext, const void *, evWaitFunc, void *, evWaitID *)); +int evDo __P((evContext, const void *)); +int evUnwait __P((evContext, evWaitID)); int evDefer __P((evContext, evWaitFunc, void *)); #ifdef __EVENTLIB_P_DEFINED # undef __P #endif #endif /*_EVENTLIB_H*/ diff --git a/contrib/bind/include/isc/irpmarshall.h b/contrib/bind/include/isc/irpmarshall.h index 30cef41109bc..2ad85fc8c730 100644 --- a/contrib/bind/include/isc/irpmarshall.h +++ b/contrib/bind/include/isc/irpmarshall.h @@ -1,116 +1,115 @@ /* * Copyright (c) 1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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: irpmarshall.h,v 8.2 2001/05/29 05:47:10 marka Exp $ + * $Id: irpmarshall.h,v 8.2.10.1 2003/06/02 06:04:03 marka Exp $ */ #ifndef _IRPMARSHALL_H_INCLUDED #define _IRPMARSHALL_H_INCLUDED /* Hide function names */ #define irp_marshall_gr __irp_marshall_gr #define irp_marshall_ho __irp_marshall_ho #define irp_marshall_ne __irp_marshall_ne #define irp_marshall_ng __irp_marshall_ng #define irp_marshall_nw __irp_marshall_nw #define irp_marshall_pr __irp_marshall_pr #define irp_marshall_pw __irp_marshall_pw #define irp_marshall_sv __irp_marshall_sv #define irp_unmarshall_gr __irp_unmarshall_gr #define irp_unmarshall_ho __irp_unmarshall_ho #define irp_unmarshall_ne __irp_unmarshall_ne #define irp_unmarshall_ng __irp_unmarshall_ng #define irp_unmarshall_nw __irp_unmarshall_nw #define irp_unmarshall_pr __irp_unmarshall_pr #define irp_unmarshall_pw __irp_unmarshall_pw #define irp_unmarshall_sv __irp_unmarshall_sv #define MAXPADDRSIZE (sizeof "255.255.255.255" + 1) #define ADDR_T_STR(x) (x == AF_INET ? "AF_INET" :\ (x == AF_INET6 ? "AF_INET6" : "UNKNOWN")) /* See comment below on usage */ -int irp_marshall_pw(const struct passwd *pw, char **buffer, size_t *len); -int irp_unmarshall_pw(struct passwd *pw, char *buffer); -int irp_marshall_gr(const struct group *gr, char **buffer, size_t *len); -int irp_unmarshall_gr(struct group *gr, char *buffer); -int irp_marshall_sv(const struct servent *sv, char **buffer, size_t *len); -int irp_unmarshall_sv(struct servent *sv, char *buffer); -int irp_marshall_pr(struct protoent *pr, char **buffer, size_t *len); -int irp_unmarshall_pr(struct protoent *pr, char *buffer); -int irp_marshall_ho(struct hostent *ho, char **buffer, size_t *len); -int irp_unmarshall_ho(struct hostent *ho, char *buffer); -int irp_marshall_ng(const char *host, const char *user, const char *domain, - char **buffer, size_t *len); -int irp_unmarshall_ng(const char **host, const char **user, - const char **domain, char *buffer); -int irp_marshall_nw(struct nwent *ne, char **buffer, size_t *len); -int irp_unmarshall_nw(struct nwent *ne, char *buffer); -int irp_marshall_ne(struct netent *ne, char **buffer, size_t *len); -int irp_unmarshall_ne(struct netent *ne, char *buffer); +int irp_marshall_pw(const struct passwd *, char **, size_t *); +int irp_unmarshall_pw(struct passwd *, char *); +int irp_marshall_gr(const struct group *, char **, size_t *); +int irp_unmarshall_gr(struct group *, char *); +int irp_marshall_sv(const struct servent *, char **, size_t *); +int irp_unmarshall_sv(struct servent *, char *); +int irp_marshall_pr(struct protoent *, char **, size_t *); +int irp_unmarshall_pr(struct protoent *, char *); +int irp_marshall_ho(struct hostent *, char **, size_t *); +int irp_unmarshall_ho(struct hostent *, char *); +int irp_marshall_ng(const char *, const char *, const char *, + char **, size_t *); +int irp_unmarshall_ng(const char **, const char **, const char **, char *); +int irp_marshall_nw(struct nwent *, char **, size_t *); +int irp_unmarshall_nw(struct nwent *, char *); +int irp_marshall_ne(struct netent *, char **, size_t *); +int irp_unmarshall_ne(struct netent *, char *); /* * Functions to marshall and unmarshall various system data structures. We * use a printable ascii format that is as close to various system config * files as reasonable (e.g. /etc/passwd format). * * We are not forgiving with unmarhsalling misformatted buffers. In * particular whitespace in fields is not ignored. So a formatted password * entry "brister :1364:100:...." will yield a username of "brister " * * We potentially do a lot of mallocs to fill fields that are of type * (char **) like a hostent h_addr field. Building (for example) the * h_addr field and its associated addresses all in one buffer is * certainly possible, but not done here. * * The following description is true for all the marshalling functions: * */ /* int irp_marshall_XX(struct yyyy *XX, char **buffer, size_t *len); * * The argument XX (of type struct passwd for example) is marshalled in the * buffer pointed at by *BUFFER, which is of length *LEN. Returns 0 * on success and -1 on failure. Failure will occur if *LEN is * smaller than needed. * * If BUFFER is NULL, then *LEN is set to the size of the buffer * needed to marshall the data and no marshalling is actually done. * * If *BUFFER is NULL, then a buffer large enough will be allocated * with memget() and the size allocated will be stored in *LEN. An extra 2 * bytes will be allocated for the client to append CRLF if wanted. The * value of *LEN will include these two bytes. * * All the marshalling functions produce a buffer with the fields * separated by colons (except for the hostent marshalling, which uses '@' * to separate fields). Fields that have multiple subfields (like the * gr_mem field in struct group) have their subparts separated by * commas. */ /* * int irp_unmarshall_XX(struct YYYYY *XX, char *buffer); * * The unmashalling functions break apart the buffer and store the * values in the struct pointed to by XX. All pointer values inside * XX are allocated with malloc. All arrays of pointers have a NULL * as the last element. */ #endif diff --git a/contrib/bind/include/isc/logging.h b/contrib/bind/include/isc/logging.h index 6d6976f1a8ee..dc491dc095f8 100644 --- a/contrib/bind/include/isc/logging.h +++ b/contrib/bind/include/isc/logging.h @@ -1,109 +1,112 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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 LOGGING_H #define LOGGING_H #include #include #include #include #define log_critical (-5) #define log_error (-4) #define log_warning (-3) #define log_notice (-2) #define log_info (-1) #define log_debug(level) (level) typedef enum { log_syslog, log_file, log_null } log_channel_type; #define LOG_MAX_VERSIONS 99 #define LOG_CLOSE_STREAM 0x0001 #define LOG_TIMESTAMP 0x0002 #define LOG_TRUNCATE 0x0004 #define LOG_USE_CONTEXT_LEVEL 0x0008 #define LOG_PRINT_LEVEL 0x0010 #define LOG_REQUIRE_DEBUG 0x0020 #define LOG_CHANNEL_BROKEN 0x0040 #define LOG_PRINT_CATEGORY 0x0080 #define LOG_CHANNEL_OFF 0x0100 typedef struct log_context *log_context; typedef struct log_channel *log_channel; #define LOG_OPTION_DEBUG 0x01 #define LOG_OPTION_LEVEL 0x02 #define log_open_stream __log_open_stream #define log_close_stream __log_close_stream #define log_get_stream __log_get_stream #define log_get_filename __log_get_filename #define log_check_channel __log_check_channel #define log_check __log_check #define log_vwrite __log_vwrite #define log_write __log_write #define log_new_context __log_new_context #define log_free_context __log_free_context #define log_add_channel __log_add_channel #define log_remove_channel __log_remove_channel #define log_option __log_option #define log_category_is_active __log_category_is_active #define log_new_syslog_channel __log_new_syslog_channel #define log_new_file_channel __log_new_file_channel #define log_set_file_owner __log_set_file_owner #define log_new_null_channel __log_new_null_channel #define log_inc_references __log_inc_references #define log_dec_references __log_dec_references #define log_get_channel_type __log_get_channel_type #define log_free_channel __log_free_channel #define log_close_debug_channels __log_close_debug_channels FILE * log_open_stream(log_channel); int log_close_stream(log_channel); FILE * log_get_stream(log_channel); char * log_get_filename(log_channel); int log_check_channel(log_context, int, log_channel); int log_check(log_context, int, int); -void log_vwrite(log_context, int, int, const char *, - va_list args); #ifdef __GNUC__ +void log_vwrite(log_context, int, int, const char *, + va_list args) + __attribute__((__format__(__printf__, 4, 0))); void log_write(log_context, int, int, const char *, ...) __attribute__((__format__(__printf__, 4, 5))); #else +void log_vwrite(log_context, int, int, const char *, + va_list args); void log_write(log_context, int, int, const char *, ...); #endif int log_new_context(int, char **, log_context *); void log_free_context(log_context); int log_add_channel(log_context, int, log_channel); int log_remove_channel(log_context, int, log_channel); int log_option(log_context, int, int); int log_category_is_active(log_context, int); log_channel log_new_syslog_channel(unsigned int, int, int); log_channel log_new_file_channel(unsigned int, int, const char *, FILE *, unsigned int, unsigned long); int log_set_file_owner(log_channel, uid_t, gid_t); log_channel log_new_null_channel(void); int log_inc_references(log_channel); int log_dec_references(log_channel); log_channel_type log_get_channel_type(log_channel); int log_free_channel(log_channel); void log_close_debug_channels(log_context); #endif /* !LOGGING_H */ diff --git a/contrib/bind/include/isc/misc.h b/contrib/bind/include/isc/misc.h index 4de1ceeb029b..d6e7b0c17346 100644 --- a/contrib/bind/include/isc/misc.h +++ b/contrib/bind/include/isc/misc.h @@ -1,39 +1,39 @@ /* * Copyright (c) 1995-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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: misc.h,v 8.5 2001/06/18 06:40:43 marka Exp $ + * $Id: misc.h,v 8.5.10.1 2003/06/02 06:04:03 marka Exp $ */ #ifndef _ISC_MISC_H #define _ISC_MISC_H #include #define bitncmp __bitncmp /*#define isc_movefile __isc_movefile */ -extern int bitncmp(const void *l, const void *r, int n); +extern int bitncmp(const void *, const void *, int); extern int isc_movefile(const char *, const char *); extern int isc_gethexstring(unsigned char *, size_t, int, FILE *, int *); extern void isc_puthexstring(FILE *, const unsigned char *, size_t, size_t, size_t, const char *); extern void isc_tohex(const unsigned char *, size_t, char *); #endif /*_ISC_MISC_H*/ diff --git a/contrib/bind/include/isc/tree.h b/contrib/bind/include/isc/tree.h index 24e2a10f72f5..53c69b7d378c 100644 --- a/contrib/bind/include/isc/tree.h +++ b/contrib/bind/include/isc/tree.h @@ -1,48 +1,58 @@ /* tree.h - declare structures used by tree library * * vix 22jan93 [revisited; uses RCS, ANSI, POSIX; has bug fixes] * vix 27jun86 [broken out of tree.c] * - * $Id: tree.h,v 8.2 1996/10/25 18:11:03 vixie Exp $ + * $Id: tree.h,v 8.3 2002/12/03 05:26:48 marka Exp $ */ #ifndef _TREE_H_INCLUDED #define _TREE_H_INCLUDED #ifndef __P # if defined(__STDC__) || defined(__GNUC__) # define __P(x) x # else # define __P(x) () # endif #endif /* * tree_t is our package-specific anonymous pointer. */ #if defined(__STDC__) || defined(__GNUC__) typedef void *tree_t; #else typedef char *tree_t; #endif +/* + * Do not taint namespace + */ +#define tree_add __tree_add +#define tree_delete __tree_delete +#define tree_init __tree_init +#define tree_mung __tree_mung +#define tree_srch __tree_srch +#define tree_trav __tree_trav + typedef struct tree_s { tree_t data; struct tree_s *left, *right; short bal; } tree; void tree_init __P((tree **)); tree_t tree_srch __P((tree **, int (*)(), tree_t)); tree_t tree_add __P((tree **, int (*)(), tree_t, void (*)())); int tree_delete __P((tree **, int (*)(), tree_t, void (*)())); int tree_trav __P((tree **, int (*)())); void tree_mung __P((tree **, void (*)())); #endif /* _TREE_H_INCLUDED */ diff --git a/contrib/bind/include/netgroup.h b/contrib/bind/include/netgroup.h index 30efb9414413..fee6787b3f5d 100644 --- a/contrib/bind/include/netgroup.h +++ b/contrib/bind/include/netgroup.h @@ -1,17 +1,19 @@ #ifndef netgroup_h #define netgroup_h -int getnetgrent(const char **machinep, const char **userp, - const char **domainp); +/* + * The standard is crazy. These values "belong" to getnetgrent() and + * shouldn't be altered by the caller. + */ +int getnetgrent __P((/* const */ char **, /* const */ char **, + /* const */ char **)); -int getnetgrent_r(char **machinep, char **userp, char **domainp, - char *buffer, int buflen); +int getnetgrent_r __P((char **, char **, char **, char *, int)); -void setnetgrent(const char *netgroup); +void setnetgrent __P((const char *)); -void endnetgrent(void); +void endnetgrent __P((void)); -int innetgr(const char *netgroup, const char *machine, - const char *user, const char *domain); +int innetgr __P((const char *, const char *, const char *, const char *)); #endif diff --git a/contrib/bind/include/resolv.h b/contrib/bind/include/resolv.h index 5497f304f502..d69deff98969 100644 --- a/contrib/bind/include/resolv.h +++ b/contrib/bind/include/resolv.h @@ -1,484 +1,491 @@ /* * Copyright (c) 1983, 1987, 1989 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * @(#)resolv.h 8.1 (Berkeley) 6/2/93 - * $Id: resolv.h,v 8.48 2002/05/31 06:05:29 marka Exp $ + * $Id: resolv.h,v 8.50.6.2 2003/06/02 06:01:16 marka Exp $ */ #ifndef _RESOLV_H_ #define _RESOLV_H_ #include #if (!defined(BSD)) || (BSD < 199306) # include #else # include #endif #include #include #include #include /* * Revision information. This is the release date in YYYYMMDD format. * It can change every day so the right thing to do with it is use it * in preprocessor commands such as "#if (__RES > 19931104)". Do not * compare for equality; rather, use it to determine whether your resolver * is new enough to contain a certain feature. */ -#define __RES 19991006 +#define __RES 20030124 /* * This used to be defined in res_query.c, now it's in herror.c. * [XXX no it's not. It's in irs/irs_data.c] * It was * never extern'd by any *.h file before it was placed here. For thread * aware programs, the last h_errno value set is stored in res->h_errno. * * XXX: There doesn't seem to be a good reason for exposing RES_SET_H_ERRNO * (and __h_errno_set) to the public via . * XXX: __h_errno_set is really part of IRS, not part of the resolver. * If somebody wants to build and use a resolver that doesn't use IRS, * what do they do? Perhaps something like * #ifdef WANT_IRS * # define RES_SET_H_ERRNO(r,x) __h_errno_set(r,x) * #else * # define RES_SET_H_ERRNO(r,x) (h_errno = (r)->res_h_errno = (x)) * #endif */ #define RES_SET_H_ERRNO(r,x) __h_errno_set(r,x) struct __res_state; /* forward */ __BEGIN_DECLS void __h_errno_set(struct __res_state *res, int err); __END_DECLS /* * Resolver configuration file. * Normally not present, but may contain the address of the - * inital name server(s) to query and the domain search list. + * initial name server(s) to query and the domain search list. */ #ifndef _PATH_RESCONF #define _PATH_RESCONF "/etc/resolv.conf" #endif typedef enum { res_goahead, res_nextns, res_modified, res_done, res_error } res_sendhookact; -typedef res_sendhookact (*res_send_qhook)__P((struct sockaddr * const *ns, - const u_char **query, - int *querylen, - u_char *ans, - int anssiz, - int *resplen)); +typedef res_sendhookact (*res_send_qhook)__P((struct sockaddr * const *, + const u_char **, int *, + u_char *, int, int *)); -typedef res_sendhookact (*res_send_rhook)__P((const struct sockaddr *ns, - const u_char *query, - int querylen, - u_char *ans, - int anssiz, - int *resplen)); +typedef res_sendhookact (*res_send_rhook)__P((const struct sockaddr *, + const u_char *, int, u_char *, + int, int *)); struct res_sym { int number; /* Identifying number, like T_MX */ const char * name; /* Its symbolic name, like "MX" */ const char * humanname; /* Its fun name, like "mail exchanger" */ }; /* * Global defines and variables for resolver stub. */ #define MAXNS 3 /* max # name servers we'll track */ #define MAXDFLSRCH 3 /* # default domain levels to try */ #define MAXDNSRCH 6 /* max # domains in search path */ #define LOCALDOMAINPARTS 2 /* min levels in name that is "local" */ #define RES_TIMEOUT 5 /* min. seconds between retries */ #define MAXRESOLVSORT 10 /* number of net to sort on */ #define RES_MAXNDOTS 15 /* should reflect bit field size */ #define RES_MAXRETRANS 30 /* only for resolv.conf/RES_OPTIONS */ #define RES_MAXRETRY 5 /* only for resolv.conf/RES_OPTIONS */ #define RES_DFLRETRY 2 /* Default #/tries. */ #define RES_MAXTIME 65535 /* Infinity, in milliseconds. */ struct __res_state_ext; struct __res_state { - int retrans; /* retransmition time interval */ + int retrans; /* retransmission time interval */ int retry; /* number of times to retransmit */ #ifdef sun u_int options; /* option flags - see below. */ #else u_long options; /* option flags - see below. */ #endif int nscount; /* number of name servers */ struct sockaddr_in nsaddr_list[MAXNS]; /* address of name server */ #define nsaddr nsaddr_list[0] /* for backward compatibility */ u_short id; /* current message id */ char *dnsrch[MAXDNSRCH+1]; /* components of domain to search */ char defdname[256]; /* default domain (deprecated) */ #ifdef sun u_int pfcode; /* RES_PRF_ flags - see below. */ #else u_long pfcode; /* RES_PRF_ flags - see below. */ #endif unsigned ndots:4; /* threshold for initial abs. query */ unsigned nsort:4; /* number of elements in sort_list[] */ char unused[3]; struct { struct in_addr addr; u_int32_t mask; } sort_list[MAXRESOLVSORT]; res_send_qhook qhook; /* query hook */ res_send_rhook rhook; /* response hook */ int res_h_errno; /* last one set for this context */ int _vcsock; /* PRIVATE: for res_send VC i/o */ u_int _flags; /* PRIVATE: see below */ u_int _pad; /* make _u 64 bit aligned */ union { /* On an 32-bit arch this means 512b total. */ char pad[72 - 4*sizeof (int) - 2*sizeof (void *)]; struct { u_int16_t nscount; u_int16_t nstimes[MAXNS]; /* ms. */ int nssocks[MAXNS]; struct __res_state_ext *ext; /* extention for IPv6 */ } _ext; } _u; }; typedef struct __res_state *res_state; union res_sockaddr_union { struct sockaddr_in sin; #ifdef IN6ADDR_ANY_INIT struct sockaddr_in6 sin6; #endif #ifdef ISC_ALIGN64 int64_t __align64; /* 64bit alignment */ #else int32_t __align32; /* 32bit alignment */ #endif char __space[128]; /* max size */ }; /* * Resolver flags (used to be discrete per-module statics ints). */ #define RES_F_VC 0x00000001 /* socket is TCP */ #define RES_F_CONN 0x00000002 /* socket is connected */ #define RES_F_EDNS0ERR 0x00000004 /* EDNS0 caused errors */ +#define RES_F__UNUSED 0x00000008 /* (unused) */ +#define RES_F_LASTMASK 0x000000F0 /* ordinal server of last res_nsend */ +#define RES_F_LASTSHIFT 4 /* bit position of LASTMASK "flag" */ +#define RES_GETLAST(res) (((res)._flags & RES_F_LASTMASK) >> RES_F_LASTSHIFT) /* res_findzonecut2() options */ #define RES_EXHAUSTIVE 0x00000001 /* always do all queries */ #define RES_IPV4ONLY 0x00000002 /* IPv4 only */ #define RES_IPV6ONLY 0x00000004 /* IPv6 only */ /* * Resolver options (keep these in synch with res_debug.c, please) */ #define RES_INIT 0x00000001 /* address initialized */ #define RES_DEBUG 0x00000002 /* print debug messages */ #define RES_AAONLY 0x00000004 /* authoritative answers only (!IMPL)*/ #define RES_USEVC 0x00000008 /* use virtual circuit */ #define RES_PRIMARY 0x00000010 /* query primary server only (!IMPL) */ #define RES_IGNTC 0x00000020 /* ignore trucation errors */ #define RES_RECURSE 0x00000040 /* recursion desired */ #define RES_DEFNAMES 0x00000080 /* use default domain name */ #define RES_STAYOPEN 0x00000100 /* Keep TCP socket open */ #define RES_DNSRCH 0x00000200 /* search up local domain tree */ #define RES_INSECURE1 0x00000400 /* type 1 security disabled */ #define RES_INSECURE2 0x00000800 /* type 2 security disabled */ #define RES_NOALIASES 0x00001000 /* shuts off HOSTALIASES feature */ #define RES_USE_INET6 0x00002000 /* use/map IPv6 in gethostbyname() */ #define RES_ROTATE 0x00004000 /* rotate ns list after each query */ #define RES_NOCHECKNAME 0x00008000 /* do not check names for sanity. */ #define RES_KEEPTSIG 0x00010000 /* do not strip TSIG records */ #define RES_BLAST 0x00020000 /* blast all recursive servers */ -#define RES_NO_NIBBLE 0x00040000 /* disable IPv6 nibble mode reverse */ -#define RES_NO_BITSTRING 0x00080000 /* disable IPv6 bitstring mode reverse */ #define RES_NOTLDQUERY 0x00100000 /* don't unqualified name as a tld */ #define RES_USE_DNSSEC 0x00200000 /* use DNSSEC using OK bit in OPT */ /* KAME extensions: use higher bit to avoid conflict with ISC use */ #define RES_USE_DNAME 0x10000000 /* use DNAME */ -#define RES_USE_A6 0x20000000 /* use A6 */ #define RES_USE_EDNS0 0x40000000 /* use EDNS0 if configured */ #define RES_NO_NIBBLE2 0x80000000 /* disable alternate nibble lookup */ #define RES_DEFAULT (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH) /* * Resolver "pfcode" values. Used by dig. */ #define RES_PRF_STATS 0x00000001 #define RES_PRF_UPDATE 0x00000002 #define RES_PRF_CLASS 0x00000004 #define RES_PRF_CMD 0x00000008 #define RES_PRF_QUES 0x00000010 #define RES_PRF_ANS 0x00000020 #define RES_PRF_AUTH 0x00000040 #define RES_PRF_ADD 0x00000080 #define RES_PRF_HEAD1 0x00000100 #define RES_PRF_HEAD2 0x00000200 #define RES_PRF_TTLID 0x00000400 #define RES_PRF_HEADX 0x00000800 #define RES_PRF_QUERY 0x00001000 #define RES_PRF_REPLY 0x00002000 #define RES_PRF_INIT 0x00004000 #define RES_PRF_TRUNC 0x00008000 /* 0x00010000 */ /* Things involving an internal (static) resolver context. */ #ifdef _REENTRANT __BEGIN_DECLS extern struct __res_state *__res_state(void); __END_DECLS #define _res (*__res_state()) #else #ifndef __BIND_NOSTATIC extern struct __res_state _res; #endif #endif #ifndef __BIND_NOSTATIC #define fp_nquery __fp_nquery #define fp_query __fp_query #define hostalias __hostalias #define p_query __p_query #define res_close __res_close #define res_init __res_init #define res_isourserver __res_isourserver #define res_mkquery __res_mkquery #define res_query __res_query #define res_querydomain __res_querydomain #define res_search __res_search #define res_send __res_send #define res_sendsigned __res_sendsigned __BEGIN_DECLS void fp_nquery __P((const u_char *, int, FILE *)); void fp_query __P((const u_char *, FILE *)); const char * hostalias __P((const char *)); void p_query __P((const u_char *)); void res_close __P((void)); int res_init __P((void)); int res_isourserver __P((const struct sockaddr_in *)); int res_mkquery __P((int, const char *, int, int, const u_char *, int, const u_char *, u_char *, int)); int res_query __P((const char *, int, int, u_char *, int)); int res_querydomain __P((const char *, const char *, int, int, u_char *, int)); int res_search __P((const char *, int, int, u_char *, int)); int res_send __P((const u_char *, int, u_char *, int)); int res_sendsigned __P((const u_char *, int, ns_tsig_key *, u_char *, int)); __END_DECLS #endif #if !defined(SHARED_LIBBIND) || defined(LIB) /* * If libbind is a shared object (well, DLL anyway) * these externs break the linker when resolv.h is * included by a lib client (like named) * Make them go away if a client is including this * */ extern const struct res_sym __p_key_syms[]; extern const struct res_sym __p_cert_syms[]; extern const struct res_sym __p_class_syms[]; extern const struct res_sym __p_type_syms[]; extern const struct res_sym __p_rcode_syms[]; #endif /* SHARED_LIBBIND */ #define b64_ntop __b64_ntop #define b64_pton __b64_pton #define dn_comp __dn_comp #define dn_count_labels __dn_count_labels #define dn_expand __dn_expand #define dn_skipname __dn_skipname #define fp_resstat __fp_resstat #define loc_aton __loc_aton #define loc_ntoa __loc_ntoa #define p_cdname __p_cdname #define p_cdnname __p_cdnname #define p_class __p_class #define p_fqname __p_fqname #define p_fqnname __p_fqnname #define p_option __p_option #define p_secstodate __p_secstodate #define p_section __p_section #define p_time __p_time #define p_type __p_type #define p_rcode __p_rcode +#define p_sockun __p_sockun #define putlong __putlong #define putshort __putshort #define res_dnok __res_dnok #define res_findzonecut __res_findzonecut #define res_findzonecut2 __res_findzonecut2 #define res_hnok __res_hnok #define res_hostalias __res_hostalias #define res_mailok __res_mailok #define res_nameinquery __res_nameinquery #define res_nclose __res_nclose #define res_ninit __res_ninit #define res_nmkquery __res_nmkquery #define res_pquery __res_pquery #define res_nquery __res_nquery #define res_nquerydomain __res_nquerydomain #define res_nsearch __res_nsearch #define res_nsend __res_nsend #define res_nsendsigned __res_nsendsigned #define res_nisourserver __res_nisourserver #define res_ownok __res_ownok #define res_queriesmatch __res_queriesmatch #define res_randomid __res_randomid #define sym_ntop __sym_ntop #define sym_ntos __sym_ntos #define sym_ston __sym_ston #define res_nopt __res_nopt #define res_ndestroy __res_ndestroy #define res_nametoclass __res_nametoclass #define res_nametotype __res_nametotype #define res_setservers __res_setservers #define res_getservers __res_getservers +#define res_buildprotolist __res_buildprotolist +#define res_destroyprotolist __res_destroyprotolist +#define res_destroyservicelist __res_destroyservicelist +#define res_get_nibblesuffix __res_get_nibblesuffix +#define res_get_nibblesuffix2 __res_get_nibblesuffix2 +#define res_ourserver_p __res_ourserver_p +#define res_protocolname __res_protocolname +#define res_protocolnumber __res_protocolnumber +#define res_send_setqhook __res_send_setqhook +#define res_send_setrhook __res_send_setrhook +#define res_servicename __res_servicename +#define res_servicenumber __res_servicenumber __BEGIN_DECLS int res_hnok __P((const char *)); int res_ownok __P((const char *)); int res_mailok __P((const char *)); int res_dnok __P((const char *)); int sym_ston __P((const struct res_sym *, const char *, int *)); const char * sym_ntos __P((const struct res_sym *, int, int *)); const char * sym_ntop __P((const struct res_sym *, int, int *)); int b64_ntop __P((u_char const *, size_t, char *, size_t)); int b64_pton __P((char const *, u_char *, size_t)); -int loc_aton __P((const char *ascii, u_char *binary)); -const char * loc_ntoa __P((const u_char *binary, char *ascii)); +int loc_aton __P((const char *, u_char *)); +const char * loc_ntoa __P((const u_char *, char *)); int dn_skipname __P((const u_char *, const u_char *)); void putlong __P((u_int32_t, u_char *)); void putshort __P((u_int16_t, u_char *)); #ifndef __ultrix__ -u_int16_t _getshort __P((const u_char *src)); -u_int32_t _getlong __P((const u_char *src)); +u_int16_t _getshort __P((const u_char *)); +u_int32_t _getlong __P((const u_char *)); #endif const char * p_class __P((int)); const char * p_time __P((u_int32_t)); const char * p_type __P((int)); const char * p_rcode __P((int)); +const char * p_sockun __P((union res_sockaddr_union, char *, size_t)); const u_char * p_cdnname __P((const u_char *, const u_char *, int, FILE *)); const u_char * p_cdname __P((const u_char *, const u_char *, FILE *)); -const u_char * p_fqnname __P((const u_char *cp, const u_char *msg, +const u_char * p_fqnname __P((const u_char *, const u_char *, int, char *, int)); const u_char * p_fqname __P((const u_char *, const u_char *, FILE *)); -const char * p_option __P((u_long option)); +const char * p_option __P((u_long)); char * p_secstodate __P((u_long)); int dn_count_labels __P((const char *)); int dn_comp __P((const char *, u_char *, int, u_char **, u_char **)); int dn_expand __P((const u_char *, const u_char *, const u_char *, char *, int)); u_int res_randomid __P((void)); -int res_nameinquery __P((const char *, int, int, - const u_char *, const u_char *)); +int res_nameinquery __P((const char *, int, int, const u_char *, + const u_char *)); int res_queriesmatch __P((const u_char *, const u_char *, const u_char *, const u_char *)); -const char * p_section __P((int section, int opcode)); +const char * p_section __P((int, int)); /* Things involving a resolver context. */ int res_ninit __P((res_state)); int res_nisourserver __P((const res_state, const struct sockaddr_in *)); void fp_resstat __P((const res_state, FILE *)); void res_pquery __P((const res_state, const u_char *, int, FILE *)); const char * res_hostalias __P((const res_state, const char *, char *, size_t)); -int res_nquery __P((res_state, - const char *, int, int, u_char *, int)); -int res_nsearch __P((res_state, const char *, int, - int, u_char *, int)); -int res_nquerydomain __P((res_state, - const char *, const char *, int, int, - u_char *, int)); -int res_nmkquery __P((res_state, - int, const char *, int, int, const u_char *, - int, const u_char *, u_char *, int)); +int res_nquery __P((res_state, const char *, int, int, + u_char *, int)); +int res_nsearch __P((res_state, const char *, int, int, u_char *, + int)); +int res_nquerydomain __P((res_state, const char *, const char *, + int, int, u_char *, int)); +int res_nmkquery __P((res_state, int, const char *, int, int, + const u_char *, int, const u_char *, + u_char *, int)); int res_nsend __P((res_state, const u_char *, int, u_char *, int)); int res_nsendsigned __P((res_state, const u_char *, int, ns_tsig_key *, u_char *, int)); int res_findzonecut __P((res_state, const char *, ns_class, int, char *, size_t, struct in_addr *, int)); int res_findzonecut2 __P((res_state, const char *, ns_class, int, char *, size_t, union res_sockaddr_union *, int)); void res_nclose __P((res_state)); int res_nopt __P((res_state, int, u_char *, int, int)); -void res_send_setqhook __P((res_send_qhook hook)); -void res_send_setrhook __P((res_send_rhook hook)); +void res_send_setqhook __P((res_send_qhook)); +void res_send_setrhook __P((res_send_rhook)); int __res_vinit __P((res_state, int)); void res_destroyservicelist __P((void)); -const char * res_servicename __P((u_int16_t port, const char *proto)); -const char * res_protocolname __P((int num)); +const char * res_servicename __P((u_int16_t, const char *)); +const char * res_protocolname __P((int)); void res_destroyprotolist __P((void)); void res_buildprotolist __P((void)); const char * res_get_nibblesuffix __P((res_state)); const char * res_get_nibblesuffix2 __P((res_state)); -const char * res_get_bitstringsuffix __P((res_state)); void res_ndestroy __P((res_state)); -u_int16_t res_nametoclass __P((const char *buf, int *success)); -u_int16_t res_nametotype __P((const char *buf, int *success)); +u_int16_t res_nametoclass __P((const char *, int *)); +u_int16_t res_nametotype __P((const char *, int *)); void res_setservers __P((res_state, const union res_sockaddr_union *, int)); int res_getservers __P((res_state, union res_sockaddr_union *, int)); __END_DECLS #endif /* !_RESOLV_H_ */ diff --git a/contrib/bind/lib/dst/bsafe_link.c b/contrib/bind/lib/dst/bsafe_link.c index 7c9e481e5df9..e1173c65677f 100644 --- a/contrib/bind/lib/dst/bsafe_link.c +++ b/contrib/bind/lib/dst/bsafe_link.c @@ -1,1121 +1,1123 @@ #if defined(BSAFE) || defined(DNSSAFE) -static const char rcsid[] = "$Header: /proj/cvs/isc/bind8/src/lib/dst/bsafe_link.c,v 1.15 2001/09/25 04:50:28 marka Exp $"; +static const char rcsid[] = "$Header: /proj/cvs/isc/bind8/src/lib/dst/bsafe_link.c,v 1.16 2002/12/03 05:26:49 marka Exp $"; /* * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. * * Permission to use, copy modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. */ /* * This file contains two components * 1. Interface to the BSAFE library to allow compilation of Bind * with TIS/DNSSEC when BSAFE is not available * all calls to BSAFE are contained inside this file. * 2. The glue to connvert RSA KEYS to and from external formats */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include "dst_internal.h" # ifdef __STDC__ # define PROTOTYPES 1 # else # define PROTOTYPES 0 # endif # ifdef BSAFE # include # include # else # include # include # include # endif #include "port_after.h" typedef struct bsafekey { char *rk_signer; B_KEY_OBJ rk_Private_Key; B_KEY_OBJ rk_Public_Key; } RSA_Key; #ifndef MAX_RSA_MODULUS_BITS #define MAX_RSA_MODULUS_BITS 4096 #define MAX_RSA_MODULUS_LEN (MAX_RSA_MODULUS_BITS/8) #define MAX_RSA_PRIME_LEN (MAX_RSA_MODULUS_LEN/2) #endif #define NULL_SURRENDER (A_SURRENDER_CTX *)NULL_PTR #define NULL_RANDOM (B_ALGORITHM_OBJ)NULL_PTR B_ALGORITHM_METHOD *CHOOSER[] = { &AM_MD5, &AM_MD5_RANDOM, &AM_RSA_KEY_GEN, &AM_RSA_ENCRYPT, &AM_RSA_DECRYPT, &AM_RSA_CRT_ENCRYPT, &AM_RSA_CRT_DECRYPT, (B_ALGORITHM_METHOD *) NULL_PTR }; static u_char pkcs1[] = { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 }; static int dst_bsafe_md5digest(const int mode, B_ALGORITHM_OBJ *digest_obj, const u_char *data, const int len, u_char *digest, const int digest_len); static int dst_bsafe_key_size(RSA_Key *r_key); static int dst_bsafe_sign(const int mode, DST_KEY *dkey, void **context, const u_char *data, const int len, u_char *signature, const int sig_len); static int dst_bsafe_verify(const int mode, DST_KEY *dkey, void **context, const u_char *data, const int len, const u_char *signature, const int sig_len); static int dst_bsafe_to_dns_key(const DST_KEY *in_key, u_char *out_str, const int out_len); static int dst_bsafe_from_dns_key(DST_KEY *s_key, const u_char *key, const int len); static int dst_bsafe_key_to_file_format(const DST_KEY *key, char *buff, const int buff_len); static int dst_bsafe_key_from_file_format(DST_KEY *d_key, const char *buff, const int buff_len); static int dst_bsafe_generate_keypair(DST_KEY *key, int exp); static int dst_bsafe_compare_keys(const DST_KEY *key1, const DST_KEY *key2); static void *dst_bsafe_free_key_structure(void *key); /* * dst_bsafe_init() Function to answer set up function pointers for * BSAFE/DNSSAFE related functions */ int dst_bsafe_init(void) { if (dst_t_func[KEY_RSA] != NULL) return (1); dst_t_func[KEY_RSA] = malloc(sizeof(struct dst_func)); if (dst_t_func[KEY_RSA] == NULL) return (0); memset(dst_t_func[KEY_RSA], 0, sizeof(struct dst_func)); dst_t_func[KEY_RSA]->sign = dst_bsafe_sign; dst_t_func[KEY_RSA]->verify = dst_bsafe_verify; dst_t_func[KEY_RSA]->compare = dst_bsafe_compare_keys; dst_t_func[KEY_RSA]->generate = dst_bsafe_generate_keypair; dst_t_func[KEY_RSA]->destroy = dst_bsafe_free_key_structure; dst_t_func[KEY_RSA]->from_dns_key = dst_bsafe_from_dns_key; dst_t_func[KEY_RSA]->to_dns_key = dst_bsafe_to_dns_key; dst_t_func[KEY_RSA]->from_file_fmt = dst_bsafe_key_from_file_format; dst_t_func[KEY_RSA]->to_file_fmt = dst_bsafe_key_to_file_format; return (1); } /* * dst_bsafe_sign * Call BSAFE signing functions to sign a block of data. * There are three steps to signing, INIT (initialize structures), * UPDATE (hash (more) data), FINAL (generate a signature). This * routine performs one or more of these steps. * Parameters * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. * dkey structure holds context for a sign done in multiple calls. * context the context to use for this computation * data data to be signed. * len length in bytes of data. * priv_key key to use for signing. * signature location to store signature. * sig_len size in bytes of signature field. * returns * N Success on SIG_MODE_FINAL = returns signature length in bytes * 0 Success on SIG_MODE_INIT and UPDATE * <0 Failure */ static int dst_bsafe_sign(const int mode, DST_KEY *dkey, void **context, const u_char *data, const int len, u_char *signature, const int sig_len) { u_int sign_len = 0; int status = 0; B_ALGORITHM_OBJ *md5_ctx = NULL; int w_bytes = 0; u_int u_bytes = 0; u_char work_area[NS_MD5RSA_MAX_SIZE]; if (mode & SIG_MODE_INIT) { md5_ctx = (B_ALGORITHM_OBJ *) malloc(sizeof(B_ALGORITHM_OBJ)); if ((status = B_CreateAlgorithmObject(md5_ctx))) return (-1); if ((status = B_SetAlgorithmInfo(*md5_ctx, AI_MD5, NULL))) return (-1); } else if (context) md5_ctx = (B_ALGORITHM_OBJ *) *context; if (md5_ctx == NULL) return (-1); w_bytes = dst_bsafe_md5digest(mode, md5_ctx, data, len,work_area, sizeof(work_area)); if (w_bytes < 0 || (mode & SIG_MODE_FINAL)) { B_DestroyAlgorithmObject(md5_ctx); SAFE_FREE(md5_ctx); if (w_bytes < 0) return (w_bytes); } if (mode & SIG_MODE_FINAL) { RSA_Key *key; int ret = 0; B_ALGORITHM_OBJ rsaEncryptor = (B_ALGORITHM_OBJ) NULL_PTR; if (dkey == NULL || dkey->dk_KEY_struct == NULL) return (-1); key = (RSA_Key *) dkey->dk_KEY_struct; if (key == NULL || key->rk_Private_Key == NULL) return (-1); if ((status = B_CreateAlgorithmObject(&rsaEncryptor))) return (SIGN_FINAL_FAILURE); if ((status = B_SetAlgorithmInfo(rsaEncryptor, AI_PKCS_RSAPrivate, NULL_PTR))) ret = SIGN_FINAL_FAILURE; if (ret == 0 && (status = B_EncryptInit(rsaEncryptor, key->rk_Private_Key, CHOOSER, NULL_SURRENDER))) ret = SIGN_FINAL_FAILURE; if (ret == 0 && (status = B_EncryptUpdate(rsaEncryptor, signature, &u_bytes, sig_len, pkcs1, sizeof(pkcs1), NULL_PTR, NULL_SURRENDER))) ret = SIGN_FINAL_FAILURE; if (ret == 0 && (status = B_EncryptUpdate(rsaEncryptor, signature, &u_bytes, sig_len, work_area, w_bytes, NULL_PTR, NULL_SURRENDER))) ret = SIGN_FINAL_FAILURE; if (ret == 0 && (status = B_EncryptFinal(rsaEncryptor, signature + u_bytes, &sign_len, sig_len - u_bytes, NULL_PTR, NULL_SURRENDER))) ret = SIGN_FINAL_FAILURE; B_DestroyAlgorithmObject(&rsaEncryptor); if (ret != 0) return (ret); } else { if (context == NULL) return (-1); *context = (void *) md5_ctx; } return (sign_len); } /* * Dst_bsafe_verify * Calls BSAFE verification routines. There are three steps to * verification, INIT (initialize structures), UPDATE (hash (more) data), * FINAL (generate a signature). This routine performs one or more of * these steps. * Parameters * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. * dkey structure holds context for a verify done in multiple calls. * context the context to use for this computation * data data signed. * len length in bytes of data. * pub_key key to use for verify. * signature signature. * sig_len length in bytes of signature. * returns * 0 Success * <0 Failure */ static int dst_bsafe_verify(const int mode, DST_KEY *dkey, void **context, const u_char *data, const int len, const u_char *signature, const int sig_len) { B_ALGORITHM_OBJ *md5_ctx = NULL; u_char digest[DST_HASH_SIZE]; u_char work_area[DST_HASH_SIZE + sizeof(pkcs1)]; int status = 0, w_bytes = 0; u_int u_bytes = 0; if (mode & SIG_MODE_INIT) { md5_ctx = (B_ALGORITHM_OBJ *) malloc(sizeof(B_ALGORITHM_OBJ)); if ((status = B_CreateAlgorithmObject(md5_ctx))) return (-1); if ((status = B_SetAlgorithmInfo(*md5_ctx, AI_MD5, NULL))) return (-1); } else if (context) md5_ctx = (B_ALGORITHM_OBJ *) *context; if (md5_ctx == NULL) return (-1); w_bytes = dst_bsafe_md5digest(mode, md5_ctx, data, len, digest, sizeof(digest)); if (w_bytes < 0 || (mode & SIG_MODE_FINAL)) { B_DestroyAlgorithmObject(md5_ctx); SAFE_FREE(md5_ctx); if (w_bytes < 0) return (-1); } if (mode & SIG_MODE_FINAL) { RSA_Key *key; int ret = 0; B_ALGORITHM_OBJ rsaEncryptor = (B_ALGORITHM_OBJ) NULL_PTR; if (dkey == NULL || dkey->dk_KEY_struct == NULL) return (-1); key = (RSA_Key *) dkey->dk_KEY_struct; if (key->rk_Public_Key == NULL) return (-2); if (rsaEncryptor == NULL_PTR) { if ((status = B_CreateAlgorithmObject(&rsaEncryptor))) ret = SIGN_FINAL_FAILURE; if (ret == 0 && (status = B_SetAlgorithmInfo(rsaEncryptor, AI_PKCS_RSAPublic, NULL_PTR))) ret = VERIFY_FINAL_FAILURE; } if (ret == 0 && (status = B_DecryptInit(rsaEncryptor, key->rk_Public_Key, CHOOSER, NULL_SURRENDER))) ret = VERIFY_FINAL_FAILURE; if (ret == 0 && (status = B_DecryptUpdate(rsaEncryptor, work_area, &u_bytes, 0, (const u_char *) signature, sig_len, NULL_PTR, NULL_SURRENDER))) ret = VERIFY_FINAL_FAILURE; if (ret == 0 && (status = B_DecryptFinal(rsaEncryptor, work_area + u_bytes, &u_bytes, sizeof(work_area) - u_bytes, NULL_PTR, NULL_SURRENDER))) ret = VERIFY_FINAL_FAILURE; B_DestroyAlgorithmObject(&rsaEncryptor); /* skip PKCS#1 header in output from Decrypt function */ if (ret) return (ret); ret = memcmp(digest, &work_area[sizeof(pkcs1)], w_bytes); if (ret == 0) return(0); else return(VERIFY_FINAL_FAILURE); } else { if (context == NULL) return (-1); *context = (void *) md5_ctx; } return (0); } /* * dst_bsafe_to_dns_key * Converts key from RSA to DNS distribution format * This function gets in a pointer to the public key and a work area * to write the key into. * Parameters * public KEY structure * out_str buffer to write encoded key into * out_len size of out_str * Return * N >= 0 length of encoded key * n < 0 error */ static int dst_bsafe_to_dns_key(const DST_KEY *in_key, u_char *out_str, const int out_len) { B_KEY_OBJ public; A_RSA_KEY *pub = NULL; u_char *op = out_str; int n = 0; if (in_key == NULL || in_key->dk_KEY_struct == NULL || out_len <= 0 || out_str == NULL) return (-1); public = (B_KEY_OBJ)((RSA_Key *) in_key->dk_KEY_struct)->rk_Public_Key; n = B_GetKeyInfo((POINTER *) &pub, public, KI_RSAPublic); if (n != 0) return (-1); if (pub->exponent.len < 256) { /* key exponent is <= 2040 bits */ if ((unsigned int)out_len < pub->exponent.len + 1) return (-1); *op++ = (u_int8_t) pub->exponent.len; } else { /* key exponent is > 2040 bits */ u_int16_t e = (u_int16_t) pub->exponent.len; if ((unsigned int)out_len < pub->exponent.len + 3) return (-1); *op++ = 0; /* 3 byte length field */ dst_s_put_int16(op, e); op += sizeof(e); n = 2; } n++; memcpy(op, pub->exponent.data, pub->exponent.len); op += pub->exponent.len; n += pub->exponent.len; if ((unsigned int)(out_len - n) >= pub->modulus.len) { /*copy exponent */ memcpy(op, pub->modulus.data, pub->modulus.len); n += pub->modulus.len; } else n = -1; return (n); } /* * dst_bsafe_from_dns_key * Converts from a DNS KEY RR format to an RSA KEY. * Parameters * len Length in bytes of DNS key * key DNS key * name Key name * s_key DST structure that will point to the RSA key this routine * will build. * Return * 0 The input key, s_key or name was null. * 1 Success */ static int dst_bsafe_from_dns_key(DST_KEY *s_key, const u_char *key, const int len) { int bytes; const u_char *key_ptr; RSA_Key *r_key; A_RSA_KEY *public; if (s_key == NULL || len < 0 || key == NULL) return (0); r_key = (RSA_Key *) s_key->dk_KEY_struct; if (r_key != NULL) /* do not reuse */ s_key->dk_func->destroy(r_key); if (len == 0) return (1); if ((r_key = (RSA_Key *) malloc(sizeof(RSA_Key))) == NULL) { EREPORT(("dst_bsafe_from_dns_key(): Memory allocation error 1")); return (0); } memset(r_key, 0, sizeof(RSA_Key)); s_key->dk_KEY_struct = (void *) r_key; r_key->rk_signer = strdup(s_key->dk_key_name); if (B_CreateKeyObject(&r_key->rk_Public_Key) != 0) { EREPORT(("dst_bsafe_from_dns_key(): Memory allocation error 3")); s_key->dk_func->destroy(r_key); return (0); } key_ptr = key; bytes = (int) *key_ptr++; /* length of exponent in bytes */ if (bytes == 0) { /* special case for long exponents */ bytes = (int) dst_s_get_int16(key_ptr); key_ptr += sizeof(u_int16_t); } if (bytes > MAX_RSA_MODULUS_LEN) { dst_bsafe_free_key_structure(r_key); return (-1); } if ((public = (A_RSA_KEY *) malloc(sizeof(A_RSA_KEY))) == NULL) return (0); memset(public, 0, sizeof(*public)); public->exponent.len = bytes; if ((public->exponent.data = (u_char *) malloc(bytes)) == NULL) return (0); memcpy(public->exponent.data, key_ptr, bytes); key_ptr += bytes; /* beginning of modulus */ bytes = len - bytes - 1; /* length of modulus */ if (bytes > MAX_RSA_MODULUS_LEN) { dst_bsafe_free_key_structure(r_key); return (-1); } public->modulus.len = bytes; if ((public->modulus.data = (u_char *) malloc(bytes)) == NULL) return (0); memcpy(public->modulus.data, key_ptr, bytes); B_SetKeyInfo(r_key->rk_Public_Key, KI_RSAPublic, (POINTER) public); s_key->dk_key_size = dst_bsafe_key_size(r_key); SAFE_FREE(public->modulus.data); SAFE_FREE(public->exponent.data); SAFE_FREE(public); return (1); } /* * dst_bsafe_key_to_file_format * Encodes an RSA Key into the portable file format. * Parameters * rkey RSA KEY structure * buff output buffer * buff_len size of output buffer * Return * 0 Failure - null input rkey * -1 Failure - not enough space in output area * N Success - Length of data returned in buff */ static int dst_bsafe_key_to_file_format(const DST_KEY *key, char *buff, const int buff_len) { char *bp; int len, b_len; B_KEY_OBJ rkey; A_PKCS_RSA_PRIVATE_KEY *private = NULL; if (key == NULL || key->dk_KEY_struct == NULL) /* no output */ return (0); if (buff == NULL || buff_len <= (int) strlen(key_file_fmt_str)) return (-1); /* no OR not enough space in output area */ rkey = (B_KEY_OBJ)((RSA_Key *) key->dk_KEY_struct)->rk_Private_Key; B_GetKeyInfo((POINTER *) &private, rkey, KI_PKCS_RSAPrivate); memset(buff, 0, buff_len); /* just in case */ /* write file header */ sprintf(buff, key_file_fmt_str, KEY_FILE_FORMAT, KEY_RSA, "RSA"); bp = strchr(buff, '\0'); b_len = buff_len - (bp - buff); if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Modulus: ", private->modulus.data, private->modulus.len)) <= 0) return (-1); bp += len; b_len -= len; if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "PublicExponent: ", private->publicExponent.data, private->publicExponent.len)) <= 0) return (-2); bp += len; b_len -= len; if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "PrivateExponent: ", private->privateExponent.data, private->privateExponent.len)) <= 0) return (-3); bp += len; b_len -= len; if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Prime1: ", private->prime[0].data, private->prime[0].len)) < 0) return (-4); bp += len; b_len -= len; if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Prime2: ", private->prime[1].data, private->prime[1].len)) < 0) return (-5); bp += len; b_len -= len; if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Exponent1: ", private->primeExponent[0].data, private->primeExponent[0].len)) < 0) return (-6); bp += len; b_len -= len; if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Exponent2: ", private->primeExponent[1].data, private->primeExponent[1].len)) < 0) return (-7); bp += len; b_len -= len; if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Coefficient: ", private->coefficient.data, private->coefficient.len)) < 0) return (-8); bp += len; b_len -= len; return (buff_len - b_len); } /* * dst_bsafe_key_from_file_format * Converts contents of a private key file into a private RSA key. * Parameters * RSA_Key structure to put key into * buff buffer containing the encoded key * buff_len the length of the buffer * Return * n >= 0 Foot print of the key converted * n < 0 Error in conversion */ static int dst_bsafe_key_from_file_format(DST_KEY *d_key, const char *buff, const int buff_len) { int status; char s[RAW_KEY_SIZE]; int len, s_len = sizeof(s); const char *p = buff; RSA_Key *b_key; A_RSA_KEY *public; A_PKCS_RSA_PRIVATE_KEY *private; if (d_key == NULL || buff == NULL || buff_len <= 0) return (-1); b_key = (RSA_Key *) malloc(sizeof(RSA_Key)); public = (A_RSA_KEY *) malloc(sizeof(A_RSA_KEY)); private = (A_PKCS_RSA_PRIVATE_KEY *) malloc(sizeof(A_PKCS_RSA_PRIVATE_KEY)); if (b_key == NULL || private == NULL || public == NULL) { SAFE_FREE(b_key); SAFE_FREE(public); SAFE_FREE(private); return (-2); } memset(b_key, 0, sizeof(*b_key)); memset(public, 0, sizeof(A_RSA_KEY)); memset(private, 0, sizeof(A_PKCS_RSA_PRIVATE_KEY)); d_key->dk_KEY_struct = (void *) b_key; if (!dst_s_verify_str(&p, "Modulus: ")) return (-3); memset(s, 0, s_len); if ((len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s, s_len)) == 0) return (-4); private->modulus.len = len; if ((private->modulus.data = malloc(len)) == NULL) return (-5); memcpy(private->modulus.data, s + s_len - len, len); while (*(++p) && p < (const char *) &buff[buff_len]) { if (dst_s_verify_str(&p, "PublicExponent: ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s, s_len))) return (-5); private->publicExponent.len = len; if ((private->publicExponent.data = malloc(len)) == NULL) return (-6); memcpy(private->publicExponent.data, s + s_len - len, len); } else if (dst_s_verify_str(&p, "PrivateExponent: ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s, s_len))) return (-6); private->privateExponent.len = len; if ((private->privateExponent.data = malloc(len)) == NULL) return (-7); memcpy(private->privateExponent.data, s + s_len - len, len); } else if (dst_s_verify_str(&p, "Prime1: ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s, MAX_RSA_PRIME_LEN))) return (-7); private->prime[0].len = len; if ((private->prime[0].data = malloc(len)) == NULL) return (-8); memcpy(private->prime[0].data, s + MAX_RSA_PRIME_LEN - len, len); } else if (dst_s_verify_str(&p, "Prime2: ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s, MAX_RSA_PRIME_LEN))) return (-8); private->prime[1].len = len; if ((private->prime[1].data = malloc(len)) == NULL) return (-9); memcpy(private->prime[1].data, s + MAX_RSA_PRIME_LEN - len, len); } else if (dst_s_verify_str(&p, "Exponent1: ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s, MAX_RSA_PRIME_LEN))) return (-9); private->primeExponent[0].len = len; if ((private->primeExponent[0].data = malloc(len)) == NULL) return (-10); memcpy(private->primeExponent[0].data, s + MAX_RSA_PRIME_LEN - len, len); } else if (dst_s_verify_str(&p, "Exponent2: ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s, MAX_RSA_PRIME_LEN))) return (-10); private->primeExponent[1].len = len; if ((private->primeExponent[1].data = malloc(len)) == NULL) return (-11); memcpy(private->primeExponent[1].data, s + MAX_RSA_PRIME_LEN - len, len); } else if (dst_s_verify_str(&p, "Coefficient: ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, (u_char *)s, MAX_RSA_PRIME_LEN))) return (-11); private->coefficient.len = len; if ((private->coefficient.data = malloc(len)) == NULL) return (-12); memcpy(private->coefficient.data, s + MAX_RSA_PRIME_LEN - len, len); } else { EREPORT(("Decode_RSAKey(): Bad keyword %s\n", p)); return (-12); } } /* while p */ public->modulus.len = private->modulus.len; if ((public->modulus.data = (u_char *) malloc(public->modulus.len)) == NULL) return (-13); memcpy(public->modulus.data, private->modulus.data, private->modulus.len); public->exponent.len = private->publicExponent.len; if ((public->exponent.data = (u_char *) malloc(public->exponent.len)) == NULL) return (-14); memcpy(public->exponent.data, private->publicExponent.data, private->publicExponent.len); status = B_CreateKeyObject(&(b_key->rk_Public_Key)); if (status) return (-1); status = B_SetKeyInfo(b_key->rk_Public_Key, KI_RSAPublic, (POINTER) public); if (status) return (-1); status = B_CreateKeyObject(&b_key->rk_Private_Key); if (status) return (-1); status = B_SetKeyInfo(b_key->rk_Private_Key, KI_PKCS_RSAPrivate, (POINTER) private); if (status) return (-1); d_key->dk_key_size = dst_bsafe_key_size(b_key); SAFE_FREE(private->modulus.data); SAFE_FREE(private->publicExponent.data); SAFE_FREE(private->privateExponent.data); SAFE_FREE(private->prime[0].data); SAFE_FREE(private->prime[1].data); SAFE_FREE(private->primeExponent[0].data); SAFE_FREE(private->primeExponent[1].data); SAFE_FREE(private->coefficient.data); SAFE_FREE(private); /* is this the right thing to do ??? XXXX */ SAFE_FREE(public->modulus.data); SAFE_FREE(public->exponent.data); SAFE_FREE(public); return (0); } /* * dst_bsafe_free_key_structure * Frees all dynamicly allocated structures in RSA_Key. */ static void * dst_bsafe_free_key_structure(void *key) { RSA_Key *r_key = (RSA_Key *) key; if (r_key != NULL) { if (r_key->rk_Private_Key) B_DestroyKeyObject(&r_key->rk_Private_Key); if (r_key->rk_Public_Key) B_DestroyKeyObject(&r_key->rk_Public_Key); SAFE_FREE2(r_key->rk_signer, strlen(r_key->rk_signer)); SAFE_FREE(r_key); } return (NULL); } /* * dst_bsafe_generate_keypair * Generates unique keys that are hard to predict. * Parameters * key generic Key structure * exp the public exponent * Return * 0 Failure * 1 Success */ static int dst_bsafe_generate_keypair(DST_KEY *key, int exp) { int i, status; B_KEY_OBJ private; B_KEY_OBJ public; B_ALGORITHM_OBJ keypairGenerator; B_ALGORITHM_OBJ randomAlgorithm; A_RSA_KEY_GEN_PARAMS keygenParams; char exponent[4]; int exponent_len; RSA_Key *rsa; POINTER randomSeed = NULL_PTR; int randomSeedLen; A_RSA_KEY *pk_access = NULL; if (key == NULL || key->dk_alg != KEY_RSA) return (0); if ((rsa = (RSA_Key *) malloc(sizeof(RSA_Key))) == NULL) { EREPORT(("dst_bsafe_generate_keypair: Memory allocation error 3")); return (0); } memset(rsa, 0, sizeof(*rsa)); if ((status = B_CreateAlgorithmObject(&keypairGenerator)) != 0) return (0); keygenParams.modulusBits = key->dk_key_size; /* exp = 0 or 1 are special (mean 3 or F4) */ if (exp == 0) exp = 3; else if (exp == 1) exp = 65537; /* Now encode the exponent and its length */ if (exp < 256) { exponent_len = 1; exponent[0] = exp; } else if (exp < (1 << 16)) { exponent_len = 2; exponent[0] = exp >> 8; exponent[1] = exp; } else if (exp < (1 << 24)) { exponent_len = 3; exponent[0] = exp >> 16; exponent[1] = exp >> 8; exponent[2] = exp; } else { exponent_len = 4; exponent[0] = exp >> 24; exponent[1] = exp >> 16; exponent[2] = exp >> 8; exponent[3] = exp; } if ((keygenParams.publicExponent.data = (u_char *) malloc(exponent_len)) == NULL) return (0); memcpy(keygenParams.publicExponent.data, exponent, exponent_len); keygenParams.publicExponent.len = exponent_len; if ((status = B_SetAlgorithmInfo (keypairGenerator, AI_RSAKeyGen, (POINTER) &keygenParams)) != 0) return (0); if ((status = B_GenerateInit(keypairGenerator, CHOOSER, NULL_SURRENDER)) != 0) return (0); if ((status = B_CreateKeyObject(&public)) != 0) return (0); if ((status = B_CreateKeyObject(&private)) != 0) return (0); if ((status = B_CreateAlgorithmObject(&randomAlgorithm)) != 0) return (0); if ((status = B_SetAlgorithmInfo(randomAlgorithm, AI_MD5Random, NULL_PTR)) != 0) return (0); if ((status = B_RandomInit(randomAlgorithm, CHOOSER, NULL_SURRENDER)) != 0) return (0); randomSeedLen = 256; if ((randomSeed = malloc(randomSeedLen)) == NULL) return (0); if ((status = (randomSeed == NULL_PTR)) != 0) return (0); /* gets random seed from /dev/random if present, generates random * values if it is not present. * first fill the buffer with semi random data * then fill as much as possible with good random data */ i = dst_random(DST_RAND_SEMI, randomSeedLen, randomSeed); i += dst_random(DST_RAND_KEY, randomSeedLen, randomSeed); if (i <= randomSeedLen) { SAFE_FREE(rsa); return(0); } if ((status = B_RandomUpdate(randomAlgorithm, randomSeed, randomSeedLen, NULL_SURRENDER)) != 0) { SAFE_FREE(rsa); return (0); } SAFE_FREE2(randomSeed, randomSeedLen); if ((status = B_GenerateKeypair(keypairGenerator, public, private, randomAlgorithm, NULL_SURRENDER)) != 0) { SAFE_FREE(rsa); return (0); } rsa->rk_signer = strdup(key->dk_key_name); rsa->rk_Private_Key = private; rsa->rk_Public_Key = public; key->dk_KEY_struct = (void *) rsa; B_GetKeyInfo((POINTER *) &pk_access, public, KI_RSAPublic); return (1); } /************************************************************************** * dst_bsafe_compare_keys * Compare two keys for equality. * Return * 0 The keys are equal * NON-ZERO The keys are not equal */ static int dst_s_bsafe_itemcmp(ITEM i1, ITEM i2) { if (i1.len != i2.len || memcmp (i1.data, i2.data, i1.len)) return (1); else return (0); } static int dst_bsafe_compare_keys(const DST_KEY *key1, const DST_KEY *key2) { int status, s1 = 0, s2 = 0; RSA_Key *rkey1 = (RSA_Key *) key1->dk_KEY_struct; RSA_Key *rkey2 = (RSA_Key *) key2->dk_KEY_struct; A_RSA_KEY *public1 = NULL, *public2 = NULL; A_PKCS_RSA_PRIVATE_KEY *p1 = NULL, *p2 = NULL; if (rkey1 == NULL && rkey2 == NULL) return(0); else if (rkey1 == NULL) return (1); else if (rkey2 == NULL) return (2); if (rkey1->rk_Public_Key) B_GetKeyInfo((POINTER *) &public1, rkey1->rk_Public_Key, KI_RSAPublic); if (rkey2->rk_Public_Key) B_GetKeyInfo((POINTER *) &public2, rkey2->rk_Public_Key, KI_RSAPublic); if (public1 == NULL && public2 == NULL) return (0); else if (public1 == NULL || public2 == NULL) return (1); status = dst_s_bsafe_itemcmp(public1->modulus, public2->modulus) || dst_s_bsafe_itemcmp(public1->exponent, public2->exponent); if (status) return (status); if (rkey1->rk_Private_Key == NULL || rkey2->rk_Private_Key == NULL) /* if neither or only one is private key consider identical */ return (status); if (rkey1->rk_Private_Key) s1 = B_GetKeyInfo((POINTER *) &p1, rkey1->rk_Private_Key, KI_PKCS_RSAPrivate); if (rkey2->rk_Private_Key) s2 = B_GetKeyInfo((POINTER *) &p2, rkey2->rk_Private_Key, KI_PKCS_RSAPrivate); if (p1 == NULL || p2 == NULL) return (0); status = dst_s_bsafe_itemcmp(p1->modulus, p2->modulus) || dst_s_bsafe_itemcmp (p1->publicExponent, p2->publicExponent) || dst_s_bsafe_itemcmp (p1->privateExponent, p2->privateExponent) || dst_s_bsafe_itemcmp (p1->prime[0], p2->prime[0]) || dst_s_bsafe_itemcmp (p1->prime[1], p2->prime[1]) || dst_s_bsafe_itemcmp (p1->primeExponent[0], p2->primeExponent[0])|| dst_s_bsafe_itemcmp (p1->primeExponent[1], p2->primeExponent[1])|| dst_s_bsafe_itemcmp (p1->coefficient, p2->coefficient); return (status); } /* * dst_bsafe_key_size() * Function to calculate how the size of the key in bits */ static int dst_bsafe_key_size(RSA_Key *r_key) { int size; A_PKCS_RSA_PRIVATE_KEY *private = NULL; if (r_key == NULL) return (-1); if (r_key->rk_Private_Key) B_GetKeyInfo((POINTER *) &private, r_key->rk_Private_Key, KI_PKCS_RSAPrivate); else if (r_key->rk_Public_Key) B_GetKeyInfo((POINTER *) &private, r_key->rk_Public_Key, KI_RSAPublic); size = dst_s_calculate_bits(private->modulus.data, private->modulus.len * 8); return (size); } /* * dst_bsafe_md5digest(): function to digest data using MD5 digest function * if needed */ static int dst_bsafe_md5digest(const int mode, B_ALGORITHM_OBJ *digest_obj, const u_char *data, const int len, u_char *digest, const int digest_len) { int status = 0; u_int work_size = 0; if (digest_obj == NULL || *digest_obj == NULL) { printf("NO digest obj\n"); exit(33); } if ((mode & SIG_MODE_INIT) && (status = B_DigestInit(*digest_obj, (B_KEY_OBJ) NULL, CHOOSER, NULL_SURRENDER))) return (SIGN_INIT_FAILURE); if ((mode & SIG_MODE_UPDATE) && data && (len > 0) && (status = B_DigestUpdate(*digest_obj, data, len, NULL_SURRENDER))) return (SIGN_UPDATE_FAILURE); if (mode & SIG_MODE_FINAL) { if (digest == NULL || (status = B_DigestFinal(*digest_obj, digest, &work_size, digest_len, NULL_SURRENDER))) return (SIGN_FINAL_FAILURE); return (work_size); } return (0); } /* * just use the standard memory functions for bsafe */ void T_free(POINTER block) { free(block); } POINTER T_malloc(unsigned int len) { return (malloc(len)); } int T_memcmp(CPOINTER firstBlock, CPOINTER secondBlock, unsigned int len) { return (memcmp(firstBlock, secondBlock, len)); } void T_memcpy(POINTER output, CPOINTER input, unsigned int len) { memcpy(output, input, len); } void T_memmove(POINTER output, POINTER input, unsigned int len) { memmove(output, input, len); } void T_memset(POINTER output, int value, unsigned int len) { memset(output, value, len); } POINTER T_realloc(POINTER block, unsigned int len) { return (realloc(block, len)); } #else /* BSAFE NOT available */ +#define dst_bsafe_init __dst_bsafe_init + int dst_bsafe_init() { return (0); } #endif /* BSAFE */ diff --git a/contrib/bind/lib/dst/cylink_link.c b/contrib/bind/lib/dst/cylink_link.c index 6c2bdaede0b5..5d2207454d85 100644 --- a/contrib/bind/lib/dst/cylink_link.c +++ b/contrib/bind/lib/dst/cylink_link.c @@ -1,670 +1,672 @@ #ifdef CYLINK_DSS -static const char rcsid[] = "$Header: /proj/cvs/isc/bind8/src/lib/dst/cylink_link.c,v 1.9 2001/05/29 05:48:05 marka Exp $"; +static const char rcsid[] = "$Header: /proj/cvs/isc/bind8/src/lib/dst/cylink_link.c,v 1.10 2002/12/03 05:26:49 marka Exp $"; /* * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. * * Permission to use, copy modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. */ /* * This file contains two components * 1. Interface to the CYLINK library to allow compilation of Bind * with TIS/DNSSEC when CYLINK is not available * all calls to CYLINK are contained inside this file. * 2. The glue to connvert DSA KEYS to and from external formats */ #include "port_before.h" #include #include #include #include #include #include #include #include #include "dst_internal.h" #include #include "port_after.h" typedef struct cylinkkey { char *dk_signer; uchar *dk_p; uchar *dk_q; uchar *dk_g; uchar *dk_x; uchar *dk_y; ushort dk_p_bytes; } DSA_Key; #define NULL_PRIV_KEY(k)(k == NULL || k->dk_p == NULL || k->dk_q == NULL || \ k->dk_g == NULL || k->dk_x == NULL) #define NULL_PUB_KEY(k)(k == NULL || k->dk_p == NULL || k->dk_q == NULL || \ k->dk_g == NULL || k->dk_y == NULL) static int dst_cylink_sign(const int mode, DST_KEY *dkey, void **context, const u_char *data, const int len, u_char *signature, const int sig_len); static int dst_cylink_verify(const int mode, DST_KEY *dkey, void **context, const u_char *data, const int len, const u_char *signature, const int sig_len); static int dst_cylink_to_dns_key(const DST_KEY *in_key, u_char *out_str, const int out_len); static int dst_cylink_from_dns_key(DST_KEY *s_key, const u_char *key, const int len); static int dst_cylink_key_to_file_format(const DST_KEY *key, char *buff, const int buff_len); static int dst_cylink_key_from_file_format(DST_KEY *d_key, const char *buff, const int buff_len); static void *dst_cylink_free_key_structure(void *key); static int dst_cylink_generate_keypair(DST_KEY *key, int exp); static int dst_cylink_compare_keys(const DST_KEY *key1, const DST_KEY *key2); static void *memcpyend(void *dest, const void *src, size_t n, size_t size); /* * dst_cylink_init() Function to answer set up function pointers for * CYLINK related functions */ int dst_cylink_init() { if (dst_t_func[KEY_DSA] != NULL) return (1); dst_t_func[KEY_DSA] = malloc(sizeof(struct dst_func)); if (dst_t_func[KEY_DSA] == NULL) return (0); memset(dst_t_func[KEY_DSA], 0, sizeof(struct dst_func)); dst_t_func[KEY_DSA]->sign = dst_cylink_sign; dst_t_func[KEY_DSA]->verify = dst_cylink_verify; dst_t_func[KEY_DSA]->compare = dst_cylink_compare_keys; dst_t_func[KEY_DSA]->generate = dst_cylink_generate_keypair; dst_t_func[KEY_DSA]->destroy = dst_cylink_free_key_structure; dst_t_func[KEY_DSA]->from_dns_key = dst_cylink_from_dns_key; dst_t_func[KEY_DSA]->to_dns_key = dst_cylink_to_dns_key; dst_t_func[KEY_DSA]->from_file_fmt = dst_cylink_key_from_file_format; dst_t_func[KEY_DSA]->to_file_fmt = dst_cylink_key_to_file_format; SetDataOrder(1); return (1); } /* * dst_cylink_sign * Call CYLINK signing functions to sign a block of data. * There are three steps to signing, INIT (initialize structures), * UPDATE (hash (more) data), FINAL (generate a signature). This * routine performs one or more of these steps. * Parameters * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. * algobj structure holds context for a sign done in multiple calls. * context the context to use for this computation * data data to be signed. * len length in bytes of data. * priv_key key to use for signing. * signature location to store signature. * sig_len size in bytes of signature field. * returns * N Success on SIG_MODE_FINAL = returns signature length in bytes * N is 41 for DNS * 0 Success on SIG_MODE_INIT and UPDATE * <0 Failure */ static int dst_cylink_sign(const int mode, DST_KEY *dkey, void **context, const u_char *data, const int len, u_char *signature, const int sig_len) { int sign_len = 0; int status; SHA_context *ctx = NULL; if (mode & SIG_MODE_INIT) ctx = (SHA_context *) malloc(sizeof(SHA_context)); else if (context) ctx = (SHA_context *) *context; if (ctx == NULL) return (-1); if (mode & SIG_MODE_INIT) SHAInit(ctx); if ((mode & SIG_MODE_UPDATE) && (data && len > 0)) { status = SHAUpdate(ctx, data, len); if (status != SUCCESS) return (SIGN_UPDATE_FAILURE); } if (mode & SIG_MODE_FINAL) { DSA_Key *key; uchar digest[SHA_LENGTH]; uchar rand[SHA_LENGTH]; uchar r[SHA_LENGTH], s[SHA_LENGTH]; if (signature == NULL || sig_len < 2 * SHA_LENGTH) return (SIGN_FINAL_FAILURE); if ((status = SHAFinal(ctx, digest)) != SUCCESS) return (SIGN_FINAL_FAILURE); SAFE_FREE(ctx); if (dkey == NULL || dkey->dk_KEY_struct == NULL) return (-1); key = (DSA_Key *) dkey->dk_KEY_struct; if (NULL_PRIV_KEY(key)) return (-2); dst_random(DST_RAND_STD, sizeof(rand), rand); status = GenDSSSignature(key->dk_p_bytes, key->dk_p, key->dk_q, key->dk_g, key->dk_x, rand, r, s, digest); if (status != SUCCESS) return (SIGN_FINAL_FAILURE); *signature = (dkey->dk_key_size - 512)/64; sign_len = 1; memcpy(signature + sign_len, r, SHA_LENGTH); sign_len += SHA_LENGTH; memcpy(signature + sign_len, s, SHA_LENGTH); sign_len += SHA_LENGTH; } else { if (context == NULL) return (-1); *context = (void *) ctx; } return (sign_len); } /* * Dst_cylink_verify * Calls CYLINK verification routines. There are three steps to * verification, INIT (initialize structures), UPDATE (hash (more) data), * FINAL (generate a signature). This routine performs one or more of * these steps. * Parameters * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. * dkey structure holds context for a verify done in multiple calls. * context algorithm specific context for the current context processing * data data signed. * len length in bytes of data. * pub_key key to use for verify. * signature signature. * sig_len length in bytes of signature. * returns * 0 Success * <0 Failure */ static int dst_cylink_verify(const int mode, DST_KEY *dkey, void **context, const u_char *data, const int len, const u_char *signature, const int sig_len) { int status; SHA_context *ctx = NULL; if (mode & SIG_MODE_INIT) ctx = (SHA_context *) malloc(sizeof(SHA_context)); else if (context) ctx = (SHA_context *) *context; if (ctx == NULL) return (-1); if (mode & SIG_MODE_INIT) SHAInit(ctx); if ((mode & SIG_MODE_UPDATE) && (data && len > 0)) { status = SHAUpdate(ctx, data, len); if (status != SUCCESS) return (VERIFY_UPDATE_FAILURE); } if (mode & SIG_MODE_FINAL) { DSA_Key *key; uchar digest[SHA_LENGTH]; uchar r[SHA_LENGTH], s[SHA_LENGTH]; if (dkey == NULL || dkey->dk_KEY_struct == NULL) return (-1); key = (DSA_Key *) dkey->dk_KEY_struct; if (NULL_PUB_KEY(key)) return (-2); if (signature == NULL || sig_len != (2 * SHA_LENGTH +1)) return (SIGN_FINAL_FAILURE); status = SHAFinal(ctx, digest); SAFE_FREE(ctx); if (status != SUCCESS) return (SIGN_FINAL_FAILURE); if (((int)*signature) != ((key->dk_p_bytes -64)/8)) return(VERIFY_FINAL_FAILURE); memcpy(r, signature +1, SHA_LENGTH); memcpy(s, signature + SHA_LENGTH +1, SHA_LENGTH); status = VerDSSSignature(key->dk_p_bytes, key->dk_p, key->dk_q, key->dk_g, key->dk_y, r, s, digest); if (status != SUCCESS) return (VERIFY_FINAL_FAILURE); } else { if (context == NULL) return (-1); *context = (void *) ctx; } return (0); } /* * dst_cylink_to_dns_key * Converts key from DSA to DNS distribution format * This function gets in a pointer to the public key and a work area * to write the key into. * Parameters * public KEY structure * out_str buffer to write encoded key into * out_len size of out_str * Return * N >= 0 length of encoded key * n < 0 error */ static int dst_cylink_to_dns_key(const DST_KEY *in_key, u_char *out_str, const int out_len) { u_char *op = out_str; int t; DSA_Key *key; if (in_key == NULL || in_key->dk_KEY_struct == NULL || out_len <= 0 || out_str == NULL) return (-1); key = (DSA_Key *) in_key->dk_KEY_struct; t = (key->dk_p_bytes - 64) / 8; *op++ = t; memcpy(op, key->dk_q, SHA_LENGTH); op += SHA_LENGTH; memcpy(op, key->dk_p, key->dk_p_bytes); op += key->dk_p_bytes; memcpy(op, key->dk_g, key->dk_p_bytes); op += key->dk_p_bytes; memcpy(op, key->dk_y, key->dk_p_bytes); op += key->dk_p_bytes; return (op - out_str); } /* * dst_cylink_from_dns_key * Converts from a DNS KEY RR format to an RSA KEY. * Parameters * len Length in bytes of DNS key * key DNS key * name Key name * s_key DST structure that will point to the RSA key this routine * will build. * Return * 0 The input key, s_key or name was null. * 1 Success */ static int dst_cylink_from_dns_key(DST_KEY *s_key, const u_char *key, const int len) { int t; const u_char *key_ptr = key; DSA_Key *d_key; if (s_key == NULL || len < 0 || key == NULL) return (0); if (len == 0) /* process null key */ return (1); if (key_ptr == NULL) return (0); t = (int) *key_ptr++; /* length of exponent in bytes */ if ((3 * (t * 8 + 64) + SHA_LENGTH + 1) != len) return (0); if ((d_key = (DSA_Key *) malloc(sizeof(DSA_Key))) == NULL) { EREPORT(("dst_cylink_from_dns_key(): Memory allocation error 1")); return (0); } memset(d_key, 0, sizeof(DSA_Key)); s_key->dk_KEY_struct = (void *) d_key; d_key->dk_signer = strdup(s_key->dk_key_name); d_key->dk_p_bytes = 64 + 8 * t; if ((d_key->dk_q = (uchar *) malloc(SHA_LENGTH)) == NULL) return (0); memcpy(d_key->dk_q, key_ptr, SHA_LENGTH); key_ptr += SHA_LENGTH; if ((d_key->dk_p = (uchar *) malloc(d_key->dk_p_bytes)) == NULL) return (0); memcpy(d_key->dk_p, key_ptr, d_key->dk_p_bytes); key_ptr += d_key->dk_p_bytes; if ((d_key->dk_g = (uchar *) malloc(d_key->dk_p_bytes)) == NULL) return (0); memcpy(d_key->dk_g, key_ptr, d_key->dk_p_bytes); key_ptr += d_key->dk_p_bytes; if ((d_key->dk_y = (uchar *) malloc(d_key->dk_p_bytes)) == NULL) return (0); memcpy(d_key->dk_y, key_ptr, d_key->dk_p_bytes); key_ptr += d_key->dk_p_bytes; s_key->dk_key_size = d_key->dk_p_bytes * 8; return (1); } /************************************************************************** * dst_cylink_key_to_file_format * Encodes an DSA Key into the portable file format. * Parameters * key DSA KEY structure * buff output buffer * buff_len size of output buffer * Return * 0 Failure - null input rkey * -1 Failure - not enough space in output area * N Success - Length of data returned in buff */ static int dst_cylink_key_to_file_format(const DST_KEY *key, char *buff, const int buff_len) { char *bp; int len, b_len; DSA_Key *dkey; u_char num[256]; /* More than long enough for DSA keys */ if (key == NULL || key->dk_KEY_struct == NULL) /* no output */ return (0); if (buff == NULL || buff_len <= (int) strlen(key_file_fmt_str)) return (-1); /* no OR not enough space in output area */ dkey = (DSA_Key *) key->dk_KEY_struct; memset(buff, 0, buff_len); /* just in case */ /* write file header */ sprintf(buff, key_file_fmt_str, KEY_FILE_FORMAT, KEY_DSA, "DSA"); bp = (char *) strchr(buff, '\0'); b_len = buff_len - (bp - buff); memcpy(num, dkey->dk_p, dkey->dk_p_bytes); if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Prime(p): ", num, dkey->dk_p_bytes)) <= 0) return (-1); bp = (char *) strchr(buff, '\0'); b_len = buff_len - (bp - buff); memcpy(num, dkey->dk_q, dkey->dk_p_bytes); if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Subprime(q): ", num, SHA_LENGTH)) <= 0) return (-2); bp = (char *) strchr(buff, '\0'); b_len = buff_len - (bp - buff); memcpy(num, dkey->dk_g, dkey->dk_p_bytes); if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Base(g): ", num, dkey->dk_p_bytes)) <= 0) return (-3); bp = (char *) strchr(buff, '\0'); b_len = buff_len - (bp - buff); memcpy(num, dkey->dk_x, dkey->dk_p_bytes); if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Private_value(x): ", num, SHA_LENGTH)) <= 0) return (-4); bp = (char *) strchr(buff, '\0'); b_len = buff_len - (bp - buff); memcpy(num, dkey->dk_y, dkey->dk_p_bytes); if ((len = dst_s_conv_bignum_u8_to_b64(bp, b_len, "Public_value(y): ", num, dkey->dk_p_bytes)) <= 0) return (-4); bp += len; b_len -= len; return (buff_len - b_len); } /************************************************************************** * dst_cylink_key_from_file_format * Converts contents of a private key file into a private DSA key. * Parameters * DSA_Key structure to put key into * buff buffer containing the encoded key * buff_len the length of the buffer * Return * n >= 0 Foot print of the key converted * n < 0 Error in conversion */ static int dst_cylink_key_from_file_format(DST_KEY *d_key, const char *buff, const int buff_len) { u_char s[DSS_LENGTH_MAX]; int len, s_len = sizeof(s); const char *p = buff; DSA_Key *dsa_key; if (d_key == NULL || buff == NULL || buff_len <= 0) return (-1); dsa_key = (DSA_Key *) malloc(sizeof(DSA_Key)); if (dsa_key == NULL) { return (-2); } memset(dsa_key, 0, sizeof(*dsa_key)); d_key->dk_KEY_struct = (void *) dsa_key; if (!dst_s_verify_str(&p, "Prime(p): ")) return (-3); memset(s, 0, s_len); if ((len = dst_s_conv_bignum_b64_to_u8(&p, s, s_len)) == 0) return (-4); dsa_key->dk_p_bytes = len; if ((dsa_key->dk_p = malloc(len)) == NULL) return (-5); memcpy(dsa_key->dk_p, s + s_len - len, len); while (*++p && p < (const char *) &buff[buff_len]) { if (dst_s_verify_str(&p, "Subprime(q): ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, s, s_len))) return (-6); if ((dsa_key->dk_q = malloc(SHA_LENGTH)) == NULL) return (-7); memcpyend(dsa_key->dk_q, s + s_len - len, len, SHA_LENGTH); } else if (dst_s_verify_str(&p, "Base(g): ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, s, s_len))) return (-8); if ((dsa_key->dk_g = malloc(dsa_key->dk_p_bytes)) == NULL) return (-9); memcpyend(dsa_key->dk_g, s + s_len - len, len, dsa_key->dk_p_bytes); } else if (dst_s_verify_str(&p, "Private_value(x): ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, s, s_len))) return (-10); if ((dsa_key->dk_x = malloc(SHA_LENGTH)) == NULL) return (-11); memcpyend(dsa_key->dk_x, s + s_len - len, len, SHA_LENGTH); } else if (dst_s_verify_str(&p, "Public_value(y): ")) { if (!(len = dst_s_conv_bignum_b64_to_u8(&p, s, s_len))) return (-10); if ((dsa_key->dk_y = malloc(dsa_key->dk_p_bytes)) == NULL) return (-11); memcpyend(dsa_key->dk_y, s + s_len - len, len, dsa_key->dk_p_bytes); } else { EREPORT(("Decode_DSAKey(): Bad keyword %s\n", p)); return (-12); } } /* while p */ d_key->dk_key_size = dsa_key->dk_p_bytes * 8; return (0); } /************************************************************************** * dst_cylink_free_key_structure * Frees all dynamicly allocated structures in DSA_Key. */ static void * dst_cylink_free_key_structure(void *key) { DSA_Key *d_key = (DSA_Key *) key; if (d_key != NULL) { SAFE_FREE(d_key->dk_signer); SAFE_FREE(d_key->dk_p); SAFE_FREE(d_key->dk_q); SAFE_FREE(d_key->dk_g); SAFE_FREE(d_key->dk_x); SAFE_FREE(d_key->dk_y); SAFE_FREE(d_key); } return (NULL); } /************************************************************************** * dst_cylink_generate_keypair * Generates unique keys that are hard to predict. * Parameters * key generic Key structure * exp the public exponent * Return * 0 Failure * 1 Success */ static int dst_cylink_generate_keypair(DST_KEY *key, int nothing) { int status, n; DSA_Key *dsa; u_char rand[SHA_LENGTH]; UNUSED(nothing); if (key == NULL || key->dk_alg != KEY_DSA) return (0); if ((dsa = (DSA_Key *) malloc(sizeof(DSA_Key))) == NULL) { EREPORT(("dst_cylink_generate_keypair: Memory allocation error 3")); return (0); } memset(dsa, 0, sizeof(*dsa)); dsa->dk_p_bytes = key->dk_key_size / 8; dsa->dk_p = (uchar *) malloc(dsa->dk_p_bytes); dsa->dk_q = (uchar *) malloc(SHA_LENGTH); dsa->dk_g = (uchar *) malloc(dsa->dk_p_bytes); dsa->dk_x = (uchar *) malloc(SHA_LENGTH); dsa->dk_y = (uchar *) malloc(dsa->dk_p_bytes); if (!dsa->dk_p || !dsa->dk_q || !dsa->dk_g || !dsa->dk_x || !dsa->dk_y) { EREPORT(("dst_cylink_generate_keypair: Memory allocation error 4")); return (0); } n = dst_random(DST_RAND_KEY, sizeof(rand), rand); if (n != sizeof(rand)) return (0); status = GenDSSParameters(dsa->dk_p_bytes, dsa->dk_p, dsa->dk_q, dsa->dk_g, rand, NULL); if (status != SUCCESS) return (0); status = GenDSSKey(dsa->dk_p_bytes, dsa->dk_p, dsa->dk_q, dsa->dk_g, dsa->dk_x, dsa->dk_y, rand); if (status != SUCCESS) return (0); memset(rand, 0, sizeof(rand)); key->dk_KEY_struct = (void *) dsa; return (1); } /* * dst_cylink_compare_keys * Compare two keys for equality. * Return * 0 The keys are equal * NON-ZERO The keys are not equal */ static int dst_cylink_compare_keys(const DST_KEY *key1, const DST_KEY *key2) { int status; DSA_Key *dkey1 = (DSA_Key *) key1->dk_KEY_struct; DSA_Key *dkey2 = (DSA_Key *) key2->dk_KEY_struct; if (dkey1 == NULL && dkey2 == NULL) return (0); else if (dkey1 == NULL) return (2); else if (dkey2 == NULL) return(1); if (dkey1->dk_p_bytes != dkey2->dk_p_bytes) return (201); status = memcmp(dkey1->dk_p, dkey2->dk_p, dkey1->dk_p_bytes) || memcmp(dkey1->dk_q, dkey2->dk_q, SHA_LENGTH) || memcmp(dkey1->dk_g, dkey2->dk_g, dkey1->dk_p_bytes) || memcmp(dkey1->dk_y, dkey2->dk_y, dkey1->dk_p_bytes); if (status) return (status); if (dkey1->dk_x || dkey2->dk_x) { if (dkey1->dk_x == NULL || dkey2->dk_x == NULL) return (202); return (memcmp(dkey1->dk_x, dkey2->dk_x, dkey1->dk_p_bytes)); } else return (0); } static void * memcpyend(void *dest, const void *src, size_t n, size_t size) { if (n < size) memset(dest, 0, size - n); memcpy((char *)dest + size - n, src, n); return dest; } #else +#define dst_cylink_init __dst_cylink_init + int dst_cylink_init() { return (0); } #endif /* CYLINK */ diff --git a/contrib/bind/lib/dst/dst_api.c b/contrib/bind/lib/dst/dst_api.c index cc2a7e4814cc..e5f8cdb6faa2 100644 --- a/contrib/bind/lib/dst/dst_api.c +++ b/contrib/bind/lib/dst/dst_api.c @@ -1,1093 +1,1092 @@ #ifndef LINT -static const char rcsid[] = "$Header: /proj/cvs/isc/bind8/src/lib/dst/dst_api.c,v 1.20 2001/07/26 01:20:08 marka Exp $"; +static const char rcsid[] = "$Header: /proj/cvs/isc/bind8/src/lib/dst/dst_api.c,v 1.21 2002/06/28 06:58:19 marka Exp $"; #endif /* * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. * * Permission to use, copy modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. */ /* * This file contains the interface between the DST API and the crypto API. * This is the only file that needs to be changed if the crypto system is * changed. Exported functions are: * void dst_init() Initialize the toolkit * int dst_check_algorithm() Function to determines if alg is suppored. * int dst_compare_keys() Function to compare two keys for equality. * int dst_sign_data() Incremental signing routine. * int dst_verify_data() Incremental verify routine. * int dst_generate_key() Function to generate new KEY * DST_KEY *dst_read_key() Function to retrieve private/public KEY. * void dst_write_key() Function to write out a key. * DST_KEY *dst_dnskey_to_key() Function to convert DNS KEY RR to a DST * KEY structure. * int dst_key_to_dnskey() Function to return a public key in DNS * format binary * DST_KEY *dst_buffer_to_key() Converst a data in buffer to KEY * int *dst_key_to_buffer() Writes out DST_KEY key matterial in buffer * void dst_free_key() Releases all memory referenced by key structure */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dst_internal.h" #include "port_after.h" /* static variables */ static int done_init = 0; dst_func *dst_t_func[DST_MAX_ALGS]; const char *key_file_fmt_str = "Private-key-format: v%s\nAlgorithm: %d (%s)\n"; const char *dst_path = ""; /* internal I/O functions */ static DST_KEY *dst_s_read_public_key(const char *in_name, const u_int16_t in_id, int in_alg); static int dst_s_read_private_key_file(char *name, DST_KEY *pk_key, u_int16_t in_id, int in_alg); static int dst_s_write_public_key(const DST_KEY *key); static int dst_s_write_private_key(const DST_KEY *key); /* internal function to set up data structure */ static DST_KEY *dst_s_get_key_struct(const char *name, const int alg, const int flags, const int protocol, const int bits); /* * dst_init * This function initializes the Digital Signature Toolkit. * Right now, it just checks the DSTKEYPATH environment variable. * Parameters * none * Returns * none */ void dst_init() { char *s; int len; if (done_init != 0) return; done_init = 1; s = getenv("DSTKEYPATH"); len = 0; if (s) { struct stat statbuf; len = strlen(s); if (len > PATH_MAX) { EREPORT(("%s is longer than %d characters, ignoring\n", s, PATH_MAX)); } else if (stat(s, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) { EREPORT(("%s is not a valid directory\n", s)); } else { char *tmp; tmp = (char *) malloc(len + 2); memcpy(tmp, s, len + 1); if (tmp[strlen(tmp) - 1] != '/') { tmp[strlen(tmp) + 1] = 0; tmp[strlen(tmp)] = '/'; } dst_path = tmp; } } memset(dst_t_func, 0, sizeof(dst_t_func)); /* first one is selected */ dst_bsafe_init(); dst_rsaref_init(); dst_hmac_md5_init(); dst_eay_dss_init(); dst_cylink_init(); } /* * dst_check_algorithm * This function determines if the crypto system for the specified * algorithm is present. * Parameters * alg 1 KEY_RSA * 3 KEY_DSA * 157 KEY_HMAC_MD5 * future algorithms TBD and registered with IANA. * Returns * 1 - The algorithm is available. * 0 - The algorithm is not available. */ int dst_check_algorithm(const int alg) { return (dst_t_func[alg] != NULL); } /* * dst_s_get_key_struct * This function allocates key structure and fills in some of the * fields of the structure. * Parameters: * name: the name of the key * alg: the algorithm number * flags: the dns flags of the key * protocol: the dns protocol of the key * bits: the size of the key * Returns: * NULL if error * valid pointer otherwise */ static DST_KEY * dst_s_get_key_struct(const char *name, const int alg, const int flags, const int protocol, const int bits) { DST_KEY *new_key = NULL; if (dst_check_algorithm(alg)) /* make sure alg is available */ new_key = (DST_KEY *) malloc(sizeof(*new_key)); if (new_key == NULL) return (NULL); memset(new_key, 0, sizeof(*new_key)); new_key->dk_key_name = strdup(name); new_key->dk_alg = alg; new_key->dk_flags = flags; new_key->dk_proto = protocol; new_key->dk_KEY_struct = NULL; new_key->dk_key_size = bits; new_key->dk_func = dst_t_func[alg]; return (new_key); } /* * dst_compare_keys * Compares two keys for equality. * Parameters * key1, key2 Two keys to be compared. * Returns * 0 The keys are equal. * non-zero The keys are not equal. */ int dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2) { if (key1 == key2) return (0); if (key1 == NULL || key2 == NULL) return (4); if (key1->dk_alg != key2->dk_alg) return (1); if (key1->dk_key_size != key2->dk_key_size) return (2); if (key1->dk_id != key2->dk_id) return (3); return (key1->dk_func->compare(key1, key2)); } /* * dst_sign_data * An incremental signing function. Data is signed in steps. * First the context must be initialized (SIG_MODE_INIT). * Then data is hashed (SIG_MODE_UPDATE). Finally the signature * itself is created (SIG_MODE_FINAL). This function can be called * once with INIT, UPDATE and FINAL modes all set, or it can be - * called separately with a different mode set for each step. The * UPDATE step can be repeated. * Parameters * mode A bit mask used to specify operation(s) to be performed. * SIG_MODE_INIT 1 Initialize digest * SIG_MODE_UPDATE 2 Add data to digest * SIG_MODE_FINAL 4 Generate signature * from signature * SIG_MODE_ALL (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL * data Data to be signed. * len The length in bytes of data to be signed. * in_key Contains a private key to sign with. * KEY structures should be handled (created, converted, * compared, stored, freed) by the DST. * signature * The location to which the signature will be written. * sig_len Length of the signature field in bytes. * Return * 0 Successfull INIT or Update operation * >0 success FINAL (sign) operation * <0 failure */ int dst_sign_data(const int mode, DST_KEY *in_key, void **context, const u_char *data, const int len, u_char *signature, const int sig_len) { DUMP(data, mode, len, "dst_sign_data()"); if (mode & SIG_MODE_FINAL && (in_key->dk_KEY_struct == NULL || signature == NULL)) return (MISSING_KEY_OR_SIGNATURE); if (in_key->dk_func && in_key->dk_func->sign) return (in_key->dk_func->sign(mode, in_key, context, data, len, signature, sig_len)); return (UNKNOWN_KEYALG); } /* * dst_verify_data * An incremental verify function. Data is verified in steps. * First the context must be initialized (SIG_MODE_INIT). * Then data is hashed (SIG_MODE_UPDATE). Finally the signature * is verified (SIG_MODE_FINAL). This function can be called * once with INIT, UPDATE and FINAL modes all set, or it can be * called separately with a different mode set for each step. The * UPDATE step can be repeated. * Parameters * mode Operations to perform this time. * SIG_MODE_INIT 1 Initialize digest * SIG_MODE_UPDATE 2 add data to digest * SIG_MODE_FINAL 4 verify signature * SIG_MODE_ALL * (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL) * data Data to pass through the hash function. * len Length of the data in bytes. * in_key Key for verification. * signature Location of signature. * sig_len Length of the signature in bytes. * Returns * 0 Verify success * Non-Zero Verify Failure */ int dst_verify_data(const int mode, DST_KEY *in_key, void **context, const u_char *data, const int len, const u_char *signature, const int sig_len) { DUMP(data, mode, len, "dst_verify_data()"); if (mode & SIG_MODE_FINAL && (in_key->dk_KEY_struct == NULL || signature == NULL)) return (MISSING_KEY_OR_SIGNATURE); if (in_key->dk_func == NULL || in_key->dk_func->verify == NULL) return (UNSUPPORTED_KEYALG); return (in_key->dk_func->verify(mode, in_key, context, data, len, signature, sig_len)); } /* * dst_read_private_key * Access a private key. First the list of private keys that have * already been read in is searched, then the key accessed on disk. * If the private key can be found, it is returned. If the key cannot * be found, a null pointer is returned. The options specify required * key characteristics. If the private key requested does not have * these characteristics, it will not be read. * Parameters * in_keyname The private key name. * in_id The id of the private key. * options DST_FORCE_READ Read from disk - don't use a previously * read key. * DST_CAN_SIGN The key must be useable for signing. * DST_NO_AUTHEN The key must be useable for authentication. * DST_STANDARD Return any key * Returns * NULL If there is no key found in the current directory or * this key has not been loaded before. * !NULL Success - KEY structure returned. */ DST_KEY * dst_read_key(const char *in_keyname, const u_int16_t in_id, const int in_alg, const int type) { char keyname[PATH_MAX]; DST_KEY *dg_key = NULL, *pubkey = NULL; if (!dst_check_algorithm(in_alg)) { /* make sure alg is available */ EREPORT(("dst_read_private_key(): Algorithm %d not suppored\n", in_alg)); return (NULL); } if ((type & (DST_PUBLIC | DST_PRIVATE)) == 0) return (NULL); if (in_keyname == NULL) { EREPORT(("dst_read_private_key(): Null key name passed in\n")); return (NULL); } else strcpy(keyname, in_keyname); /* before I read in the public key, check if it is allowed to sign */ if ((pubkey = dst_s_read_public_key(keyname, in_id, in_alg)) == NULL) return (NULL); if (type == DST_PUBLIC) return pubkey; if (!(dg_key = dst_s_get_key_struct(keyname, pubkey->dk_alg, pubkey->dk_flags, pubkey->dk_proto, 0))) return (dg_key); /* Fill in private key and some fields in the general key structure */ if (dst_s_read_private_key_file(keyname, dg_key, pubkey->dk_id, pubkey->dk_alg) == 0) dg_key = dst_free_key(dg_key); pubkey = dst_free_key(pubkey); return (dg_key); } int dst_write_key(const DST_KEY *key, const int type) { int pub = 0, priv = 0; if (key == NULL) return (0); if (!dst_check_algorithm(key->dk_alg)) { /* make sure alg is available */ EREPORT(("dst_write_key(): Algorithm %d not suppored\n", key->dk_alg)); return (UNSUPPORTED_KEYALG); } if ((type & (DST_PRIVATE|DST_PUBLIC)) == 0) return (0); if (type & DST_PUBLIC) if ((pub = dst_s_write_public_key(key)) < 0) return (pub); if (type & DST_PRIVATE) if ((priv = dst_s_write_private_key(key)) < 0) return (priv); return (priv+pub); } /* * dst_write_private_key * Write a private key to disk. The filename will be of the form: * Kdk_name>+dk_alg>+dk_id>.. * If there is already a file with this name, an error is returned. * * Parameters * key A DST managed key structure that contains * all information needed about a key. * Return * >= 0 Correct behavior. Returns length of encoded key value * written to disk. * < 0 error. */ static int dst_s_write_private_key(const DST_KEY *key) { u_char encoded_block[RAW_KEY_SIZE]; char file[PATH_MAX]; int len; FILE *fp; /* First encode the key into the portable key format */ if (key == NULL) return (-1); if (key->dk_KEY_struct == NULL) return (0); /* null key has no private key */ if (key->dk_func == NULL || key->dk_func->to_file_fmt == NULL) { EREPORT(("dst_write_private_key(): Unsupported operation %d\n", key->dk_alg)); return (-5); } else if ((len = key->dk_func->to_file_fmt(key, (char *)encoded_block, sizeof(encoded_block))) <= 0) { EREPORT(("dst_write_private_key(): Failed encoding private RSA bsafe key %d\n", len)); return (-8); } /* Now I can create the file I want to use */ dst_s_build_filename(file, key->dk_key_name, key->dk_id, key->dk_alg, PRIVATE_KEY, PATH_MAX); /* Do not overwrite an existing file */ if ((fp = dst_s_fopen(file, "w", 0600)) != NULL) { int nn; if ((nn = fwrite(encoded_block, 1, len, fp)) != len) { EREPORT(("dst_write_private_key(): Write failure on %s %d != %d errno=%d\n", file, len, nn, errno)); return (-5); } fclose(fp); } else { EREPORT(("dst_write_private_key(): Can not create file %s\n" ,file)); return (-6); } memset(encoded_block, 0, len); return (len); } /* * * dst_read_public_key * Read a public key from disk and store in a DST key structure. * Parameters * in_name K. is the * filename of the key file to be read. * Returns * NULL If the key does not exist or no name is supplied. - * NON-NULL Initalized key structure if the key exists. + * NON-NULL Initialized key structure if the key exists. */ static DST_KEY * dst_s_read_public_key(const char *in_name, const u_int16_t in_id, int in_alg) { int flags, proto, alg, len, dlen; int c; char name[PATH_MAX], enckey[RAW_KEY_SIZE], *notspace; u_char deckey[RAW_KEY_SIZE]; FILE *fp; if (in_name == NULL) { EREPORT(("dst_read_public_key(): No key name given\n")); return (NULL); } if (dst_s_build_filename(name, in_name, in_id, in_alg, PUBLIC_KEY, PATH_MAX) == -1) { EREPORT(("dst_read_public_key(): Cannot make filename from %s, %d, and %s\n", in_name, in_id, PUBLIC_KEY)); return (NULL); } /* * Open the file and read it's formatted contents up to key * File format: * domain.name [ttl] [IN] KEY * flags, proto, alg stored as decimal (or hex numbers FIXME). * (FIXME: handle parentheses for line continuation.) */ if ((fp = dst_s_fopen(name, "r", 0)) == NULL) { EREPORT(("dst_read_public_key(): Public Key not found %s\n", name)); return (NULL); } /* Skip domain name, which ends at first blank */ while ((c = getc(fp)) != EOF) if (isspace(c)) break; /* Skip blank to get to next field */ while ((c = getc(fp)) != EOF) if (!isspace(c)) break; /* Skip optional TTL -- if initial digit, skip whole word. */ if (isdigit(c)) { while ((c = getc(fp)) != EOF) if (isspace(c)) break; while ((c = getc(fp)) != EOF) if (!isspace(c)) break; } /* Skip optional "IN" */ if (c == 'I' || c == 'i') { while ((c = getc(fp)) != EOF) if (isspace(c)) break; while ((c = getc(fp)) != EOF) if (!isspace(c)) break; } /* Locate and skip "KEY" */ if (c != 'K' && c != 'k') { EREPORT(("\"KEY\" doesn't appear in file: %s", name)); return NULL; } while ((c = getc(fp)) != EOF) if (isspace(c)) break; while ((c = getc(fp)) != EOF) if (!isspace(c)) break; ungetc(c, fp); /* return the charcter to the input field */ /* Handle hex!! FIXME. */ if (fscanf(fp, "%d %d %d", &flags, &proto, &alg) != 3) { EREPORT(("dst_read_public_key(): Can not read flag/proto/alg field from %s\n" ,name)); return (NULL); } /* read in the key string */ fgets(enckey, sizeof(enckey), fp); /* If we aren't at end-of-file, something is wrong. */ while ((c = getc(fp)) != EOF) if (!isspace(c)) break; if (!feof(fp)) { EREPORT(("Key too long in file: %s", name)); return NULL; } fclose(fp); if ((len = strlen(enckey)) <= 0) return (NULL); /* discard \n */ enckey[--len] = '\0'; /* remove leading spaces */ for (notspace = (char *) enckey; isspace((*notspace)&0xff); len--) notspace++; dlen = b64_pton(notspace, deckey, sizeof(deckey)); if (dlen < 0) { EREPORT(("dst_read_public_key: bad return from b64_pton = %d", dlen)); return (NULL); } /* store key and info in a key structure that is returned */ /* return dst_store_public_key(in_name, alg, proto, 666, flags, deckey, dlen);*/ return dst_buffer_to_key(in_name, alg, flags, proto, deckey, dlen); } /* * dst_write_public_key * Write a key to disk in DNS format. * Parameters * key Pointer to a DST key structure. * Returns * 0 Failure * 1 Success */ static int dst_s_write_public_key(const DST_KEY *key) { FILE *fp; char filename[PATH_MAX]; u_char out_key[RAW_KEY_SIZE]; char enc_key[RAW_KEY_SIZE]; int len = 0; int mode; memset(out_key, 0, sizeof(out_key)); if (key == NULL) { EREPORT(("dst_write_public_key(): No key specified \n")); return (0); } else if ((len = dst_key_to_dnskey(key, out_key, sizeof(out_key)))< 0) return (0); /* Make the filename */ if (dst_s_build_filename(filename, key->dk_key_name, key->dk_id, key->dk_alg, PUBLIC_KEY, PATH_MAX) == -1) { EREPORT(("dst_write_public_key(): Cannot make filename from %s, %d, and %s\n", key->dk_key_name, key->dk_id, PUBLIC_KEY)); return (0); } /* XXX in general this should be a check for symmetric keys */ mode = (key->dk_alg == KEY_HMAC_MD5) ? 0600 : 0644; /* create public key file */ if ((fp = dst_s_fopen(filename, "w+", mode)) == NULL) { EREPORT(("DST_write_public_key: open of file:%s failed (errno=%d)\n", filename, errno)); return (0); } /*write out key first base64 the key data */ if (key->dk_flags & DST_EXTEND_FLAG) b64_ntop(&out_key[6], len - 6, enc_key, sizeof(enc_key)); else b64_ntop(&out_key[4], len - 4, enc_key, sizeof(enc_key)); fprintf(fp, "%s IN KEY %d %d %d %s\n", key->dk_key_name, key->dk_flags, key->dk_proto, key->dk_alg, enc_key); fclose(fp); return (1); } /* * dst_dnskey_to_public_key * This function converts the contents of a DNS KEY RR into a DST * key structure. * Paramters * len Length of the RDATA of the KEY RR RDATA * rdata A pointer to the the KEY RR RDATA. * in_name Key name to be stored in key structure. * Returns * NULL Failure * NON-NULL Success. Pointer to key structure. * Caller's responsibility to free() it. */ DST_KEY * dst_dnskey_to_key(const char *in_name, const u_char *rdata, const int len) { DST_KEY *key_st; int alg ; int start = DST_KEY_START; if (rdata == NULL || len <= DST_KEY_ALG) /* no data */ return (NULL); alg = (u_int8_t) rdata[DST_KEY_ALG]; if (!dst_check_algorithm(alg)) { /* make sure alg is available */ EREPORT(("dst_dnskey_to_key(): Algorithm %d not suppored\n", alg)); return (NULL); } if ((key_st = dst_s_get_key_struct(in_name, alg, 0, 0, 0)) == NULL) return (NULL); if (in_name == NULL) return (NULL); key_st->dk_id = dst_s_dns_key_id(rdata, len); key_st->dk_flags = dst_s_get_int16(rdata); key_st->dk_proto = (u_int16_t) rdata[DST_KEY_PROT]; if (key_st->dk_flags & DST_EXTEND_FLAG) { u_int32_t ext_flags; ext_flags = (u_int32_t) dst_s_get_int16(&rdata[DST_EXT_FLAG]); key_st->dk_flags = key_st->dk_flags | (ext_flags << 16); start += 2; } /* * now point to the begining of the data representing the encoding * of the key */ if (key_st->dk_func && key_st->dk_func->from_dns_key) { if (key_st->dk_func->from_dns_key(key_st, &rdata[start], len - start) > 0) return (key_st); } else EREPORT(("dst_dnskey_to_public_key(): unsuppored alg %d\n", alg)); SAFE_FREE(key_st); return (key_st); } /* * dst_public_key_to_dnskey * Function to encode a public key into DNS KEY wire format * Parameters * key Key structure to encode. * out_storage Location to write the encoded key to. * out_len Size of the output array. * Returns * <0 Failure * >=0 Number of bytes written to out_storage */ int dst_key_to_dnskey(const DST_KEY *key, u_char *out_storage, const int out_len) { u_int16_t val; int loc = 0; int enc_len = 0; if (key == NULL) return (-1); if (!dst_check_algorithm(key->dk_alg)) { /* make sure alg is available */ EREPORT(("dst_key_to_dnskey(): Algorithm %d not suppored\n", key->dk_alg)); return (UNSUPPORTED_KEYALG); } memset(out_storage, 0, out_len); val = (u_int16_t)(key->dk_flags & 0xffff); dst_s_put_int16(out_storage, val); loc += 2; out_storage[loc++] = (u_char) key->dk_proto; out_storage[loc++] = (u_char) key->dk_alg; if (key->dk_flags > 0xffff) { /* Extended flags */ val = (u_int16_t)((key->dk_flags >> 16) & 0xffff); dst_s_put_int16(&out_storage[loc], val); loc += 2; } if (key->dk_KEY_struct == NULL) return (loc); if (key->dk_func && key->dk_func->to_dns_key) { enc_len = key->dk_func->to_dns_key(key, (u_char *) &out_storage[loc], out_len - loc); if (enc_len > 0) return (enc_len + loc); else return (-1); } else EREPORT(("dst_key_to_dnskey(): Unsupported ALG %d\n", key->dk_alg)); return (-1); } /* * dst_buffer_to_key * Function to encode a string of raw data into a DST key * Parameters * alg The algorithm (HMAC only) * key A pointer to the data * keylen The length of the data * Returns * NULL an error occurred * NON-NULL the DST key */ DST_KEY * dst_buffer_to_key(const char *key_name, /* name of the key */ const int alg, /* algorithm */ const int flags, /* dns flags */ const int protocol, /* dns protocol */ const u_char *key_buf, /* key in dns wire fmt */ const int key_len) /* size of key */ { DST_KEY *dkey = NULL; int dnslen; u_char dns[2048]; if (!dst_check_algorithm(alg)) { /* make sure alg is available */ EREPORT(("dst_buffer_to_key(): Algorithm %d not suppored\n", alg)); return (NULL); } dkey = dst_s_get_key_struct(key_name, alg, flags, protocol, -1); if (dkey == NULL) return (NULL); if (dkey->dk_func == NULL || dkey->dk_func->from_dns_key == NULL) return NULL; if (dkey->dk_func->from_dns_key(dkey, key_buf, key_len) < 0) { EREPORT(("dst_buffer_to_key(): dst_buffer_to_hmac failed\n")); return (dst_free_key(dkey)); } dnslen = dst_key_to_dnskey(dkey, dns, sizeof(dns)); dkey->dk_id = dst_s_dns_key_id(dns, dnslen); return (dkey); } int dst_key_to_buffer(DST_KEY *key, u_char *out_buff, int buf_len) { int len; /* this function will extrac the secret of HMAC into a buffer */ if (key == NULL) return (0); if (key->dk_func != NULL && key->dk_func->to_dns_key != NULL) { len = key->dk_func->to_dns_key(key, out_buff, buf_len); if (len < 0) return (0); return (len); } return (0); } /* * dst_s_read_private_key_file * Function reads in private key from a file. * Fills out the KEY structure. * Parameters * name Name of the key to be read. * pk_key Structure that the key is returned in. * in_id Key identifier (tag) * Return * 1 if everthing works * 0 if there is any problem */ static int dst_s_read_private_key_file(char *name, DST_KEY *pk_key, u_int16_t in_id, int in_alg) { int cnt, alg, len, major, minor, file_major, file_minor; int ret, id; char filename[PATH_MAX]; u_char in_buff[RAW_KEY_SIZE], *p; FILE *fp; int dnslen; u_char dns[2048]; if (name == NULL || pk_key == NULL) { EREPORT(("dst_read_private_key_file(): No key name given\n")); return (0); } /* Make the filename */ if (dst_s_build_filename(filename, name, in_id, in_alg, PRIVATE_KEY, PATH_MAX) == -1) { EREPORT(("dst_read_private_key(): Cannot make filename from %s, %d, and %s\n", name, in_id, PRIVATE_KEY)); return (0); } /* first check if we can find the key file */ if ((fp = dst_s_fopen(filename, "r", 0)) == NULL) { EREPORT(("dst_s_read_private_key_file: Could not open file %s in directory %s\n", filename, dst_path[0] ? dst_path : (char *) getcwd(NULL, PATH_MAX - 1))); return (0); } /* now read the header info from the file */ if ((cnt = fread(in_buff, 1, sizeof(in_buff), fp)) < 5) { fclose(fp); EREPORT(("dst_s_read_private_key_file: error reading file %s (empty file)\n", filename)); return (0); } /* decrypt key */ fclose(fp); if (memcmp(in_buff, "Private-key-format: v", 20) != 0) goto fail; len = cnt; p = in_buff; if (!dst_s_verify_str((const char **) &p, "Private-key-format: v")) { EREPORT(("dst_s_read_private_key_file(): Not a Key file/Decrypt failed %s\n", name)); goto fail; } /* read in file format */ sscanf((char *)p, "%d.%d", &file_major, &file_minor); sscanf(KEY_FILE_FORMAT, "%d.%d", &major, &minor); if (file_major < 1) { EREPORT(("dst_s_read_private_key_file(): Unknown keyfile %d.%d version for %s\n", file_major, file_minor, name)); goto fail; } else if (file_major > major || file_minor > minor) EREPORT(( "dst_s_read_private_key_file(): Keyfile %s version higher than mine %d.%d MAY FAIL\n", name, file_major, file_minor)); while (*p++ != '\n') ; /* skip to end of line */ if (!dst_s_verify_str((const char **) &p, "Algorithm: ")) goto fail; if (sscanf((char *)p, "%d", &alg) != 1) goto fail; while (*p++ != '\n') ; /* skip to end of line */ if (pk_key->dk_key_name && !strcmp(pk_key->dk_key_name, name)) SAFE_FREE2(pk_key->dk_key_name, strlen(pk_key->dk_key_name)); pk_key->dk_key_name = (char *) strdup(name); /* allocate and fill in key structure */ if (pk_key->dk_func == NULL || pk_key->dk_func->from_file_fmt == NULL) goto fail; ret = pk_key->dk_func->from_file_fmt(pk_key, (char *)p, &in_buff[len] - p); if (ret < 0) goto fail; dnslen = dst_key_to_dnskey(pk_key, dns, sizeof(dns)); id = dst_s_dns_key_id(dns, dnslen); /* Make sure the actual key tag matches the input tag used in the filename */ if (id != in_id) { EREPORT(("dst_s_read_private_key_file(): actual tag of key read %d != input tag used to build filename %d.\n", id, in_id)); goto fail; } pk_key->dk_id = (u_int16_t) id; pk_key->dk_alg = alg; memset(in_buff, 0, cnt); return (1); fail: memset(in_buff, 0, cnt); return (0); } /* * dst_generate_key * Generate and store a public/private keypair. * Keys will be stored in formatted files. * Parameters * name Name of the new key. Used to create key files * K++.public and K++.private. * bits Size of the new key in bits. * exp What exponent to use: * 0 use exponent 3 * non-zero use Fermant4 * flags The default value of the DNS Key flags. * The DNS Key RR Flag field is defined in RFC 2065, * section 3.3. The field has 16 bits. * protocol * Default value of the DNS Key protocol field. * The DNS Key protocol field is defined in RFC 2065, * section 3.4. The field has 8 bits. * alg What algorithm to use. Currently defined: * KEY_RSA 1 * KEY_DSA 3 * KEY_HMAC 157 * out_id The key tag is returned. * * Return * NULL Failure * non-NULL the generated key pair * Caller frees the result, and its dk_name pointer. */ DST_KEY * dst_generate_key(const char *name, const int bits, const int exp, const int flags, const int protocol, const int alg) { DST_KEY *new_key = NULL; int res; int dnslen; u_char dns[2048]; if (name == NULL) return (NULL); if (!dst_check_algorithm(alg)) { /* make sure alg is available */ EREPORT(("dst_generate_key(): Algorithm %d not suppored\n", alg)); return (NULL); } new_key = dst_s_get_key_struct(name, alg, flags, protocol, bits); if (new_key == NULL) return (NULL); if (bits == 0) /* null key we are done */ return (new_key); if (new_key->dk_func == NULL || new_key->dk_func->generate == NULL) { EREPORT(("dst_generate_key_pair():Unsupported algorithm %d\n", alg)); return (dst_free_key(new_key)); } if ((res = new_key->dk_func->generate(new_key, exp)) <= 0) { EREPORT(("dst_generate_key_pair(): Key generation failure %s %d %d %d\n", new_key->dk_key_name, new_key->dk_alg, new_key->dk_key_size, exp)); return (dst_free_key(new_key)); } dnslen = dst_key_to_dnskey(new_key, dns, sizeof(dns)); if (dnslen != UNSUPPORTED_KEYALG) new_key->dk_id = dst_s_dns_key_id(dns, dnslen); else new_key->dk_id = 0; return (new_key); } /* * dst_free_key * Release all data structures pointed to by a key structure. * Parameters * f_key Key structure to be freed. */ DST_KEY * dst_free_key(DST_KEY *f_key) { if (f_key == NULL) return (f_key); if (f_key->dk_func && f_key->dk_func->destroy) f_key->dk_KEY_struct = f_key->dk_func->destroy(f_key->dk_KEY_struct); else { EREPORT(("dst_free_key(): Unknown key alg %d\n", f_key->dk_alg)); free(f_key->dk_KEY_struct); /* SHOULD NOT happen */ } if (f_key->dk_KEY_struct) { free(f_key->dk_KEY_struct); f_key->dk_KEY_struct = NULL; } if (f_key->dk_key_name) SAFE_FREE(f_key->dk_key_name); SAFE_FREE(f_key); return (NULL); } /* * dst_sig_size * Return the maximim size of signature from the key specified in bytes * Parameters * key * Returns * bytes */ int dst_sig_size(DST_KEY *key) { switch (key->dk_alg) { case KEY_HMAC_MD5: return (16); case KEY_HMAC_SHA1: return (20); case KEY_RSA: return (key->dk_key_size + 7) / 8; case KEY_DSA: return (40); default: EREPORT(("dst_sig_size(): Unknown key alg %d\n", key->dk_alg)); return -1; } } /* * dst_random * function that multiplexes number of random number generators * Parameters * mode: select the random number generator * wanted is how many bytes of random data are requested * outran is a buffer of size at least wanted for the output data * * Returns * number of bytes written to outran */ int dst_random(const int mode, int wanted, u_char *outran) { u_int32_t *buff = NULL, *bp = NULL; int i; if (wanted <= 0 || outran == NULL) return (0); switch (mode) { case DST_RAND_SEMI: bp = buff = (u_int32_t *) malloc(wanted+sizeof(u_int32_t)); for (i = 0; i < wanted; i+= sizeof(u_int32_t), bp++) { *bp = dst_s_quick_random(i); } memcpy(outran, buff, wanted); SAFE_FREE(buff); return (wanted); case DST_RAND_STD: return (dst_s_semi_random(outran, wanted)); case DST_RAND_KEY: return (dst_s_random(outran, wanted)); case DST_RAND_DSS: default: /* need error case here XXX OG */ return (0); } } diff --git a/contrib/bind/lib/dst/hmac_link.c b/contrib/bind/lib/dst/hmac_link.c index 12e22a8b24e4..b6895242c4e2 100644 --- a/contrib/bind/lib/dst/hmac_link.c +++ b/contrib/bind/lib/dst/hmac_link.c @@ -1,495 +1,490 @@ #ifdef HMAC_MD5 #ifndef LINT -static const char rcsid[] = "$Header: /proj/cvs/isc/bind8/src/lib/dst/hmac_link.c,v 1.9 2001/05/29 05:48:10 marka Exp $"; +static const char rcsid[] = "$Header: /proj/cvs/isc/bind8/src/lib/dst/hmac_link.c,v 1.10 2002/12/03 05:26:49 marka Exp $"; #endif /* * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. * * Permission to use, copy modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. */ /* * This file contains an implementation of the HMAC-MD5 algorithm. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include "dst_internal.h" #ifdef USE_MD5 # include "md5.h" # ifndef _MD5_H_ # define _MD5_H_ 1 /* make sure we do not include rsaref md5.h file */ # endif #endif #include "port_after.h" #define HMAC_LEN 64 #define HMAC_IPAD 0x36 #define HMAC_OPAD 0x5c #define MD5_LEN 16 typedef struct hmackey { u_char hk_ipad[64], hk_opad[64]; } HMAC_Key; /************************************************************************** * dst_hmac_md5_sign * Call HMAC signing functions to sign a block of data. * There are three steps to signing, INIT (initialize structures), * UPDATE (hash (more) data), FINAL (generate a signature). This * routine performs one or more of these steps. * Parameters * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. * priv_key key to use for signing. * context the context to be used in this digest * data data to be signed. * len length in bytes of data. * signature location to store signature. * sig_len size of the signature location * returns * N Success on SIG_MODE_FINAL = returns signature length in bytes * 0 Success on SIG_MODE_INIT and UPDATE * <0 Failure */ static int dst_hmac_md5_sign(const int mode, DST_KEY *d_key, void **context, const u_char *data, const int len, u_char *signature, const int sig_len) { HMAC_Key *key; int sign_len = 0; MD5_CTX *ctx = NULL; if (mode & SIG_MODE_INIT) ctx = (MD5_CTX *) malloc(sizeof(*ctx)); else if (context) ctx = (MD5_CTX *) *context; if (ctx == NULL) return (-1); if (d_key == NULL || d_key->dk_KEY_struct == NULL) return (-1); key = (HMAC_Key *) d_key->dk_KEY_struct; if (mode & SIG_MODE_INIT) { MD5Init(ctx); MD5Update(ctx, key->hk_ipad, HMAC_LEN); } if ((mode & SIG_MODE_UPDATE) && (data && len > 0)) MD5Update(ctx, data, len); if (mode & SIG_MODE_FINAL) { if (signature == NULL || sig_len < MD5_LEN) return (SIGN_FINAL_FAILURE); MD5Final(signature, ctx); /* perform outer MD5 */ MD5Init(ctx); MD5Update(ctx, key->hk_opad, HMAC_LEN); MD5Update(ctx, signature, MD5_LEN); MD5Final(signature, ctx); sign_len = MD5_LEN; SAFE_FREE(ctx); } else { if (context == NULL) return (-1); *context = (void *) ctx; } return (sign_len); } /************************************************************************** * dst_hmac_md5_verify() * Calls HMAC verification routines. There are three steps to * verification, INIT (initialize structures), UPDATE (hash (more) data), * FINAL (generate a signature). This routine performs one or more of * these steps. * Parameters * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. * dkey key to use for verify. * data data signed. * len length in bytes of data. * signature signature. * sig_len length in bytes of signature. * returns * 0 Success * <0 Failure */ static int dst_hmac_md5_verify(const int mode, DST_KEY *d_key, void **context, const u_char *data, const int len, const u_char *signature, const int sig_len) { HMAC_Key *key; MD5_CTX *ctx = NULL; if (mode & SIG_MODE_INIT) ctx = (MD5_CTX *) malloc(sizeof(*ctx)); else if (context) ctx = (MD5_CTX *) *context; if (ctx == NULL) return (-1); if (d_key == NULL || d_key->dk_KEY_struct == NULL) return (-1); key = (HMAC_Key *) d_key->dk_KEY_struct; if (mode & SIG_MODE_INIT) { MD5Init(ctx); MD5Update(ctx, key->hk_ipad, HMAC_LEN); } if ((mode & SIG_MODE_UPDATE) && (data && len > 0)) MD5Update(ctx, data, len); if (mode & SIG_MODE_FINAL) { u_char digest[MD5_LEN]; if (signature == NULL || key == NULL || sig_len != MD5_LEN) return (VERIFY_FINAL_FAILURE); MD5Final(digest, ctx); /* perform outer MD5 */ MD5Init(ctx); MD5Update(ctx, key->hk_opad, HMAC_LEN); MD5Update(ctx, digest, MD5_LEN); MD5Final(digest, ctx); SAFE_FREE(ctx); if (memcmp(digest, signature, MD5_LEN) != 0) return (VERIFY_FINAL_FAILURE); } else { if (context == NULL) return (-1); *context = (void *) ctx; } return (0); } /************************************************************************** * dst_buffer_to_hmac_md5 * Converts key from raw data to an HMAC Key * This function gets in a pointer to the data * Parameters * hkey the HMAC key to be filled in * key the key in raw format * keylen the length of the key * Return * 0 Success * <0 Failure */ static int dst_buffer_to_hmac_md5(DST_KEY *dkey, const u_char *key, const int keylen) { int i; HMAC_Key *hkey = NULL; MD5_CTX ctx; int local_keylen = keylen; if (dkey == NULL || key == NULL || keylen < 0) return (-1); if ((hkey = (HMAC_Key *) malloc(sizeof(HMAC_Key))) == NULL) return (-2); memset(hkey->hk_ipad, 0, sizeof(hkey->hk_ipad)); memset(hkey->hk_opad, 0, sizeof(hkey->hk_opad)); /* if key is longer than HMAC_LEN bytes reset it to key=MD5(key) */ if (keylen > HMAC_LEN) { u_char tk[MD5_LEN]; MD5Init(&ctx); MD5Update(&ctx, key, keylen); MD5Final(tk, &ctx); memset((void *) &ctx, 0, sizeof(ctx)); key = tk; local_keylen = MD5_LEN; } /* start out by storing key in pads */ memcpy(hkey->hk_ipad, key, local_keylen); memcpy(hkey->hk_opad, key, local_keylen); /* XOR key with hk_ipad and opad values */ for (i = 0; i < HMAC_LEN; i++) { hkey->hk_ipad[i] ^= HMAC_IPAD; hkey->hk_opad[i] ^= HMAC_OPAD; } dkey->dk_key_size = local_keylen; dkey->dk_KEY_struct = (void *) hkey; return (1); } /************************************************************************** * dst_hmac_md5_key_to_file_format * Encodes an HMAC Key into the portable file format. * Parameters * hkey HMAC KEY structure * buff output buffer * buff_len size of output buffer * Return * 0 Failure - null input hkey * -1 Failure - not enough space in output area * N Success - Length of data returned in buff */ static int dst_hmac_md5_key_to_file_format(const DST_KEY *dkey, char *buff, const int buff_len) { char *bp; int len, b_len, i, key_len; u_char key[HMAC_LEN]; HMAC_Key *hkey; if (dkey == NULL || dkey->dk_KEY_struct == NULL) return (0); if (buff == NULL || buff_len <= (int) strlen(key_file_fmt_str)) return (-1); /* no OR not enough space in output area */ hkey = (HMAC_Key *) dkey->dk_KEY_struct; memset(buff, 0, buff_len); /* just in case */ /* write file header */ sprintf(buff, key_file_fmt_str, KEY_FILE_FORMAT, KEY_HMAC_MD5, "HMAC"); bp = (char *) strchr(buff, '\0'); b_len = buff_len - (bp - buff); memset(key, 0, HMAC_LEN); for (i = 0; i < HMAC_LEN; i++) key[i] = hkey->hk_ipad[i] ^ HMAC_IPAD; for (i = HMAC_LEN - 1; i >= 0; i--) if (key[i] != 0) break; key_len = i + 1; strcat(bp, "Key: "); bp += strlen("Key: "); b_len = buff_len - (bp - buff); len = b64_ntop(key, key_len, bp, b_len); if (len < 0) return (-1); bp += len; *(bp++) = '\n'; *bp = '\0'; b_len = buff_len - (bp - buff); return (buff_len - b_len); } /************************************************************************** * dst_hmac_md5_key_from_file_format * Converts contents of a key file into an HMAC key. * Parameters * hkey structure to put key into * buff buffer containing the encoded key * buff_len the length of the buffer * Return * n >= 0 Foot print of the key converted * n < 0 Error in conversion */ static int dst_hmac_md5_key_from_file_format(DST_KEY *dkey, const char *buff, const int buff_len) { const char *p = buff, *eol; u_char key[HMAC_LEN+1]; /* b64_pton needs more than 64 bytes do decode * it should probably be fixed rather than doing * this */ u_char *tmp; int key_len, len; if (dkey == NULL) return (-2); if (buff == NULL || buff_len < 0) return (-1); memset(key, 0, sizeof(key)); if (!dst_s_verify_str(&p, "Key: ")) return (-3); eol = strchr(p, '\n'); if (eol == NULL) return (-4); len = eol - p; tmp = malloc(len + 2); memcpy(tmp, p, len); *(tmp + len) = 0x0; key_len = b64_pton((char *)tmp, key, HMAC_LEN+1); /* see above */ SAFE_FREE2(tmp, len + 2); if (dst_buffer_to_hmac_md5(dkey, key, key_len) < 0) { return (-6); } return (0); } /* * dst_hmac_md5_to_dns_key() * function to extract hmac key from DST_KEY structure * intput: * in_key: HMAC-MD5 key * output: * out_str: buffer to write ot * out_len: size of output buffer * returns: * number of bytes written to output buffer */ static int dst_hmac_md5_to_dns_key(const DST_KEY *in_key, u_char *out_str, const int out_len) { HMAC_Key *hkey; int i; if (in_key == NULL || in_key->dk_KEY_struct == NULL || out_len <= in_key->dk_key_size || out_str == NULL) return (-1); hkey = (HMAC_Key *) in_key->dk_KEY_struct; for (i = 0; i < in_key->dk_key_size; i++) out_str[i] = hkey->hk_ipad[i] ^ HMAC_IPAD; return (i); } /************************************************************************** * dst_hmac_md5_compare_keys * Compare two keys for equality. * Return * 0 The keys are equal * NON-ZERO The keys are not equal */ static int dst_hmac_md5_compare_keys(const DST_KEY *key1, const DST_KEY *key2) { HMAC_Key *hkey1 = (HMAC_Key *) key1->dk_KEY_struct; HMAC_Key *hkey2 = (HMAC_Key *) key2->dk_KEY_struct; return memcmp(hkey1->hk_ipad, hkey2->hk_ipad, HMAC_LEN); } /************************************************************************** * dst_hmac_md5_free_key_structure * Frees all (none) dynamically allocated structures in hkey */ static void * dst_hmac_md5_free_key_structure(void *key) { HMAC_Key *hkey = key; SAFE_FREE(hkey); return (NULL); } /*************************************************************************** * dst_hmac_md5_generate_key * Creates a HMAC key of size size with a maximum size of 63 bytes * generating a HMAC key larger than 63 bytes makes no sense as that key * is digested before use. */ static int dst_hmac_md5_generate_key(DST_KEY *key, const int nothing) { u_char *buff; int i, n, size; i = nothing; if (key == NULL || key->dk_alg != KEY_HMAC_MD5) return (0); size = (key->dk_key_size + 7) / 8; /* convert to bytes */ if (size <= 0) return(0); i = size > 64 ? 64 : size; buff = malloc(i+8); n = dst_random(DST_RAND_SEMI, i, buff); n += dst_random(DST_RAND_KEY, i, buff); if (n <= i) { /* failed getting anything */ SAFE_FREE2(buff, i); return (-1); } n = dst_buffer_to_hmac_md5(key, buff, i); SAFE_FREE2(buff, i); if (n <= 0) return (n); return (1); } /* * dst_hmac_md5_init() Function to answer set up function pointers for HMAC * related functions */ int dst_hmac_md5_init() { if (dst_t_func[KEY_HMAC_MD5] != NULL) return (1); dst_t_func[KEY_HMAC_MD5] = malloc(sizeof(struct dst_func)); if (dst_t_func[KEY_HMAC_MD5] == NULL) return (0); memset(dst_t_func[KEY_HMAC_MD5], 0, sizeof(struct dst_func)); dst_t_func[KEY_HMAC_MD5]->sign = dst_hmac_md5_sign; dst_t_func[KEY_HMAC_MD5]->verify = dst_hmac_md5_verify; dst_t_func[KEY_HMAC_MD5]->compare = dst_hmac_md5_compare_keys; dst_t_func[KEY_HMAC_MD5]->generate = dst_hmac_md5_generate_key; dst_t_func[KEY_HMAC_MD5]->destroy = dst_hmac_md5_free_key_structure; dst_t_func[KEY_HMAC_MD5]->to_dns_key = dst_hmac_md5_to_dns_key; dst_t_func[KEY_HMAC_MD5]->from_dns_key = dst_buffer_to_hmac_md5; dst_t_func[KEY_HMAC_MD5]->to_file_fmt = dst_hmac_md5_key_to_file_format; dst_t_func[KEY_HMAC_MD5]->from_file_fmt = dst_hmac_md5_key_from_file_format; return (1); } #else +#define dst_hmac_md5_init __dst_hmac_md5_init + int dst_hmac_md5_init(){ return (0); } #endif - - - - - - - diff --git a/contrib/bind/lib/irs/dns_gr.c b/contrib/bind/lib/irs/dns_gr.c index a62234556a68..b314f113d59c 100644 --- a/contrib/bind/lib/irs/dns_gr.c +++ b/contrib/bind/lib/irs/dns_gr.c @@ -1,293 +1,293 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: dns_gr.c,v 1.20 2001/05/29 05:48:27 marka Exp $"; +static const char rcsid[] = "$Id: dns_gr.c,v 1.21 2002/07/08 06:26:09 marka Exp $"; #endif /* * dns_gr.c --- this file contains the functions for accessing * group information from Hesiod. */ #include "port_before.h" #ifndef WANT_IRS_GR static int __bind_irs_gr_unneeded; #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "hesiod.h" #include "dns_p.h" /* Types. */ struct pvt { /* * This is our private accessor data. It has a shared hesiod context. */ struct dns_p * dns; /* * Need space to store the entries read from the group file. * The members list also needs space per member, and the * strings making up the user names must be allocated * somewhere. Rather than doing lots of small allocations, * we keep one buffer and resize it as needed. */ struct group group; size_t nmemb; /* Malloc'd max index of gr_mem[]. */ char * membuf; size_t membufsize; }; /* Forward. */ static struct group * gr_next(struct irs_gr *); static struct group * gr_byname(struct irs_gr *, const char *); static struct group * gr_bygid(struct irs_gr *, gid_t); static void gr_rewind(struct irs_gr *); static void gr_close(struct irs_gr *); static int gr_list(struct irs_gr *, const char *, gid_t, gid_t *, int *); static void gr_minimize(struct irs_gr *); static struct __res_state * gr_res_get(struct irs_gr *); static void gr_res_set(struct irs_gr *, struct __res_state *, void (*)(void *)); static struct group * get_hes_group(struct irs_gr *this, const char *name, const char *type); /* Public. */ struct irs_gr * irs_dns_gr(struct irs_acc *this) { struct dns_p *dns = (struct dns_p *)this->private; struct irs_gr *gr; struct pvt *pvt; if (!dns || !dns->hes_ctx) { errno = ENODEV; return (NULL); } if (!(pvt = memget(sizeof *pvt))) { errno = ENOMEM; return (NULL); } memset(pvt, 0, sizeof *pvt); pvt->dns = dns; if (!(gr = memget(sizeof *gr))) { memput(pvt, sizeof *pvt); errno = ENOMEM; return (NULL); } memset(gr, 0x5e, sizeof *gr); gr->private = pvt; gr->next = gr_next; gr->byname = gr_byname; gr->bygid = gr_bygid; gr->rewind = gr_rewind; gr->close = gr_close; gr->list = gr_list; gr->minimize = gr_minimize; gr->res_get = gr_res_get; gr->res_set = gr_res_set; return (gr); } /* methods */ static void gr_close(struct irs_gr *this) { struct pvt *pvt = (struct pvt *)this->private; if (pvt->group.gr_mem) free(pvt->group.gr_mem); if (pvt->membuf) free(pvt->membuf); memput(pvt, sizeof *pvt); memput(this, sizeof *this); } static struct group * gr_next(struct irs_gr *this) { UNUSED(this); return (NULL); } static struct group * gr_byname(struct irs_gr *this, const char *name) { return (get_hes_group(this, name, "group")); } static struct group * gr_bygid(struct irs_gr *this, gid_t gid) { char name[32]; sprintf(name, "%ld", (long)gid); return (get_hes_group(this, name, "gid")); } static void gr_rewind(struct irs_gr *this) { UNUSED(this); /* NOOP */ } static int gr_list(struct irs_gr *this, const char *name, gid_t basegid, gid_t *groups, int *ngroups) { UNUSED(this); UNUSED(name); UNUSED(basegid); UNUSED(groups); *ngroups = 0; /* There's some way to do this in Hesiod. */ return (-1); } static void gr_minimize(struct irs_gr *this) { UNUSED(this); /* NOOP */ } /* Private. */ static struct group * get_hes_group(struct irs_gr *this, const char *name, const char *type) { struct pvt *pvt = (struct pvt *)this->private; char **hes_list, *cp, **new; size_t num_members = 0; u_long t; hes_list = hesiod_resolve(pvt->dns->hes_ctx, name, type); if (!hes_list) return (NULL); /* * Copy the returned hesiod string into storage space. */ if (pvt->membuf) free(pvt->membuf); pvt->membuf = strdup(*hes_list); hesiod_free_list(pvt->dns->hes_ctx, hes_list); cp = pvt->membuf; pvt->group.gr_name = cp; if (!(cp = strchr(cp, ':'))) goto cleanup; *cp++ = '\0'; pvt->group.gr_passwd = cp; if (!(cp = strchr(cp, ':'))) goto cleanup; *cp++ = '\0'; - errno = -1; + errno = 0; t = strtoul(cp, NULL, 10); if (errno == ERANGE) goto cleanup; pvt->group.gr_gid = (gid_t) t; if (!(cp = strchr(cp, ':'))) goto cleanup; cp++; /* * Parse the members out. */ while (*cp) { if (num_members+1 >= pvt->nmemb || pvt->group.gr_mem == NULL) { pvt->nmemb += 10; new = realloc(pvt->group.gr_mem, pvt->nmemb * sizeof(char *)); if (new == NULL) goto cleanup; pvt->group.gr_mem = new; } pvt->group.gr_mem[num_members++] = cp; if (!(cp = strchr(cp, ','))) break; *cp++ = '\0'; } if (!pvt->group.gr_mem) { pvt->group.gr_mem = malloc(sizeof(char*)); if (!pvt->group.gr_mem) goto cleanup; } pvt->group.gr_mem[num_members] = NULL; return (&pvt->group); cleanup: if (pvt->group.gr_mem) { free(pvt->group.gr_mem); pvt->group.gr_mem = NULL; } if (pvt->membuf) { free(pvt->membuf); pvt->membuf = NULL; } return (NULL); } static struct __res_state * gr_res_get(struct irs_gr *this) { struct pvt *pvt = (struct pvt *)this->private; struct dns_p *dns = pvt->dns; return (__hesiod_res_get(dns->hes_ctx)); } static void gr_res_set(struct irs_gr *this, struct __res_state * res, void (*free_res)(void *)) { struct pvt *pvt = (struct pvt *)this->private; struct dns_p *dns = pvt->dns; __hesiod_res_set(dns->hes_ctx, res, free_res); } #endif /* WANT_IRS_GR */ diff --git a/contrib/bind/lib/irs/dns_ho.c b/contrib/bind/lib/irs/dns_ho.c index 6c0a6e7373fe..1b37e3d999f9 100644 --- a/contrib/bind/lib/irs/dns_ho.c +++ b/contrib/bind/lib/irs/dns_ho.c @@ -1,1654 +1,1156 @@ /* * Copyright (c) 1985, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* from gethostnamadr.c 8.1 (Berkeley) 6/4/93 */ /* BIND Id: gethnamaddr.c,v 8.15 1996/05/22 04:56:30 vixie Exp $ */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: dns_ho.c,v 1.39 2002/06/27 03:56:32 marka Exp $"; +static const char rcsid[] = "$Id: dns_ho.c,v 1.42.6.1 2003/06/02 09:24:40 marka Exp $"; #endif /* LIBC_SCCS and not lint */ /* Imports. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "dns_p.h" #ifdef SPRINTF_CHAR # define SPRINTF(x) strlen(sprintf/**/x) #else # define SPRINTF(x) sprintf x #endif /* Definitions. */ #define MAXALIASES 35 #define MAXADDRS 35 -#define MAXPACKET (1024*64) +#define MAXPACKET (65535) /* Maximum TCP message size */ #define BOUNDS_CHECK(ptr, count) \ if ((ptr) + (count) > eom) { \ had_error++; \ continue; \ } else (void)0 typedef union { HEADER hdr; u_char buf[MAXPACKET]; } querybuf; struct dns_res_target { struct dns_res_target *next; querybuf qbuf; /* query buffer */ u_char *answer; /* buffer to put answer */ int anslen; /* size of answer buffer */ int qclass, qtype; /* class and type of query */ int action; /* condition whether query is really issued */ char qname[MAXDNAME +1]; /* domain name */ #if 0 int n; /* result length */ #endif }; enum {RESTGT_DOALWAYS, RESTGT_AFTERFAILURE, RESTGT_IGNORE}; enum {RESQRY_SUCCESS, RESQRY_FAIL}; struct pvt { struct hostent host; char * h_addr_ptrs[MAXADDRS + 1]; char * host_aliases[MAXALIASES]; char hostbuf[8*1024]; u_char host_addr[16]; /* IPv4 or IPv6 */ struct __res_state *res; void (*free_res)(void *); }; typedef union { int32_t al; char ac; } align; static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff }; static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 }; /* Note: the IPv6 loopback address is in the "tunnel" space */ static const u_char v6local[] = { 0,0, 0,1 }; /* last 4 bytes of IPv6 addr */ /* Forwards. */ static void ho_close(struct irs_ho *this); static struct hostent * ho_byname(struct irs_ho *this, const char *name); static struct hostent * ho_byname2(struct irs_ho *this, const char *name, int af); static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr, int len, int af); static struct hostent * ho_next(struct irs_ho *this); static void ho_rewind(struct irs_ho *this); static void ho_minimize(struct irs_ho *this); static struct __res_state * ho_res_get(struct irs_ho *this); static void ho_res_set(struct irs_ho *this, struct __res_state *res, void (*free_res)(void *)); static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai); static void map_v4v6_hostent(struct hostent *hp, char **bp, char *ep); static void addrsort(res_state, char **, int); static struct hostent * gethostans(struct irs_ho *this, const u_char *ansbuf, int anslen, const char *qname, int qtype, int af, int size, struct addrinfo **ret_aip, const struct addrinfo *pai); static int add_hostent(struct pvt *pvt, char *bp, char **hap, struct addrinfo *ai); -static const u_char * ar_head(const u_char *, int, const u_char *, - const u_char *, struct pvt *, - int (*)(const char *)); -static struct addrinfo * a6_expand(const u_char *, const u_char *, int, - const u_char *, const u_char *, - const struct in6_addr *, int, - const struct addrinfo *, - struct pvt *, int (*)(const char *), int *); -static const char *dname_subst(const char *, const char *, const char *); static int init(struct irs_ho *this); /* Exports. */ struct irs_ho * irs_dns_ho(struct irs_acc *this) { struct irs_ho *ho; struct pvt *pvt; UNUSED(this); if (!(pvt = memget(sizeof *pvt))) { errno = ENOMEM; return (NULL); } memset(pvt, 0, sizeof *pvt); if (!(ho = memget(sizeof *ho))) { memput(pvt, sizeof *pvt); errno = ENOMEM; return (NULL); } memset(ho, 0x5e, sizeof *ho); ho->private = pvt; ho->close = ho_close; ho->byname = ho_byname; ho->byname2 = ho_byname2; ho->byaddr = ho_byaddr; ho->next = ho_next; ho->rewind = ho_rewind; ho->minimize = ho_minimize; ho->res_get = ho_res_get; ho->res_set = ho_res_set; ho->addrinfo = ho_addrinfo; return (ho); } /* Methods. */ static void ho_close(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; ho_minimize(this); if (pvt->res && pvt->free_res) (*pvt->free_res)(pvt->res); if (pvt) memput(pvt, sizeof *pvt); memput(this, sizeof *this); } static struct hostent * ho_byname(struct irs_ho *this, const char *name) { struct pvt *pvt = (struct pvt *)this->private; struct hostent *hp; if (init(this) == -1) return (NULL); if (pvt->res->options & RES_USE_INET6) { hp = ho_byname2(this, name, AF_INET6); if (hp) return (hp); } return (ho_byname2(this, name, AF_INET)); } static struct hostent * ho_byname2(struct irs_ho *this, const char *name, int af) { struct pvt *pvt = (struct pvt *)this->private; struct hostent *hp = NULL; int n, size; char tmp[NS_MAXDNAME]; const char *cp; struct addrinfo ai; - struct dns_res_target *q, *q2, *p; + struct dns_res_target *q, *p; int querystate = RESQRY_FAIL; if (init(this) == -1) return (NULL); q = memget(sizeof(*q)); - q2 = memget(sizeof(*q2)); - if (q == NULL || q2 == NULL) { + if (q == NULL) { RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); errno = ENOMEM; goto cleanup; } memset(q, 0, sizeof(q)); - memset(q2, 0, sizeof(q2)); switch (af) { case AF_INET: size = INADDRSZ; q->qclass = C_IN; q->qtype = T_A; q->answer = q->qbuf.buf; q->anslen = sizeof(q->qbuf); q->action = RESTGT_DOALWAYS; break; case AF_INET6: size = IN6ADDRSZ; q->qclass = C_IN; - q->qtype = ns_t_a6; + q->qtype = T_AAAA; q->answer = q->qbuf.buf; q->anslen = sizeof(q->qbuf); - q->next = q2; -#ifdef RES_USE_A6 - if ((pvt->res->options & RES_USE_A6) == 0) - q->action = RESTGT_IGNORE; - else -#endif - q->action = RESTGT_DOALWAYS; - q2->qclass = C_IN; - q2->qtype = T_AAAA; - q2->answer = q2->qbuf.buf; - q2->anslen = sizeof(q2->qbuf); - q2->action = RESTGT_AFTERFAILURE; + q->action = RESTGT_DOALWAYS; break; default: RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); errno = EAFNOSUPPORT; hp = NULL; goto cleanup; } /* * if there aren't any dots, it could be a user-level alias. * this is also done in res_nquery() since we are not the only * function that looks up host names. */ if (!strchr(name, '.') && (cp = res_hostalias(pvt->res, name, tmp, sizeof tmp))) name = cp; for (p = q; p; p = p->next) { switch(p->action) { case RESTGT_DOALWAYS: break; case RESTGT_AFTERFAILURE: if (querystate == RESQRY_SUCCESS) continue; break; case RESTGT_IGNORE: continue; } if ((n = res_nsearch(pvt->res, name, p->qclass, p->qtype, p->answer, p->anslen)) < 0) { querystate = RESQRY_FAIL; continue; } memset(&ai, 0, sizeof(ai)); ai.ai_family = af; if ((hp = gethostans(this, p->answer, n, name, p->qtype, af, size, NULL, (const struct addrinfo *)&ai)) != NULL) goto cleanup; /* no more loop is necessary */ querystate = RESQRY_FAIL; continue; } cleanup: if (q != NULL) memput(q, sizeof(*q)); - if (q2 != NULL) - memput(q2, sizeof(*q2)); return(hp); } static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) { struct pvt *pvt = (struct pvt *)this->private; const u_char *uaddr = addr; char *qp; struct hostent *hp = NULL; struct addrinfo ai; struct dns_res_target *q, *q2, *p; - int n, size; + int n, size, i; int querystate = RESQRY_FAIL; if (init(this) == -1) return (NULL); q = memget(sizeof(*q)); q2 = memget(sizeof(*q2)); if (q == NULL || q2 == NULL) { RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); errno = ENOMEM; goto cleanup; } memset(q, 0, sizeof(q)); memset(q2, 0, sizeof(q2)); if (af == AF_INET6 && len == IN6ADDRSZ && (!memcmp(uaddr, mapped, sizeof mapped) || (!memcmp(uaddr, tunnelled, sizeof tunnelled) && memcmp(&uaddr[sizeof tunnelled], v6local, sizeof(v6local))))) { /* Unmap. */ addr = (const char *)addr + sizeof mapped; uaddr += sizeof mapped; af = AF_INET; len = INADDRSZ; } switch (af) { case AF_INET: size = INADDRSZ; q->qclass = C_IN; q->qtype = T_PTR; q->answer = q->qbuf.buf; q->anslen = sizeof(q->qbuf); q->action = RESTGT_DOALWAYS; break; case AF_INET6: size = IN6ADDRSZ; q->qclass = C_IN; q->qtype = T_PTR; q->answer = q->qbuf.buf; q->anslen = sizeof(q->qbuf); q->next = q2; q->action = RESTGT_DOALWAYS; q2->qclass = C_IN; q2->qtype = T_PTR; q2->answer = q2->qbuf.buf; q2->anslen = sizeof(q2->qbuf); if ((pvt->res->options & RES_NO_NIBBLE2) != 0) q2->action = RESTGT_IGNORE; else q2->action = RESTGT_AFTERFAILURE; break; default: errno = EAFNOSUPPORT; RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); hp = NULL; goto cleanup; } if (size > len) { errno = EINVAL; RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); hp = NULL; goto cleanup; } switch (af) { case AF_INET: qp = q->qname; (void) sprintf(qp, "%u.%u.%u.%u.in-addr.arpa", (uaddr[3] & 0xff), (uaddr[2] & 0xff), (uaddr[1] & 0xff), (uaddr[0] & 0xff)); break; case AF_INET6: if (q->action != RESTGT_IGNORE) { qp = q->qname; for (n = IN6ADDRSZ - 1; n >= 0; n--) { - qp += SPRINTF((qp, "%x.%x.", + i = SPRINTF((qp, "%x.%x.", uaddr[n] & 0xf, (uaddr[n] >> 4) & 0xf)); + if (i < 0) + abort(); + qp += i; } +#ifdef HAVE_STRLCAT + strlcat(q->qname, res_get_nibblesuffix(pvt->res), + sizeof(q->qname)); +#else strcpy(qp, res_get_nibblesuffix(pvt->res)); +#endif } if (q2->action != RESTGT_IGNORE) { qp = q2->qname; for (n = IN6ADDRSZ - 1; n >= 0; n--) { - qp += SPRINTF((qp, "%x.%x.", + i = SPRINTF((qp, "%x.%x.", uaddr[n] & 0xf, (uaddr[n] >> 4) & 0xf)); + if (i < 0) + abort(); + qp += i; } +#ifdef HAVE_STRLCAT + strlcat(q->qname, res_get_nibblesuffix2(pvt->res), + sizeof(q->qname)); +#else strcpy(qp, res_get_nibblesuffix2(pvt->res)); +#endif } break; default: abort(); } for (p = q; p; p = p->next) { switch(p->action) { case RESTGT_DOALWAYS: break; case RESTGT_AFTERFAILURE: if (querystate == RESQRY_SUCCESS) continue; break; case RESTGT_IGNORE: continue; } if ((n = res_nquery(pvt->res, p->qname, p->qclass, p->qtype, p->answer, p->anslen)) < 0) { querystate = RESQRY_FAIL; continue; } memset(&ai, 0, sizeof(ai)); ai.ai_family = af; hp = gethostans(this, p->answer, n, p->qname, T_PTR, af, size, NULL, (const struct addrinfo *)&ai); if (!hp) { querystate = RESQRY_FAIL; continue; } memcpy(pvt->host_addr, addr, len); pvt->h_addr_ptrs[0] = (char *)pvt->host_addr; pvt->h_addr_ptrs[1] = NULL; if (af == AF_INET && (pvt->res->options & RES_USE_INET6)) { map_v4v6_address((char*)pvt->host_addr, (char*)pvt->host_addr); pvt->host.h_addrtype = AF_INET6; pvt->host.h_length = IN6ADDRSZ; } RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); goto cleanup; /* no more loop is necessary. */ } hp = NULL; /* H_ERRNO was set by subroutines */ cleanup: if (q != NULL) memput(q, sizeof(*q)); if (q2 != NULL) memput(q2, sizeof(*q2)); return(hp); } static struct hostent * ho_next(struct irs_ho *this) { UNUSED(this); return (NULL); } static void ho_rewind(struct irs_ho *this) { UNUSED(this); /* NOOP */ } static void ho_minimize(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; if (pvt->res) res_nclose(pvt->res); } static struct __res_state * ho_res_get(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; if (!pvt->res) { struct __res_state *res; res = (struct __res_state *)malloc(sizeof *res); if (!res) { errno = ENOMEM; return (NULL); } memset(res, 0, sizeof *res); ho_res_set(this, res, free); } return (pvt->res); } /* XXX */ extern struct addrinfo *addr2addrinfo __P((const struct addrinfo *, const char *)); static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai) { struct pvt *pvt = (struct pvt *)this->private; int n; char tmp[NS_MAXDNAME]; const char *cp; - struct dns_res_target *q, *q2, *q3, *p; + struct dns_res_target *q, *q2, *p; struct addrinfo sentinel, *cur; int querystate = RESQRY_FAIL; if (init(this) == -1) return (NULL); memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; q = memget(sizeof(*q)); q2 = memget(sizeof(*q2)); - q3 = memget(sizeof(*q3)); - if (q == NULL || q2 == NULL || q3 == NULL) { + if (q == NULL || q2 == NULL) { RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); errno = ENOMEM; goto cleanup; } memset(q, 0, sizeof(q2)); memset(q2, 0, sizeof(q2)); - memset(q3, 0, sizeof(q3)); switch (pai->ai_family) { case AF_UNSPEC: /* prefer IPv6 */ q->qclass = C_IN; - q->qtype = ns_t_a6; + q->qtype = T_AAAA; q->answer = q->qbuf.buf; q->anslen = sizeof(q->qbuf); q->next = q2; -#ifdef RES_USE_A6 - if ((pvt->res->options & RES_USE_A6) == 0) - q->action = RESTGT_IGNORE; - else -#endif - q->action = RESTGT_DOALWAYS; + q->action = RESTGT_DOALWAYS; q2->qclass = C_IN; - q2->qtype = T_AAAA; + q2->qtype = T_A; q2->answer = q2->qbuf.buf; q2->anslen = sizeof(q2->qbuf); - q2->next = q3; - /* try AAAA only when A6 query fails */ - q2->action = RESTGT_AFTERFAILURE; - q3->qclass = C_IN; - q3->qtype = T_A; - q3->answer = q3->qbuf.buf; - q3->anslen = sizeof(q3->qbuf); - q3->action = RESTGT_DOALWAYS; + q2->action = RESTGT_DOALWAYS; break; case AF_INET: q->qclass = C_IN; q->qtype = T_A; q->answer = q->qbuf.buf; q->anslen = sizeof(q->qbuf); q->action = RESTGT_DOALWAYS; break; case AF_INET6: q->qclass = C_IN; - q->qtype = ns_t_a6; + q->qtype = T_AAAA; q->answer = q->qbuf.buf; q->anslen = sizeof(q->qbuf); - q->next = q2; -#ifdef RES_USE_A6 - if ((pvt->res->options & RES_USE_A6) == 0) - q->action = RESTGT_IGNORE; - else -#endif - q->action = RESTGT_DOALWAYS; - q2->qclass = C_IN; - q2->qtype = T_AAAA; - q2->answer = q2->qbuf.buf; - q2->anslen = sizeof(q2->qbuf); - q2->action = RESTGT_AFTERFAILURE; + q->action = RESTGT_DOALWAYS; break; default: RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); /* better error? */ goto cleanup; } /* * if there aren't any dots, it could be a user-level alias. * this is also done in res_nquery() since we are not the only * function that looks up host names. */ if (!strchr(name, '.') && (cp = res_hostalias(pvt->res, name, tmp, sizeof tmp))) name = cp; for (p = q; p; p = p->next) { struct addrinfo *ai; switch(p->action) { case RESTGT_DOALWAYS: break; case RESTGT_AFTERFAILURE: if (querystate == RESQRY_SUCCESS) continue; break; case RESTGT_IGNORE: continue; } if ((n = res_nsearch(pvt->res, name, p->qclass, p->qtype, p->answer, p->anslen)) < 0) { querystate = RESQRY_FAIL; continue; } (void)gethostans(this, p->answer, n, name, p->qtype, pai->ai_family, /* XXX: meaningless */ 0, &ai, pai); if (ai) { querystate = RESQRY_SUCCESS; cur->ai_next = ai; while (cur && cur->ai_next) cur = cur->ai_next; } else querystate = RESQRY_FAIL; } cleanup: if (q != NULL) memput(q, sizeof(*q)); if (q2 != NULL) memput(q2, sizeof(*q2)); - if (q3 != NULL) - memput(q3, sizeof(*q3)); return(sentinel.ai_next); } -static const u_char * -ar_head(cp, count, msg, eom, pvt, name_ok) - const u_char *cp, *msg, *eom; - int count; - struct pvt *pvt; - int (*name_ok)(const char *); -{ - int n; - char buf[1024]; /* XXX */ - - while (count-- > 0 && cp < eom) { - n = dn_expand(msg, eom, cp, buf, sizeof(buf)); - if (n < 0 || !maybe_ok(pvt->res, buf, name_ok)) - goto end; - cp += n; /* name */ - if (cp + 3 * INT16SZ + INT32SZ >= eom) - goto end; - cp += INT16SZ; /* type */ - cp += INT16SZ + INT32SZ; /* class, TTL */ - n = ns_get16(cp); - cp += n + INT16SZ; /* len */ - } - return(cp); - - end: - return(eom); /* XXX */ -} - -/* XXX: too many arguments */ -static struct addrinfo * -a6_expand(const u_char *ansbuf, const u_char *a6p, - int a6len, const u_char *arp, const u_char *eom, - const struct in6_addr *in6, int plen, const struct addrinfo *pai, - struct pvt *pvt, int (*name_ok)(const char *), int *errorp) -{ - struct in6_addr a; - int n, pbyte, plen1, pbyte1, error = 0; - const u_char *cp; - struct addrinfo sentinel, *cur; - char pname[1024], buf[1024]; /* XXX */ - - *errorp = NETDB_SUCCESS; - memset(&sentinel, 0, sizeof(sentinel)); - cur = &sentinel; - - /* - * Validate A6 parameters. - */ - if (a6len == 0) { /* an A6 record must contain at least 1 byte. */ - error = NO_RECOVERY; - goto bad; - } - /* prefix length check. */ - if ((plen1 = *a6p) > 128) { - error = NO_RECOVERY; - goto bad; - } - if (plen1 > plen) { - /* - * New length must not be greater than old one. - * Ignore the record as specified in RFC 2874 - * Section 3.1.2. - */ - return(NULL); /* just ignore. */ - } - /* boundary check for new plen and prefix addr */ - pbyte1 = (plen1 & ~7) / 8; - if ((int)sizeof(struct in6_addr) - pbyte1 > a6len - 1) { - error = NO_RECOVERY; - goto bad; - } - - /* - * merge the new prefix portion. - * <--- plen(bits) ---> - * <--- pbyte ---><-b-> - * 000000000000000pppppxxxxxxxxxxx(= in6, 0: unknown, x: known, p: pad) - * PP++++++++(+ should be merged. P: padding, must be 0) - * <-- plen1--> - * <-pbyte1-> - * ^a6p+1 - * The result should be: - * 0000000000PP++++++++xxxxxxxxxxx(= a) - */ - pbyte = (plen & ~7) / 8; - a = *in6; - if (pbyte > pbyte1) { - /* N.B. the case of "pbyte1 == 128" is implicitly excluded. */ - int b = plen % 8; /* = the length of "pp..." above */ - u_char c_hi, c_lo; - - memcpy(&a.s6_addr[pbyte1], a6p + 1, pbyte - pbyte1); - if (b > 0) { - c_hi = a6p[pbyte - pbyte1 + 1]; - c_lo = in6->s6_addr[pbyte]; - a.s6_addr[pbyte] = - (c_hi & (0xff << (8 - b))) | - ((0x00ff >> b) & c_lo); - } - } - -#if 0 /* for debug */ - if ((pvt->res->options & RES_DEBUG) != 0) { - u_char ntopbuf[INET6_ADDRSTRLEN]; - - inet_ntop(AF_INET6, &a, ntopbuf, sizeof(ntopbuf)); - printf("a6_expand: %s\\%d\n", ntopbuf, plen1); - } -#endif - - if (plen1 == 0) { - /* Here is the end of A6 chain. make addrinfo, then return. */ - return(addr2addrinfo(pai, (const char *)&a)); - } - - /* - * Expand the new prefix name. Since the prefix name must not be - * compressed (RFC 2874 Section 3.1.1), we could use ns_name_ntop() - * here if it had a stricter boundary check. - */ - cp = a6p + 1 + (sizeof(*in6) - pbyte1); - n = dn_expand(ansbuf, eom, cp, pname, sizeof(pname)); - if (n < 0 || !maybe_ok(pvt->res, pname, name_ok)) { - error = NO_RECOVERY; - goto bad; - } - if (cp + n != a6p + a6len) { /* length mismatch */ - error = NO_RECOVERY; - goto bad; - } - - /* - * we need (more) additional section records, but no one is - * available, which possibly means a malformed answer. - */ - if (arp == NULL) { - error = NO_RECOVERY; /* we can't resolve the chain. */ - goto bad; - } - - /* - * Loop thru the rest of the buffer, searching for the next A6 record - * that has the same owner name as the prefix name. If found, then - * recursively call this function to expand the whole A6 chain. - */ - plen = plen1; - for (cp = arp; cp != NULL && cp < eom; cp += n) { - int class, type; - - n = dn_expand(ansbuf, eom, cp, buf, sizeof(buf)); - if (n < 0 || !maybe_ok(pvt->res, buf, name_ok)) { - error = NO_RECOVERY; - goto bad; - } - cp += n; /* name */ - if (cp + 3 * INT16SZ + INT32SZ > eom) { - error = NO_RECOVERY; - goto bad; - } - type = ns_get16(cp); - cp += INT16SZ; /* type */ - class = ns_get16(cp); - cp += INT16SZ + INT32SZ; /* class, TTL */ - n = ns_get16(cp); - cp += INT16SZ; /* len */ - if (cp + n > eom) { - error = NO_RECOVERY; - goto bad; - } - if (class != C_IN || type != ns_t_a6) { - /* we are only interested in A6 records. skip others */ - continue; - } - - if (ns_samename(buf, pname) != 1) { - continue; - } - - /* Proceed to the next record in the chain. */ - cur->ai_next = a6_expand(ansbuf, cp, n, cp + n, eom, - (const struct in6_addr *)&a, - plen, pai, pvt, name_ok, &error); - if (error != NETDB_SUCCESS) - goto bad; - while (cur && cur->ai_next) - cur = cur->ai_next; - } - - return(sentinel.ai_next); - - bad: - *errorp = error; - if (sentinel.ai_next) - freeaddrinfo(sentinel.ai_next); - return(NULL); -} - -static const char * -dname_subst(const char *qname0, const char *owner0, const char *target) { - char owner[MAXDNAME]; - static char qname[MAXDNAME]; - const char blabelhead[] = "\\[x"; /* we can assume hex strings */ - int qlen, olen; - int bufsiz = sizeof(qname); - - /* make local copies, which are canonicalized. */ - if (ns_makecanon(qname0, qname, sizeof(qname)) < 0 || - ns_makecanon(owner0, owner, sizeof(owner)) < 0) - return(NULL); - qlen = strlen(qname); - olen = strlen(owner); - /* from now on, do not refer to qname0 nor owner0. */ - - /* - * check if QNAME is a subdomain of OWNER. - * XXX: currently, we only handle the following two cases: - * (A) none of the labels are bitlabels, or - * (B) both of the head labels are bitlabels (and the following - * labels are NOT bitlabels). - * If we pass the check, then subtract the remaining part from QNAME. - * ex. (A) qname=www.foo.com,owner=foo.com => new qname=www. - * (B) qname=\[x3ffe0501/32].foo.com,owner=\[x3ffe/16].foo.com - * => new qname=\[x0501/16]. - */ - if (ns_samedomain(qname, owner)) { /* check (A) */ - /* at this point, qlen must not be smaller than olen */ - qname[qlen - olen] = 0; - bufsiz -= (qlen - olen); - } else { /* check (B) */ - char *parent0, *parent1; - /* the following 3 have enough size to store 1 bitlabel */ - u_char qlabel[64], olabel[64], newlabel[64]; - int qlabellen, olabellen; - - if (strncmp(qname, blabelhead, 3) != 0 || - strncmp(owner, blabelhead, 3) != 0) - return(NULL); - /* - * Both two begin with bitlabels. The succeeding parts - * must exact match. - */ - if ((parent0 = strchr(qname, '.')) == NULL || - (parent1 = strchr(owner, '.')) == NULL) - return(NULL); - - /* ns_samename allows names to begin with '.' */ - if (ns_samename(parent0, parent1) != 1) - return(NULL); - - /* cut the upper domain parts off. */ - *(parent0 + 1) = 0; - *(parent1 + 1) = 0; - /* convert the textual form into binary one. */ - if (ns_name_pton(qname, qlabel, sizeof(qlabel)) < 0 || - ns_name_pton(owner, olabel, sizeof(olabel)) < 0) - return(NULL); - if ((qlabellen = *(qlabel + 1)) == 0) - qlabellen = 256; - if ((olabellen = *(olabel + 1)) == 0) - olabellen = 256; - if (olabellen > qlabellen) - return(NULL); /* owner does not contain qname. */ - else { - int qplen = (qlabellen + 7) / 8; - int oplen = (olabellen + 7) / 8; - int sft = olabellen % 8; - int nllen, n; - u_char *qp, *op, *np; - - /* skip ELT and Count. */ - qp = qlabel + 2; - op = olabel + 2; - - /* check if olabel is a "subdomain" of qlabel. */ - if (memcmp(qp, op, oplen - 1) != 0) - return(NULL); - if (sft > 0) { - /* compare trailing bits (between 1 and 7) */ - if ((qp[qplen - 1] & (0xff << sft)) != - op[qplen - 1]) - return(NULL); - } - - /* OK, get remaining bits from qlabel. */ - np = newlabel; - if (olabellen == qlabellen) { - /* - * Two names (including bitlabels) are exactly - * same. Discard the whole names. - * XXX: ns_samename() above should exclude - * this case... - */ - qname[0] = 0; - goto maketarget; - } - *np++ = 0x41; /* XXX hardcoding */ - *np++ = nllen = (qlabellen - olabellen); - if (sft == 0) { - /* - * No alignment issue. can just use memcpy. - * Note that the "else" part below contains - * this case. We separate the two cases just - * for efficiency. - * We assume that ns_name_pton above ensures - * QP does not contain trailing garbages. - */ - memcpy(np, qp + oplen, qplen - oplen); - np += qplen - oplen; - *np = 0; - } else { - /* - * copy the lower (8-SFT) bits of QP to the - * upper (8-SFT) bits of NP, then copy the - * upper SFT bits of QP+1 to the lower SFT bits - * of NP, and so on... - * if QP is xxxyyyyy zzzwww..., then - * NP would be yyyyyzzz ... - * Again, we assume QP does not contain - * trailing garbages. - */ - qp += (oplen - 1); - while (nllen > 0) { - *np = (*qp << sft) & 0xff; - if ((nllen -= (8 - sft)) <= 0) - break; /* done */ - qp++; - *np |= ((*qp >> sft) & 0xff); - np++; - nllen -= sft; - } - *++np = 0; - } - - /* - * make a new bitlabel with the remaining bits. - * Note that there's no buffer boundary issue, since - * qlabel, olabel, and newlabel all have the same size. - * ns_name_ntop() must not return 0, since we have - * a non-empty bitlabel. - */ - if ((n = ns_name_ntop(newlabel, qname, sizeof(qname))) - <= 0) - return(NULL); - bufsiz -= n; - if (qname[n - 1] != '.') { /* XXX no trailing dot */ - qname[n - 1] = '.'; - qname[n] = 0; - bufsiz--; - } - - } - } - - maketarget: - /* - * Finally, append the remaining part (maybe empty) to the new target. - */ - if (bufsiz < (int)strlen(target)) /* bufsiz takes care of the \0. */ - return(NULL); - strcat(qname, target); - - return((const char *)qname); -} - static void ho_res_set(struct irs_ho *this, struct __res_state *res, void (*free_res)(void *)) { struct pvt *pvt = (struct pvt *)this->private; if (pvt->res && pvt->free_res) { res_nclose(pvt->res); (*pvt->free_res)(pvt->res); } pvt->res = res; pvt->free_res = free_res; } /* Private. */ static struct hostent * gethostans(struct irs_ho *this, const u_char *ansbuf, int anslen, const char *qname, int qtype, int af, int size, /* meaningless for addrinfo cases */ struct addrinfo **ret_aip, const struct addrinfo *pai) { struct pvt *pvt = (struct pvt *)this->private; int type, class, ancount, qdcount, n, haveanswer, had_error; int error = NETDB_SUCCESS, arcount; int (*name_ok)(const char *); const HEADER *hp; const u_char *eom; const u_char *eor; const u_char *cp; const char *tname; const char *hname; char *bp, *ep, **ap, **hap; char tbuf[MAXDNAME+1]; struct addrinfo sentinel, *cur, ai; - const u_char *arp = NULL; if (pai == NULL) abort(); if (ret_aip != NULL) *ret_aip = NULL; memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; tname = qname; eom = ansbuf + anslen; switch (qtype) { - case ns_t_a6: case T_A: case T_AAAA: case T_ANY: /* use T_ANY only for T_A/T_AAAA lookup */ name_ok = res_hnok; break; case T_PTR: name_ok = res_dnok; break; default: abort(); } pvt->host.h_addrtype = af; pvt->host.h_length = size; hname = pvt->host.h_name = NULL; /* * Find first satisfactory answer. */ if (ansbuf + HFIXEDSZ > eom) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } hp = (const HEADER *)ansbuf; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); arcount = ntohs(hp->arcount); bp = pvt->hostbuf; ep = pvt->hostbuf + sizeof(pvt->hostbuf); cp = ansbuf + HFIXEDSZ; if (qdcount != 1) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } n = dn_expand(ansbuf, eom, cp, bp, ep - bp); if (n < 0 || !maybe_ok(pvt->res, bp, name_ok)) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } cp += n + QFIXEDSZ; if (cp > eom) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } - if (qtype == T_A || qtype == T_AAAA || - qtype == ns_t_a6 || qtype == T_ANY) { + if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) { /* res_nsend() has already verified that the query name is the * same as the one we sent; this just gets the expanded name * (i.e., with the succeeding search-domain tacked on). */ n = strlen(bp) + 1; /* for the \0 */ if (n > MAXHOSTNAMELEN) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } pvt->host.h_name = bp; hname = bp; bp += n; /* The qname can be abbreviated, but hname is now absolute. */ qname = pvt->host.h_name; } ap = pvt->host_aliases; *ap = NULL; pvt->host.h_aliases = pvt->host_aliases; hap = pvt->h_addr_ptrs; *hap = NULL; pvt->host.h_addr_list = pvt->h_addr_ptrs; haveanswer = 0; had_error = 0; while (ancount-- > 0 && cp < eom && !had_error) { n = dn_expand(ansbuf, eom, cp, bp, ep - bp); if (n < 0 || !maybe_ok(pvt->res, bp, name_ok)) { had_error++; continue; } cp += n; /* name */ BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); type = ns_get16(cp); cp += INT16SZ; /* type */ class = ns_get16(cp); cp += INT16SZ + INT32SZ; /* class, TTL */ n = ns_get16(cp); cp += INT16SZ; /* len */ BOUNDS_CHECK(cp, n); if (class != C_IN) { cp += n; continue; } eor = cp + n; - if ((qtype == T_A || qtype == T_AAAA || qtype == ns_t_a6 || - qtype == T_ANY) && type == T_CNAME) { + if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && + type == T_CNAME) { if (haveanswer) { int level = LOG_CRIT; #ifdef LOG_SECURITY level |= LOG_SECURITY; #endif syslog(level, "gethostans: possible attempt to exploit buffer overflow while looking up %s", *qname ? qname : "."); } n = dn_expand(ansbuf, eor, cp, tbuf, sizeof tbuf); if (n < 0 || !maybe_ok(pvt->res, tbuf, name_ok)) { had_error++; continue; } cp += n; /* Store alias. */ if (ap >= &pvt->host_aliases[MAXALIASES-1]) continue; *ap++ = bp; n = strlen(bp) + 1; /* for the \0 */ bp += n; /* Get canonical name. */ n = strlen(tbuf) + 1; /* for the \0 */ if (n > (ep - bp) || n > MAXHOSTNAMELEN) { had_error++; continue; } +#ifdef HAVE_STRLCPY + strlcpy(bp, tbuf, ep - bp); +#else strcpy(bp, tbuf); +#endif pvt->host.h_name = bp; hname = bp; bp += n; continue; } - if (type == ns_t_dname) { - const char *t0, *t; - - /* - * just replace the query target; do not update the - * alias list. (Or should we?) - */ - t0 = (qtype == T_PTR) ? tname : hname; - - n = dn_expand(ansbuf, eor, cp, tbuf, sizeof(tbuf)); - if (n < 0 || !maybe_dnok(pvt->res, tbuf)) { - had_error++; - continue; - } -#ifdef RES_USE_DNAME - if ((pvt ->res->options & RES_USE_DNAME) == 0) { - cp += n; - continue; - } -#endif - if ((t = dname_subst(t0, bp, tbuf)) == NULL) { - cp += n; - continue; - } -#if 0 /* for debug */ - if ((pvt->res->options & RES_DEBUG) != 0) { - printf("DNAME owner=%s, target=%s, next=%s\n", - bp, tbuf, t); - } -#endif - cp += n; - - n = strlen(t) + 1; /* for the \0 */ - if (n > (ep - bp)) { - had_error++; - continue; - } - strcpy(bp, t); - if (qtype == T_PTR) - tname = bp; - else - hname = bp; - bp += n; - - continue; - } if (qtype == T_PTR && type == T_CNAME) { n = dn_expand(ansbuf, eor, cp, tbuf, sizeof tbuf); if (n < 0 || !maybe_dnok(pvt->res, tbuf)) { had_error++; continue; } cp += n; #ifdef RES_USE_DNAME if ((pvt->res->options & RES_USE_DNAME) != 0) #endif { /* * We may be able to check this regardless * of the USE_DNAME bit, but we add the check * for now since the DNAME support is * experimental. */ if (ns_samename(tname, bp) != 1) continue; } /* Get canonical name. */ n = strlen(tbuf) + 1; /* for the \0 */ if (n > (ep - bp)) { had_error++; continue; } +#ifdef HAVE_STRLCPY + strlcpy(bp, tbuf, ep - bp); +#else strcpy(bp, tbuf); +#endif tname = bp; bp += n; continue; } if (qtype == T_ANY) { - if (!(type == T_A || type == T_AAAA || - type == ns_t_a6)) { + if (!(type == T_A || type == T_AAAA)) { cp += n; continue; } } else if (type != qtype) { cp += n; continue; } switch (type) { case T_PTR: if (ret_aip != NULL) { /* addrinfo never needs T_PTR */ cp += n; continue; } if (ns_samename(tname, bp) != 1) { cp += n; continue; } n = dn_expand(ansbuf, eor, cp, bp, ep - bp); if (n < 0 || !maybe_hnok(pvt->res, bp) || n >= MAXHOSTNAMELEN) { had_error++; break; } cp += n; if (!haveanswer) { pvt->host.h_name = bp; hname = bp; } else if (ap < &pvt->host_aliases[MAXALIASES-1]) *ap++ = bp; else n = -1; if (n != -1) { n = strlen(bp) + 1; /* for the \0 */ bp += n; } break; - case ns_t_a6: { - struct in6_addr in6; - struct addrinfo ai; - -#ifdef RES_USE_A6 - if ((pvt->res->options & RES_USE_A6) == 0) { - cp += n; - continue; - } -#endif - - if (ns_samename(hname, bp) != 1) { - cp += n; - continue; - } - - /* - * search for the top of the additional section. - * once found, keep it for the case where we have - * more than one A6 record. - * XXX: however, we may not need this part. - */ - if (arp == NULL && arcount > 0) { - int nscount = ntohs(hp->nscount); - - arp = ar_head(cp + n, nscount + ancount - 1, - ansbuf, eom, pvt, name_ok); - } - - /* recursively collect the whole A6 chain */ - ai = *pai; /* XXX: we can't override constant pai */ - ai.ai_family = AF_INET6; - memset(&in6, 0, sizeof(in6)); /* just for safety */ - cur->ai_next = a6_expand(ansbuf, cp, n, arp, eom, - &in6, 128, - (const struct addrinfo *)&ai, - pvt, name_ok, &error); - if (error != NETDB_SUCCESS) { -#ifdef DEBUG - /* in this case, cur->ai_next must be NULL. */ - if (cur->ai_next != NULL) - abort(); -#endif - had_error++; - continue; - } - - /* - * We don't bother even if cur->ai_next is NULL unless - * the expansion failed by a fatal error. The list - * can be NULL if the given A6 is incomplete, but we - * may have another complete A6 chain in this answer. - * See the last paragraph of RFC 2874 Section 3.1.4. - */ - if (cur->ai_next == NULL) { - cp += n; - continue; /* no error, no answer */ - } - goto convertinfo; - } /* FALLTHROUGH */ case T_A: case T_AAAA: if (ns_samename(hname, bp) != 1) { cp += n; continue; } if (type == T_A && n != INADDRSZ) { cp += n; continue; } if (type == T_AAAA && n != IN6ADDRSZ) { cp += n; continue; } /* make addrinfo. don't overwrite constant PAI */ ai = *pai; ai.ai_family = (type == T_AAAA) ? AF_INET6 : AF_INET; cur->ai_next = addr2addrinfo( (const struct addrinfo *)&ai, (const char *)cp); if (cur->ai_next == NULL) had_error++; - convertinfo: /* convert addrinfo into hostent form */ if (!haveanswer) { int nn; nn = strlen(bp) + 1; /* for the \0 */ if (nn >= MAXHOSTNAMELEN) { cp += n; had_error++; continue; } pvt->host.h_name = bp; hname = bp; bp += nn; } /* Ensure alignment. */ bp = (char *)(((u_long)bp + (sizeof(align) - 1)) & ~(sizeof(align) - 1)); /* Avoid overflows. */ if (bp + n >= &pvt->hostbuf[sizeof pvt->hostbuf]) { had_error++; continue; } if (ret_aip) { /* need addrinfo. keep it. */ while (cur && cur->ai_next) cur = cur->ai_next; } else if (cur->ai_next) { /* need hostent */ struct addrinfo *aip = cur->ai_next; for (aip = cur->ai_next; aip; aip = aip->ai_next) { int m; m = add_hostent(pvt, bp, hap, aip); if (m < 0) { had_error++; break; } if (m == 0) continue; if (hap < &pvt->h_addr_ptrs[MAXADDRS-1]) hap++; - + *hap = NULL; bp += m; } freeaddrinfo(cur->ai_next); cur->ai_next = NULL; } cp += n; break; default: abort(); } if (!had_error) haveanswer++; } if (haveanswer) { if (ret_aip == NULL) { *ap = NULL; *hap = NULL; if (pvt->res->nsort && haveanswer > 1 && qtype == T_A) addrsort(pvt->res, pvt->h_addr_ptrs, haveanswer); if (pvt->host.h_name == NULL) { n = strlen(qname) + 1; /* for the \0 */ if (n > (ep - bp) || n >= MAXHOSTNAMELEN) goto no_recovery; +#ifdef HAVE_STRLCPY + strlcpy(bp, qname, ep - bp); +#else strcpy(bp, qname); +#endif pvt->host.h_name = bp; bp += n; } if (pvt->res->options & RES_USE_INET6) map_v4v6_hostent(&pvt->host, &bp, ep); RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); return (&pvt->host); } else { if ((pai->ai_flags & AI_CANONNAME) != 0) { if (pvt->host.h_name == NULL) { sentinel.ai_next->ai_canonname = strdup(qname); } else { sentinel.ai_next->ai_canonname = strdup(pvt->host.h_name); } } *ret_aip = sentinel.ai_next; return(NULL); } } no_recovery: if (sentinel.ai_next) { /* this should be impossible, but check it for safety */ freeaddrinfo(sentinel.ai_next); } if (error == NETDB_SUCCESS) RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); else RES_SET_H_ERRNO(pvt->res, error); return(NULL); } static int add_hostent(struct pvt *pvt, char *bp, char **hap, struct addrinfo *ai) { int addrlen; char *addrp; const char **tap; char *obp = bp; switch(ai->ai_addr->sa_family) { case AF_INET6: addrlen = IN6ADDRSZ; addrp = (char *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; break; case AF_INET: addrlen = INADDRSZ; addrp = (char *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr; break; default: return(-1); /* abort? */ } /* Ensure alignment. */ bp = (char *)(((u_long)bp + (sizeof(align) - 1)) & ~(sizeof(align) - 1)); /* Avoid overflows. */ if (bp + addrlen >= &pvt->hostbuf[sizeof pvt->hostbuf]) return(-1); if (hap >= &pvt->h_addr_ptrs[MAXADDRS-1]) return(0); /* fail, but not treat it as an error. */ /* Suppress duplicates. */ for (tap = (const char **)pvt->h_addr_ptrs; *tap != NULL; tap++) if (memcmp(*tap, addrp, addrlen) == 0) break; if (*tap != NULL) return (0); memcpy(*hap = bp, addrp, addrlen); return((bp + addrlen) - obp); } static void map_v4v6_hostent(struct hostent *hp, char **bpp, char *ep) { char **ap; if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ) return; hp->h_addrtype = AF_INET6; hp->h_length = IN6ADDRSZ; for (ap = hp->h_addr_list; *ap; ap++) { int i = (u_long)*bpp % sizeof(align); if (i != 0) i = sizeof(align) - i; if ((ep - *bpp) < (i + IN6ADDRSZ)) { /* Out of memory. Truncate address list here. */ *ap = NULL; return; } *bpp += i; map_v4v6_address(*ap, *bpp); *ap = *bpp; *bpp += IN6ADDRSZ; } } static void addrsort(res_state statp, char **ap, int num) { int i, j, needsort = 0, aval[MAXADDRS]; char **p; p = ap; for (i = 0; i < num; i++, p++) { for (j = 0 ; (unsigned)j < statp->nsort; j++) if (statp->sort_list[j].addr.s_addr == (((struct in_addr *)(*p))->s_addr & statp->sort_list[j].mask)) break; aval[i] = j; if (needsort == 0 && i > 0 && j < aval[i-1]) needsort = i; } if (!needsort) return; while (needsort < num) { for (j = needsort - 1; j >= 0; j--) { if (aval[j] > aval[j+1]) { char *hp; i = aval[j]; aval[j] = aval[j+1]; aval[j+1] = i; hp = ap[j]; ap[j] = ap[j+1]; ap[j+1] = hp; } else break; } needsort++; } } static int init(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; if (!pvt->res && !ho_res_get(this)) return (-1); if (((pvt->res->options & RES_INIT) == 0) && res_ninit(pvt->res) == -1) return (-1); return (0); } diff --git a/contrib/bind/lib/irs/dns_nw.c b/contrib/bind/lib/irs/dns_nw.c index f39a59a05a2c..a322f4494628 100644 --- a/contrib/bind/lib/irs/dns_nw.c +++ b/contrib/bind/lib/irs/dns_nw.c @@ -1,589 +1,594 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: dns_nw.c,v 1.23 2002/06/26 07:42:06 marka Exp $"; +static const char rcsid[] = "$Id: dns_nw.c,v 1.25 2002/07/18 02:07:43 marka Exp $"; #endif /* LIBC_SCCS and not lint */ /* Imports. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "dns_p.h" #ifdef SPRINTF_CHAR # define SPRINTF(x) strlen(sprintf/**/x) #else # define SPRINTF(x) sprintf x #endif /* Definitions. */ #define MAXALIASES 35 #define MAXPACKET (64*1024) struct pvt { struct nwent net; char * ali[MAXALIASES]; char buf[BUFSIZ+1]; struct __res_state * res; void (*free_res)(void *); }; typedef union { long al; char ac; } align; enum by_what { by_addr, by_name }; /* Forwards. */ static void nw_close(struct irs_nw *); static struct nwent * nw_byname(struct irs_nw *, const char *, int); static struct nwent * nw_byaddr(struct irs_nw *, void *, int, int); static struct nwent * nw_next(struct irs_nw *); static void nw_rewind(struct irs_nw *); static void nw_minimize(struct irs_nw *); static struct __res_state * nw_res_get(struct irs_nw *this); static void nw_res_set(struct irs_nw *this, struct __res_state *res, void (*free_res)(void *)); static struct nwent * get1101byaddr(struct irs_nw *, u_char *, int); static struct nwent * get1101byname(struct irs_nw *, const char *); static struct nwent * get1101answer(struct irs_nw *, u_char *ansbuf, int anslen, enum by_what by_what, int af, const char *name, const u_char *addr, int addrlen); static struct nwent * get1101mask(struct irs_nw *this, struct nwent *); static int make1101inaddr(const u_char *, int, char *, int); static void normalize_name(char *name); static int init(struct irs_nw *this); /* Exports. */ struct irs_nw * irs_dns_nw(struct irs_acc *this) { struct irs_nw *nw; struct pvt *pvt; UNUSED(this); if (!(pvt = memget(sizeof *pvt))) { errno = ENOMEM; return (NULL); } memset(pvt, 0, sizeof *pvt); if (!(nw = memget(sizeof *nw))) { memput(pvt, sizeof *pvt); errno = ENOMEM; return (NULL); } memset(nw, 0x5e, sizeof *nw); nw->private = pvt; nw->close = nw_close; nw->byname = nw_byname; nw->byaddr = nw_byaddr; nw->next = nw_next; nw->rewind = nw_rewind; nw->minimize = nw_minimize; nw->res_get = nw_res_get; nw->res_set = nw_res_set; return (nw); } /* Methods. */ static void nw_close(struct irs_nw *this) { struct pvt *pvt = (struct pvt *)this->private; nw_minimize(this); if (pvt->res && pvt->free_res) (*pvt->free_res)(pvt->res); memput(pvt, sizeof *pvt); memput(this, sizeof *this); } static struct nwent * nw_byname(struct irs_nw *this, const char *name, int af) { struct pvt *pvt = (struct pvt *)this->private; if (init(this) == -1) return (NULL); switch (af) { case AF_INET: return (get1101byname(this, name)); default: (void)NULL; } RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); errno = EAFNOSUPPORT; return (NULL); } static struct nwent * nw_byaddr(struct irs_nw *this, void *net, int len, int af) { struct pvt *pvt = (struct pvt *)this->private; if (init(this) == -1) return (NULL); switch (af) { case AF_INET: return (get1101byaddr(this, net, len)); default: (void)NULL; } RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); errno = EAFNOSUPPORT; return (NULL); } static struct nwent * nw_next(struct irs_nw *this) { UNUSED(this); return (NULL); } static void nw_rewind(struct irs_nw *this) { UNUSED(this); /* NOOP */ } static void nw_minimize(struct irs_nw *this) { struct pvt *pvt = (struct pvt *)this->private; if (pvt->res) res_nclose(pvt->res); } static struct __res_state * nw_res_get(struct irs_nw *this) { struct pvt *pvt = (struct pvt *)this->private; if (!pvt->res) { struct __res_state *res; res = (struct __res_state *)malloc(sizeof *res); if (!res) { errno = ENOMEM; return (NULL); } memset(res, 0, sizeof *res); nw_res_set(this, res, free); } return (pvt->res); } static void nw_res_set(struct irs_nw *this, struct __res_state *res, void (*free_res)(void *)) { struct pvt *pvt = (struct pvt *)this->private; if (pvt->res && pvt->free_res) { res_nclose(pvt->res); (*pvt->free_res)(pvt->res); } pvt->res = res; pvt->free_res = free_res; } /* Private. */ static struct nwent * get1101byname(struct irs_nw *this, const char *name) { struct pvt *pvt = (struct pvt *)this->private; u_char *ansbuf; int anslen; struct nwent *result; ansbuf = memget(MAXPACKET); if (ansbuf == NULL) { errno = ENOMEM; RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); return (NULL); } anslen = res_nsearch(pvt->res, name, C_IN, T_PTR, ansbuf, MAXPACKET); if (anslen < 0) { memput(ansbuf, MAXPACKET); return (NULL); } result = get1101mask(this, get1101answer(this, ansbuf, anslen, by_name, AF_INET, name, NULL, 0)); memput(ansbuf, MAXPACKET); return (result); } static struct nwent * get1101byaddr(struct irs_nw *this, u_char *net, int len) { struct pvt *pvt = (struct pvt *)this->private; char qbuf[sizeof "255.255.255.255.in-addr.arpa"]; struct nwent *result; u_char *ansbuf; int anslen; if (len < 1 || len > 32) { errno = EINVAL; RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); return (NULL); } if (make1101inaddr(net, len, qbuf, sizeof qbuf) < 0) return (NULL); ansbuf = memget(MAXPACKET); if (ansbuf == NULL) { errno = ENOMEM; RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); return (NULL); } anslen = res_nquery(pvt->res, qbuf, C_IN, T_PTR, ansbuf, MAXPACKET); if (anslen < 0) { memput(ansbuf, MAXPACKET); return (NULL); } result = get1101mask(this, get1101answer(this, ansbuf, anslen, by_addr, AF_INET, NULL, net, len)); memput(ansbuf, MAXPACKET); return (result); } static struct nwent * get1101answer(struct irs_nw *this, u_char *ansbuf, int anslen, enum by_what by_what, int af, const char *name, const u_char *addr, int addrlen) { struct pvt *pvt = (struct pvt *)this->private; int type, class, ancount, qdcount, haveanswer; char *bp, *ep, **ap; u_char *cp, *eom; HEADER *hp; /* Initialize, and parse header. */ eom = ansbuf + anslen; if (ansbuf + HFIXEDSZ > eom) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } hp = (HEADER *)ansbuf; cp = ansbuf + HFIXEDSZ; qdcount = ntohs(hp->qdcount); while (qdcount-- > 0) { int n = dn_skipname(cp, eom); cp += n + QFIXEDSZ; if (n < 0 || cp > eom) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } } ancount = ntohs(hp->ancount); if (!ancount) { if (hp->aa) RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND); else RES_SET_H_ERRNO(pvt->res, TRY_AGAIN); return (NULL); } /* Prepare a return structure. */ bp = pvt->buf; ep = pvt->buf + sizeof(pvt->buf); pvt->net.n_name = NULL; pvt->net.n_aliases = pvt->ali; pvt->net.n_addrtype = af; pvt->net.n_addr = NULL; pvt->net.n_length = addrlen; /* Save input key if given. */ switch (by_what) { case by_name: if (name != NULL) { int n = strlen(name) + 1; if (n > (ep - bp)) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } +#ifdef HAVE_STRLCPY + strlcpy(bp, name, ep - bp); + pvt->net.n_name = bp; +#else pvt->net.n_name = strcpy(bp, name); +#endif bp += n; } break; case by_addr: if (addr != NULL && addrlen != 0) { int n = addrlen / 8 + ((addrlen % 8) != 0); if (INADDRSZ > (ep - bp)) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } memset(bp, 0, INADDRSZ); memcpy(bp, addr, n); pvt->net.n_addr = bp; bp += INADDRSZ; } break; default: abort(); } /* Parse the answer, collect aliases. */ ap = pvt->ali; haveanswer = 0; while (--ancount >= 0 && cp < eom) { int n = dn_expand(ansbuf, eom, cp, bp, ep - bp); cp += n; /* Owner */ if (n < 0 || !maybe_dnok(pvt->res, bp) || cp + 3 * INT16SZ + INT32SZ > eom) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } GETSHORT(type, cp); /* Type */ GETSHORT(class, cp); /* Class */ cp += INT32SZ; /* TTL */ GETSHORT(n, cp); /* RDLENGTH */ if (class == C_IN && type == T_PTR) { int nn; nn = dn_expand(ansbuf, eom, cp, bp, ep - bp); if (nn < 0 || !maybe_hnok(pvt->res, bp) || nn != n) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } normalize_name(bp); switch (by_what) { case by_addr: { if (pvt->net.n_name == NULL) pvt->net.n_name = bp; else if (ns_samename(pvt->net.n_name, bp) == 1) break; else *ap++ = bp; nn = strlen(bp) + 1; bp += nn; haveanswer++; break; } case by_name: { u_int b1, b2, b3, b4; if (pvt->net.n_addr != NULL || sscanf(bp, "%u.%u.%u.%u.in-addr.arpa", &b1, &b2, &b3, &b4) != 4) break; if ((ep - bp) < INADDRSZ) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } pvt->net.n_addr = bp; *bp++ = b4; *bp++ = b3; *bp++ = b2; *bp++ = b1; pvt->net.n_length = INADDRSZ * 8; haveanswer++; } } } cp += n; /* RDATA */ } if (!haveanswer) { RES_SET_H_ERRNO(pvt->res, TRY_AGAIN); return (NULL); } *ap = NULL; return (&pvt->net); } static struct nwent * get1101mask(struct irs_nw *this, struct nwent *nwent) { struct pvt *pvt = (struct pvt *)this->private; char qbuf[sizeof "255.255.255.255.in-addr.arpa"], owner[MAXDNAME]; int anslen, type, class, ancount, qdcount; u_char *ansbuf, *cp, *eom; HEADER *hp; if (!nwent) return (NULL); if (make1101inaddr(nwent->n_addr, nwent->n_length, qbuf, sizeof qbuf) < 0) { /* "First, do no harm." */ return (nwent); } ansbuf = memget(MAXPACKET); if (ansbuf == NULL) { errno = ENOMEM; RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); return (NULL); } /* Query for the A RR that would hold this network's mask. */ anslen = res_nquery(pvt->res, qbuf, C_IN, T_A, ansbuf, MAXPACKET); if (anslen < HFIXEDSZ) { memput(ansbuf, MAXPACKET); return (nwent); } /* Initialize, and parse header. */ hp = (HEADER *)ansbuf; cp = ansbuf + HFIXEDSZ; eom = ansbuf + anslen; qdcount = ntohs(hp->qdcount); while (qdcount-- > 0) { int n = dn_skipname(cp, eom); cp += n + QFIXEDSZ; if (n < 0 || cp > eom) { memput(ansbuf, MAXPACKET); return (nwent); } } ancount = ntohs(hp->ancount); /* Parse the answer, collect aliases. */ while (--ancount >= 0 && cp < eom) { int n = dn_expand(ansbuf, eom, cp, owner, sizeof owner); if (n < 0 || !maybe_dnok(pvt->res, owner)) break; cp += n; /* Owner */ if (cp + 3 * INT16SZ + INT32SZ > eom) break; GETSHORT(type, cp); /* Type */ GETSHORT(class, cp); /* Class */ cp += INT32SZ; /* TTL */ GETSHORT(n, cp); /* RDLENGTH */ if (cp + n > eom) break; if (n == INADDRSZ && class == C_IN && type == T_A && ns_samename(qbuf, owner) == 1) { /* This A RR indicates the actual netmask. */ int nn, mm; nwent->n_length = 0; for (nn = 0; nn < INADDRSZ; nn++) for (mm = 7; mm >= 0; mm--) if (cp[nn] & (1 << mm)) nwent->n_length++; else break; } cp += n; /* RDATA */ } memput(ansbuf, MAXPACKET); return (nwent); } static int make1101inaddr(const u_char *net, int bits, char *name, int size) { int n, m; + char *ep; + + ep = name + size; /* Zero fill any whole bytes left out of the prefix. */ for (n = (32 - bits) / 8; n > 0; n--) { - if (size < (int)(sizeof "0.")) + if (ep - name < (int)(sizeof "0.")) goto emsgsize; m = SPRINTF((name, "0.")); name += m; - size -= m; } /* Format the partial byte, if any, within the prefix. */ if ((n = bits % 8) != 0) { - if (size < (int)(sizeof "255.")) + if (ep - name < (int)(sizeof "255.")) goto emsgsize; m = SPRINTF((name, "%u.", net[bits / 8] & ~((1 << (8 - n)) - 1))); name += m; - size -= m; } /* Format the whole bytes within the prefix. */ for (n = bits / 8; n > 0; n--) { - if (size < (int)(sizeof "255.")) + if (ep - name < (int)(sizeof "255.")) goto emsgsize; m = SPRINTF((name, "%u.", net[n - 1])); name += m; - size -= m; } /* Add the static text. */ - if (size < (int)(sizeof "in-addr.arpa")) + if (ep - name < (int)(sizeof "in-addr.arpa")) goto emsgsize; (void) SPRINTF((name, "in-addr.arpa")); return (0); emsgsize: errno = EMSGSIZE; return (-1); } static void normalize_name(char *name) { char *t; /* Make lower case. */ for (t = name; *t; t++) if (isascii((unsigned char)*t) && isupper((unsigned char)*t)) *t = tolower(*t); /* Remove trailing dots. */ while (t > name && t[-1] == '.') *--t = '\0'; } static int init(struct irs_nw *this) { struct pvt *pvt = (struct pvt *)this->private; if (!pvt->res && !nw_res_get(this)) return (-1); if (((pvt->res->options & RES_INIT) == 0) && res_ninit(pvt->res) == -1) return (-1); return (0); } diff --git a/contrib/bind/lib/irs/gen_gr.c b/contrib/bind/lib/irs/gen_gr.c index 4b1b282fe0c8..9a7af3df02a1 100644 --- a/contrib/bind/lib/irs/gen_gr.c +++ b/contrib/bind/lib/irs/gen_gr.c @@ -1,469 +1,482 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: gen_gr.c,v 1.25 2001/06/07 02:12:26 marka Exp $"; +static const char rcsid[] = "$Id: gen_gr.c,v 1.26 2002/07/18 02:07:44 marka Exp $"; #endif /* Imports */ #include "port_before.h" #ifndef WANT_IRS_GR static int __bind_irs_gr_unneeded; #else #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "gen_p.h" /* Definitions */ struct pvt { struct irs_rule * rules; struct irs_rule * rule; struct irs_gr * gr; /* * Need space to store the entries read from the group file. * The members list also needs space per member, and the * strings making up the user names must be allocated * somewhere. Rather than doing lots of small allocations, * we keep one buffer and resize it as needed. */ struct group group; size_t nmemb; /* Malloc'd max index of gr_mem[]. */ char * membuf; size_t membufsize; struct __res_state * res; void (*free_res)(void *); }; /* Forward */ static void gr_close(struct irs_gr *); static struct group * gr_next(struct irs_gr *); static struct group * gr_byname(struct irs_gr *, const char *); static struct group * gr_bygid(struct irs_gr *, gid_t); static void gr_rewind(struct irs_gr *); static int gr_list(struct irs_gr *, const char *, gid_t, gid_t *, int *); static void gr_minimize(struct irs_gr *); static struct __res_state * gr_res_get(struct irs_gr *); static void gr_res_set(struct irs_gr *, struct __res_state *, void (*)(void *)); static void grmerge(struct irs_gr *gr, const struct group *src, int preserve); static int countvec(char **vec); static int isnew(char **old, char *new); static int countnew(char **old, char **new); static size_t sizenew(char **old, char **new); static int newgid(int, gid_t *, gid_t); /* Public */ struct irs_gr * irs_gen_gr(struct irs_acc *this) { struct gen_p *accpvt = (struct gen_p *)this->private; struct irs_gr *gr; struct pvt *pvt; if (!(gr = memget(sizeof *gr))) { errno = ENOMEM; return (NULL); } memset(gr, 0x5e, sizeof *gr); if (!(pvt = memget(sizeof *pvt))) { memput(gr, sizeof *gr); errno = ENOMEM; return (NULL); } memset(pvt, 0, sizeof *pvt); pvt->rules = accpvt->map_rules[irs_gr]; pvt->rule = pvt->rules; gr->private = pvt; gr->close = gr_close; gr->next = gr_next; gr->byname = gr_byname; gr->bygid = gr_bygid; gr->rewind = gr_rewind; gr->list = gr_list; gr->minimize = gr_minimize; gr->res_get = gr_res_get; gr->res_set = gr_res_set; return (gr); } /* Methods. */ static void gr_close(struct irs_gr *this) { struct pvt *pvt = (struct pvt *)this->private; memput(pvt, sizeof *pvt); memput(this, sizeof *this); } static struct group * gr_next(struct irs_gr *this) { struct pvt *pvt = (struct pvt *)this->private; struct group *rval; struct irs_gr *gr; while (pvt->rule) { gr = pvt->rule->inst->gr; rval = (*gr->next)(gr); if (rval) return (rval); if (!(pvt->rule->flags & IRS_CONTINUE)) break; pvt->rule = pvt->rule->next; if (pvt->rule) { gr = pvt->rule->inst->gr; (*gr->rewind)(gr); } } return (NULL); } static struct group * gr_byname(struct irs_gr *this, const char *name) { struct pvt *pvt = (struct pvt *)this->private; struct irs_rule *rule; struct group *tval; struct irs_gr *gr; int dirty; dirty = 0; for (rule = pvt->rules; rule; rule = rule->next) { gr = rule->inst->gr; tval = (*gr->byname)(gr, name); if (tval) { grmerge(this, tval, dirty++); if (!(rule->flags & IRS_MERGE)) break; } else { if (!(rule->flags & IRS_CONTINUE)) break; } } if (dirty) return (&pvt->group); return (NULL); } static struct group * gr_bygid(struct irs_gr *this, gid_t gid) { struct pvt *pvt = (struct pvt *)this->private; struct irs_rule *rule; struct group *tval; struct irs_gr *gr; int dirty; dirty = 0; for (rule = pvt->rules; rule; rule = rule->next) { gr = rule->inst->gr; tval = (*gr->bygid)(gr, gid); if (tval) { grmerge(this, tval, dirty++); if (!(rule->flags & IRS_MERGE)) break; } else { if (!(rule->flags & IRS_CONTINUE)) break; } } if (dirty) return (&pvt->group); return (NULL); } static void gr_rewind(struct irs_gr *this) { struct pvt *pvt = (struct pvt *)this->private; struct irs_gr *gr; pvt->rule = pvt->rules; if (pvt->rule) { gr = pvt->rule->inst->gr; (*gr->rewind)(gr); } } static int gr_list(struct irs_gr *this, const char *name, gid_t basegid, gid_t *groups, int *ngroups) { struct pvt *pvt = (struct pvt *)this->private; struct irs_rule *rule; struct irs_gr *gr; int t_ngroups, maxgroups; gid_t *t_groups; int n, t, rval = 0; maxgroups = *ngroups; *ngroups = 0; t_groups = (gid_t *)malloc(maxgroups * sizeof(gid_t)); if (!t_groups) { errno = ENOMEM; return (-1); } for (rule = pvt->rules; rule; rule = rule->next) { t_ngroups = maxgroups; gr = rule->inst->gr; t = (*gr->list)(gr, name, basegid, t_groups, &t_ngroups); for (n = 0; n < t_ngroups; n++) { if (newgid(*ngroups, groups, t_groups[n])) { if (*ngroups == maxgroups) { rval = -1; goto done; } groups[(*ngroups)++] = t_groups[n]; } } if (t == 0) { if (!(rule->flags & IRS_MERGE)) break; } else { if (!(rule->flags & IRS_CONTINUE)) break; } } done: free(t_groups); return (rval); } static void gr_minimize(struct irs_gr *this) { struct pvt *pvt = (struct pvt *)this->private; struct irs_rule *rule; for (rule = pvt->rules; rule != NULL; rule = rule->next) { struct irs_gr *gr = rule->inst->gr; (*gr->minimize)(gr); } } static struct __res_state * gr_res_get(struct irs_gr *this) { struct pvt *pvt = (struct pvt *)this->private; if (!pvt->res) { struct __res_state *res; res = (struct __res_state *)malloc(sizeof *res); if (!res) { errno = ENOMEM; return (NULL); } memset(res, 0, sizeof *res); gr_res_set(this, res, free); } return (pvt->res); } static void gr_res_set(struct irs_gr *this, struct __res_state *res, void (*free_res)(void *)) { struct pvt *pvt = (struct pvt *)this->private; struct irs_rule *rule; if (pvt->res && pvt->free_res) { res_nclose(pvt->res); (*pvt->free_res)(pvt->res); } pvt->res = res; pvt->free_res = free_res; for (rule = pvt->rules; rule != NULL; rule = rule->next) { struct irs_gr *gr = rule->inst->gr; if (gr->res_set) (*gr->res_set)(gr, pvt->res, NULL); } } /* Private. */ static void grmerge(struct irs_gr *this, const struct group *src, int preserve) { struct pvt *pvt = (struct pvt *)this->private; - char *cp, **m, **p, *oldmembuf; + char *cp, **m, **p, *oldmembuf, *ep; int n, ndst, nnew; size_t used; if (!preserve) { pvt->group.gr_gid = src->gr_gid; if (pvt->nmemb < 1) { m = malloc(sizeof *m); if (!m) { /* No harm done, no work done. */ return; } pvt->group.gr_mem = m; pvt->nmemb = 1; } pvt->group.gr_mem[0] = NULL; } ndst = countvec(pvt->group.gr_mem); nnew = countnew(pvt->group.gr_mem, src->gr_mem); /* * Make sure destination member array is large enough. * p points to new portion. */ n = ndst + nnew + 1; if ((size_t)n > pvt->nmemb) { m = realloc(pvt->group.gr_mem, n * sizeof *m); if (!m) { /* No harm done, no work done. */ return; } pvt->group.gr_mem = m; pvt->nmemb = n; } p = pvt->group.gr_mem + ndst; /* * Enlarge destination membuf; cp points at new portion. */ n = sizenew(pvt->group.gr_mem, src->gr_mem); INSIST((nnew == 0) == (n == 0)); if (!preserve) { n += strlen(src->gr_name) + 1; n += strlen(src->gr_passwd) + 1; } if (n == 0) { /* No work to do. */ return; } used = preserve ? pvt->membufsize : 0; cp = malloc(used + n); if (!cp) { /* No harm done, no work done. */ return; } + ep = cp + used + n; if (used != 0) memcpy(cp, pvt->membuf, used); oldmembuf = pvt->membuf; pvt->membuf = cp; pvt->membufsize = used + n; cp += used; /* * Adjust group.gr_mem. */ if (pvt->membuf != oldmembuf) for (m = pvt->group.gr_mem; *m; m++) *m = pvt->membuf + (*m - oldmembuf); /* * Add new elements. */ for (m = src->gr_mem; *m; m++) if (isnew(pvt->group.gr_mem, *m)) { *p++ = cp; *p = NULL; +#ifdef HAVE_STRLCPY + strlcpy(cp, *m, ep - cp); +#else strcpy(cp, *m); +#endif cp += strlen(cp) + 1; } if (preserve) { pvt->group.gr_name = pvt->membuf + (pvt->group.gr_name - oldmembuf); pvt->group.gr_passwd = pvt->membuf + (pvt->group.gr_passwd - oldmembuf); } else { pvt->group.gr_name = cp; +#ifdef HAVE_STRLCPY + strlcpy(cp, src->gr_name, ep - cp); +#else strcpy(cp, src->gr_name); +#endif cp += strlen(src->gr_name) + 1; pvt->group.gr_passwd = cp; +#ifdef HAVE_STRLCPY + strlcpy(cp, src->gr_passwd, ep - cp); +#else strcpy(cp, src->gr_passwd); +#endif cp += strlen(src->gr_passwd) + 1; } if (oldmembuf != NULL) free(oldmembuf); INSIST(cp >= pvt->membuf && cp <= &pvt->membuf[pvt->membufsize]); } static int countvec(char **vec) { int n = 0; while (*vec++) n++; return (n); } static int isnew(char **old, char *new) { for (; *old; old++) if (strcmp(*old, new) == 0) return (0); return (1); } static int countnew(char **old, char **new) { int n = 0; for (; *new; new++) n += isnew(old, *new); return (n); } static size_t sizenew(char **old, char **new) { size_t n = 0; for (; *new; new++) if (isnew(old, *new)) n += strlen(*new) + 1; return (n); } static int newgid(int ngroups, gid_t *groups, gid_t group) { ngroups--, groups++; for (; ngroups-- > 0; groups++) if (*groups == group) return (0); return (1); } #endif /* WANT_IRS_GR */ diff --git a/contrib/bind/lib/irs/getaddrinfo.c b/contrib/bind/lib/irs/getaddrinfo.c index 243f10656c3c..89db519fcfd7 100644 --- a/contrib/bind/lib/irs/getaddrinfo.c +++ b/contrib/bind/lib/irs/getaddrinfo.c @@ -1,1218 +1,1229 @@ /* $KAME: getaddrinfo.c,v 1.14 2001/01/06 09:41:15 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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. */ /* * Issues to be discussed: * - Thread safe-ness must be checked. * - Return values. There are nonstandard return values defined and used * in the source code. This is because RFC2553 is silent about which error * code must be returned for which situation. * - IPv4 classful (shortened) form. RFC2553 is silent about it. XNET 5.2 * says to use inet_aton() to convert IPv4 numeric to binary (allows * classful form as a result). * current code - disallow classful form for IPv4 (due to use of inet_pton). * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is * invalid. * current code - SEGV on freeaddrinfo(NULL) * Note: * - We use getipnodebyname() just for thread-safeness. There's no intent * to let it do PF_UNSPEC (actually we never pass PF_UNSPEC to * getipnodebyname(). * - The code filters out AFs that are not supported by the kernel, * when globbing NULL hostname (to loopback, or wildcard). Is it the right * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG * in ai_flags? * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague. * (1) what should we do against numeric hostname (2) what should we do * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready? * non-loopback address configured? global address configured? * - To avoid search order issue, we have a big amount of code duplicate * from gethnamaddr.c and some other places. The issues that there's no * lower layer function to lookup "IPv4 or IPv6" record. Calling * gethostbyname2 from getaddrinfo will end up in wrong search order, as * follows: * - The code makes use of following calls when asked to resolver with * ai_family = PF_UNSPEC: * getipnodebyname(host, AF_INET6); * getipnodebyname(host, AF_INET); * This will result in the following queries if the node is configure to * prefer /etc/hosts than DNS: * lookup /etc/hosts for IPv6 address * lookup DNS for IPv6 address * lookup /etc/hosts for IPv4 address * lookup DNS for IPv4 address * which may not meet people's requirement. * The right thing to happen is to have underlying layer which does * PF_UNSPEC lookup (lookup both) and return chain of addrinfos. * This would result in a bit of code duplicate with _dns_ghbyname() and * friends. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "port_after.h" #include "irs_data.h" -/* - * if we enable it, we will see duplicated addrinfo entries on reply if both - * AAAA and A6 records are found. disable it for default installation. - */ -#undef T_A6 - #define SUCCESS 0 #define ANY 0 #define YES 1 #define NO 0 static const char in_addrany[] = { 0, 0, 0, 0 }; static const char in6_addrany[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static const char in_loopback[] = { 127, 0, 0, 1 }; static const char in6_loopback[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; static const struct afd { int a_af; int a_addrlen; int a_socklen; int a_off; const char *a_addrany; const char *a_loopback; int a_scoped; } afdl [] = { {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), offsetof(struct sockaddr_in6, sin6_addr), in6_addrany, in6_loopback, 1}, {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), offsetof(struct sockaddr_in, sin_addr), in_addrany, in_loopback, 0}, {0, 0, 0, 0, NULL, NULL, 0}, }; struct explore { int e_af; int e_socktype; int e_protocol; const char *e_protostr; int e_wild; #define WILD_AF(ex) ((ex)->e_wild & 0x01) #define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02) #define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04) }; static const struct explore explore[] = { #if 0 { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 }, #endif { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, { -1, 0, 0, NULL, 0 }, }; #define PTON_MAX 16 static int str_isnumber __P((const char *)); static int explore_fqdn __P((const struct addrinfo *, const char *, const char *, struct addrinfo **)); static int explore_copy __P((const struct addrinfo *, const struct addrinfo *, struct addrinfo **)); static int explore_null __P((const struct addrinfo *, const char *, struct addrinfo **)); static int explore_numeric __P((const struct addrinfo *, const char *, const char *, struct addrinfo **)); static int explore_numeric_scope __P((const struct addrinfo *, const char *, const char *, struct addrinfo **)); static int get_canonname __P((const struct addrinfo *, struct addrinfo *, const char *)); static struct addrinfo *get_ai __P((const struct addrinfo *, const struct afd *, const char *)); static struct addrinfo *copy_ai __P((const struct addrinfo *)); static int get_portmatch __P((const struct addrinfo *, const char *)); static int get_port __P((const struct addrinfo *, const char *, int)); static const struct afd *find_afd __P((int)); static int addrconfig __P((int)); -static int ip6_str2scopeid __P((char *, struct sockaddr_in6 *)); +static int ip6_str2scopeid __P((char *, struct sockaddr_in6 *, + u_int32_t *scopeidp)); static struct net_data *init __P((void)); struct addrinfo *hostent2addrinfo __P((struct hostent *, const struct addrinfo *)); struct addrinfo *addr2addrinfo __P((const struct addrinfo *, const char *)); #if 0 static const char *ai_errlist[] = { "Success", "Address family for hostname not supported", /* EAI_ADDRFAMILY */ "Temporary failure in name resolution", /* EAI_AGAIN */ "Invalid value for ai_flags", /* EAI_BADFLAGS */ "Non-recoverable failure in name resolution", /* EAI_FAIL */ "ai_family not supported", /* EAI_FAMILY */ "Memory allocation failure", /* EAI_MEMORY */ "No address associated with hostname", /* EAI_NODATA */ "hostname nor servname provided, or not known", /* EAI_NONAME */ "servname not supported for ai_socktype", /* EAI_SERVICE */ "ai_socktype not supported", /* EAI_SOCKTYPE */ "System error returned in errno", /* EAI_SYSTEM */ "Invalid value for hints", /* EAI_BADHINTS */ "Resolved protocol is unknown", /* EAI_PROTOCOL */ "Unknown error", /* EAI_MAX */ }; #endif /* XXX macros that make external reference is BAD. */ #define GET_AI(ai, afd, addr) \ do { \ /* external reference: pai, error, and label free */ \ (ai) = get_ai(pai, (afd), (addr)); \ if ((ai) == NULL) { \ error = EAI_MEMORY; \ goto free; \ } \ } while (/*CONSTCOND*/0) #define GET_PORT(ai, serv) \ do { \ /* external reference: error and label free */ \ error = get_port((ai), (serv), 0); \ if (error != 0) \ goto free; \ } while (/*CONSTCOND*/0) #define GET_CANONNAME(ai, str) \ do { \ /* external reference: pai, error and label free */ \ error = get_canonname(pai, (ai), (str)); \ if (error != 0) \ goto free; \ } while (/*CONSTCOND*/0) #define ERR(err) \ do { \ /* external reference: error, and label bad */ \ error = (err); \ goto bad; \ /*NOTREACHED*/ \ } while (/*CONSTCOND*/0) #define MATCH_FAMILY(x, y, w) \ ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC))) #define MATCH(x, y, w) \ ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY))) #if 0 /* bind8 has its own version */ char * gai_strerror(ecode) int ecode; { if (ecode < 0 || ecode > EAI_MAX) ecode = EAI_MAX; return ai_errlist[ecode]; } #endif void freeaddrinfo(ai) struct addrinfo *ai; { struct addrinfo *next; do { next = ai->ai_next; if (ai->ai_canonname) free(ai->ai_canonname); /* no need to free(ai->ai_addr) */ free(ai); ai = next; } while (ai); } static int str_isnumber(p) const char *p; { char *ep; if (*p == '\0') return NO; ep = NULL; + errno = 0; (void)strtoul(p, &ep, 10); - if (ep && *ep == '\0') + if (errno == 0 && ep && *ep == '\0') return YES; else return NO; } int getaddrinfo(hostname, servname, hints, res) const char *hostname, *servname; const struct addrinfo *hints; struct addrinfo **res; { struct addrinfo sentinel; struct addrinfo *cur; int error = 0; struct addrinfo ai, ai0, *afai = NULL; struct addrinfo *pai; const struct explore *ex; memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; pai = &ai; pai->ai_flags = 0; pai->ai_family = PF_UNSPEC; pai->ai_socktype = ANY; pai->ai_protocol = ANY; pai->ai_addrlen = 0; pai->ai_canonname = NULL; pai->ai_addr = NULL; pai->ai_next = NULL; if (hostname == NULL && servname == NULL) return EAI_NONAME; if (hints) { /* error check for hints */ if (hints->ai_addrlen || hints->ai_canonname || hints->ai_addr || hints->ai_next) ERR(EAI_BADHINTS); /* xxx */ if (hints->ai_flags & ~AI_MASK) ERR(EAI_BADFLAGS); switch (hints->ai_family) { case PF_UNSPEC: case PF_INET: case PF_INET6: break; default: ERR(EAI_FAMILY); } memcpy(pai, hints, sizeof(*pai)); /* * if both socktype/protocol are specified, check if they * are meaningful combination. */ if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { for (ex = explore; ex->e_af >= 0; ex++) { if (pai->ai_family != ex->e_af) continue; if (ex->e_socktype == ANY) continue; if (ex->e_protocol == ANY) continue; if (pai->ai_socktype == ex->e_socktype && pai->ai_protocol != ex->e_protocol) { ERR(EAI_BADHINTS); } } } } /* * post-2553: AI_ALL and AI_V4MAPPED are effective only against * AF_INET6 query. They needs to be ignored if specified in other * occassions. */ switch (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) { case AI_V4MAPPED: case AI_ALL | AI_V4MAPPED: if (pai->ai_family != AF_INET6) pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); break; case AI_ALL: #if 1 /* illegal */ ERR(EAI_BADFLAGS); #else pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); break; #endif } /* * check for special cases. (1) numeric servname is disallowed if * socktype/protocol are left unspecified. (2) servname is disallowed * for raw and other inet{,6} sockets. */ if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) #ifdef PF_INET6 || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) #endif ) { ai0 = *pai; /* backup *pai */ if (pai->ai_family == PF_UNSPEC) { #ifdef PF_INET6 pai->ai_family = PF_INET6; #else pai->ai_family = PF_INET; #endif } error = get_portmatch(pai, servname); if (error) ERR(error); *pai = ai0; } ai0 = *pai; /* NULL hostname, or numeric hostname */ for (ex = explore; ex->e_af >= 0; ex++) { *pai = ai0; if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) continue; if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) continue; if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) continue; if (pai->ai_family == PF_UNSPEC) pai->ai_family = ex->e_af; if (pai->ai_socktype == ANY && ex->e_socktype != ANY) pai->ai_socktype = ex->e_socktype; if (pai->ai_protocol == ANY && ex->e_protocol != ANY) pai->ai_protocol = ex->e_protocol; /* * if the servname does not match socktype/protocol, ignore it. */ if (get_portmatch(pai, servname) != 0) continue; if (hostname == NULL) { /* * filter out AFs that are not supported by the kernel * XXX errno? */ if (!addrconfig(pai->ai_family)) continue; error = explore_null(pai, servname, &cur->ai_next); } else error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next); if (error) goto free; while (cur && cur->ai_next) cur = cur->ai_next; } /* * XXX * If numreic representation of AF1 can be interpreted as FQDN * representation of AF2, we need to think again about the code below. */ if (sentinel.ai_next) goto good; if (pai->ai_flags & AI_NUMERICHOST) ERR(EAI_NONAME); if (hostname == NULL) ERR(EAI_NONAME); /* * hostname as alphabetical name. * We'll make sure that * - if returning addrinfo list is empty, return non-zero error * value (already known one or EAI_NONAME). * - otherwise, * + if we haven't had any errors, return 0 (i.e. success). * + if we've had an error, free the list and return the error. * without any assumption on the behavior of explore_fqdn(). */ /* first, try to query DNS for all possible address families. */ *pai = ai0; error = explore_fqdn(pai, hostname, servname, &afai); if (error) { if (afai != NULL) freeaddrinfo(afai); goto free; } if (afai == NULL) { error = EAI_NONAME; /* we've had no errors. */ goto free; } /* * we would like to prefer AF_INET6 than AF_INET, so we'll make an * outer loop by AFs. */ for (ex = explore; ex->e_af >= 0; ex++) { *pai = ai0; if (pai->ai_family == PF_UNSPEC) pai->ai_family = ex->e_af; if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) continue; if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) { continue; } if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) { continue; } #ifdef AI_ADDRCONFIG /* * If AI_ADDRCONFIG is specified, check if we are * expected to return the address family or not. */ if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(pai->ai_family)) continue; #endif if (pai->ai_family == PF_UNSPEC) pai->ai_family = ex->e_af; if (pai->ai_socktype == ANY && ex->e_socktype != ANY) pai->ai_socktype = ex->e_socktype; if (pai->ai_protocol == ANY && ex->e_protocol != ANY) pai->ai_protocol = ex->e_protocol; /* * if the servname does not match socktype/protocol, ignore it. */ if (get_portmatch(pai, servname) != 0) continue; if ((error = explore_copy(pai, afai, &cur->ai_next)) != 0) { freeaddrinfo(afai); goto free; } while (cur && cur->ai_next) cur = cur->ai_next; } freeaddrinfo(afai); /* afai must not be NULL at this point. */ /* we must not have got any errors. */ if (error != 0) /* just for diagnosis */ abort(); if (sentinel.ai_next) { good: *res = sentinel.ai_next; return(SUCCESS); } else { /* * All the process succeeded, but we've had an empty list. * This can happen if the given hints do not match our * candidates. */ error = EAI_NONAME; } free: bad: if (sentinel.ai_next) freeaddrinfo(sentinel.ai_next); *res = NULL; return(error); } /* * FQDN hostname, DNS lookup */ static int explore_fqdn(pai, hostname, servname, res) const struct addrinfo *pai; const char *hostname; const char *servname; struct addrinfo **res; { struct addrinfo *result; struct addrinfo *cur; struct net_data *net_data = init(); struct irs_ho *ho; int error = 0; char tmp[NS_MAXDNAME]; const char *cp; - result = NULL; + INSIST(res != NULL && *res == NULL); /* * if the servname does not match socktype/protocol, ignore it. */ if (get_portmatch(pai, servname) != 0) return(0); if (!net_data || !(ho = net_data->ho)) return(0); #if 0 /* XXX (notyet) */ if (net_data->ho_stayopen && net_data->ho_last && net_data->ho_last->h_addrtype == af) { if (ns_samename(name, net_data->ho_last->h_name) == 1) return (net_data->ho_last); for (hap = net_data->ho_last->h_aliases; hap && *hap; hap++) if (ns_samename(name, *hap) == 1) return (net_data->ho_last); } #endif if (!strchr(hostname, '.') && (cp = res_hostalias(net_data->res, hostname, tmp, sizeof(tmp)))) hostname = cp; result = (*ho->addrinfo)(ho, hostname, pai); if (!net_data->ho_stayopen) { (*ho->minimize)(ho); } if (result == NULL) { int e = h_errno; switch(e) { case NETDB_INTERNAL: error = EAI_SYSTEM; break; case TRY_AGAIN: error = EAI_AGAIN; break; case NO_RECOVERY: error = EAI_FAIL; break; case HOST_NOT_FOUND: case NO_DATA: error = EAI_NONAME; break; default: case NETDB_SUCCESS: /* should be impossible... */ error = EAI_NONAME; break; } goto free; } for (cur = result; cur; cur = cur->ai_next) { GET_PORT(cur, servname); /* XXX: redundant lookups... */ /* canonname should already be filled. */ } *res = result; return(0); free: if (result) freeaddrinfo(result); return error; } static int explore_copy(pai, src0, res) const struct addrinfo *pai; /* seed */ const struct addrinfo *src0; /* source */ struct addrinfo **res; { int error; struct addrinfo sentinel, *cur; const struct addrinfo *src; error = 0; sentinel.ai_next = NULL; cur = &sentinel; for (src = src0; src != NULL; src = src->ai_next) { if (src->ai_family != pai->ai_family) continue; cur->ai_next = copy_ai(src); if (!cur->ai_next) { error = EAI_MEMORY; goto fail; } cur->ai_next->ai_socktype = pai->ai_socktype; cur->ai_next->ai_protocol = pai->ai_protocol; cur = cur->ai_next; } *res = sentinel.ai_next; return 0; fail: freeaddrinfo(sentinel.ai_next); return error; } /* * hostname == NULL. * passive socket -> anyaddr (0.0.0.0 or ::) * non-passive socket -> localhost (127.0.0.1 or ::1) */ static int explore_null(pai, servname, res) const struct addrinfo *pai; const char *servname; struct addrinfo **res; { const struct afd *afd; struct addrinfo *cur; struct addrinfo sentinel; int error; *res = NULL; sentinel.ai_next = NULL; cur = &sentinel; afd = find_afd(pai->ai_family); if (afd == NULL) return 0; if (pai->ai_flags & AI_PASSIVE) { GET_AI(cur->ai_next, afd, afd->a_addrany); /* xxx meaningless? * GET_CANONNAME(cur->ai_next, "anyaddr"); */ GET_PORT(cur->ai_next, servname); } else { GET_AI(cur->ai_next, afd, afd->a_loopback); /* xxx meaningless? * GET_CANONNAME(cur->ai_next, "localhost"); */ GET_PORT(cur->ai_next, servname); } cur = cur->ai_next; *res = sentinel.ai_next; return 0; free: if (sentinel.ai_next) freeaddrinfo(sentinel.ai_next); return error; } /* * numeric hostname */ static int explore_numeric(pai, hostname, servname, res) const struct addrinfo *pai; const char *hostname; const char *servname; struct addrinfo **res; { const struct afd *afd; struct addrinfo *cur; struct addrinfo sentinel; int error; char pton[PTON_MAX]; *res = NULL; sentinel.ai_next = NULL; cur = &sentinel; afd = find_afd(pai->ai_family); if (afd == NULL) return 0; switch (afd->a_af) { #if 0 /*X/Open spec*/ case AF_INET: if (inet_aton(hostname, (struct in_addr *)pton) == 1) { if (pai->ai_family == afd->a_af || pai->ai_family == PF_UNSPEC /*?*/) { GET_AI(cur->ai_next, afd, pton); GET_PORT(cur->ai_next, servname); while (cur && cur->ai_next) cur = cur->ai_next; } else ERR(EAI_FAMILY); /*xxx*/ } break; #endif default: if (inet_pton(afd->a_af, hostname, pton) == 1) { if (pai->ai_family == afd->a_af || pai->ai_family == PF_UNSPEC /*?*/) { GET_AI(cur->ai_next, afd, pton); GET_PORT(cur->ai_next, servname); while (cur && cur->ai_next) cur = cur->ai_next; } else ERR(EAI_FAMILY); /*xxx*/ } break; } *res = sentinel.ai_next; return 0; free: bad: if (sentinel.ai_next) freeaddrinfo(sentinel.ai_next); return error; } /* * numeric hostname with scope */ static int explore_numeric_scope(pai, hostname, servname, res) const struct addrinfo *pai; const char *hostname; const char *servname; struct addrinfo **res; { #ifndef SCOPE_DELIMITER return explore_numeric(pai, hostname, servname, res); #else const struct afd *afd; struct addrinfo *cur; int error; char *cp, *hostname2 = NULL, *scope, *addr; struct sockaddr_in6 *sin6; afd = find_afd(pai->ai_family); if (afd == NULL) return 0; if (!afd->a_scoped) return explore_numeric(pai, hostname, servname, res); cp = strchr(hostname, SCOPE_DELIMITER); if (cp == NULL) return explore_numeric(pai, hostname, servname, res); /* * Handle special case of */ hostname2 = strdup(hostname); if (hostname2 == NULL) return EAI_MEMORY; /* terminate at the delimiter */ hostname2[cp - hostname] = '\0'; addr = hostname2; scope = cp + 1; error = explore_numeric(pai, addr, servname, res); if (error == 0) { - int scopeid; + u_int32_t scopeid = 0; for (cur = *res; cur; cur = cur->ai_next) { if (cur->ai_family != AF_INET6) continue; sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr; - if ((scopeid = ip6_str2scopeid(scope, sin6)) == -1) { + if (!ip6_str2scopeid(scope, sin6, &scopeid)) { free(hostname2); return(EAI_NONAME); /* XXX: is return OK? */ } #ifdef HAVE_SIN6_SCOPE_ID sin6->sin6_scope_id = scopeid; #endif } } free(hostname2); return error; #endif } static int get_canonname(pai, ai, str) const struct addrinfo *pai; struct addrinfo *ai; const char *str; { if ((pai->ai_flags & AI_CANONNAME) != 0) { ai->ai_canonname = (char *)malloc(strlen(str) + 1); if (ai->ai_canonname == NULL) return EAI_MEMORY; strcpy(ai->ai_canonname, str); } return 0; } static struct addrinfo * get_ai(pai, afd, addr) const struct addrinfo *pai; const struct afd *afd; const char *addr; { char *p; struct addrinfo *ai; ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) + (afd->a_socklen)); if (ai == NULL) return NULL; memcpy(ai, pai, sizeof(struct addrinfo)); ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); memset(ai->ai_addr, 0, (size_t)afd->a_socklen); #ifdef HAVE_SA_LEN ai->ai_addr->sa_len = afd->a_socklen; #endif ai->ai_addrlen = afd->a_socklen; ai->ai_addr->sa_family = ai->ai_family = afd->a_af; p = (char *)(void *)(ai->ai_addr); memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); return ai; } /* XXX need to malloc() the same way we do from other functions! */ static struct addrinfo * copy_ai(pai) const struct addrinfo *pai; { struct addrinfo *ai; size_t l; l = sizeof(*ai) + pai->ai_addrlen; if ((ai = (struct addrinfo *)malloc(l)) == NULL) return NULL; memset(ai, 0, l); memcpy(ai, pai, sizeof(*ai)); ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); memcpy(ai->ai_addr, pai->ai_addr, pai->ai_addrlen); if (pai->ai_canonname) { l = strlen(pai->ai_canonname) + 1; if ((ai->ai_canonname = malloc(l)) == NULL) { free(ai); return NULL; } #ifdef HAVE_STRLCPY strlcpy(ai->ai_canonname, pai->ai_canonname, l); #else strncpy(ai->ai_canonname, pai->ai_canonname, l); #endif } else { /* just to make sure */ ai->ai_canonname = NULL; } ai->ai_next = NULL; return ai; } static int get_portmatch(const struct addrinfo *ai, const char *servname) { /* get_port does not touch first argument. when matchonly == 1. */ /* LINTED const cast */ return get_port((const struct addrinfo *)ai, servname, 1); } static int get_port(const struct addrinfo *ai, const char *servname, int matchonly) { const char *proto; struct servent *sp; int port; int allownumeric; if (servname == NULL) return 0; switch (ai->ai_family) { case AF_INET: #ifdef AF_INET6 case AF_INET6: #endif break; default: return 0; } switch (ai->ai_socktype) { case SOCK_RAW: return EAI_SERVICE; case SOCK_DGRAM: case SOCK_STREAM: allownumeric = 1; break; case ANY: - allownumeric = 0; + switch (ai->ai_family) { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif + allownumeric = 1; + break; + default: + allownumeric = 0; + break; + } break; default: return EAI_SOCKTYPE; } if (str_isnumber(servname)) { if (!allownumeric) return EAI_SERVICE; - port = htons(atoi(servname)); + port = atoi(servname); if (port < 0 || port > 65535) return EAI_SERVICE; + port = htons(port); } else { switch (ai->ai_socktype) { case SOCK_DGRAM: proto = "udp"; break; case SOCK_STREAM: proto = "tcp"; break; default: proto = NULL; break; } if ((sp = getservbyname(servname, proto)) == NULL) return EAI_SERVICE; port = sp->s_port; } if (!matchonly) { switch (ai->ai_family) { case AF_INET: ((struct sockaddr_in *)(void *) ai->ai_addr)->sin_port = port; break; case AF_INET6: ((struct sockaddr_in6 *)(void *) ai->ai_addr)->sin6_port = port; break; } } return 0; } static const struct afd * find_afd(af) int af; { const struct afd *afd; if (af == PF_UNSPEC) return NULL; for (afd = afdl; afd->a_af; afd++) { if (afd->a_af == af) return afd; } return NULL; } /* * post-2553: AI_ADDRCONFIG check. if we use getipnodeby* as backend, backend * will take care of it. * the semantics of AI_ADDRCONFIG is not defined well. we are not sure * if the code is right or not. */ static int addrconfig(af) int af; { int s; /* XXX errno */ s = socket(af, SOCK_DGRAM, 0); if (s < 0) { if (errno != EMFILE) return 0; } else close(s); return 1; } /* convert a string to a scope identifier. XXX: IPv6 specific */ static int -ip6_str2scopeid(scope, sin6) - char *scope; - struct sockaddr_in6 *sin6; +ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, + u_int32_t *scopeidp) { - int scopeid; + u_int32_t scopeid; + u_long lscopeid; struct in6_addr *a6 = &sin6->sin6_addr; char *ep; - + /* empty scopeid portion is invalid */ if (*scope == '\0') - return -1; + return (0); #ifdef USE_IFNAMELINKID if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) { /* * Using interface names as link indices can be allowed * only when we can assume a one-to-one mappings between * links and interfaces. See comments in getnameinfo.c. */ scopeid = if_nametoindex(scope); if (scopeid == 0) - goto trynumeric; - return(scopeid); + *scopeidp = scopeid; + return (1); } #endif /* still unclear about literal, allow numeric only - placeholder */ if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6)) goto trynumeric; if (IN6_IS_ADDR_MC_ORGLOCAL(a6)) goto trynumeric; else goto trynumeric; /* global */ /* try to convert to a numeric id as a last resort */ trynumeric: - scopeid = (int)strtoul(scope, &ep, 10); - if (*ep == '\0') - return scopeid; - else - return -1; + errno = 0; + lscopeid = strtoul(scope, &ep, 10); + scopeid = lscopeid & 0xffffffff; + if (errno == 0 && ep && *ep == '\0' && scopeid == lscopeid) { + *scopeidp = scopeid; + return (1); + } else + return (0); } struct addrinfo * hostent2addrinfo(hp, pai) struct hostent *hp; const struct addrinfo *pai; { int i, af, error = 0; char **aplist = NULL, *ap; struct addrinfo sentinel, *cur; const struct afd *afd; af = hp->h_addrtype; if (pai->ai_family != AF_UNSPEC && af != pai->ai_family) return(NULL); afd = find_afd(af); if (afd == NULL) return(NULL); aplist = hp->h_addr_list; memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; for (i = 0; (ap = aplist[i]) != NULL; i++) { #if 0 /* the trick seems too much */ af = hp->h_addr_list; if (af == AF_INET6 && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) { af = AF_INET; ap = ap + sizeof(struct in6_addr) - sizeof(struct in_addr); } afd = find_afd(af); if (afd == NULL) continue; #endif /* 0 */ GET_AI(cur->ai_next, afd, ap); /* GET_PORT(cur->ai_next, servname); */ if ((pai->ai_flags & AI_CANONNAME) != 0) { /* * RFC2553 says that ai_canonname will be set only for * the first element. we do it for all the elements, * just for convenience. */ GET_CANONNAME(cur->ai_next, hp->h_name); } while (cur && cur->ai_next) /* no need to loop, actually. */ cur = cur->ai_next; continue; free: if (cur->ai_next) freeaddrinfo(cur->ai_next); cur->ai_next = NULL; /* continue, without tht pointer CUR advanced. */ } return(sentinel.ai_next); } struct addrinfo * addr2addrinfo(pai, cp) const struct addrinfo *pai; const char *cp; { const struct afd *afd; afd = find_afd(pai->ai_family); if (afd == NULL) return(NULL); return(get_ai(pai, afd, cp)); } static struct net_data * init() { struct net_data *net_data; if (!(net_data = net_data_init(NULL))) goto error; if (!net_data->ho) { net_data->ho = (*net_data->irs->ho_map)(net_data->irs); if (!net_data->ho || !net_data->res) { error: errno = EIO; if (net_data && net_data->res) RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); return (NULL); } (*net_data->ho->res_set)(net_data->ho, net_data->res, NULL); } return (net_data); } diff --git a/contrib/bind/lib/irs/gethostent.c b/contrib/bind/lib/irs/gethostent.c index 8e7e7179f66b..3964b5f13472 100644 --- a/contrib/bind/lib/irs/gethostent.c +++ b/contrib/bind/lib/irs/gethostent.c @@ -1,1042 +1,1069 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: gethostent.c,v 1.32 2002/05/27 06:50:55 marka Exp $"; +static const char rcsid[] = "$Id: gethostent.c,v 1.32.10.2 2003/06/04 01:09:43 marka Exp $"; #endif /* Imports */ #include "port_before.h" #if !defined(__BIND_NOSTATIC) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "irs_data.h" /* Definitions */ struct pvt { char * aliases[1]; char * addrs[2]; char addr[NS_IN6ADDRSZ]; char name[NS_MAXDNAME + 1]; struct hostent host; }; /* Forward */ static struct net_data *init(void); static void freepvt(struct net_data *); static struct hostent *fakeaddr(const char *, int, struct net_data *); /* Public */ struct hostent * gethostbyname(const char *name) { struct net_data *net_data = init(); return (gethostbyname_p(name, net_data)); } struct hostent * gethostbyname2(const char *name, int af) { struct net_data *net_data = init(); return (gethostbyname2_p(name, af, net_data)); } struct hostent * gethostbyaddr(const char *addr, int len, int af) { struct net_data *net_data = init(); return (gethostbyaddr_p(addr, len, af, net_data)); } struct hostent * gethostent() { struct net_data *net_data = init(); return (gethostent_p(net_data)); } void sethostent(int stayopen) { struct net_data *net_data = init(); sethostent_p(stayopen, net_data); } void endhostent() { struct net_data *net_data = init(); endhostent_p(net_data); } /* Shared private. */ struct hostent * gethostbyname_p(const char *name, struct net_data *net_data) { struct hostent *hp; if (!net_data) return (NULL); if (net_data->res->options & RES_USE_INET6) { hp = gethostbyname2_p(name, AF_INET6, net_data); if (hp) return (hp); } return (gethostbyname2_p(name, AF_INET, net_data)); } struct hostent * gethostbyname2_p(const char *name, int af, struct net_data *net_data) { struct irs_ho *ho; char tmp[NS_MAXDNAME]; struct hostent *hp; const char *cp; char **hap; if (!net_data || !(ho = net_data->ho)) return (NULL); if (net_data->ho_stayopen && net_data->ho_last && net_data->ho_last->h_addrtype == af) { if (ns_samename(name, net_data->ho_last->h_name) == 1) return (net_data->ho_last); for (hap = net_data->ho_last->h_aliases; hap && *hap; hap++) if (ns_samename(name, *hap) == 1) return (net_data->ho_last); } if (!strchr(name, '.') && (cp = res_hostalias(net_data->res, name, tmp, sizeof tmp))) name = cp; if ((hp = fakeaddr(name, af, net_data)) != NULL) return (hp); net_data->ho_last = (*ho->byname2)(ho, name, af); if (!net_data->ho_stayopen) endhostent(); return (net_data->ho_last); } struct hostent * gethostbyaddr_p(const char *addr, int len, int af, struct net_data *net_data) { struct irs_ho *ho; char **hap; if (!net_data || !(ho = net_data->ho)) return (NULL); if (net_data->ho_stayopen && net_data->ho_last && net_data->ho_last->h_length == len) for (hap = net_data->ho_last->h_addr_list; hap && *hap; hap++) if (!memcmp(addr, *hap, len)) return (net_data->ho_last); net_data->ho_last = (*ho->byaddr)(ho, addr, len, af); if (!net_data->ho_stayopen) endhostent(); return (net_data->ho_last); } struct hostent * gethostent_p(struct net_data *net_data) { struct irs_ho *ho; struct hostent *hp; if (!net_data || !(ho = net_data->ho)) return (NULL); while ((hp = (*ho->next)(ho)) != NULL && hp->h_addrtype == AF_INET6 && (net_data->res->options & RES_USE_INET6) == 0) continue; net_data->ho_last = hp; return (net_data->ho_last); } void sethostent_p(int stayopen, struct net_data *net_data) { struct irs_ho *ho; if (!net_data || !(ho = net_data->ho)) return; freepvt(net_data); (*ho->rewind)(ho); net_data->ho_stayopen = (stayopen != 0); if (stayopen == 0) net_data_minimize(net_data); } void endhostent_p(struct net_data *net_data) { struct irs_ho *ho; if ((net_data != NULL) && ((ho = net_data->ho) != NULL)) (*ho->minimize)(ho); } #ifndef IN6_IS_ADDR_V4COMPAT static const unsigned char in6addr_compat[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; #define IN6_IS_ADDR_V4COMPAT(x) (!memcmp((x)->s6_addr, in6addr_compat, 12) && \ ((x)->s6_addr[12] != 0 || \ (x)->s6_addr[13] != 0 || \ (x)->s6_addr[14] != 0 || \ ((x)->s6_addr[15] != 0 && \ (x)->s6_addr[15] != 1))) #endif #ifndef IN6_IS_ADDR_V4MAPPED #define IN6_IS_ADDR_V4MAPPED(x) (!memcmp((x)->s6_addr, in6addr_mapped, 12)) #endif static const unsigned char in6addr_mapped[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; static int scan_interfaces(int *, int *); static struct hostent *copyandmerge(struct hostent *, struct hostent *, int, int *); /* * Public functions */ /* * AI_V4MAPPED + AF_INET6 * If no IPv6 address then a query for IPv4 and map returned values. * * AI_ALL + AI_V4MAPPED + AF_INET6 * Return IPv6 and IPv4 mapped. * * AI_ADDRCONFIG * Only return IPv6 / IPv4 address if there is an interface of that * type active. */ struct hostent * getipnodebyname(const char *name, int af, int flags, int *error_num) { int have_v4 = 1, have_v6 = 1; struct in_addr in4; struct in6_addr in6; struct hostent he, *he1 = NULL, *he2 = NULL, *he3; int v4 = 0, v6 = 0; struct net_data *net_data = init(); u_long options; int tmp_err; if (net_data == NULL) { *error_num = NO_RECOVERY; return (NULL); } /* If we care about active interfaces then check. */ if ((flags & AI_ADDRCONFIG) != 0) if (scan_interfaces(&have_v4, &have_v6) == -1) { *error_num = NO_RECOVERY; return (NULL); } /* Check for literal address. */ if ((v4 = inet_pton(AF_INET, name, &in4)) != 1) v6 = inet_pton(AF_INET6, name, &in6); /* Impossible combination? */ if ((af == AF_INET6 && (flags & AI_V4MAPPED) == 0 && v4 == 1) || (af == AF_INET && v6 == 1) || (have_v4 == 0 && v4 == 1) || (have_v6 == 0 && v6 == 1) || (have_v4 == 0 && af == AF_INET) || (have_v6 == 0 && af == AF_INET6)) { *error_num = HOST_NOT_FOUND; return (NULL); } /* Literal address? */ if (v4 == 1 || v6 == 1) { char *addr_list[2]; char *aliases[1]; DE_CONST(name, he.h_name); he.h_addr_list = addr_list; he.h_addr_list[0] = (v4 == 1) ? (char *)&in4 : (char *)&in6; he.h_addr_list[1] = NULL; he.h_aliases = aliases; he.h_aliases[0] = NULL; he.h_length = (v4 == 1) ? INADDRSZ : IN6ADDRSZ; he.h_addrtype = (v4 == 1) ? AF_INET : AF_INET6; return (copyandmerge(&he, NULL, af, error_num)); } options = net_data->res->options; net_data->res->options &= ~RES_USE_INET6; tmp_err = NO_RECOVERY; if (have_v6 && af == AF_INET6) { he2 = gethostbyname2_p(name, AF_INET6, net_data); if (he2 != NULL) { he1 = copyandmerge(he2, NULL, af, error_num); if (he1 == NULL) return (NULL); he2 = NULL; } else { tmp_err = net_data->res->res_h_errno; } } if (have_v4 && ((af == AF_INET) || (af == AF_INET6 && (flags & AI_V4MAPPED) != 0 && (he1 == NULL || (flags & AI_ALL) != 0)))) { he2 = gethostbyname2_p(name, AF_INET, net_data); if (he1 == NULL && he2 == NULL) { *error_num = net_data->res->res_h_errno; return (NULL); } } else *error_num = tmp_err; net_data->res->options = options; he3 = copyandmerge(he1, he2, af, error_num); if (he1 != NULL) freehostent(he1); return (he3); } struct hostent * getipnodebyaddr(const void *src, size_t len, int af, int *error_num) { struct hostent *he1, *he2; struct net_data *net_data = init(); /* Sanity Checks. */ if (src == NULL) { *error_num = NO_RECOVERY; return (NULL); } switch (af) { case AF_INET: if (len != INADDRSZ) { *error_num = NO_RECOVERY; return (NULL); } break; case AF_INET6: if (len != IN6ADDRSZ) { *error_num = NO_RECOVERY; return (NULL); } break; default: *error_num = NO_RECOVERY; return (NULL); } /* * Lookup IPv4 and IPv4 mapped/compatible addresses */ if ((af == AF_INET6 && IN6_IS_ADDR_V4COMPAT((const struct in6_addr *)src)) || (af == AF_INET6 && IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)src)) || (af == AF_INET)) { const char *cp = src; if (af == AF_INET6) cp += 12; he1 = gethostbyaddr_p(cp, 4, AF_INET, net_data); if (he1 == NULL) { *error_num = net_data->res->res_h_errno; return (NULL); } he2 = copyandmerge(he1, NULL, af, error_num); if (he2 == NULL) return (NULL); /* * Restore original address if mapped/compatible. */ if (af == AF_INET6) memcpy(he1->h_addr, src, len); return (he2); } /* * Lookup IPv6 address. */ if (memcmp((const struct in6_addr *)src, &in6addr_any, 16) == 0) { *error_num = HOST_NOT_FOUND; return (NULL); } he1 = gethostbyaddr_p(src, 16, AF_INET6, net_data); if (he1 == NULL) { *error_num = net_data->res->res_h_errno; return (NULL); } return (copyandmerge(he1, NULL, af, error_num)); } void freehostent(struct hostent *he) { char **cpp; int names = 1; int addresses = 1; memput(he->h_name, strlen(he->h_name) + 1); cpp = he->h_addr_list; while (*cpp != NULL) { memput(*cpp, (he->h_addrtype == AF_INET) ? INADDRSZ : IN6ADDRSZ); *cpp = NULL; cpp++; addresses++; } cpp = he->h_aliases; while (*cpp != NULL) { memput(*cpp, strlen(*cpp) + 1); cpp++; names++; } memput(he->h_aliases, sizeof(char *) * (names)); memput(he->h_addr_list, sizeof(char *) * (addresses)); memput(he, sizeof *he); } /* * Private */ /* * Scan the interface table and set have_v4 and have_v6 depending * upon whether there are IPv4 and IPv6 interface addresses. * * Returns: * 0 on success * -1 on failure. */ #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \ !defined(IRIX_EMUL_IOCTL_SIOCGIFCONF) #ifdef __hpux #define lifc_len iflc_len #define lifc_buf iflc_buf #define lifc_req iflc_req #define LIFCONF if_laddrconf #else #define SETFAMILYFLAGS #define LIFCONF lifconf #endif #ifdef __hpux #define lifr_addr iflr_addr #define lifr_name iflr_name #define lifr_dstaddr iflr_dstaddr #define lifr_flags iflr_flags #define ss_family sa_family #define LIFREQ if_laddrreq #else #define LIFREQ lifreq #endif -static int +static void scan_interfaces6(int *have_v4, int *have_v6) { struct LIFCONF lifc; struct LIFREQ lifreq; struct in_addr in4; struct in6_addr in6; char *buf = NULL, *cp, *cplim; static unsigned int bufsiz = 4095; int s, cpsize, n; - /* Set to zero. Used as loop terminators below. */ - *have_v4 = *have_v6 = 0; - /* Get interface list from system. */ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) - goto err_ret; + goto cleanup; /* * Grow buffer until large enough to contain all interface * descriptions. */ for (;;) { buf = memget(bufsiz); if (buf == NULL) - goto err_ret; + goto cleanup; #ifdef SETFAMILYFLAGS lifc.lifc_family = AF_UNSPEC; /* request all families */ lifc.lifc_flags = 0; #endif lifc.lifc_len = bufsiz; lifc.lifc_buf = buf; if ((n = ioctl(s, SIOCGLIFCONF, (char *)&lifc)) != -1) { /* * Some OS's just return what will fit rather * than set EINVAL if the buffer is too small * to fit all the interfaces in. If * lifc.lifc_len is too near to the end of the * buffer we will grow it just in case and * retry. */ if (lifc.lifc_len + 2 * sizeof(lifreq) < bufsiz) break; } if ((n == -1) && errno != EINVAL) - goto err_ret; + goto cleanup; if (bufsiz > 1000000) - goto err_ret; + goto cleanup; memput(buf, bufsiz); bufsiz += 4096; } /* Parse system's interface list. */ cplim = buf + lifc.lifc_len; /* skip over if's with big ifr_addr's */ for (cp = buf; (*have_v4 == 0 || *have_v6 == 0) && cp < cplim; cp += cpsize) { memcpy(&lifreq, cp, sizeof lifreq); #ifdef HAVE_SA_LEN #ifdef FIX_ZERO_SA_LEN if (lifreq.lifr_addr.sa_len == 0) lifreq.lifr_addr.sa_len = 16; #endif #ifdef HAVE_MINIMUM_IFREQ cpsize = sizeof lifreq; if (lifreq.lifr_addr.sa_len > sizeof (struct sockaddr)) cpsize += (int)lifreq.lifr_addr.sa_len - (int)(sizeof (struct sockaddr)); #else cpsize = sizeof lifreq.lifr_name + lifreq.lifr_addr.sa_len; #endif /* HAVE_MINIMUM_IFREQ */ #elif defined SIOCGIFCONF_ADDR cpsize = sizeof lifreq; #else cpsize = sizeof lifreq.lifr_name; /* XXX maybe this should be a hard error? */ if (ioctl(s, SIOCGLIFADDR, (char *)&lifreq) < 0) continue; #endif switch (lifreq.lifr_addr.ss_family) { case AF_INET: if (*have_v4 == 0) { memcpy(&in4, &((struct sockaddr_in *) &lifreq.lifr_addr)->sin_addr, sizeof in4); if (in4.s_addr == INADDR_ANY) break; n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq); if (n < 0) break; if ((lifreq.lifr_flags & IFF_UP) == 0) break; *have_v4 = 1; } break; case AF_INET6: if (*have_v6 == 0) { memcpy(&in6, &((struct sockaddr_in6 *) &lifreq.lifr_addr)->sin6_addr, sizeof in6); if (memcmp(&in6, &in6addr_any, sizeof in6) == 0) break; n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq); if (n < 0) break; if ((lifreq.lifr_flags & IFF_UP) == 0) break; *have_v6 = 1; } break; } } if (buf != NULL) memput(buf, bufsiz); close(s); /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */ - return (0); - err_ret: + return; + cleanup: if (buf != NULL) memput(buf, bufsiz); if (s != -1) close(s); /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */ - return (-1); + return; } +#endif +#ifdef __linux +#ifndef IF_NAMESIZE +# ifdef IFNAMSIZ +# define IF_NAMESIZE IFNAMSIZ +# else +# define IF_NAMESIZE 16 +# endif +#endif +static void +scan_linux6(int *have_v6) { + FILE *proc = NULL; + char address[33]; + char name[IF_NAMESIZE+1]; + int ifindex, prefix, flag3, flag4; + + proc = fopen("/proc/net/if_inet6", "r"); + if (proc == NULL) + return; + + if (fscanf(proc, "%32[a-f0-9] %x %x %x %x %16s\n", + address, &ifindex, &prefix, &flag3, &flag4, name) == 6) + *have_v6 = 1; + fclose(proc); + return; +} #endif static int scan_interfaces(int *have_v4, int *have_v6) { struct ifconf ifc; union { char _pad[256]; /* leave space for IPv6 addresses */ struct ifreq ifreq; } u; struct in_addr in4; struct in6_addr in6; char *buf = NULL, *cp, *cplim; static unsigned int bufsiz = 4095; int s, n; size_t cpsize; + /* Set to zero. Used as loop terminators below. */ + *have_v4 = *have_v6 = 0; + #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \ !defined(IRIX_EMUL_IOCTL_SIOCGIFCONF) /* * Try to scan the interfaces using IPv6 ioctls(). */ - if (!scan_interfaces6(have_v4, have_v6)) + scan_interfaces6(have_v4, have_v6); + if (*have_v4 != 0 && *have_v6 != 0) return (0); #endif - - /* Set to zero. Used as loop terminators below. */ - *have_v4 = *have_v6 = 0; +#ifdef __linux + scan_linux6(have_v6); +#endif /* Get interface list from system. */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) goto err_ret; /* * Grow buffer until large enough to contain all interface * descriptions. */ for (;;) { buf = memget(bufsiz); if (buf == NULL) goto err_ret; ifc.ifc_len = bufsiz; ifc.ifc_buf = buf; #ifdef IRIX_EMUL_IOCTL_SIOCGIFCONF /* * This is a fix for IRIX OS in which the call to ioctl with * the flag SIOCGIFCONF may not return an entry for all the * interfaces like most flavors of Unix. */ if (emul_ioctl(&ifc) >= 0) break; #else if ((n = ioctl(s, SIOCGIFCONF, (char *)&ifc)) != -1) { /* * Some OS's just return what will fit rather * than set EINVAL if the buffer is too small * to fit all the interfaces in. If * ifc.ifc_len is too near to the end of the * buffer we will grow it just in case and * retry. */ if (ifc.ifc_len + 2 * sizeof(u.ifreq) < bufsiz) break; } #endif if ((n == -1) && errno != EINVAL) goto err_ret; if (bufsiz > 1000000) goto err_ret; memput(buf, bufsiz); bufsiz += 4096; } /* Parse system's interface list. */ cplim = buf + ifc.ifc_len; /* skip over if's with big ifr_addr's */ for (cp = buf; (*have_v4 == 0 || *have_v6 == 0) && cp < cplim; cp += cpsize) { memcpy(&u.ifreq, cp, sizeof u.ifreq); #ifdef HAVE_SA_LEN #ifdef FIX_ZERO_SA_LEN if (u.ifreq.ifr_addr.sa_len == 0) u.ifreq.ifr_addr.sa_len = 16; #endif #ifdef HAVE_MINIMUM_IFREQ cpsize = sizeof u.ifreq; if (u.ifreq.ifr_addr.sa_len > sizeof (struct sockaddr)) cpsize += (int)u.ifreq.ifr_addr.sa_len - (int)(sizeof (struct sockaddr)); #else cpsize = sizeof u.ifreq.ifr_name + u.ifreq.ifr_addr.sa_len; #endif /* HAVE_MINIMUM_IFREQ */ if (cpsize > sizeof u.ifreq && cpsize <= sizeof u) memcpy(&u.ifreq, cp, cpsize); #elif defined SIOCGIFCONF_ADDR cpsize = sizeof u.ifreq; #else cpsize = sizeof u.ifreq.ifr_name; /* XXX maybe this should be a hard error? */ if (ioctl(s, SIOCGIFADDR, (char *)&u.ifreq) < 0) continue; #endif switch (u.ifreq.ifr_addr.sa_family) { case AF_INET: if (*have_v4 == 0) { memcpy(&in4, &((struct sockaddr_in *) &u.ifreq.ifr_addr)->sin_addr, sizeof in4); if (in4.s_addr == INADDR_ANY) break; n = ioctl(s, SIOCGIFFLAGS, (char *)&u.ifreq); if (n < 0) break; if ((u.ifreq.ifr_flags & IFF_UP) == 0) break; *have_v4 = 1; } break; case AF_INET6: if (*have_v6 == 0) { memcpy(&in6, &((struct sockaddr_in6 *) &u.ifreq.ifr_addr)->sin6_addr, sizeof in6); if (memcmp(&in6, &in6addr_any, sizeof in6) == 0) break; n = ioctl(s, SIOCGIFFLAGS, (char *)&u.ifreq); if (n < 0) break; if ((u.ifreq.ifr_flags & IFF_UP) == 0) break; *have_v6 = 1; } break; } } if (buf != NULL) memput(buf, bufsiz); close(s); /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */ return (0); err_ret: if (buf != NULL) memput(buf, bufsiz); if (s != -1) close(s); /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */ return (-1); } static struct hostent * copyandmerge(struct hostent *he1, struct hostent *he2, int af, int *error_num) { struct hostent *he = NULL; int addresses = 1; /* NULL terminator */ int names = 1; /* NULL terminator */ int len = 0; char **cpp, **npp; /* * Work out array sizes; */ if (he1 != NULL) { cpp = he1->h_addr_list; while (*cpp != NULL) { addresses++; cpp++; } cpp = he1->h_aliases; while (*cpp != NULL) { names++; cpp++; } } if (he2 != NULL) { cpp = he2->h_addr_list; while (*cpp != NULL) { addresses++; cpp++; } if (he1 == NULL) { cpp = he2->h_aliases; while (*cpp != NULL) { names++; cpp++; } } } if (addresses == 1) { *error_num = NO_ADDRESS; return (NULL); } he = memget(sizeof *he); if (he == NULL) goto no_recovery; he->h_addr_list = memget(sizeof(char *) * (addresses)); if (he->h_addr_list == NULL) goto cleanup0; memset(he->h_addr_list, 0, sizeof(char *) * (addresses)); /* copy addresses */ npp = he->h_addr_list; if (he1 != NULL) { cpp = he1->h_addr_list; while (*cpp != NULL) { *npp = memget((af == AF_INET) ? INADDRSZ : IN6ADDRSZ); if (*npp == NULL) goto cleanup1; /* convert to mapped if required */ if (af == AF_INET6 && he1->h_addrtype == AF_INET) { memcpy(*npp, in6addr_mapped, sizeof in6addr_mapped); memcpy(*npp + sizeof in6addr_mapped, *cpp, INADDRSZ); } else { memcpy(*npp, *cpp, (af == AF_INET) ? INADDRSZ : IN6ADDRSZ); } cpp++; npp++; } } if (he2 != NULL) { cpp = he2->h_addr_list; while (*cpp != NULL) { *npp = memget((af == AF_INET) ? INADDRSZ : IN6ADDRSZ); if (*npp == NULL) goto cleanup1; /* convert to mapped if required */ if (af == AF_INET6 && he2->h_addrtype == AF_INET) { memcpy(*npp, in6addr_mapped, sizeof in6addr_mapped); memcpy(*npp + sizeof in6addr_mapped, *cpp, INADDRSZ); } else { memcpy(*npp, *cpp, (af == AF_INET) ? INADDRSZ : IN6ADDRSZ); } cpp++; npp++; } } he->h_aliases = memget(sizeof(char *) * (names)); if (he->h_aliases == NULL) goto cleanup1; memset(he->h_aliases, 0, sizeof(char *) * (names)); /* copy aliases */ npp = he->h_aliases; cpp = (he1 != NULL) ? he1->h_aliases : he2->h_aliases; while (*cpp != NULL) { len = strlen (*cpp) + 1; *npp = memget(len); if (*npp == NULL) goto cleanup2; strcpy(*npp, *cpp); npp++; cpp++; } /* copy hostname */ he->h_name = memget(strlen((he1 != NULL) ? he1->h_name : he2->h_name) + 1); if (he->h_name == NULL) goto cleanup2; strcpy(he->h_name, (he1 != NULL) ? he1->h_name : he2->h_name); /* set address type and length */ he->h_addrtype = af; he->h_length = (af == AF_INET) ? INADDRSZ : IN6ADDRSZ; return(he); cleanup2: cpp = he->h_aliases; while (*cpp != NULL) { memput(*cpp, strlen(*cpp) + 1); cpp++; } memput(he->h_aliases, sizeof(char *) * (names)); cleanup1: cpp = he->h_addr_list; while (*cpp != NULL) { memput(*cpp, (af == AF_INET) ? INADDRSZ : IN6ADDRSZ); *cpp = NULL; cpp++; } memput(he->h_addr_list, sizeof(char *) * (addresses)); cleanup0: memput(he, sizeof *he); no_recovery: *error_num = NO_RECOVERY; return (NULL); } static struct net_data * init() { struct net_data *net_data; if (!(net_data = net_data_init(NULL))) goto error; if (!net_data->ho) { net_data->ho = (*net_data->irs->ho_map)(net_data->irs); if (!net_data->ho || !net_data->res) { error: errno = EIO; if (net_data && net_data->res) RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); return (NULL); } (*net_data->ho->res_set)(net_data->ho, net_data->res, NULL); } return (net_data); } static void freepvt(struct net_data *net_data) { if (net_data->ho_data) { free(net_data->ho_data); net_data->ho_data = NULL; } } static struct hostent * fakeaddr(const char *name, int af, struct net_data *net_data) { struct pvt *pvt; freepvt(net_data); net_data->ho_data = malloc(sizeof (struct pvt)); if (!net_data->ho_data) { errno = ENOMEM; RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); return (NULL); } pvt = net_data->ho_data; #ifndef __bsdi__ /* * Unlike its forebear(inet_aton), our friendly inet_pton() is strict * in its interpretation of its input, and it will only return "1" if * the input string is a formally valid(and thus unambiguous with * respect to host names) internet address specification for this AF. * * This means "telnet 0xdeadbeef" and "telnet 127.1" are dead now. */ if (inet_pton(af, name, pvt->addr) != 1) { #else /* BSDI XXX * We put this back to inet_aton -- we really want the old behavior * Long live 127.1... */ if ((af != AF_INET || inet_aton(name, (struct in_addr *)pvt->addr) != 1) && inet_pton(af, name, pvt->addr) != 1) { #endif RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND); return (NULL); } strncpy(pvt->name, name, NS_MAXDNAME); pvt->name[NS_MAXDNAME] = '\0'; if (af == AF_INET && (net_data->res->options & RES_USE_INET6) != 0) { map_v4v6_address(pvt->addr, pvt->addr); af = AF_INET6; } pvt->host.h_addrtype = af; switch(af) { case AF_INET: pvt->host.h_length = NS_INADDRSZ; break; case AF_INET6: pvt->host.h_length = NS_IN6ADDRSZ; break; default: errno = EAFNOSUPPORT; RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); return (NULL); } pvt->host.h_name = pvt->name; pvt->host.h_aliases = pvt->aliases; pvt->aliases[0] = NULL; pvt->addrs[0] = (char *)pvt->addr; pvt->addrs[1] = NULL; pvt->host.h_addr_list = pvt->addrs; RES_SET_H_ERRNO(net_data->res, NETDB_SUCCESS); return (&pvt->host); } #ifdef grot /* for future use in gethostbyaddr(), for "SUNSECURITY" */ struct hostent *rhp; char **haddr; u_long old_options; char hname2[MAXDNAME+1]; if (af == AF_INET) { /* * turn off search as the name should be absolute, * 'localhost' should be matched by defnames */ strncpy(hname2, hp->h_name, MAXDNAME); hname2[MAXDNAME] = '\0'; old_options = net_data->res->options; net_data->res->options &= ~RES_DNSRCH; net_data->res->options |= RES_DEFNAMES; if (!(rhp = gethostbyname(hname2))) { net_data->res->options = old_options; RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND); return (NULL); } net_data->res->options = old_options; for (haddr = rhp->h_addr_list; *haddr; haddr++) if (!memcmp(*haddr, addr, INADDRSZ)) break; if (!*haddr) { RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND); return (NULL); } } #endif /* grot */ #endif /*__BIND_NOSTATIC*/ diff --git a/contrib/bind/lib/irs/getnameinfo.c b/contrib/bind/lib/irs/getnameinfo.c index 9b26c641ff67..702b932bf39a 100644 --- a/contrib/bind/lib/irs/getnameinfo.c +++ b/contrib/bind/lib/irs/getnameinfo.c @@ -1,321 +1,322 @@ /* * Issues to be discussed: * - Thread safe-ness must be checked */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by WIDE Project and * its contributors. * 4. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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 #include #include #include #include #include +#include #include #include #include #include #include /* * Note that a_off will be dynamically adjusted so that to be consistent * with the definition of sockaddr_in{,6}. * The value presented below is just a guess. */ static struct afd { int a_af; int a_addrlen; size_t a_socklen; int a_off; } afdl [] = { /* first entry is linked last... */ {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), offsetof(struct sockaddr_in, sin_addr)}, {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), offsetof(struct sockaddr_in6, sin6_addr)}, {0, 0, 0, 0}, }; struct sockinet { #ifdef HAVE_SA_LEN u_char si_len; #endif u_char si_family; u_short si_port; }; static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *, size_t, int)); #ifdef HAVE_SIN6_SCOPE_ID static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, int)); #endif int getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) const struct sockaddr *sa; size_t salen; char *host; size_t hostlen; char *serv; size_t servlen; int flags; { struct afd *afd; struct servent *sp; struct hostent *hp; u_short port; #ifdef HAVE_SA_LEN size_t len; #endif int family, i; const char *addr; char *p; char numserv[512]; char numaddr[512]; const struct sockaddr_in6 *sin6; if (sa == NULL) return EAI_FAIL; #ifdef HAVE_SA_LEN len = sa->sa_len; if (len != salen) return EAI_FAIL; #endif family = sa->sa_family; for (i = 0; afdl[i].a_af; i++) if (afdl[i].a_af == family) { afd = &afdl[i]; goto found; } return EAI_FAMILY; found: if (salen != afd->a_socklen) return EAI_FAIL; port = ((const struct sockinet *)sa)->si_port; /* network byte order */ addr = (const char *)sa + afd->a_off; if (serv == NULL || servlen == 0) { /* * rfc2553bis says that serv == NULL or servlen == 0 means that * the caller does not want the result. */ } else if (flags & NI_NUMERICSERV) { sprintf(numserv, "%d", ntohs(port)); if (strlen(numserv) > servlen) return EAI_MEMORY; strcpy(serv, numserv); } else { sp = getservbyport(port, (flags & NI_DGRAM) ? "udp" : "tcp"); if (sp) { if (strlen(sp->s_name) + 1 > servlen) return EAI_MEMORY; strcpy(serv, sp->s_name); } else return EAI_NONAME; } switch (sa->sa_family) { case AF_INET: if (ntohl(*(const u_long *)addr) >> IN_CLASSA_NSHIFT == 0) flags |= NI_NUMERICHOST; break; case AF_INET6: sin6 = (const struct sockaddr_in6 *)sa; switch (sin6->sin6_addr.s6_addr[0]) { case 0x00: if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) ; else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) ; else flags |= NI_NUMERICHOST; break; default: if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) flags |= NI_NUMERICHOST; else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) flags |= NI_NUMERICHOST; break; } break; } if (host == NULL || hostlen == 0) { /* * rfc2553bis says that host == NULL or hostlen == 0 means that * the caller does not want the result. */ } else if (flags & NI_NUMERICHOST) { goto numeric; } else { hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); if (hp) { if (flags & NI_NOFQDN) { p = strchr(hp->h_name, '.'); if (p) *p = '\0'; } if (strlen(hp->h_name) + 1 > hostlen) return EAI_MEMORY; strcpy(host, hp->h_name); } else { if (flags & NI_NAMEREQD) return EAI_NONAME; numeric: switch(afd->a_af) { case AF_INET6: { int error; if ((error = ip6_parsenumeric(sa, addr, host, hostlen, flags)) != 0) return(error); break; } default: if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) == NULL) return EAI_NONAME; if (strlen(numaddr) + 1 > hostlen) return EAI_MEMORY; strcpy(host, numaddr); } } } return(0); } static int ip6_parsenumeric(const struct sockaddr *sa, const char *addr, char *host, size_t hostlen, int flags) { size_t numaddrlen; char numaddr[512]; #ifndef HAVE_SIN6_SCOPE_ID UNUSED(sa); UNUSED(flags); #endif if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL) return EAI_SYSTEM; numaddrlen = strlen(numaddr); if (numaddrlen + 1 > hostlen) /* don't forget terminator */ return EAI_MEMORY; strcpy(host, numaddr); #ifdef HAVE_SIN6_SCOPE_ID if (((const struct sockaddr_in6 *)sa)->sin6_scope_id) { char scopebuf[MAXHOSTNAMELEN]; /* XXX */ int scopelen; /* ip6_sa2str never fails */ scopelen = ip6_sa2str((const struct sockaddr_in6 *)sa, scopebuf, sizeof(scopebuf), flags); if (scopelen + 1 + numaddrlen + 1 > hostlen) return EAI_MEMORY; /* construct */ memcpy(host + numaddrlen + 1, scopebuf, scopelen); host[numaddrlen] = SCOPE_DELIMITER; host[numaddrlen + 1 + scopelen] = '\0'; } #endif return 0; } #ifdef HAVE_SIN6_SCOPE_ID /* ARGSUSED */ static int ip6_sa2str(const struct sockaddr_in6 *sa6, char *buf, size_t bufsiz, int flags) { #ifdef USE_IFNAMELINKID unsigned int ifindex = (unsigned int)sa6->sin6_scope_id; const struct in6_addr *a6 = &sa6->sin6_addr; #endif char tmp[64]; #ifdef NI_NUMERICSCOPE if (flags & NI_NUMERICSCOPE) { sprintf(tmp, "%u", sa6->sin6_scope_id); if (bufsiz != 0) { strncpy(buf, tmp, bufsiz - 1); buf[bufsiz - 1] = '\0'; } return(strlen(tmp)); } #endif #ifdef USE_IFNAMELINKID /* * For a link-local address, convert the index to an interface * name, assuming a one-to-one mapping between links and interfaces. * Note, however, that this assumption is stronger than the * specification of the scoped address architecture; the * specficication says that more than one interfaces can belong to * a single link. */ /* if_indextoname() does not take buffer size. not a good api... */ if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) && bufsiz >= IF_NAMESIZE) { char *p = if_indextoname(ifindex, buf); if (p) { return(strlen(p)); } } #endif /* last resort */ sprintf(tmp, "%u", sa6->sin6_scope_id); if (bufsiz != 0) { strncpy(buf, tmp, bufsiz - 1); buf[bufsiz - 1] = '\0'; } return(strlen(tmp)); } #endif diff --git a/contrib/bind/lib/irs/getnetgrent.c b/contrib/bind/lib/irs/getnetgrent.c index e966ea6b6234..9c13b01eeae6 100644 --- a/contrib/bind/lib/irs/getnetgrent.c +++ b/contrib/bind/lib/irs/getnetgrent.c @@ -1,141 +1,156 @@ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: getnetgrent.c,v 1.15 2001/05/29 05:48:49 marka Exp $"; +static const char rcsid[] = "$Id: getnetgrent.c,v 1.16.6.1 2003/06/02 06:06:58 marka Exp $"; #endif /* LIBC_SCCS and not lint */ /* Imports */ #include "port_before.h" #if !defined(__BIND_NOSTATIC) #include #include #include #include #include #include #include #include "port_after.h" #include "irs_data.h" /* Forward */ static struct net_data *init(void); /* Public */ +#ifndef SETNETGRENT_ARGS +#define SETNETGRENT_ARGS const char *netgroup +#endif void -setnetgrent(const char *netgroup) { +setnetgrent(SETNETGRENT_ARGS) { struct net_data *net_data = init(); setnetgrent_p(netgroup, net_data); } void endnetgrent(void) { struct net_data *net_data = init(); endnetgrent_p(net_data); } +#ifndef INNETGR_ARGS +#define INNETGR_ARGS const char *netgroup, const char *host, \ + const char *user, const char *domain +#endif int -innetgr(const char *netgroup, const char *host, - const char *user, const char *domain) { +innetgr(INNETGR_ARGS) { struct net_data *net_data = init(); return (innetgr_p(netgroup, host, user, domain, net_data)); } int -getnetgrent(const char **host, const char **user, const char **domain) { +getnetgrent(char **host, char **user, char **domain) { struct net_data *net_data = init(); + const char *ch, *cu, *cd; + int ret; - return (getnetgrent_p(host, user, domain, net_data)); + ret = getnetgrent_p(&ch, &cu, &cd, net_data); + if (ret != 1) + return (ret); + + DE_CONST(ch, *host); + DE_CONST(cu, *user); + DE_CONST(cd, *domain); + return (ret); } /* Shared private. */ void setnetgrent_p(const char *netgroup, struct net_data *net_data) { struct irs_ng *ng; if ((net_data != NULL) && ((ng = net_data->ng) != NULL)) (*ng->rewind)(ng, netgroup); } void endnetgrent_p(struct net_data *net_data) { struct irs_ng *ng; if (!net_data) return; if ((ng = net_data->ng) != NULL) (*ng->close)(ng); net_data->ng = NULL; } int innetgr_p(const char *netgroup, const char *host, const char *user, const char *domain, struct net_data *net_data) { struct irs_ng *ng; if (!net_data || !(ng = net_data->ng)) return (0); return ((*ng->test)(ng, netgroup, host, user, domain)); } int getnetgrent_p(const char **host, const char **user, const char **domain, struct net_data *net_data ) { struct irs_ng *ng; if (!net_data || !(ng = net_data->ng)) return (0); return ((*ng->next)(ng, host, user, domain)); } /* Private */ static struct net_data * init(void) { struct net_data *net_data; if (!(net_data = net_data_init(NULL))) goto error; if (!net_data->ng) { net_data->ng = (*net_data->irs->ng_map)(net_data->irs); if (!net_data->ng) { error: errno = EIO; return (NULL); } } return (net_data); } #endif /*__BIND_NOSTATIC*/ diff --git a/contrib/bind/lib/irs/getnetgrent_r.c b/contrib/bind/lib/irs/getnetgrent_r.c index adae817e1303..0a88ea4eb060 100644 --- a/contrib/bind/lib/irs/getnetgrent_r.c +++ b/contrib/bind/lib/irs/getnetgrent_r.c @@ -1,159 +1,165 @@ /* * Copyright (c) 1998-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: getnetgrent_r.c,v 8.6 2001/11/01 08:02:12 marka Exp $"; +static const char rcsid[] = "$Id: getnetgrent_r.c,v 8.6.10.1 2003/06/02 06:06:58 marka Exp $"; #endif /* LIBC_SCCS and not lint */ #include #if !defined(_REENTRANT) || !defined(DO_PTHREADS) static int getnetgrent_r_not_required = 0; #else #include #include #include #include #include #include #include #include #include #ifdef NGR_R_RETURN static NGR_R_RETURN copy_protoent(char **, char **, char **, const char *, const char *, const char *, NGR_R_COPY_ARGS); NGR_R_RETURN innetgr_r(const char *netgroup, const char *host, const char *user, - const char *domain) { + const char *domain) { + char *ng, *ho, *us, *dom; - return (innetgr(netgroup, host, user, domain)); + DE_CONST(netgroup, ng); + DE_CONST(host, ho); + DE_CONST(user, us); + DE_CONST(domain, dom); + + return (innetgr(ng, ho, us, dom)); } /* * These assume a single context is in operation per thread. * If this is not the case we will need to call irs directly * rather than through the base functions. */ NGR_R_RETURN getnetgrent_r(char **machinep, char **userp, char **domainp, NGR_R_ARGS) { - const char *mp, *up, *dp; + char *mp, *up, *dp; int res = getnetgrent(&mp, &up, &dp); if (res != 1) return (res); return (copy_protoent(machinep, userp, domainp, mp, up, dp, NGR_R_COPY)); } NGR_R_SET_RETURN #ifdef NGR_R_ENT_ARGS setnetgrent_r(const char *netgroup, NGR_R_ENT_ARGS) #else setnetgrent_r(const char *netgroup) #endif { setnetgrent(netgroup); #ifdef NGR_R_PRIVATE *buf = NULL; #endif #ifdef NGR_R_SET_RESULT return (NGR_R_SET_RESULT); #endif } NGR_R_END_RETURN #ifdef NGR_R_ENT_ARGS endnetgrent_r(NGR_R_ENT_ARGS) #else endnetgrent_r(void) #endif { endnetgrent(); #ifdef NGR_R_PRIVATE if (*buf != NULL) free(*buf); *buf = NULL; #endif NGR_R_END_RESULT(NGR_R_OK); } /* Private */ static int copy_protoent(char **machinep, char **userp, char **domainp, const char *mp, const char *up, const char *dp, NGR_R_COPY_ARGS) { char *cp; int n; int len; /* Find out the amount of space required to store the answer. */ len = 0; if (mp != NULL) len += strlen(mp) + 1; if (up != NULL) len += strlen(up) + 1; if (dp != NULL) len += strlen(dp) + 1; #ifdef NGR_R_PRIVATE free(*buf); *buf = malloc(len); if (*buf == NULL) return(NGR_R_BAD); cp = *buf; #else if (len > (int)buflen) { errno = ERANGE; return (NGR_R_BAD); } cp = buf; #endif if (mp != NULL) { n = strlen(mp) + 1; strcpy(cp, mp); *machinep = cp; cp += n; } else *machinep = NULL; if (up != NULL) { n = strlen(up) + 1; strcpy(cp, up); *userp = cp; cp += n; } else *userp = NULL; if (dp != NULL) { n = strlen(dp) + 1; strcpy(cp, dp); *domainp = cp; cp += n; } else *domainp = NULL; return (NGR_R_OK); } #else /* NGR_R_RETURN */ static int getnetgrent_r_unknown_system = 0; #endif /* NGR_R_RETURN */ #endif /* !defined(_REENTRANT) || !defined(DO_PTHREADS) */ diff --git a/contrib/bind/lib/irs/hesiod.c b/contrib/bind/lib/irs/hesiod.c index 2b0891150675..5afe6611ee42 100644 --- a/contrib/bind/lib/irs/hesiod.c +++ b/contrib/bind/lib/irs/hesiod.c @@ -1,506 +1,524 @@ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: hesiod.c,v 1.22 2001/05/29 05:48:55 marka Exp $"; +static const char rcsid[] = "$Id: hesiod.c,v 1.23 2002/07/18 02:07:45 marka Exp $"; #endif /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * This file is primarily maintained by and . */ /* * hesiod.c --- the core portion of the hesiod resolver. * * This file is derived from the hesiod library from Project Athena; * It has been extensively rewritten by Theodore Ts'o to have a more * thread-safe interface. */ /* Imports */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "pathnames.h" #include "hesiod.h" #include "hesiod_p.h" /* Forward */ int hesiod_init(void **context); void hesiod_end(void *context); char * hesiod_to_bind(void *context, const char *name, const char *type); char ** hesiod_resolve(void *context, const char *name, const char *type); void hesiod_free_list(void *context, char **list); static int parse_config_file(struct hesiod_p *ctx, const char *filename); static char ** get_txt_records(struct hesiod_p *ctx, int class, const char *name); static int init(struct hesiod_p *ctx); /* Public */ /* * This function is called to initialize a hesiod_p. */ int hesiod_init(void **context) { struct hesiod_p *ctx; char *cp; ctx = malloc(sizeof(struct hesiod_p)); if (ctx == 0) { errno = ENOMEM; return (-1); } ctx->LHS = NULL; ctx->RHS = NULL; ctx->res = NULL; if (parse_config_file(ctx, _PATH_HESIOD_CONF) < 0) { #ifdef DEF_RHS /* * Use compiled in defaults. */ ctx->LHS = malloc(strlen(DEF_LHS)+1); ctx->RHS = malloc(strlen(DEF_RHS)+1); if (ctx->LHS == 0 || ctx->RHS == 0) { errno = ENOMEM; goto cleanup; } +#ifdef HAVE_STRLCPY + strlcpy(ctx->LHS, DEF_LHS, strlen(DEF_LHS) + 1); + strlcpy(ctx->RHS, DEF_RHS, strlen(DEF_RHS) + 1); +#else strcpy(ctx->LHS, DEF_LHS); strcpy(ctx->RHS, DEF_RHS); +#endif #else goto cleanup; #endif } /* * The default RHS can be overridden by an environment * variable. */ if ((cp = getenv("HES_DOMAIN")) != NULL) { + size_t RHSlen = strlen(cp) + 2; if (ctx->RHS) free(ctx->RHS); - ctx->RHS = malloc(strlen(cp)+2); + ctx->RHS = malloc(RHSlen); if (!ctx->RHS) { errno = ENOMEM; goto cleanup; } - if (cp[0] == '.') + if (cp[0] == '.') { +#ifdef HAVE_STRLCPY + strlcpy(ctx->RHS, cp, RHSlen); +#else strcpy(ctx->RHS, cp); - else { +#endif + } else { +#ifdef HAVE_STRLCPY + strlcpy(ctx->RHS, ".", RHSlen); +#else strcpy(ctx->RHS, "."); +#endif +#ifdef HAVE_STRLCAT + strlcat(ctx->RHS, cp, RHSlen); +#else strcat(ctx->RHS, cp); +#endif } } /* * If there is no default hesiod realm set, we return an * error. */ if (!ctx->RHS) { errno = ENOEXEC; goto cleanup; } #if 0 if (res_ninit(ctx->res) < 0) goto cleanup; #endif *context = ctx; return (0); cleanup: hesiod_end(ctx); return (-1); } /* * This function deallocates the hesiod_p */ void hesiod_end(void *context) { struct hesiod_p *ctx = (struct hesiod_p *) context; int save_errno = errno; if (ctx->res) res_nclose(ctx->res); if (ctx->RHS) free(ctx->RHS); if (ctx->LHS) free(ctx->LHS); if (ctx->res && ctx->free_res) (*ctx->free_res)(ctx->res); free(ctx); errno = save_errno; } /* * This function takes a hesiod (name, type) and returns a DNS * name which is to be resolved. */ char * hesiod_to_bind(void *context, const char *name, const char *type) { struct hesiod_p *ctx = (struct hesiod_p *) context; char *bindname; char **rhs_list = NULL; const char *RHS, *cp; /* Decide what our RHS is, and set cp to the end of the actual name. */ if ((cp = strchr(name, '@')) != NULL) { if (strchr(cp + 1, '.')) RHS = cp + 1; else if ((rhs_list = hesiod_resolve(context, cp + 1, "rhs-extension")) != NULL) RHS = *rhs_list; else { errno = ENOENT; return (NULL); } } else { RHS = ctx->RHS; cp = name + strlen(name); } /* * Allocate the space we need, including up to three periods and * the terminating NUL. */ if ((bindname = malloc((cp - name) + strlen(type) + strlen(RHS) + (ctx->LHS ? strlen(ctx->LHS) : 0) + 4)) == NULL) { errno = ENOMEM; if (rhs_list) hesiod_free_list(context, rhs_list); return NULL; } /* Now put together the DNS name. */ memcpy(bindname, name, cp - name); bindname[cp - name] = '\0'; strcat(bindname, "."); strcat(bindname, type); if (ctx->LHS) { if (ctx->LHS[0] != '.') strcat(bindname, "."); strcat(bindname, ctx->LHS); } if (RHS[0] != '.') strcat(bindname, "."); strcat(bindname, RHS); if (rhs_list) hesiod_free_list(context, rhs_list); return (bindname); } /* * This is the core function. Given a hesiod (name, type), it * returns an array of strings returned by the resolver. */ char ** hesiod_resolve(void *context, const char *name, const char *type) { struct hesiod_p *ctx = (struct hesiod_p *) context; char *bindname = hesiod_to_bind(context, name, type); char **retvec; if (bindname == NULL) return (NULL); if (init(ctx) == -1) { free(bindname); return (NULL); } if ((retvec = get_txt_records(ctx, C_IN, bindname))) { free(bindname); return (retvec); } if (errno != ENOENT) return (NULL); retvec = get_txt_records(ctx, C_HS, bindname); free(bindname); return (retvec); } void hesiod_free_list(void *context, char **list) { char **p; UNUSED(context); for (p = list; *p; p++) free(*p); free(list); } /* * This function parses the /etc/hesiod.conf file */ static int parse_config_file(struct hesiod_p *ctx, const char *filename) { char *key, *data, *cp, **cpp; char buf[MAXDNAME+7]; FILE *fp; /* * Clear the existing configuration variable, just in case * they're set. */ if (ctx->RHS) free(ctx->RHS); if (ctx->LHS) free(ctx->LHS); ctx->RHS = ctx->LHS = 0; /* * Now open and parse the file... */ if (!(fp = fopen(filename, "r"))) return (-1); while (fgets(buf, sizeof(buf), fp) != NULL) { cp = buf; if (*cp == '#' || *cp == '\n' || *cp == '\r') continue; while(*cp == ' ' || *cp == '\t') cp++; key = cp; while(*cp != ' ' && *cp != '\t' && *cp != '=') cp++; *cp++ = '\0'; while(*cp == ' ' || *cp == '\t' || *cp == '=') cp++; data = cp; while(*cp != ' ' && *cp != '\n' && *cp != '\r') cp++; *cp++ = '\0'; if (strcmp(key, "lhs") == 0) cpp = &ctx->LHS; else if (strcmp(key, "rhs") == 0) cpp = &ctx->RHS; else continue; *cpp = malloc(strlen(data) + 1); if (!*cpp) { errno = ENOMEM; goto cleanup; } strcpy(*cpp, data); } fclose(fp); return (0); cleanup: fclose(fp); if (ctx->RHS) free(ctx->RHS); if (ctx->LHS) free(ctx->LHS); ctx->RHS = ctx->LHS = 0; return (-1); } /* * Given a DNS class and a DNS name, do a lookup for TXT records, and * return a list of them. */ static char ** get_txt_records(struct hesiod_p *ctx, int class, const char *name) { struct { int type; /* RR type */ int class; /* RR class */ int dlen; /* len of data section */ u_char *data; /* pointer to data */ } rr; HEADER *hp; u_char qbuf[MAX_HESRESP], abuf[MAX_HESRESP]; u_char *cp, *erdata, *eom; char *dst, *edst, **list; int ancount, qdcount; int i, j, n, skip; /* * Construct the query and send it. */ n = res_nmkquery(ctx->res, QUERY, name, class, T_TXT, NULL, 0, NULL, qbuf, MAX_HESRESP); if (n < 0) { errno = EMSGSIZE; return (NULL); } n = res_nsend(ctx->res, qbuf, n, abuf, MAX_HESRESP); if (n < 0) { errno = ECONNREFUSED; return (NULL); } if (n < HFIXEDSZ) { errno = EMSGSIZE; return (NULL); } /* * OK, parse the result. */ hp = (HEADER *) abuf; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); cp = abuf + sizeof(HEADER); eom = abuf + n; /* Skip query, trying to get to the answer section which follows. */ for (i = 0; i < qdcount; i++) { skip = dn_skipname(cp, eom); if (skip < 0 || cp + skip + QFIXEDSZ > eom) { errno = EMSGSIZE; return (NULL); } cp += skip + QFIXEDSZ; } list = malloc((ancount + 1) * sizeof(char *)); if (!list) { errno = ENOMEM; return (NULL); } j = 0; for (i = 0; i < ancount; i++) { skip = dn_skipname(cp, eom); if (skip < 0) { errno = EMSGSIZE; goto cleanup; } cp += skip; if (cp + 3 * INT16SZ + INT32SZ > eom) { errno = EMSGSIZE; goto cleanup; } rr.type = ns_get16(cp); cp += INT16SZ; rr.class = ns_get16(cp); cp += INT16SZ + INT32SZ; /* skip the ttl, too */ rr.dlen = ns_get16(cp); cp += INT16SZ; if (cp + rr.dlen > eom) { errno = EMSGSIZE; goto cleanup; } rr.data = cp; cp += rr.dlen; if (rr.class != class || rr.type != T_TXT) continue; if (!(list[j] = malloc(rr.dlen))) goto cleanup; dst = list[j++]; edst = dst + rr.dlen; erdata = rr.data + rr.dlen; cp = rr.data; while (cp < erdata) { n = (unsigned char) *cp++; if (cp + n > eom || dst + n > edst) { errno = EMSGSIZE; goto cleanup; } memcpy(dst, cp, n); cp += n; dst += n; } if (cp != erdata) { errno = EMSGSIZE; goto cleanup; } *dst = '\0'; } list[j] = NULL; if (j == 0) { errno = ENOENT; goto cleanup; } return (list); cleanup: for (i = 0; i < j; i++) free(list[i]); free(list); return (NULL); } struct __res_state * __hesiod_res_get(void *context) { struct hesiod_p *ctx = context; if (!ctx->res) { struct __res_state *res; res = (struct __res_state *)malloc(sizeof *res); if (res == NULL) { errno = ENOMEM; return (NULL); } memset(res, 0, sizeof *res); __hesiod_res_set(ctx, res, free); } return (ctx->res); } void __hesiod_res_set(void *context, struct __res_state *res, void (*free_res)(void *)) { struct hesiod_p *ctx = context; if (ctx->res && ctx->free_res) { res_nclose(ctx->res); (*ctx->free_res)(ctx->res); } ctx->res = res; ctx->free_res = free_res; } static int init(struct hesiod_p *ctx) { if (!ctx->res && !__hesiod_res_get(ctx)) return (-1); if (((ctx->res->options & RES_INIT) == 0) && (res_ninit(ctx->res) == -1)) return (-1); return (0); } diff --git a/contrib/bind/lib/irs/irp_p.h b/contrib/bind/lib/irs/irp_p.h index 0f68b28973ea..8a02b1b757fc 100644 --- a/contrib/bind/lib/irs/irp_p.h +++ b/contrib/bind/lib/irs/irp_p.h @@ -1,61 +1,59 @@ /* * Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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: irp_p.h,v 8.2 2001/08/10 02:40:52 marka Exp $ + * $Id: irp_p.h,v 8.2.10.1 2003/06/02 05:55:14 marka Exp $ */ #ifndef _IRP_P_H_INCLUDED #define _IRP_P_H_INCLUDED #include struct irp_p { char inbuffer[1024]; int inlast; /* index of one past the last char in buffer */ int incurr; /* index of the next char to be read from buffer */ int fdCxn; }; /* * Externs. */ extern struct irs_acc * irs_irp_acc __P((const char *)); extern struct irs_gr * irs_irp_gr __P((struct irs_acc *)); extern struct irs_pw * irs_irp_pw __P((struct irs_acc *)); extern struct irs_sv * irs_irp_sv __P((struct irs_acc *)); extern struct irs_pr * irs_irp_pr __P((struct irs_acc *)); extern struct irs_ho * irs_irp_ho __P((struct irs_acc *)); extern struct irs_nw * irs_irp_nw __P((struct irs_acc *)); extern struct irs_ng * irs_irp_ng __P((struct irs_acc *)); int irs_irp_connect(struct irp_p *pvt); int irs_irp_is_connected(struct irp_p *pvt); void irs_irp_disconnect(struct irp_p *pvt); int irs_irp_read_response(struct irp_p *pvt, char *text, size_t textlen); char *irs_irp_read_body(struct irp_p *pvt, size_t *size); int irs_irp_get_full_response(struct irp_p *pvt, int *code, char *text, size_t textlen, char **body, size_t *bodylen); -int irs_irp_send_command(struct irp_p *pvt, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3); - extern int irp_log_errors; #endif diff --git a/contrib/bind/lib/irs/irs_data.c b/contrib/bind/lib/irs/irs_data.c index 000da0c7b153..f69a6b45a3c2 100644 --- a/contrib/bind/lib/irs/irs_data.c +++ b/contrib/bind/lib/irs/irs_data.c @@ -1,197 +1,214 @@ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: irs_data.c,v 1.19 2001/08/20 07:08:41 marka Exp $"; +static const char rcsid[] = "$Id: irs_data.c,v 1.20.2.1 2003/06/02 10:09:48 marka Exp $"; #endif #include "port_before.h" #ifndef __BIND_NOSTATIC #include #include #include #include #include #include #include #ifdef DO_PTHREADS #include #endif #include +#include #include "port_after.h" #include "irs_data.h" #undef _res #undef h_errno extern struct __res_state _res; extern int h_errno; #ifdef DO_PTHREADS static pthread_key_t key; static int once = 0; #else static struct net_data *net_data; #endif void irs_destroy(void) { #ifndef DO_PTHREADS if (net_data != NULL) net_data_destroy(net_data); net_data = NULL; #endif } void net_data_destroy(void *p) { struct net_data *net_data = p; res_ndestroy(net_data->res); if (net_data->gr != NULL) { (*net_data->gr->close)(net_data->gr); net_data->gr = NULL; } if (net_data->pw != NULL) { (*net_data->pw->close)(net_data->pw); net_data->pw = NULL; } if (net_data->sv != NULL) { (*net_data->sv->close)(net_data->sv); net_data->sv = NULL; } if (net_data->pr != NULL) { (*net_data->pr->close)(net_data->pr); net_data->pr = NULL; } if (net_data->ho != NULL) { (*net_data->ho->close)(net_data->ho); net_data->ho = NULL; } if (net_data->nw != NULL) { (*net_data->nw->close)(net_data->nw); net_data->nw = NULL; } if (net_data->ng != NULL) { (*net_data->ng->close)(net_data->ng); net_data->ng = NULL; } + if (net_data->ho_data != NULL) { + free(net_data->ho_data); + net_data->ho_data = NULL; + } + if (net_data->nw_data != NULL) { + free(net_data->nw_data); + net_data->nw_data = NULL; + } (*net_data->irs->close)(net_data->irs); memput(net_data, sizeof *net_data); } /* applications that need a specific config file other than * _PATH_IRS_CONF should call net_data_init directly rather than letting * the various wrapper functions make the first call. - brister */ struct net_data * net_data_init(const char *conf_file) { #ifdef DO_PTHREADS static pthread_mutex_t keylock = PTHREAD_MUTEX_INITIALIZER; struct net_data *net_data; if (!once) { pthread_mutex_lock(&keylock); if (!once++) pthread_key_create(&key, net_data_destroy); pthread_mutex_unlock(&keylock); } net_data = pthread_getspecific(key); #endif if (net_data == NULL) { net_data = net_data_create(conf_file); if (net_data == NULL) return (NULL); #ifdef DO_PTHREADS pthread_setspecific(key, net_data); #endif } return (net_data); } struct net_data * net_data_create(const char *conf_file) { struct net_data *net_data; net_data = memget(sizeof (struct net_data)); if (net_data == NULL) return (NULL); memset(net_data, 0, sizeof (struct net_data)); - if ((net_data->irs = irs_gen_acc("", conf_file)) == NULL) + if ((net_data->irs = irs_gen_acc("", conf_file)) == NULL) { + memput(net_data, sizeof (struct net_data)); return (NULL); + } #ifndef DO_PTHREADS (*net_data->irs->res_set)(net_data->irs, &_res, NULL); #endif net_data->res = (*net_data->irs->res_get)(net_data->irs); - if (net_data->res == NULL) + if (net_data->res == NULL) { + (*net_data->irs->close)(net_data->irs); + memput(net_data, sizeof (struct net_data)); return (NULL); + } if ((net_data->res->options & RES_INIT) == 0 && - res_ninit(net_data->res) == -1) + res_ninit(net_data->res) == -1) { + (*net_data->irs->close)(net_data->irs); + memput(net_data, sizeof (struct net_data)); return (NULL); + } return (net_data); } void net_data_minimize(struct net_data *net_data) { res_nclose(net_data->res); } #ifdef _REENTRANT struct __res_state * __res_state(void) { /* NULL param here means use the default config file. */ struct net_data *net_data = net_data_init(NULL); if (net_data && net_data->res) return (net_data->res); return (&_res); } #endif int * __h_errno(void) { /* NULL param here means use the default config file. */ struct net_data *net_data = net_data_init(NULL); if (net_data && net_data->res) return (&net_data->res->res_h_errno); return (&h_errno); } void __h_errno_set(struct __res_state *res, int err) { h_errno = res->res_h_errno = err; } #endif /*__BIND_NOSTATIC*/ diff --git a/contrib/bind/lib/irs/nis_gr.c b/contrib/bind/lib/irs/nis_gr.c index b4e09453c4cd..0ea50f5246d9 100644 --- a/contrib/bind/lib/irs/nis_gr.c +++ b/contrib/bind/lib/irs/nis_gr.c @@ -1,353 +1,353 @@ /* * Copyright (c) 1989, 1993, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: nis_gr.c,v 1.21 2001/05/29 05:49:12 marka Exp $"; +static const char rcsid[] = "$Id: nis_gr.c,v 1.22 2002/07/08 06:26:11 marka Exp $"; /* from getgrent.c 8.2 (Berkeley) 3/21/94"; */ /* from BSDI Id: getgrent.c,v 2.8 1996/05/28 18:15:14 bostic Exp $ */ #endif /* LIBC_SCCS and not lint */ /* Imports */ #include "port_before.h" #if !defined(WANT_IRS_GR) || !defined(WANT_IRS_NIS) static int __bind_irs_gr_unneeded; #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "nis_p.h" /* Definitions */ struct pvt { int needrewind; char * nis_domain; char * curkey_data; int curkey_len; char * curval_data; int curval_len; /* * Need space to store the entries read from the group file. * The members list also needs space per member, and the * strings making up the user names must be allocated * somewhere. Rather than doing lots of small allocations, * we keep one buffer and resize it as needed. */ struct group group; size_t nmemb; /* Malloc'd max index of gr_mem[]. */ char * membuf; size_t membufsize; }; enum do_what { do_none = 0x0, do_key = 0x1, do_val = 0x2, do_all = 0x3 }; static /*const*/ char group_bygid[] = "group.bygid"; static /*const*/ char group_byname[] = "group.byname"; /* Forward */ static void gr_close(struct irs_gr *); static struct group * gr_next(struct irs_gr *); static struct group * gr_byname(struct irs_gr *, const char *); static struct group * gr_bygid(struct irs_gr *, gid_t); static void gr_rewind(struct irs_gr *); static void gr_minimize(struct irs_gr *); static struct group * makegroupent(struct irs_gr *); static void nisfree(struct pvt *, enum do_what); /* Public */ struct irs_gr * irs_nis_gr(struct irs_acc *this) { struct irs_gr *gr; struct pvt *pvt; if (!(gr = memget(sizeof *gr))) { errno = ENOMEM; return (NULL); } memset(gr, 0x5e, sizeof *gr); if (!(pvt = memget(sizeof *pvt))) { memput(gr, sizeof *gr); errno = ENOMEM; return (NULL); } memset(pvt, 0, sizeof *pvt); pvt->needrewind = 1; pvt->nis_domain = ((struct nis_p *)this->private)->domain; gr->private = pvt; gr->close = gr_close; gr->next = gr_next; gr->byname = gr_byname; gr->bygid = gr_bygid; gr->rewind = gr_rewind; gr->list = make_group_list; gr->minimize = gr_minimize; gr->res_get = NULL; gr->res_set = NULL; return (gr); } /* Methods */ static void gr_close(struct irs_gr *this) { struct pvt *pvt = (struct pvt *)this->private; if (pvt->group.gr_mem) free(pvt->group.gr_mem); if (pvt->membuf) free(pvt->membuf); memput(pvt, sizeof *pvt); memput(this, sizeof *this); } static struct group * gr_next(struct irs_gr *this) { struct pvt *pvt = (struct pvt *)this->private; struct group *rval; int r; do { if (pvt->needrewind) { nisfree(pvt, do_all); r = yp_first(pvt->nis_domain, group_byname, &pvt->curkey_data, &pvt->curkey_len, &pvt->curval_data, &pvt->curval_len); pvt->needrewind = 0; } else { char *newkey_data; int newkey_len; nisfree(pvt, do_val); r = yp_next(pvt->nis_domain, group_byname, pvt->curkey_data, pvt->curkey_len, &newkey_data, &newkey_len, &pvt->curval_data, &pvt->curval_len); nisfree(pvt, do_key); pvt->curkey_data = newkey_data; pvt->curkey_len = newkey_len; } if (r != 0) { errno = ENOENT; return (NULL); } rval = makegroupent(this); } while (rval == NULL); return (rval); } static struct group * gr_byname(struct irs_gr *this, const char *name) { struct pvt *pvt = (struct pvt *)this->private; int r; nisfree(pvt, do_val); r = yp_match(pvt->nis_domain, group_byname, name, strlen(name), &pvt->curval_data, &pvt->curval_len); if (r != 0) { errno = ENOENT; return (NULL); } return (makegroupent(this)); } static struct group * gr_bygid(struct irs_gr *this, gid_t gid) { struct pvt *pvt = (struct pvt *)this->private; char tmp[sizeof "4294967295"]; int r; nisfree(pvt, do_val); (void) sprintf(tmp, "%u", (unsigned int)gid); r = yp_match(pvt->nis_domain, group_bygid, tmp, strlen(tmp), &pvt->curval_data, &pvt->curval_len); if (r != 0) { errno = ENOENT; return (NULL); } return (makegroupent(this)); } static void gr_rewind(struct irs_gr *this) { struct pvt *pvt = (struct pvt *)this->private; pvt->needrewind = 1; } static void gr_minimize(struct irs_gr *this) { UNUSED(this); /* NOOP */ } /* Private */ static struct group * makegroupent(struct irs_gr *this) { struct pvt *pvt = (struct pvt *)this->private; unsigned int num_members = 0; char *cp, **new; u_long t; if (pvt->group.gr_mem) { free(pvt->group.gr_mem); pvt->group.gr_mem = NULL; pvt->nmemb = 0; } if (pvt->membuf) free(pvt->membuf); pvt->membuf = pvt->curval_data; pvt->curval_data = NULL; cp = pvt->membuf; pvt->group.gr_name = cp; if (!(cp = strchr(cp, ':'))) goto cleanup; *cp++ = '\0'; pvt->group.gr_passwd = cp; if (!(cp = strchr(cp, ':'))) goto cleanup; *cp++ = '\0'; - errno = -1; + errno = 0; t = strtoul(cp, NULL, 10); if (errno == ERANGE) goto cleanup; pvt->group.gr_gid = (gid_t) t; if (!(cp = strchr(cp, ':'))) goto cleanup; cp++; if (*cp && cp[strlen(cp)-1] == '\n') cp[strlen(cp)-1] = '\0'; /* * Parse the members out. */ while (*cp) { if (num_members+1 >= pvt->nmemb || pvt->group.gr_mem == NULL) { pvt->nmemb += 10; new = realloc(pvt->group.gr_mem, pvt->nmemb * sizeof(char *)); if (new == NULL) goto cleanup; pvt->group.gr_mem = new; } pvt->group.gr_mem[num_members++] = cp; if (!(cp = strchr(cp, ','))) break; *cp++ = '\0'; } if (pvt->group.gr_mem == NULL) { pvt->group.gr_mem = malloc(sizeof(char*)); if (!pvt->group.gr_mem) goto cleanup; pvt->nmemb = 1; } pvt->group.gr_mem[num_members] = NULL; return (&pvt->group); cleanup: if (pvt->group.gr_mem) { free(pvt->group.gr_mem); pvt->group.gr_mem = NULL; pvt->nmemb = 0; } if (pvt->membuf) { free(pvt->membuf); pvt->membuf = NULL; } return (NULL); } static void nisfree(struct pvt *pvt, enum do_what do_what) { if ((do_what & do_key) && pvt->curkey_data) { free(pvt->curkey_data); pvt->curkey_data = NULL; } if ((do_what & do_val) && pvt->curval_data) { free(pvt->curval_data); pvt->curval_data = NULL; } } #endif /* WANT_IRS_GR && WANT_IRS_NIS */ diff --git a/contrib/bind/lib/irs/nis_ho.c b/contrib/bind/lib/irs/nis_ho.c index 72a8d17a5e90..7158f4c7b3c3 100644 --- a/contrib/bind/lib/irs/nis_ho.c +++ b/contrib/bind/lib/irs/nis_ho.c @@ -1,463 +1,530 @@ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: nis_ho.c,v 1.18 2001/06/18 14:44:00 marka Exp $"; +static const char rcsid[] = "$Id: nis_ho.c,v 1.18.10.1 2003/06/02 05:50:57 marka Exp $"; #endif /* LIBC_SCCS and not lint */ /* Imports */ #include "port_before.h" #ifndef WANT_IRS_NIS static int __bind_irs_nis_unneeded; #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #include "irs_p.h" #include "nis_p.h" /* Definitions */ #define MAXALIASES 35 #define MAXADDRS 35 #if PACKETSZ > 1024 #define MAXPACKET PACKETSZ #else #define MAXPACKET 1024 #endif struct pvt { int needrewind; char * nis_domain; char * curkey_data; int curkey_len; char * curval_data; int curval_len; struct hostent host; char * h_addr_ptrs[MAXADDRS + 1]; char * host_aliases[MAXALIASES + 1]; char hostbuf[8*1024]; u_char host_addr[16]; /* IPv4 or IPv6 */ struct __res_state *res; void (*free_res)(void *); }; enum do_what { do_none = 0x0, do_key = 0x1, do_val = 0x2, do_all = 0x3 }; static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff }; static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 }; static /*const*/ char hosts_byname[] = "hosts.byname"; static /*const*/ char hosts_byaddr[] = "hosts.byaddr"; +static /*const*/ char ipnode_byname[] = "ipnode.byname"; +static /*const*/ char ipnode_byaddr[] = "ipnode.byaddr"; +static /*const*/ char yp_multi[] = "YP_MULTI_"; /* Forwards */ static void ho_close(struct irs_ho *this); static struct hostent * ho_byname(struct irs_ho *this, const char *name); static struct hostent * ho_byname2(struct irs_ho *this, const char *name, int af); static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr, int len, int af); static struct hostent * ho_next(struct irs_ho *this); static void ho_rewind(struct irs_ho *this); static void ho_minimize(struct irs_ho *this); static struct __res_state * ho_res_get(struct irs_ho *this); static void ho_res_set(struct irs_ho *this, struct __res_state *res, void (*free_res)(void *)); static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai); static struct hostent * makehostent(struct irs_ho *this); static void nisfree(struct pvt *, enum do_what); static int init(struct irs_ho *this); /* Public */ struct irs_ho * irs_nis_ho(struct irs_acc *this) { struct irs_ho *ho; struct pvt *pvt; if (!(pvt = memget(sizeof *pvt))) { errno = ENOMEM; return (NULL); } memset(pvt, 0, sizeof *pvt); if (!(ho = memget(sizeof *ho))) { memput(pvt, sizeof *pvt); errno = ENOMEM; return (NULL); } memset(ho, 0x5e, sizeof *ho); pvt->needrewind = 1; pvt->nis_domain = ((struct nis_p *)this->private)->domain; ho->private = pvt; ho->close = ho_close; ho->byname = ho_byname; ho->byname2 = ho_byname2; ho->byaddr = ho_byaddr; ho->next = ho_next; ho->rewind = ho_rewind; ho->minimize = ho_minimize; ho->res_set = ho_res_set; ho->res_get = ho_res_get; ho->addrinfo = ho_addrinfo; return (ho); } /* Methods */ static void ho_close(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; ho_minimize(this); nisfree(pvt, do_all); if (pvt->res && pvt->free_res) (*pvt->free_res)(pvt->res); memput(pvt, sizeof *pvt); memput(this, sizeof *this); } static struct hostent * ho_byname(struct irs_ho *this, const char *name) { struct pvt *pvt = (struct pvt *)this->private; struct hostent *hp; if (init(this) == -1) return (NULL); if (pvt->res->options & RES_USE_INET6) { hp = ho_byname2(this, name, AF_INET6); if (hp) return (hp); } return (ho_byname2(this, name, AF_INET)); } static struct hostent * ho_byname2(struct irs_ho *this, const char *name, int af) { struct pvt *pvt = (struct pvt *)this->private; int r; char *tmp; UNUSED(af); if (init(this) == -1) return (NULL); nisfree(pvt, do_val); - DE_CONST(name, tmp); - r = yp_match(pvt->nis_domain, hosts_byname, tmp, + + strcpy(pvt->hostbuf, yp_multi); + strncat(pvt->hostbuf, name, sizeof(pvt->hostbuf) - sizeof(yp_multi)); + pvt->hostbuf[sizeof(pvt->hostbuf) - 1] = '\0'; + for (r = sizeof(yp_multi) - 1; pvt->hostbuf[r] != '\0'; r++) + if (isupper((unsigned char)pvt->hostbuf[r])) + tolower(pvt->hostbuf[r]); + + tmp = pvt->hostbuf; + r = yp_match(pvt->nis_domain, ipnode_byname, tmp, strlen(tmp), &pvt->curval_data, &pvt->curval_len); + if (r != 0) { + tmp = pvt->hostbuf + sizeof(yp_multi) - 1; + r = yp_match(pvt->nis_domain, ipnode_byname, tmp, + strlen(tmp), &pvt->curval_data, &pvt->curval_len); + } + if (r != 0) { + tmp = pvt->hostbuf; + r = yp_match(pvt->nis_domain, hosts_byname, tmp, + strlen(tmp), &pvt->curval_data, &pvt->curval_len); + } + if (r != 0) { + tmp = pvt->hostbuf + sizeof(yp_multi) - 1; + r = yp_match(pvt->nis_domain, hosts_byname, tmp, + strlen(tmp), &pvt->curval_data, &pvt->curval_len); + } if (r != 0) { RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND); return (NULL); } return (makehostent(this)); } static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) { struct pvt *pvt = (struct pvt *)this->private; char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; const u_char *uaddr = addr; int r; if (init(this) == -1) return (NULL); if (af == AF_INET6 && len == IN6ADDRSZ && (!memcmp(uaddr, mapped, sizeof mapped) || !memcmp(uaddr, tunnelled, sizeof tunnelled))) { /* Unmap. */ addr = (const u_char *)addr + sizeof mapped; uaddr += sizeof mapped; af = AF_INET; len = INADDRSZ; } if (inet_ntop(af, uaddr, tmp, sizeof tmp) == NULL) { RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); return (NULL); } nisfree(pvt, do_val); - r = yp_match(pvt->nis_domain, hosts_byaddr, tmp, strlen(tmp), + r = yp_match(pvt->nis_domain, ipnode_byaddr, tmp, strlen(tmp), &pvt->curval_data, &pvt->curval_len); + if (r != 0) + r = yp_match(pvt->nis_domain, hosts_byaddr, tmp, strlen(tmp), + &pvt->curval_data, &pvt->curval_len); if (r != 0) { RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND); return (NULL); } return (makehostent(this)); } static struct hostent * ho_next(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; struct hostent *rval; int r; if (init(this) == -1) return (NULL); do { if (pvt->needrewind) { nisfree(pvt, do_all); r = yp_first(pvt->nis_domain, hosts_byaddr, &pvt->curkey_data, &pvt->curkey_len, &pvt->curval_data, &pvt->curval_len); pvt->needrewind = 0; } else { char *newkey_data; int newkey_len; nisfree(pvt, do_val); r = yp_next(pvt->nis_domain, hosts_byaddr, pvt->curkey_data, pvt->curkey_len, &newkey_data, &newkey_len, &pvt->curval_data, &pvt->curval_len); nisfree(pvt, do_key); pvt->curkey_data = newkey_data; pvt->curkey_len = newkey_len; } if (r != 0) { RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND); return (NULL); } rval = makehostent(this); } while (rval == NULL); return (rval); } static void ho_rewind(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; pvt->needrewind = 1; } static void ho_minimize(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; if (pvt->res) res_nclose(pvt->res); } static struct __res_state * ho_res_get(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; if (!pvt->res) { struct __res_state *res; res = (struct __res_state *)malloc(sizeof *res); if (!res) { errno = ENOMEM; return (NULL); } memset(res, 0, sizeof *res); ho_res_set(this, res, free); } return (pvt->res); } static void ho_res_set(struct irs_ho *this, struct __res_state *res, void (*free_res)(void *)) { struct pvt *pvt = (struct pvt *)this->private; if (pvt->res && pvt->free_res) { res_nclose(pvt->res); (*pvt->free_res)(pvt->res); } pvt->res = res; pvt->free_res = free_res; } struct nis_res_target { struct nis_res_target *next; int family; }; /* XXX */ extern struct addrinfo *hostent2addrinfo __P((struct hostent *, const struct addrinfo *pai)); static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai) { struct pvt *pvt = (struct pvt *)this->private; struct hostent *hp; struct nis_res_target q, q2, *p; struct addrinfo sentinel, *cur; memset(&q, 0, sizeof(q2)); memset(&q2, 0, sizeof(q2)); memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; switch(pai->ai_family) { case AF_UNSPEC: /* INET6 then INET4 */ q.family = AF_INET6; q.next = &q2; q2.family = AF_INET; break; case AF_INET6: q.family = AF_INET6; break; case AF_INET: q.family = AF_INET; break; default: RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); /* ??? */ return(NULL); } for (p = &q; p; p = p->next) { struct addrinfo *ai; hp = (*this->byname2)(this, name, p->family); if (hp == NULL) { /* byname2 should've set an appropriate error */ continue; } if ((hp->h_name == NULL) || (hp->h_name[0] == 0) || (hp->h_addr_list[0] == NULL)) { RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); continue; } ai = hostent2addrinfo(hp, pai); if (ai) { cur->ai_next = ai; while (cur && cur->ai_next) cur = cur->ai_next; } } if (sentinel.ai_next == NULL) RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND); return(sentinel.ai_next); } /* Private */ +/* +ipnodes: +::1 localhost +127.0.0.1 localhost +1.2.3.4 FOO bar +1.2.6.4 FOO bar +1.2.6.5 host + +ipnodes.byname: +YP_MULTI_localhost ::1,127.0.0.1 localhost +YP_MULTI_foo 1.2.3.4,1.2.6.4 FOO bar +YP_MULTI_bar 1.2.3.4,1.2.6.4 FOO bar +host 1.2.6.5 host + +hosts.byname: +localhost 127.0.0.1 localhost +host 1.2.6.5 host +YP_MULTI_foo 1.2.3.4,1.2.6.4 FOO bar +YP_MULTI_bar 1.2.3.4,1.2.6.4 FOO bar +*/ + static struct hostent * makehostent(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; static const char spaces[] = " \t"; - char *cp, **q, *p; - int af, len; + char *cp, **q, *p, *comma, *ap; + int af = 0, len = 0; + int multi = 0; + int addr = 0; p = pvt->curval_data; if ((cp = strpbrk(p, "#\n")) != NULL) *cp = '\0'; if (!(cp = strpbrk(p, spaces))) return (NULL); *cp++ = '\0'; - if ((pvt->res->options & RES_USE_INET6) && - inet_pton(AF_INET6, p, pvt->host_addr) > 0) { - af = AF_INET6; - len = IN6ADDRSZ; - } else if (inet_pton(AF_INET, p, pvt->host_addr) > 0) { - if (pvt->res->options & RES_USE_INET6) { - map_v4v6_address((char*)pvt->host_addr, - (char*)pvt->host_addr); + ap = pvt->hostbuf; + do { + if ((comma = strchr(p, ',')) != NULL) { + *comma++ = '\0'; + multi = 1; + } + if ((ap + IN6ADDRSZ) > (pvt->hostbuf + sizeof(pvt->hostbuf))) + break; + if ((pvt->res->options & RES_USE_INET6) && + inet_pton(AF_INET6, p, ap) > 0) { af = AF_INET6; len = IN6ADDRSZ; + } else if (inet_pton(AF_INET, p, pvt->host_addr) > 0) { + if (pvt->res->options & RES_USE_INET6) { + map_v4v6_address((char*)pvt->host_addr, ap); + af = AF_INET6; + len = IN6ADDRSZ; + } else { + af = AF_INET; + len = INADDRSZ; + } } else { - af = AF_INET; - len = INADDRSZ; + if (!multi) + return (NULL); + continue; + } + if (addr < MAXADDRS) { + pvt->h_addr_ptrs[addr++] = ap; + pvt->h_addr_ptrs[addr] = NULL; + ap += len; } - } else { + } while ((p = comma) != NULL); + if (ap == pvt->hostbuf) return (NULL); - } - pvt->h_addr_ptrs[0] = (char *)pvt->host_addr; - pvt->h_addr_ptrs[1] = NULL; pvt->host.h_addr_list = pvt->h_addr_ptrs; pvt->host.h_length = len; pvt->host.h_addrtype = af; cp += strspn(cp, spaces); pvt->host.h_name = cp; q = pvt->host.h_aliases = pvt->host_aliases; if ((cp = strpbrk(cp, spaces)) != NULL) *cp++ = '\0'; while (cp && *cp) { if (*cp == ' ' || *cp == '\t') { cp++; continue; } if (q < &pvt->host_aliases[MAXALIASES]) *q++ = cp; if ((cp = strpbrk(cp, spaces)) != NULL) *cp++ = '\0'; } *q = NULL; RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); return (&pvt->host); } static void nisfree(struct pvt *pvt, enum do_what do_what) { if ((do_what & do_key) && pvt->curkey_data) { free(pvt->curkey_data); pvt->curkey_data = NULL; } if ((do_what & do_val) && pvt->curval_data) { free(pvt->curval_data); pvt->curval_data = NULL; } } static int init(struct irs_ho *this) { struct pvt *pvt = (struct pvt *)this->private; if (!pvt->res && !ho_res_get(this)) return (-1); if (((pvt->res->options & RES_INIT) == 0) && res_ninit(pvt->res) == -1) return (-1); return (0); } #endif /*WANT_IRS_NIS*/ diff --git a/contrib/bind/lib/isc/ctl_clnt.c b/contrib/bind/lib/isc/ctl_clnt.c index 56e50981dfb8..13d71d5e078b 100644 --- a/contrib/bind/lib/isc/ctl_clnt.c +++ b/contrib/bind/lib/isc/ctl_clnt.c @@ -1,602 +1,602 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ctl_clnt.c,v 8.17 2001/06/06 00:33:35 marka Exp $"; +static const char rcsid[] = "$Id: ctl_clnt.c,v 8.18 2002/07/08 05:10:23 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1998,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Extern. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ctl_p.h" #include "port_after.h" /* Constants. */ /* Macros. */ #define donefunc_p(ctx) ((ctx).donefunc != NULL) #define arpacode_p(line) (isdigit((unsigned char)(line[0])) && \ isdigit((unsigned char)(line[1])) && \ isdigit((unsigned char)(line[2]))) #define arpacont_p(line) (line[3] == '-') #define arpadone_p(line) (line[3] == ' ' || line[3] == '\t' || \ line[3] == '\r' || line[3] == '\0') /* Types. */ enum state { initializing = 0, connecting, connected, destroyed }; struct ctl_tran { LINK(struct ctl_tran) link; LINK(struct ctl_tran) wlink; struct ctl_cctx * ctx; struct ctl_buf outbuf; ctl_clntdone donefunc; void * uap; }; struct ctl_cctx { enum state state; evContext ev; int sock; ctl_logfunc logger; ctl_clntdone donefunc; void * uap; evConnID coID; evTimerID tiID; evFileID rdID; evStreamID wrID; struct ctl_buf inbuf; struct timespec timeout; LIST(struct ctl_tran) tran; LIST(struct ctl_tran) wtran; }; /* Forward. */ static struct ctl_tran *new_tran(struct ctl_cctx *, ctl_clntdone, void *, int); static void start_write(struct ctl_cctx *); static void destroy(struct ctl_cctx *, int); static void error(struct ctl_cctx *); static void new_state(struct ctl_cctx *, enum state); static void conn_done(evContext, void *, int, const void *, int, const void *, int); static void write_done(evContext, void *, int, int); static void start_read(struct ctl_cctx *); static void stop_read(struct ctl_cctx *); static void readable(evContext, void *, int, int); static void start_timer(struct ctl_cctx *); static void stop_timer(struct ctl_cctx *); static void touch_timer(struct ctl_cctx *); static void timer(evContext, void *, struct timespec, struct timespec); /* Private data. */ static const char * const state_names[] = { "initializing", "connecting", "connected", "destroyed" }; /* Public. */ /* * void * ctl_client() * create, condition, and connect to a listener on the control port. */ struct ctl_cctx * ctl_client(evContext lev, const struct sockaddr *cap, size_t cap_len, const struct sockaddr *sap, size_t sap_len, ctl_clntdone donefunc, void *uap, u_int timeout, ctl_logfunc logger) { static const char me[] = "ctl_client"; static const int on = 1; struct ctl_cctx *ctx; struct sockaddr *captmp; if (logger == NULL) logger = ctl_logger; ctx = memget(sizeof *ctx); if (ctx == NULL) { (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno)); goto fatal; } ctx->state = initializing; ctx->ev = lev; ctx->logger = logger; ctx->timeout = evConsTime(timeout, 0); ctx->donefunc = donefunc; ctx->uap = uap; ctx->coID.opaque = NULL; ctx->tiID.opaque = NULL; ctx->rdID.opaque = NULL; ctx->wrID.opaque = NULL; buffer_init(ctx->inbuf); INIT_LIST(ctx->tran); INIT_LIST(ctx->wtran); ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC); if (ctx->sock > evHighestFD(ctx->ev)) { ctx->sock = -1; errno = ENOTSOCK; } if (ctx->sock < 0) { (*ctx->logger)(ctl_error, "%s: socket: %s", me, strerror(errno)); goto fatal; } if (cap != NULL) { if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof on) != 0) { (*ctx->logger)(ctl_warning, "%s: setsockopt(REUSEADDR): %s", me, strerror(errno)); } DE_CONST(cap, captmp); if (bind(ctx->sock, captmp, cap_len) < 0) { (*ctx->logger)(ctl_error, "%s: bind: %s", me, strerror(errno)); goto fatal; } } if (evConnect(lev, ctx->sock, (const struct sockaddr *)sap, sap_len, conn_done, ctx, &ctx->coID) < 0) { (*ctx->logger)(ctl_error, "%s: evConnect(fd %d): %s", - me, (void *)ctx->sock, strerror(errno)); + me, ctx->sock, strerror(errno)); fatal: if (ctx != NULL) { if (ctx->sock >= 0) close(ctx->sock); memput(ctx, sizeof *ctx); } return (NULL); } new_state(ctx, connecting); return (ctx); } /* * void * ctl_endclient(ctx) * close a client and release all of its resources. */ void ctl_endclient(struct ctl_cctx *ctx) { if (ctx->state != destroyed) destroy(ctx, 0); memput(ctx, sizeof *ctx); } /* * int * ctl_command(ctx, cmd, len, donefunc, uap) * Queue a transaction, which will begin with sending cmd * and complete by calling donefunc with the answer. */ int ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len, ctl_clntdone donefunc, void *uap) { struct ctl_tran *tran; char *pc; unsigned int n; switch (ctx->state) { case destroyed: errno = ENOTCONN; return (-1); case connecting: case connected: break; default: abort(); } if (len >= MAX_LINELEN) { errno = EMSGSIZE; return (-1); } tran = new_tran(ctx, donefunc, uap, 1); if (tran == NULL) return (-1); if (ctl_bufget(&tran->outbuf, ctx->logger) < 0) return (-1); memcpy(tran->outbuf.text, cmd, len); tran->outbuf.used = len; for (pc = tran->outbuf.text, n = 0; n < tran->outbuf.used; pc++, n++) if (!isascii((unsigned char)*pc) || !isprint((unsigned char)*pc)) *pc = '\040'; start_write(ctx); return (0); } /* Private. */ static struct ctl_tran * new_tran(struct ctl_cctx *ctx, ctl_clntdone donefunc, void *uap, int w) { struct ctl_tran *new = memget(sizeof *new); if (new == NULL) return (NULL); new->ctx = ctx; buffer_init(new->outbuf); new->donefunc = donefunc; new->uap = uap; INIT_LINK(new, link); INIT_LINK(new, wlink); APPEND(ctx->tran, new, link); if (w) APPEND(ctx->wtran, new, wlink); return (new); } static void start_write(struct ctl_cctx *ctx) { static const char me[] = "isc/ctl_clnt::start_write"; struct ctl_tran *tran; struct iovec iov[2], *iovp = iov; char * tmp; REQUIRE(ctx->state == connecting || ctx->state == connected); /* If there is a write in progress, don't try to write more yet. */ if (ctx->wrID.opaque != NULL) return; /* If there are no trans, make sure timer is off, and we're done. */ if (EMPTY(ctx->wtran)) { if (ctx->tiID.opaque != NULL) stop_timer(ctx); return; } /* Pull it off the head of the write queue. */ tran = HEAD(ctx->wtran); UNLINK(ctx->wtran, tran, wlink); /* Since there are some trans, make sure timer is successfully "on". */ if (ctx->tiID.opaque != NULL) touch_timer(ctx); else start_timer(ctx); if (ctx->state == destroyed) return; /* Marshall a newline-terminated message and clock it out. */ *iovp++ = evConsIovec(tran->outbuf.text, tran->outbuf.used); DE_CONST("\r\n", tmp); *iovp++ = evConsIovec(tmp, 2); if (evWrite(ctx->ev, ctx->sock, iov, iovp - iov, write_done, tran, &ctx->wrID) < 0) { (*ctx->logger)(ctl_error, "%s: evWrite: %s", me, strerror(errno)); error(ctx); return; } if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) { (*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me, strerror(errno)); error(ctx); return; } } static void destroy(struct ctl_cctx *ctx, int notify) { struct ctl_tran *this, *next; if (ctx->sock != -1) { (void) close(ctx->sock); ctx->sock = -1; } switch (ctx->state) { case connecting: REQUIRE(ctx->wrID.opaque == NULL); REQUIRE(EMPTY(ctx->tran)); /* * This test is nec'y since destroy() can be called from * start_read() while the state is still "connecting". */ if (ctx->coID.opaque != NULL) { (void)evCancelConn(ctx->ev, ctx->coID); ctx->coID.opaque = NULL; } break; case connected: REQUIRE(ctx->coID.opaque == NULL); if (ctx->wrID.opaque != NULL) { (void)evCancelRW(ctx->ev, ctx->wrID); ctx->wrID.opaque = NULL; } if (ctx->rdID.opaque != NULL) stop_read(ctx); break; case destroyed: break; default: abort(); } if (allocated_p(ctx->inbuf)) ctl_bufput(&ctx->inbuf); for (this = HEAD(ctx->tran); this != NULL; this = next) { next = NEXT(this, link); if (allocated_p(this->outbuf)) ctl_bufput(&this->outbuf); if (notify && this->donefunc != NULL) (*this->donefunc)(ctx, this->uap, NULL, 0); memput(this, sizeof *this); } if (ctx->tiID.opaque != NULL) stop_timer(ctx); new_state(ctx, destroyed); } static void error(struct ctl_cctx *ctx) { REQUIRE(ctx->state != destroyed); destroy(ctx, 1); } static void new_state(struct ctl_cctx *ctx, enum state new_state) { static const char me[] = "isc/ctl_clnt::new_state"; (*ctx->logger)(ctl_debug, "%s: %s -> %s", me, state_names[ctx->state], state_names[new_state]); ctx->state = new_state; } static void conn_done(evContext ev, void *uap, int fd, const void *la, int lalen, const void *ra, int ralen) { static const char me[] = "isc/ctl_clnt::conn_done"; struct ctl_cctx *ctx = uap; struct ctl_tran *tran; UNUSED(ev); UNUSED(la); UNUSED(lalen); UNUSED(ra); UNUSED(ralen); ctx->coID.opaque = NULL; if (fd < 0) { (*ctx->logger)(ctl_error, "%s: evConnect: %s", me, strerror(errno)); error(ctx); return; } new_state(ctx, connected); tran = new_tran(ctx, ctx->donefunc, ctx->uap, 0); if (tran == NULL) { (*ctx->logger)(ctl_error, "%s: new_tran failed: %s", me, strerror(errno)); error(ctx); return; } start_read(ctx); if (ctx->state == destroyed) { (*ctx->logger)(ctl_error, "%s: start_read failed: %s", me, strerror(errno)); error(ctx); return; } } static void write_done(evContext lev, void *uap, int fd, int bytes) { struct ctl_tran *tran = (struct ctl_tran *)uap; struct ctl_cctx *ctx = tran->ctx; UNUSED(lev); UNUSED(fd); ctx->wrID.opaque = NULL; if (ctx->tiID.opaque != NULL) touch_timer(ctx); ctl_bufput(&tran->outbuf); start_write(ctx); if (bytes < 0) destroy(ctx, 1); else start_read(ctx); } static void start_read(struct ctl_cctx *ctx) { static const char me[] = "isc/ctl_clnt::start_read"; REQUIRE(ctx->state == connecting || ctx->state == connected); REQUIRE(ctx->rdID.opaque == NULL); if (evSelectFD(ctx->ev, ctx->sock, EV_READ, readable, ctx, &ctx->rdID) < 0) { (*ctx->logger)(ctl_error, "%s: evSelect(fd %d): %s", me, ctx->sock, strerror(errno)); error(ctx); return; } } static void stop_read(struct ctl_cctx *ctx) { REQUIRE(ctx->coID.opaque == NULL); REQUIRE(ctx->rdID.opaque != NULL); (void)evDeselectFD(ctx->ev, ctx->rdID); ctx->rdID.opaque = NULL; } static void readable(evContext ev, void *uap, int fd, int evmask) { static const char me[] = "isc/ctl_clnt::readable"; struct ctl_cctx *ctx = uap; struct ctl_tran *tran; ssize_t n; char *eos; UNUSED(ev); REQUIRE(ctx != NULL); REQUIRE(fd >= 0); REQUIRE(evmask == EV_READ); REQUIRE(ctx->state == connected); REQUIRE(!EMPTY(ctx->tran)); tran = HEAD(ctx->tran); if (!allocated_p(ctx->inbuf) && ctl_bufget(&ctx->inbuf, ctx->logger) < 0) { (*ctx->logger)(ctl_error, "%s: can't get an input buffer", me); error(ctx); return; } n = read(ctx->sock, ctx->inbuf.text + ctx->inbuf.used, MAX_LINELEN - ctx->inbuf.used); if (n <= 0) { (*ctx->logger)(ctl_warning, "%s: read: %s", me, (n == 0) ? "Unexpected EOF" : strerror(errno)); error(ctx); return; } if (ctx->tiID.opaque != NULL) touch_timer(ctx); ctx->inbuf.used += n; (*ctx->logger)(ctl_debug, "%s: read %d, used %d", me, n, ctx->inbuf.used); again: eos = memchr(ctx->inbuf.text, '\n', ctx->inbuf.used); if (eos != NULL && eos != ctx->inbuf.text && eos[-1] == '\r') { int done = 0; eos[-1] = '\0'; if (!arpacode_p(ctx->inbuf.text)) { /* XXX Doesn't FTP do this sometimes? Is it legal? */ (*ctx->logger)(ctl_error, "%s: no arpa code (%s)", me, ctx->inbuf.text); error(ctx); return; } if (arpadone_p(ctx->inbuf.text)) done = 1; else if (arpacont_p(ctx->inbuf.text)) done = 0; else { /* XXX Doesn't FTP do this sometimes? Is it legal? */ (*ctx->logger)(ctl_error, "%s: no arpa flag (%s)", me, ctx->inbuf.text); error(ctx); return; } (*tran->donefunc)(ctx, tran->uap, ctx->inbuf.text, (done ? 0 : CTL_MORE)); ctx->inbuf.used -= ((eos - ctx->inbuf.text) + 1); if (ctx->inbuf.used == 0) ctl_bufput(&ctx->inbuf); else memmove(ctx->inbuf.text, eos + 1, ctx->inbuf.used); if (done) { UNLINK(ctx->tran, tran, link); memput(tran, sizeof *tran); stop_read(ctx); start_write(ctx); return; } if (allocated_p(ctx->inbuf)) goto again; return; } if (ctx->inbuf.used == MAX_LINELEN) { (*ctx->logger)(ctl_error, "%s: line too long (%-10s...)", me, ctx->inbuf.text); error(ctx); } } /* Timer related stuff. */ static void start_timer(struct ctl_cctx *ctx) { static const char me[] = "isc/ctl_clnt::start_timer"; REQUIRE(ctx->tiID.opaque == NULL); if (evSetIdleTimer(ctx->ev, timer, ctx, ctx->timeout, &ctx->tiID) < 0){ (*ctx->logger)(ctl_error, "%s: evSetIdleTimer: %s", me, strerror(errno)); error(ctx); return; } } static void stop_timer(struct ctl_cctx *ctx) { static const char me[] = "isc/ctl_clnt::stop_timer"; REQUIRE(ctx->tiID.opaque != NULL); if (evClearIdleTimer(ctx->ev, ctx->tiID) < 0) { (*ctx->logger)(ctl_error, "%s: evClearIdleTimer: %s", me, strerror(errno)); error(ctx); return; } ctx->tiID.opaque = NULL; } static void touch_timer(struct ctl_cctx *ctx) { REQUIRE(ctx->tiID.opaque != NULL); evTouchIdleTimer(ctx->ev, ctx->tiID); } static void timer(evContext ev, void *uap, struct timespec due, struct timespec itv) { static const char me[] = "isc/ctl_clnt::timer"; struct ctl_cctx *ctx = uap; UNUSED(ev); UNUSED(due); UNUSED(itv); ctx->tiID.opaque = NULL; (*ctx->logger)(ctl_error, "%s: timeout after %u seconds while %s", me, ctx->timeout.tv_sec, state_names[ctx->state]); error(ctx); } diff --git a/contrib/bind/lib/isc/ctl_srvr.c b/contrib/bind/lib/isc/ctl_srvr.c index 91a2c1b0eede..835bcd4a0c27 100644 --- a/contrib/bind/lib/isc/ctl_srvr.c +++ b/contrib/bind/lib/isc/ctl_srvr.c @@ -1,780 +1,780 @@ #if !defined(lint) && !defined(SABER) -static const char rcsid[] = "$Id: ctl_srvr.c,v 8.25 2001/05/29 05:49:27 marka Exp $"; +static const char rcsid[] = "$Id: ctl_srvr.c,v 8.26 2002/07/08 05:10:25 marka Exp $"; #endif /* not lint */ /* * Copyright (c) 1998,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Extern. */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ctl_p.h" #include "port_after.h" #ifdef SPRINTF_CHAR # define SPRINTF(x) strlen(sprintf/**/x) #else # define SPRINTF(x) ((size_t)sprintf x) #endif /* Macros. */ #define lastverb_p(verb) (verb->name == NULL || verb->func == NULL) #define address_expr ctl_sa_ntop((struct sockaddr *)&sess->sa, \ tmp, sizeof tmp, ctx->logger) /* Types. */ enum state { available = 0, initializing, writing, reading, reading_data, processing, idling, quitting, closing }; union sa_un { struct sockaddr_in in; #ifndef NO_SOCKADDR_UN struct sockaddr_un un; #endif }; struct ctl_sess { LINK(struct ctl_sess) link; struct ctl_sctx * ctx; enum state state; int sock; union sa_un sa; evFileID rdID; evStreamID wrID; evTimerID rdtiID; evTimerID wrtiID; struct ctl_buf inbuf; struct ctl_buf outbuf; const struct ctl_verb * verb; u_int helpcode; const void * respctx; u_int respflags; ctl_srvrdone donefunc; void * uap; void * csctx; }; struct ctl_sctx { evContext ev; void * uctx; u_int unkncode; u_int timeoutcode; const struct ctl_verb * verbs; const struct ctl_verb * connverb; int sock; int max_sess; int cur_sess; struct timespec timeout; ctl_logfunc logger; evConnID acID; LIST(struct ctl_sess) sess; }; /* Forward. */ static void ctl_accept(evContext, void *, int, const void *, int, const void *, int); static void ctl_close(struct ctl_sess *); static void ctl_new_state(struct ctl_sess *, enum state, const char *); static void ctl_start_read(struct ctl_sess *); static void ctl_stop_read(struct ctl_sess *); static void ctl_readable(evContext, void *, int, int); static void ctl_rdtimeout(evContext, void *, struct timespec, struct timespec); static void ctl_wrtimeout(evContext, void *, struct timespec, struct timespec); static void ctl_docommand(struct ctl_sess *); static void ctl_writedone(evContext, void *, int, int); static void ctl_morehelp(struct ctl_sctx *, struct ctl_sess *, const struct ctl_verb *, const char *, u_int, const void *, void *); static void ctl_signal_done(struct ctl_sctx *, struct ctl_sess *); /* Private data. */ static const char * state_names[] = { "available", "initializing", "writing", "reading", "reading_data", "processing", "idling", "quitting", "closing" }; static const char space[] = " "; static const struct ctl_verb fakehelpverb = { "fakehelp", ctl_morehelp , NULL }; /* Public. */ /* * void * ctl_server() * create, condition, and start a listener on the control port. */ struct ctl_sctx * ctl_server(evContext lev, const struct sockaddr *sap, size_t sap_len, const struct ctl_verb *verbs, u_int unkncode, u_int timeoutcode, u_int timeout, int backlog, int max_sess, ctl_logfunc logger, void *uctx) { static const char me[] = "ctl_server"; static const int on = 1; const struct ctl_verb *connverb; struct ctl_sctx *ctx; int save_errno; if (logger == NULL) logger = ctl_logger; for (connverb = verbs; connverb->name != NULL && connverb->func != NULL; connverb++) if (connverb->name[0] == '\0') break; if (connverb->func == NULL) { (*logger)(ctl_error, "%s: no connection verb found", me); return (NULL); } ctx = memget(sizeof *ctx); if (ctx == NULL) { (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno)); return (NULL); } ctx->ev = lev; ctx->uctx = uctx; ctx->unkncode = unkncode; ctx->timeoutcode = timeoutcode; ctx->verbs = verbs; ctx->timeout = evConsTime(timeout, 0); ctx->logger = logger; ctx->connverb = connverb; ctx->max_sess = max_sess; ctx->cur_sess = 0; INIT_LIST(ctx->sess); ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC); if (ctx->sock > evHighestFD(ctx->ev)) { ctx->sock = -1; errno = ENOTSOCK; } if (ctx->sock < 0) { save_errno = errno; (*ctx->logger)(ctl_error, "%s: socket: %s", me, strerror(errno)); memput(ctx, sizeof *ctx); errno = save_errno; return (NULL); } if (ctx->sock > evHighestFD(lev)) { close(ctx->sock); (*ctx->logger)(ctl_error, "%s: file descriptor > evHighestFD"); errno = ENFILE; memput(ctx, sizeof *ctx); return (NULL); } #ifdef NO_UNIX_REUSEADDR if (sap->sa_family != AF_UNIX) #endif if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof on) != 0) { (*ctx->logger)(ctl_warning, "%s: setsockopt(REUSEADDR): %s", me, strerror(errno)); } if (bind(ctx->sock, sap, sap_len) < 0) { char tmp[MAX_NTOP]; save_errno = errno; (*ctx->logger)(ctl_error, "%s: bind: %s: %s", me, ctl_sa_ntop((const struct sockaddr *)sap, tmp, sizeof tmp, ctx->logger), strerror(save_errno)); close(ctx->sock); memput(ctx, sizeof *ctx); errno = save_errno; return (NULL); } if (fcntl(ctx->sock, F_SETFD, 1) < 0) { (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me, strerror(errno)); } if (evListen(lev, ctx->sock, backlog, ctl_accept, ctx, &ctx->acID) < 0) { save_errno = errno; (*ctx->logger)(ctl_error, "%s: evListen(fd %d): %s", - me, (void *)ctx->sock, strerror(errno)); + me, ctx->sock, strerror(errno)); close(ctx->sock); memput(ctx, sizeof *ctx); errno = save_errno; return (NULL); } (*ctx->logger)(ctl_debug, "%s: new ctx %p, sock %d", me, ctx, ctx->sock); return (ctx); } /* * void * ctl_endserver(ctx) * if the control listener is open, close it. clean out all eventlib * stuff. close all active sessions. */ void ctl_endserver(struct ctl_sctx *ctx) { static const char me[] = "ctl_endserver"; struct ctl_sess *this, *next; (*ctx->logger)(ctl_debug, "%s: ctx %p, sock %d, acID %p, sess %p", me, ctx, ctx->sock, ctx->acID.opaque, ctx->sess); if (ctx->acID.opaque != NULL) { (void)evCancelConn(ctx->ev, ctx->acID); ctx->acID.opaque = NULL; } if (ctx->sock != -1) { (void) close(ctx->sock); ctx->sock = -1; } for (this = HEAD(ctx->sess); this != NULL; this = next) { next = NEXT(this, link); ctl_close(this); } memput(ctx, sizeof *ctx); } /* * If body is non-NULL then it we add a "." line after it. * Caller must have escaped lines with leading ".". */ void ctl_response(struct ctl_sess *sess, u_int code, const char *text, u_int flags, const void *respctx, ctl_srvrdone donefunc, void *uap, const char *body, size_t bodylen) { static const char me[] = "ctl_response"; struct iovec iov[3], *iovp = iov; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP], *pc; int n; REQUIRE(sess->state == initializing || sess->state == processing || sess->state == reading_data || sess->state == writing); REQUIRE(sess->wrtiID.opaque == NULL); REQUIRE(sess->wrID.opaque == NULL); ctl_new_state(sess, writing, me); sess->donefunc = donefunc; sess->uap = uap; if (!allocated_p(sess->outbuf) && ctl_bufget(&sess->outbuf, ctx->logger) < 0) { (*ctx->logger)(ctl_error, "%s: %s: cant get an output buffer", me, address_expr); goto untimely; } if (sizeof "000-\r\n" + strlen(text) > MAX_LINELEN) { (*ctx->logger)(ctl_error, "%s: %s: output buffer ovf, closing", me, address_expr); goto untimely; } sess->outbuf.used = SPRINTF((sess->outbuf.text, "%03d%c%s\r\n", code, (flags & CTL_MORE) != 0 ? '-' : ' ', text)); for (pc = sess->outbuf.text, n = 0; n < (int)sess->outbuf.used-2; pc++, n++) if (!isascii((unsigned char)*pc) || !isprint((unsigned char)*pc)) *pc = '\040'; *iovp++ = evConsIovec(sess->outbuf.text, sess->outbuf.used); if (body != NULL) { char *tmp; DE_CONST(body, tmp); *iovp++ = evConsIovec(tmp, bodylen); DE_CONST(".\r\n", tmp); *iovp++ = evConsIovec(tmp, 3); } (*ctx->logger)(ctl_debug, "%s: [%d] %s", me, sess->outbuf.used, sess->outbuf.text); if (evWrite(ctx->ev, sess->sock, iov, iovp - iov, ctl_writedone, sess, &sess->wrID) < 0) { (*ctx->logger)(ctl_error, "%s: %s: evWrite: %s", me, address_expr, strerror(errno)); goto untimely; } if (evSetIdleTimer(ctx->ev, ctl_wrtimeout, sess, ctx->timeout, &sess->wrtiID) < 0) { (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me, address_expr, strerror(errno)); goto untimely; } if (evTimeRW(ctx->ev, sess->wrID, sess->wrtiID) < 0) { (*ctx->logger)(ctl_error, "%s: %s: evTimeRW: %s", me, address_expr, strerror(errno)); untimely: ctl_signal_done(ctx, sess); ctl_close(sess); return; } sess->respctx = respctx; sess->respflags = flags; } void ctl_sendhelp(struct ctl_sess *sess, u_int code) { static const char me[] = "ctl_sendhelp"; struct ctl_sctx *ctx = sess->ctx; sess->helpcode = code; sess->verb = &fakehelpverb; ctl_morehelp(ctx, sess, NULL, me, CTL_MORE, (const void *)ctx->verbs, NULL); } void * ctl_getcsctx(struct ctl_sess *sess) { return (sess->csctx); } void * ctl_setcsctx(struct ctl_sess *sess, void *csctx) { void *old = sess->csctx; sess->csctx = csctx; return (old); } /* Private functions. */ static void ctl_accept(evContext lev, void *uap, int fd, const void *lav, int lalen, const void *rav, int ralen) { static const char me[] = "ctl_accept"; struct ctl_sctx *ctx = uap; struct ctl_sess *sess = NULL; char tmp[MAX_NTOP]; UNUSED(lev); UNUSED(lalen); UNUSED(ralen); if (fd < 0) { (*ctx->logger)(ctl_error, "%s: accept: %s", me, strerror(errno)); return; } if (ctx->cur_sess == ctx->max_sess) { (*ctx->logger)(ctl_error, "%s: %s: too many control sessions", me, ctl_sa_ntop((const struct sockaddr *)rav, tmp, sizeof tmp, ctx->logger)); (void) close(fd); return; } sess = memget(sizeof *sess); if (sess == NULL) { (*ctx->logger)(ctl_error, "%s: memget: %s", me, strerror(errno)); (void) close(fd); return; } if (fcntl(fd, F_SETFD, 1) < 0) { (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me, strerror(errno)); } ctx->cur_sess++; INIT_LINK(sess, link); APPEND(ctx->sess, sess, link); sess->ctx = ctx; sess->sock = fd; sess->wrID.opaque = NULL; sess->rdID.opaque = NULL; sess->wrtiID.opaque = NULL; sess->rdtiID.opaque = NULL; sess->respctx = NULL; sess->csctx = NULL; if (((const struct sockaddr *)rav)->sa_family == AF_UNIX) ctl_sa_copy((const struct sockaddr *)lav, (struct sockaddr *)&sess->sa); else ctl_sa_copy((const struct sockaddr *)rav, (struct sockaddr *)&sess->sa); sess->donefunc = NULL; buffer_init(sess->inbuf); buffer_init(sess->outbuf); sess->state = available; ctl_new_state(sess, initializing, me); sess->verb = ctx->connverb; (*ctx->logger)(ctl_debug, "%s: %s: accepting (fd %d)", me, address_expr, sess->sock); (*ctx->connverb->func)(ctx, sess, ctx->connverb, "", 0, (const struct sockaddr *)rav, ctx->uctx); } static void ctl_new_state(struct ctl_sess *sess, enum state new_state, const char *reason) { static const char me[] = "ctl_new_state"; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP]; (*ctx->logger)(ctl_debug, "%s: %s: %s -> %s (%s)", me, address_expr, state_names[sess->state], state_names[new_state], reason); sess->state = new_state; } static void ctl_close(struct ctl_sess *sess) { static const char me[] = "ctl_close"; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP]; REQUIRE(sess->state == initializing || sess->state == writing || sess->state == reading || sess->state == processing || sess->state == reading_data || sess->state == idling); REQUIRE(sess->sock != -1); if (sess->state == reading || sess->state == reading_data) ctl_stop_read(sess); else if (sess->state == writing) { if (sess->wrID.opaque != NULL) { (void) evCancelRW(ctx->ev, sess->wrID); sess->wrID.opaque = NULL; } if (sess->wrtiID.opaque != NULL) { (void) evClearIdleTimer(ctx->ev, sess->wrtiID); sess->wrtiID.opaque = NULL; } } ctl_new_state(sess, closing, me); (void) close(sess->sock); if (allocated_p(sess->inbuf)) ctl_bufput(&sess->inbuf); if (allocated_p(sess->outbuf)) ctl_bufput(&sess->outbuf); (*ctx->logger)(ctl_debug, "%s: %s: closed (fd %d)", me, address_expr, sess->sock); UNLINK(ctx->sess, sess, link); memput(sess, sizeof *sess); ctx->cur_sess--; } static void ctl_start_read(struct ctl_sess *sess) { static const char me[] = "ctl_start_read"; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP]; REQUIRE(sess->state == initializing || sess->state == writing || sess->state == processing || sess->state == idling); REQUIRE(sess->rdtiID.opaque == NULL); REQUIRE(sess->rdID.opaque == NULL); sess->inbuf.used = 0; if (evSetIdleTimer(ctx->ev, ctl_rdtimeout, sess, ctx->timeout, &sess->rdtiID) < 0) { (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me, address_expr, strerror(errno)); ctl_close(sess); return; } if (evSelectFD(ctx->ev, sess->sock, EV_READ, ctl_readable, sess, &sess->rdID) < 0) { (*ctx->logger)(ctl_error, "%s: %s: evSelectFD: %s", me, address_expr, strerror(errno)); return; } ctl_new_state(sess, reading, me); } static void ctl_stop_read(struct ctl_sess *sess) { static const char me[] = "ctl_stop_read"; struct ctl_sctx *ctx = sess->ctx; REQUIRE(sess->state == reading || sess->state == reading_data); REQUIRE(sess->rdID.opaque != NULL); (void) evDeselectFD(ctx->ev, sess->rdID); sess->rdID.opaque = NULL; if (sess->rdtiID.opaque != NULL) { (void) evClearIdleTimer(ctx->ev, sess->rdtiID); sess->rdtiID.opaque = NULL; } ctl_new_state(sess, idling, me); } static void ctl_readable(evContext lev, void *uap, int fd, int evmask) { static const char me[] = "ctl_readable"; struct ctl_sess *sess = uap; struct ctl_sctx *ctx = sess->ctx; char *eos, tmp[MAX_NTOP]; ssize_t n; REQUIRE(sess != NULL); REQUIRE(fd >= 0); REQUIRE(evmask == EV_READ); REQUIRE(sess->state == reading || sess->state == reading_data); evTouchIdleTimer(lev, sess->rdtiID); if (!allocated_p(sess->inbuf) && ctl_bufget(&sess->inbuf, ctx->logger) < 0) { (*ctx->logger)(ctl_error, "%s: %s: cant get an input buffer", me, address_expr); ctl_close(sess); return; } n = read(sess->sock, sess->inbuf.text + sess->inbuf.used, MAX_LINELEN - sess->inbuf.used); if (n <= 0) { (*ctx->logger)(ctl_debug, "%s: %s: read: %s", me, address_expr, (n == 0) ? "Unexpected EOF" : strerror(errno)); ctl_close(sess); return; } sess->inbuf.used += n; eos = memchr(sess->inbuf.text, '\n', sess->inbuf.used); if (eos != NULL && eos != sess->inbuf.text && eos[-1] == '\r') { eos[-1] = '\0'; if ((sess->respflags & CTL_DATA) != 0) { INSIST(sess->verb != NULL); (*sess->verb->func)(sess->ctx, sess, sess->verb, sess->inbuf.text, CTL_DATA, sess->respctx, sess->ctx->uctx); } else { ctl_stop_read(sess); ctl_docommand(sess); } sess->inbuf.used -= ((eos - sess->inbuf.text) + 1); if (sess->inbuf.used == 0) ctl_bufput(&sess->inbuf); else memmove(sess->inbuf.text, eos + 1, sess->inbuf.used); return; } if (sess->inbuf.used == MAX_LINELEN) { (*ctx->logger)(ctl_error, "%s: %s: line too long, closing", me, address_expr); ctl_close(sess); } } static void ctl_wrtimeout(evContext lev, void *uap, struct timespec due, struct timespec itv) { static const char me[] = "ctl_wrtimeout"; struct ctl_sess *sess = uap; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP]; UNUSED(lev); UNUSED(due); UNUSED(itv); REQUIRE(sess->state == writing); sess->wrtiID.opaque = NULL; (*ctx->logger)(ctl_warning, "%s: %s: write timeout, closing", me, address_expr); if (sess->wrID.opaque != NULL) { (void) evCancelRW(ctx->ev, sess->wrID); sess->wrID.opaque = NULL; } ctl_signal_done(ctx, sess); ctl_new_state(sess, processing, me); ctl_close(sess); } static void ctl_rdtimeout(evContext lev, void *uap, struct timespec due, struct timespec itv) { static const char me[] = "ctl_rdtimeout"; struct ctl_sess *sess = uap; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP]; UNUSED(lev); UNUSED(due); UNUSED(itv); REQUIRE(sess->state == reading); sess->rdtiID.opaque = NULL; (*ctx->logger)(ctl_warning, "%s: %s: timeout, closing", me, address_expr); if (sess->state == reading || sess->state == reading_data) ctl_stop_read(sess); ctl_signal_done(ctx, sess); ctl_new_state(sess, processing, me); ctl_response(sess, ctx->timeoutcode, "Timeout.", CTL_EXIT, NULL, NULL, NULL, NULL, 0); } static void ctl_docommand(struct ctl_sess *sess) { static const char me[] = "ctl_docommand"; char *name, *rest, tmp[MAX_NTOP]; struct ctl_sctx *ctx = sess->ctx; const struct ctl_verb *verb; REQUIRE(allocated_p(sess->inbuf)); (*ctx->logger)(ctl_debug, "%s: %s: \"%s\" [%u]", me, address_expr, sess->inbuf.text, (u_int)sess->inbuf.used); ctl_new_state(sess, processing, me); name = sess->inbuf.text + strspn(sess->inbuf.text, space); rest = name + strcspn(name, space); if (*rest != '\0') { *rest++ = '\0'; rest += strspn(rest, space); } for (verb = ctx->verbs; verb != NULL && verb->name != NULL && verb->func != NULL; verb++) if (verb->name[0] != '\0' && strcasecmp(name, verb->name) == 0) break; if (verb != NULL && verb->name != NULL && verb->func != NULL) { sess->verb = verb; (*verb->func)(ctx, sess, verb, rest, 0, NULL, ctx->uctx); } else { char buf[1100]; if (sizeof "Unrecognized command \"\" (args \"\")" + strlen(name) + strlen(rest) > sizeof buf) strcpy(buf, "Unrecognized command (buf ovf)"); else sprintf(buf, "Unrecognized command \"%s\" (args \"%s\")", name, rest); ctl_response(sess, ctx->unkncode, buf, 0, NULL, NULL, NULL, NULL, 0); } } static void ctl_writedone(evContext lev, void *uap, int fd, int bytes) { static const char me[] = "ctl_writedone"; struct ctl_sess *sess = uap; struct ctl_sctx *ctx = sess->ctx; char tmp[MAX_NTOP]; int save_errno = errno; UNUSED(lev); UNUSED(uap); REQUIRE(sess->state == writing); REQUIRE(fd == sess->sock); REQUIRE(sess->wrtiID.opaque != NULL); sess->wrID.opaque = NULL; (void) evClearIdleTimer(ctx->ev, sess->wrtiID); sess->wrtiID.opaque = NULL; if (bytes < 0) { (*ctx->logger)(ctl_error, "%s: %s: %s", me, address_expr, strerror(save_errno)); ctl_close(sess); return; } INSIST(allocated_p(sess->outbuf)); ctl_bufput(&sess->outbuf); if ((sess->respflags & CTL_EXIT) != 0) { ctl_signal_done(ctx, sess); ctl_close(sess); return; } else if ((sess->respflags & CTL_MORE) != 0) { INSIST(sess->verb != NULL); (*sess->verb->func)(sess->ctx, sess, sess->verb, "", CTL_MORE, sess->respctx, sess->ctx->uctx); } else { ctl_signal_done(ctx, sess); ctl_start_read(sess); } } static void ctl_morehelp(struct ctl_sctx *ctx, struct ctl_sess *sess, const struct ctl_verb *verb, const char *text, u_int respflags, const void *respctx, void *uctx) { const struct ctl_verb *this = respctx, *next = this + 1; UNUSED(ctx); UNUSED(verb); UNUSED(text); UNUSED(uctx); REQUIRE(!lastverb_p(this)); REQUIRE((respflags & CTL_MORE) != 0); if (lastverb_p(next)) respflags &= ~CTL_MORE; ctl_response(sess, sess->helpcode, this->help, respflags, next, NULL, NULL, NULL, 0); } static void ctl_signal_done(struct ctl_sctx *ctx, struct ctl_sess *sess) { if (sess->donefunc != NULL) { (*sess->donefunc)(ctx, sess, sess->uap); sess->donefunc = NULL; } } diff --git a/contrib/bind/lib/isc/ev_files.c b/contrib/bind/lib/isc/ev_files.c index c6bcab675614..3a6894be1e45 100644 --- a/contrib/bind/lib/isc/ev_files.c +++ b/contrib/bind/lib/isc/ev_files.c @@ -1,283 +1,283 @@ /* * Copyright (c) 1995-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* ev_files.c - implement asynch file IO for the eventlib * vix 11sep95 [initial] */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: ev_files.c,v 1.21 2001/11/01 05:35:46 marka Exp $"; +static const char rcsid[] = "$Id: ev_files.c,v 1.22 2002/07/08 05:50:07 marka Exp $"; #endif #include "port_before.h" #include "fd_setsize.h" #include #include #include #include #include #include #include #include "eventlib_p.h" #include "port_after.h" static evFile *FindFD(const evContext_p *ctx, int fd, int eventmask); int evSelectFD(evContext opaqueCtx, int fd, int eventmask, evFileFunc func, void *uap, evFileID *opaqueID ) { evContext_p *ctx = opaqueCtx.opaque; evFile *id; int mode; evPrintf(ctx, 1, - "evSelectFD(ctx %#x, fd %d, mask 0x%x, func %#x, uap %#x)\n", + "evSelectFD(ctx %p, fd %d, mask 0x%x, func %p, uap %p)\n", ctx, fd, eventmask, func, uap); if (eventmask == 0 || (eventmask & ~EV_MASK_ALL) != 0) EV_ERR(EINVAL); if (fd > ctx->highestFD) EV_ERR(EINVAL); OK(mode = fcntl(fd, F_GETFL, NULL)); /* side effect: validate fd. */ /* * The first time we touch a file descriptor, we need to check to see * if the application already had it in O_NONBLOCK mode and if so, all * of our deselect()'s have to leave it in O_NONBLOCK. If not, then * all but our last deselect() has to leave it in O_NONBLOCK. */ id = FindFD(ctx, fd, EV_MASK_ALL); if (id == NULL) { if (mode & PORT_NONBLOCK) FD_SET(fd, &ctx->nonblockBefore); else { #ifdef USE_FIONBIO_IOCTL int on = 1; OK(ioctl(fd, FIONBIO, (char *)&on)); #else OK(fcntl(fd, F_SETFL, mode | PORT_NONBLOCK)); #endif FD_CLR(fd, &ctx->nonblockBefore); } } /* * If this descriptor is already in use, search for it again to see * if any of the eventmask bits we want to set are already captured. * We cannot usefully capture the same fd event more than once in the * same context. */ if (id != NULL && FindFD(ctx, fd, eventmask) != NULL) EV_ERR(ETOOMANYREFS); /* Allocate and fill. */ OKNEW(id); id->func = func; id->uap = uap; id->fd = fd; id->eventmask = eventmask; /* * Insert at head. Order could be important for performance if we * believe that evGetNext()'s accesses to the fd_sets will be more * serial and therefore more cache-lucky if the list is ordered by * ``fd.'' We do not believe these things, so we don't do it. * * The interesting sequence is where GetNext() has cached a select() * result and the caller decides to evSelectFD() on some descriptor. * Since GetNext() starts at the head, it can miss new entries we add * at the head. This is not a serious problem since the event being * evSelectFD()'d for has to occur before evSelectFD() is called for * the file event to be considered "missed" -- a real corner case. * Maintaining a "tail" pointer for ctx->files would fix this, but I'm * not sure it would be ``more correct.'' */ if (ctx->files != NULL) ctx->files->prev = id; id->prev = NULL; id->next = ctx->files; ctx->files = id; /* Insert into fd table. */ if (ctx->fdTable[fd] != NULL) ctx->fdTable[fd]->fdprev = id; id->fdprev = NULL; id->fdnext = ctx->fdTable[fd]; ctx->fdTable[fd] = id; /* Turn on the appropriate bits in the {rd,wr,ex}Next fd_set's. */ if (eventmask & EV_READ) FD_SET(fd, &ctx->rdNext); if (eventmask & EV_WRITE) FD_SET(fd, &ctx->wrNext); if (eventmask & EV_EXCEPT) FD_SET(fd, &ctx->exNext); /* Update fdMax. */ if (fd > ctx->fdMax) ctx->fdMax = fd; /* Remember the ID if the caller provided us a place for it. */ if (opaqueID) opaqueID->opaque = id; evPrintf(ctx, 5, "evSelectFD(fd %d, mask 0x%x): new masks: 0x%lx 0x%lx 0x%lx\n", fd, eventmask, (u_long)ctx->rdNext.fds_bits[0], (u_long)ctx->wrNext.fds_bits[0], (u_long)ctx->exNext.fds_bits[0]); return (0); } int evDeselectFD(evContext opaqueCtx, evFileID opaqueID) { evContext_p *ctx = opaqueCtx.opaque; evFile *del = opaqueID.opaque; evFile *cur; int mode, eventmask; if (!del) { evPrintf(ctx, 11, "evDeselectFD(NULL) ignored\n"); errno = EINVAL; return (-1); } evPrintf(ctx, 1, "evDeselectFD(fd %d, mask 0x%x)\n", del->fd, del->eventmask); /* Get the mode. Unless the file has been closed, errors are bad. */ mode = fcntl(del->fd, F_GETFL, NULL); if (mode == -1 && errno != EBADF) EV_ERR(errno); /* Remove from the list of files. */ if (del->prev != NULL) del->prev->next = del->next; else ctx->files = del->next; if (del->next != NULL) del->next->prev = del->prev; /* Remove from the fd table. */ if (del->fdprev != NULL) del->fdprev->fdnext = del->fdnext; else ctx->fdTable[del->fd] = del->fdnext; if (del->fdnext != NULL) del->fdnext->fdprev = del->fdprev; /* * If the file descriptor does not appear in any other select() entry, * and if !EV_WASNONBLOCK, and if we got no EBADF when we got the mode * earlier, then: restore the fd to blocking status. */ if (!(cur = FindFD(ctx, del->fd, EV_MASK_ALL)) && !FD_ISSET(del->fd, &ctx->nonblockBefore) && mode != -1) { /* * Note that we won't return an error status to the caller if * this fcntl() fails since (a) we've already done the work * and (b) the caller didn't ask us anything about O_NONBLOCK. */ #ifdef USE_FIONBIO_IOCTL int off = 1; (void) ioctl(del->fd, FIONBIO, (char *)&off); #else (void) fcntl(del->fd, F_SETFL, mode & ~PORT_NONBLOCK); #endif } /* * Now find all other uses of this descriptor and OR together an event * mask so that we don't turn off {rd,wr,ex}Next bits that some other * file event is using. As an optimization, stop if the event mask * fills. */ eventmask = 0; for ((void)NULL; cur != NULL && eventmask != EV_MASK_ALL; cur = cur->next) if (cur->fd == del->fd) eventmask |= cur->eventmask; /* OK, now we know which bits we can clear out. */ if (!(eventmask & EV_READ)) { FD_CLR(del->fd, &ctx->rdNext); if (FD_ISSET(del->fd, &ctx->rdLast)) { FD_CLR(del->fd, &ctx->rdLast); ctx->fdCount--; } } if (!(eventmask & EV_WRITE)) { FD_CLR(del->fd, &ctx->wrNext); if (FD_ISSET(del->fd, &ctx->wrLast)) { FD_CLR(del->fd, &ctx->wrLast); ctx->fdCount--; } } if (!(eventmask & EV_EXCEPT)) { FD_CLR(del->fd, &ctx->exNext); if (FD_ISSET(del->fd, &ctx->exLast)) { FD_CLR(del->fd, &ctx->exLast); ctx->fdCount--; } } /* If this was the maxFD, find the new one. */ if (del->fd == ctx->fdMax) { ctx->fdMax = -1; for (cur = ctx->files; cur; cur = cur->next) if (cur->fd > ctx->fdMax) ctx->fdMax = cur->fd; } /* If this was the fdNext, cycle that to the next entry. */ if (del == ctx->fdNext) ctx->fdNext = del->next; evPrintf(ctx, 5, "evDeselectFD(fd %d, mask 0x%x): new masks: 0x%lx 0x%lx 0x%lx\n", del->fd, eventmask, (u_long)ctx->rdNext.fds_bits[0], (u_long)ctx->wrNext.fds_bits[0], (u_long)ctx->exNext.fds_bits[0]); /* Couldn't free it before now since we were using fields out of it. */ FREE(del); return (0); } static evFile * FindFD(const evContext_p *ctx, int fd, int eventmask) { evFile *id; for (id = ctx->fdTable[fd]; id != NULL; id = id->fdnext) if (id->fd == fd && (id->eventmask & eventmask) != 0) break; return (id); } diff --git a/contrib/bind/lib/isc/ev_timers.c b/contrib/bind/lib/isc/ev_timers.c index 03436c601cae..0f3c56f1982c 100644 --- a/contrib/bind/lib/isc/ev_timers.c +++ b/contrib/bind/lib/isc/ev_timers.c @@ -1,415 +1,415 @@ /* * Copyright (c) 1995-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* ev_timers.c - implement timers for the eventlib * vix 09sep95 [initial] */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: ev_timers.c,v 1.32 2001/11/01 05:35:47 marka Exp $"; +static const char rcsid[] = "$Id: ev_timers.c,v 1.33 2002/07/08 05:50:09 marka Exp $"; #endif /* Import. */ #include "port_before.h" #include "fd_setsize.h" #include #include #include #include "eventlib_p.h" #include "port_after.h" /* Constants. */ #define MILLION 1000000 #define BILLION 1000000000 /* Forward. */ static int due_sooner(void *, void *); static void set_index(void *, int); static void free_timer(void *, void *); static void print_timer(void *, void *); static void idle_timeout(evContext, void *, struct timespec, struct timespec); /* Private type. */ typedef struct { evTimerFunc func; void * uap; struct timespec lastTouched; struct timespec max_idle; evTimer * timer; } idle_timer; /* Public. */ struct timespec evConsTime(time_t sec, long nsec) { struct timespec x; x.tv_sec = sec; x.tv_nsec = nsec; return (x); } struct timespec evAddTime(struct timespec addend1, struct timespec addend2) { struct timespec x; x.tv_sec = addend1.tv_sec + addend2.tv_sec; x.tv_nsec = addend1.tv_nsec + addend2.tv_nsec; if (x.tv_nsec >= BILLION) { x.tv_sec++; x.tv_nsec -= BILLION; } return (x); } struct timespec evSubTime(struct timespec minuend, struct timespec subtrahend) { struct timespec x; x.tv_sec = minuend.tv_sec - subtrahend.tv_sec; if (minuend.tv_nsec >= subtrahend.tv_nsec) x.tv_nsec = minuend.tv_nsec - subtrahend.tv_nsec; else { x.tv_nsec = BILLION - subtrahend.tv_nsec + minuend.tv_nsec; x.tv_sec--; } return (x); } int evCmpTime(struct timespec a, struct timespec b) { long x = a.tv_sec - b.tv_sec; if (x == 0L) x = a.tv_nsec - b.tv_nsec; return (x < 0L ? (-1) : x > 0L ? (1) : (0)); } struct timespec evNowTime() { struct timeval now; if (gettimeofday(&now, NULL) < 0) return (evConsTime(0, 0)); return (evTimeSpec(now)); } struct timespec evLastEventTime(evContext opaqueCtx) { evContext_p *ctx = opaqueCtx.opaque; return (ctx->lastEventTime); } struct timespec evTimeSpec(struct timeval tv) { struct timespec ts; ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec * 1000; return (ts); } struct timeval evTimeVal(struct timespec ts) { struct timeval tv; tv.tv_sec = ts.tv_sec; tv.tv_usec = ts.tv_nsec / 1000; return (tv); } int evSetTimer(evContext opaqueCtx, evTimerFunc func, void *uap, struct timespec due, struct timespec inter, evTimerID *opaqueID ) { evContext_p *ctx = opaqueCtx.opaque; evTimer *id; evPrintf(ctx, 1, -"evSetTimer(ctx %#x, func %#x, uap %#x, due %d.%09ld, inter %d.%09ld)\n", +"evSetTimer(ctx %p, func %p, uap %p, due %ld.%09ld, inter %ld.%09ld)\n", ctx, func, uap, - due.tv_sec, due.tv_nsec, - inter.tv_sec, inter.tv_nsec); + (long)due.tv_sec, due.tv_nsec, + (long)inter.tv_sec, inter.tv_nsec); /* due={0,0} is a magic cookie meaning "now." */ if (due.tv_sec == 0 && due.tv_nsec == 0L) due = evNowTime(); /* Allocate and fill. */ OKNEW(id); id->func = func; id->uap = uap; id->due = due; id->inter = inter; if (heap_insert(ctx->timers, id) < 0) return (-1); /* Remember the ID if the caller provided us a place for it. */ if (opaqueID) opaqueID->opaque = id; if (ctx->debug > 7) { evPrintf(ctx, 7, "timers after evSetTimer:\n"); (void) heap_for_each(ctx->timers, print_timer, (void *)ctx); } return (0); } int evClearTimer(evContext opaqueCtx, evTimerID id) { evContext_p *ctx = opaqueCtx.opaque; evTimer *del = id.opaque; if (ctx->cur != NULL && ctx->cur->type == Timer && ctx->cur->u.timer.this == del) { evPrintf(ctx, 8, "deferring delete of timer (executing)\n"); /* * Setting the interval to zero ensures that evDrop() will * clean up the timer. */ del->inter = evConsTime(0, 0); return (0); } if (heap_element(ctx->timers, del->index) != del) EV_ERR(ENOENT); if (heap_delete(ctx->timers, del->index) < 0) return (-1); FREE(del); if (ctx->debug > 7) { evPrintf(ctx, 7, "timers after evClearTimer:\n"); (void) heap_for_each(ctx->timers, print_timer, (void *)ctx); } return (0); } int evResetTimer(evContext opaqueCtx, evTimerID id, evTimerFunc func, void *uap, struct timespec due, struct timespec inter ) { evContext_p *ctx = opaqueCtx.opaque; evTimer *timer = id.opaque; struct timespec old_due; int result=0; if (heap_element(ctx->timers, timer->index) != timer) EV_ERR(ENOENT); old_due = timer->due; timer->func = func; timer->uap = uap; timer->due = due; timer->inter = inter; switch (evCmpTime(due, old_due)) { case -1: result = heap_increased(ctx->timers, timer->index); break; case 0: result = 0; break; case 1: result = heap_decreased(ctx->timers, timer->index); break; } if (ctx->debug > 7) { evPrintf(ctx, 7, "timers after evResetTimer:\n"); (void) heap_for_each(ctx->timers, print_timer, (void *)ctx); } return (result); } int evSetIdleTimer(evContext opaqueCtx, evTimerFunc func, void *uap, struct timespec max_idle, evTimerID *opaqueID ) { evContext_p *ctx = opaqueCtx.opaque; idle_timer *tt; /* Allocate and fill. */ OKNEW(tt); tt->func = func; tt->uap = uap; tt->lastTouched = ctx->lastEventTime; tt->max_idle = max_idle; if (evSetTimer(opaqueCtx, idle_timeout, tt, evAddTime(ctx->lastEventTime, max_idle), max_idle, opaqueID) < 0) { FREE(tt); return (-1); } tt->timer = opaqueID->opaque; return (0); } int evClearIdleTimer(evContext opaqueCtx, evTimerID id) { evTimer *del = id.opaque; idle_timer *tt = del->uap; FREE(tt); return (evClearTimer(opaqueCtx, id)); } int evResetIdleTimer(evContext opaqueCtx, evTimerID opaqueID, evTimerFunc func, void *uap, struct timespec max_idle ) { evContext_p *ctx = opaqueCtx.opaque; evTimer *timer = opaqueID.opaque; idle_timer *tt = timer->uap; tt->func = func; tt->uap = uap; tt->lastTouched = ctx->lastEventTime; tt->max_idle = max_idle; return (evResetTimer(opaqueCtx, opaqueID, idle_timeout, tt, evAddTime(ctx->lastEventTime, max_idle), max_idle)); } int evTouchIdleTimer(evContext opaqueCtx, evTimerID id) { evContext_p *ctx = opaqueCtx.opaque; evTimer *t = id.opaque; idle_timer *tt = t->uap; tt->lastTouched = ctx->lastEventTime; return (0); } /* Public to the rest of eventlib. */ heap_context evCreateTimers(const evContext_p *ctx) { UNUSED(ctx); return (heap_new(due_sooner, set_index, 2048)); } void evDestroyTimers(const evContext_p *ctx) { (void) heap_for_each(ctx->timers, free_timer, NULL); (void) heap_free(ctx->timers); } /* Private. */ static int due_sooner(void *a, void *b) { evTimer *a_timer, *b_timer; a_timer = a; b_timer = b; return (evCmpTime(a_timer->due, b_timer->due) < 0); } static void set_index(void *what, int index) { evTimer *timer; timer = what; timer->index = index; } static void free_timer(void *what, void *uap) { evTimer *t = what; UNUSED(uap); FREE(t); } static void print_timer(void *what, void *uap) { evTimer *cur = what; evContext_p *ctx = uap; cur = what; evPrintf(ctx, 7, - " func %p, uap %p, due %d.%09ld, inter %d.%09ld\n", + " func %p, uap %p, due %ld.%09ld, inter %ld.%09ld\n", cur->func, cur->uap, - cur->due.tv_sec, cur->due.tv_nsec, - cur->inter.tv_sec, cur->inter.tv_nsec); + (long)cur->due.tv_sec, cur->due.tv_nsec, + (long)cur->inter.tv_sec, cur->inter.tv_nsec); } static void idle_timeout(evContext opaqueCtx, void *uap, struct timespec due, struct timespec inter ) { evContext_p *ctx = opaqueCtx.opaque; idle_timer *this = uap; struct timespec idle; UNUSED(due); UNUSED(inter); idle = evSubTime(ctx->lastEventTime, this->lastTouched); if (evCmpTime(idle, this->max_idle) >= 0) { (this->func)(opaqueCtx, this->uap, this->timer->due, this->max_idle); /* * Setting the interval to zero will cause the timer to * be cleaned up in evDrop(). */ this->timer->inter = evConsTime(0, 0); FREE(this); } else { /* evDrop() will reschedule the timer. */ this->timer->inter = evSubTime(this->max_idle, idle); } } diff --git a/contrib/bind/lib/isc/ev_waits.c b/contrib/bind/lib/isc/ev_waits.c index 9608424014f5..8d9fa8d4e6c3 100644 --- a/contrib/bind/lib/isc/ev_waits.c +++ b/contrib/bind/lib/isc/ev_waits.c @@ -1,245 +1,245 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* ev_waits.c - implement deferred function calls for the eventlib * vix 05dec95 [initial] */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: ev_waits.c,v 8.11 2000/07/20 18:17:52 vixie Exp $"; +static const char rcsid[] = "$Id: ev_waits.c,v 8.12 2002/07/08 05:50:10 marka Exp $"; #endif #include "port_before.h" #include "fd_setsize.h" #include #include #include #include "eventlib_p.h" #include "port_after.h" /* Forward. */ static void print_waits(evContext_p *ctx); static evWaitList * evNewWaitList(evContext_p *); static void evFreeWaitList(evContext_p *, evWaitList *); static evWaitList * evGetWaitList(evContext_p *, const void *, int); /* Public. */ /* * Enter a new wait function on the queue. */ int evWaitFor(evContext opaqueCtx, const void *tag, evWaitFunc func, void *uap, evWaitID *id) { evContext_p *ctx = opaqueCtx.opaque; evWait *new; evWaitList *wl = evGetWaitList(ctx, tag, 1); OKNEW(new); new->func = func; new->uap = uap; new->tag = tag; new->next = NULL; if (wl->last != NULL) wl->last->next = new; else wl->first = new; wl->last = new; if (id != NULL) id->opaque = new; if (ctx->debug >= 9) print_waits(ctx); return (0); } /* * Mark runnable all waiting functions having a certain tag. */ int evDo(evContext opaqueCtx, const void *tag) { evContext_p *ctx = opaqueCtx.opaque; evWaitList *wl = evGetWaitList(ctx, tag, 0); evWait *first; if (!wl) { errno = ENOENT; return (-1); } first = wl->first; INSIST(first != NULL); if (ctx->waitDone.last != NULL) ctx->waitDone.last->next = first; else ctx->waitDone.first = first; ctx->waitDone.last = wl->last; evFreeWaitList(ctx, wl); return (0); } /* * Remove a waiting (or ready to run) function from the queue. */ int evUnwait(evContext opaqueCtx, evWaitID id) { evContext_p *ctx = opaqueCtx.opaque; evWait *this, *prev; evWaitList *wl; int found = 0; this = id.opaque; INSIST(this != NULL); wl = evGetWaitList(ctx, this->tag, 0); if (wl != NULL) { for (prev = NULL, this = wl->first; this != NULL; prev = this, this = this->next) if (this == (evWait *)id.opaque) { found = 1; if (prev != NULL) prev->next = this->next; else wl->first = this->next; if (wl->last == this) wl->last = prev; if (wl->first == NULL) evFreeWaitList(ctx, wl); break; } } if (!found) { /* Maybe it's done */ for (prev = NULL, this = ctx->waitDone.first; this != NULL; prev = this, this = this->next) if (this == (evWait *)id.opaque) { found = 1; if (prev != NULL) prev->next = this->next; else ctx->waitDone.first = this->next; if (ctx->waitDone.last == this) ctx->waitDone.last = prev; break; } } if (!found) { errno = ENOENT; return (-1); } FREE(this); if (ctx->debug >= 9) print_waits(ctx); return (0); } int evDefer(evContext opaqueCtx, evWaitFunc func, void *uap) { evContext_p *ctx = opaqueCtx.opaque; evWait *new; OKNEW(new); new->func = func; new->uap = uap; new->tag = NULL; new->next = NULL; if (ctx->waitDone.last != NULL) ctx->waitDone.last->next = new; else ctx->waitDone.first = new; ctx->waitDone.last = new; if (ctx->debug >= 9) print_waits(ctx); return (0); } /* Private. */ static void print_waits(evContext_p *ctx) { evWaitList *wl; evWait *this; evPrintf(ctx, 9, "wait waiting:\n"); for (wl = ctx->waitLists; wl != NULL; wl = wl->next) { INSIST(wl->first != NULL); - evPrintf(ctx, 9, " tag %#x:", wl->first->tag); + evPrintf(ctx, 9, " tag %p:", wl->first->tag); for (this = wl->first; this != NULL; this = this->next) - evPrintf(ctx, 9, " %#x", this); + evPrintf(ctx, 9, " %p", this); evPrintf(ctx, 9, "\n"); } evPrintf(ctx, 9, "wait done:"); for (this = ctx->waitDone.first; this != NULL; this = this->next) - evPrintf(ctx, 9, " %#x", this); + evPrintf(ctx, 9, " %p", this); evPrintf(ctx, 9, "\n"); } static evWaitList * evNewWaitList(evContext_p *ctx) { evWaitList *new; NEW(new); if (new == NULL) return (NULL); new->first = new->last = NULL; new->prev = NULL; new->next = ctx->waitLists; if (new->next != NULL) new->next->prev = new; ctx->waitLists = new; return (new); } static void evFreeWaitList(evContext_p *ctx, evWaitList *this) { INSIST(this != NULL); if (this->prev != NULL) this->prev->next = this->next; else ctx->waitLists = this->next; if (this->next != NULL) this->next->prev = this->prev; FREE(this); } static evWaitList * evGetWaitList(evContext_p *ctx, const void *tag, int should_create) { evWaitList *this; for (this = ctx->waitLists; this != NULL; this = this->next) { if (this->first != NULL && this->first->tag == tag) break; } if (this == NULL && should_create) this = evNewWaitList(ctx); return (this); } diff --git a/contrib/bind/lib/isc/eventlib.c b/contrib/bind/lib/isc/eventlib.c index 0e527336ee07..c7b1272134af 100644 --- a/contrib/bind/lib/isc/eventlib.c +++ b/contrib/bind/lib/isc/eventlib.c @@ -1,676 +1,676 @@ /* * Copyright (c) 1995-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* eventlib.c - implement glue for the eventlib * vix 09sep95 [initial] */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: eventlib.c,v 1.46 2001/11/01 05:35:48 marka Exp $"; +static const char rcsid[] = "$Id: eventlib.c,v 1.48 2002/07/17 07:37:34 marka Exp $"; #endif #include "port_before.h" #include "fd_setsize.h" #include #include #include #include #include #include #include #include #include #include #include "eventlib_p.h" #include "port_after.h" /* Forward. */ #ifdef NEED_PSELECT static int pselect(int, void *, void *, void *, struct timespec *, const sigset_t *); #endif /* Public. */ int evCreate(evContext *opaqueCtx) { evContext_p *ctx; /* Make sure the memory heap is initialized. */ if (meminit(0, 0) < 0 && errno != EEXIST) return (-1); OKNEW(ctx); /* Global. */ ctx->cur = NULL; /* Debugging. */ ctx->debug = 0; ctx->output = NULL; /* Connections. */ ctx->conns = NULL; INIT_LIST(ctx->accepts); /* Files. */ ctx->files = NULL; FD_ZERO(&ctx->rdNext); FD_ZERO(&ctx->wrNext); FD_ZERO(&ctx->exNext); FD_ZERO(&ctx->nonblockBefore); ctx->fdMax = -1; ctx->fdNext = NULL; ctx->fdCount = 0; /* Invalidate {rd,wr,ex}Last. */ ctx->highestFD = FD_SETSIZE - 1; #ifdef EVENTLIB_TIME_CHECKS ctx->lastFdCount = 0; #endif memset(ctx->fdTable, 0, sizeof ctx->fdTable); /* Streams. */ ctx->streams = NULL; ctx->strDone = NULL; ctx->strLast = NULL; /* Timers. */ ctx->lastEventTime = evNowTime(); #ifdef EVENTLIB_TIME_CHECKS ctx->lastSelectTime = ctx->lastEventTime; #endif ctx->timers = evCreateTimers(ctx); if (ctx->timers == NULL) return (-1); /* Waits. */ ctx->waitLists = NULL; ctx->waitDone.first = ctx->waitDone.last = NULL; ctx->waitDone.prev = ctx->waitDone.next = NULL; opaqueCtx->opaque = ctx; return (0); } void evSetDebug(evContext opaqueCtx, int level, FILE *output) { evContext_p *ctx = opaqueCtx.opaque; ctx->debug = level; ctx->output = output; } int evDestroy(evContext opaqueCtx) { evContext_p *ctx = opaqueCtx.opaque; int revs = 424242; /* Doug Adams. */ evWaitList *this_wl, *next_wl; evWait *this_wait, *next_wait; /* Connections. */ while (revs-- > 0 && ctx->conns != NULL) { evConnID id; id.opaque = ctx->conns; (void) evCancelConn(opaqueCtx, id); } INSIST(revs >= 0); /* Streams. */ while (revs-- > 0 && ctx->streams != NULL) { evStreamID id; id.opaque = ctx->streams; (void) evCancelRW(opaqueCtx, id); } /* Files. */ while (revs-- > 0 && ctx->files != NULL) { evFileID id; id.opaque = ctx->files; (void) evDeselectFD(opaqueCtx, id); } INSIST(revs >= 0); /* Timers. */ evDestroyTimers(ctx); /* Waits. */ for (this_wl = ctx->waitLists; revs-- > 0 && this_wl != NULL; this_wl = next_wl) { next_wl = this_wl->next; for (this_wait = this_wl->first; revs-- > 0 && this_wait != NULL; this_wait = next_wait) { next_wait = this_wait->next; FREE(this_wait); } FREE(this_wl); } for (this_wait = ctx->waitDone.first; revs-- > 0 && this_wait != NULL; this_wait = next_wait) { next_wait = this_wait->next; FREE(this_wait); } FREE(ctx); return (0); } int evGetNext(evContext opaqueCtx, evEvent *opaqueEv, int options) { evContext_p *ctx = opaqueCtx.opaque; struct timespec nextTime; evTimer *nextTimer; evEvent_p *new; int x, pselect_errno, timerPast; #ifdef EVENTLIB_TIME_CHECKS struct timespec interval; #endif /* Ensure that exactly one of EV_POLL or EV_WAIT was specified. */ x = ((options & EV_POLL) != 0) + ((options & EV_WAIT) != 0); if (x != 1) EV_ERR(EINVAL); /* Get the time of day. We'll do this again after select() blocks. */ ctx->lastEventTime = evNowTime(); again: /* Finished accept()'s do not require a select(). */ if (!EMPTY(ctx->accepts)) { OKNEW(new); new->type = Accept; new->u.accept.this = HEAD(ctx->accepts); UNLINK(ctx->accepts, HEAD(ctx->accepts), link); opaqueEv->opaque = new; return (0); } /* Stream IO does not require a select(). */ if (ctx->strDone != NULL) { OKNEW(new); new->type = Stream; new->u.stream.this = ctx->strDone; ctx->strDone = ctx->strDone->nextDone; if (ctx->strDone == NULL) ctx->strLast = NULL; opaqueEv->opaque = new; return (0); } /* Waits do not require a select(). */ if (ctx->waitDone.first != NULL) { OKNEW(new); new->type = Wait; new->u.wait.this = ctx->waitDone.first; ctx->waitDone.first = ctx->waitDone.first->next; if (ctx->waitDone.first == NULL) ctx->waitDone.last = NULL; opaqueEv->opaque = new; return (0); } /* Get the status and content of the next timer. */ if ((nextTimer = heap_element(ctx->timers, 1)) != NULL) { nextTime = nextTimer->due; timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0); } else timerPast = 0; /* Make gcc happy. */ evPrintf(ctx, 9, "evGetNext: fdCount %d\n", ctx->fdCount); if (ctx->fdCount == 0) { static const struct timespec NoTime = {0, 0L}; enum { JustPoll, Block, Timer } m; struct timespec t, *tp; /* Are there any events at all? */ if ((options & EV_WAIT) != 0 && !nextTimer && ctx->fdMax == -1) EV_ERR(ENOENT); /* Figure out what select()'s timeout parameter should be. */ if ((options & EV_POLL) != 0) { m = JustPoll; t = NoTime; tp = &t; } else if (nextTimer == NULL) { m = Block; /* ``t'' unused. */ tp = NULL; } else if (timerPast) { m = JustPoll; t = NoTime; tp = &t; } else { m = Timer; /* ``t'' filled in later. */ tp = &t; } #ifdef EVENTLIB_TIME_CHECKS if (ctx->debug > 0) { interval = evSubTime(ctx->lastEventTime, ctx->lastSelectTime); if (interval.tv_sec > 0) evPrintf(ctx, 1, "time between pselect() %u.%09u count %d\n", interval.tv_sec, interval.tv_nsec, ctx->lastFdCount); } #endif do { /* XXX need to copy only the bits we are using. */ ctx->rdLast = ctx->rdNext; ctx->wrLast = ctx->wrNext; ctx->exLast = ctx->exNext; if (m == Timer) { INSIST(tp == &t); t = evSubTime(nextTime, ctx->lastEventTime); } evPrintf(ctx, 4, - "pselect(%d, 0x%lx, 0x%lx, 0x%lx, %d.%09ld)\n", + "pselect(%d, 0x%lx, 0x%lx, 0x%lx, %ld.%09ld)\n", ctx->fdMax+1, (u_long)ctx->rdLast.fds_bits[0], (u_long)ctx->wrLast.fds_bits[0], (u_long)ctx->exLast.fds_bits[0], - tp ? tp->tv_sec : -1, + tp ? (long)tp->tv_sec : -1L, tp ? tp->tv_nsec : -1); /* XXX should predict system's earliness and adjust. */ x = pselect(ctx->fdMax+1, &ctx->rdLast, &ctx->wrLast, &ctx->exLast, tp, NULL); pselect_errno = errno; evPrintf(ctx, 4, "select() returns %d (err: %s)\n", x, (x == -1) ? strerror(errno) : "none"); /* Anything but a poll can change the time. */ if (m != JustPoll) ctx->lastEventTime = evNowTime(); /* Select() likes to finish about 10ms early. */ } while (x == 0 && m == Timer && evCmpTime(ctx->lastEventTime, nextTime) < 0); #ifdef EVENTLIB_TIME_CHECKS ctx->lastSelectTime = ctx->lastEventTime; #endif if (x < 0) { if (pselect_errno == EINTR) { if ((options & EV_NULL) != 0) goto again; OKNEW(new); new->type = Null; /* No data. */ opaqueEv->opaque = new; return (0); } if (pselect_errno == EBADF) { for (x = 0; x <= ctx->fdMax; x++) { struct stat sb; if (FD_ISSET(x, &ctx->rdNext) == 0 && FD_ISSET(x, &ctx->wrNext) == 0 && FD_ISSET(x, &ctx->exNext) == 0) continue; if (fstat(x, &sb) == -1 && errno == EBADF) evPrintf(ctx, 1, "EBADF: %d\n", x); } abort(); } EV_ERR(pselect_errno); } if (x == 0 && (nextTimer == NULL || !timerPast) && (options & EV_POLL)) EV_ERR(EWOULDBLOCK); ctx->fdCount = x; #ifdef EVENTLIB_TIME_CHECKS ctx->lastFdCount = x; #endif } INSIST(nextTimer || ctx->fdCount); /* Timers go first since we'd like them to be accurate. */ if (nextTimer && !timerPast) { /* Has anything happened since we blocked? */ timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0); } if (nextTimer && timerPast) { OKNEW(new); new->type = Timer; new->u.timer.this = nextTimer; opaqueEv->opaque = new; return (0); } /* No timers, so there should be a ready file descriptor. */ x = 0; while (ctx->fdCount > 0) { evFile *fid; int fd, eventmask; if (ctx->fdNext == NULL) { if (++x == 2) { /* * Hitting the end twice means that the last * select() found some FD's which have since * been deselected. * * On some systems, the count returned by * selects is the total number of bits in * all masks that are set, and on others it's * the number of fd's that have some bit set, * and on others, it's just broken. We * always assume that it's the number of * bits set in all masks, because that's what * the man page says it should do, and * the worst that can happen is we do an * extra select(). */ ctx->fdCount = 0; break; } ctx->fdNext = ctx->files; } fid = ctx->fdNext; ctx->fdNext = fid->next; fd = fid->fd; eventmask = 0; if (FD_ISSET(fd, &ctx->rdLast)) eventmask |= EV_READ; if (FD_ISSET(fd, &ctx->wrLast)) eventmask |= EV_WRITE; if (FD_ISSET(fd, &ctx->exLast)) eventmask |= EV_EXCEPT; eventmask &= fid->eventmask; if (eventmask != 0) { if ((eventmask & EV_READ) != 0) { FD_CLR(fd, &ctx->rdLast); ctx->fdCount--; } if ((eventmask & EV_WRITE) != 0) { FD_CLR(fd, &ctx->wrLast); ctx->fdCount--; } if ((eventmask & EV_EXCEPT) != 0) { FD_CLR(fd, &ctx->exLast); ctx->fdCount--; } OKNEW(new); new->type = File; new->u.file.this = fid; new->u.file.eventmask = eventmask; opaqueEv->opaque = new; return (0); } } if (ctx->fdCount < 0) { /* * select()'s count is off on a number of systems, and * can result in fdCount < 0. */ evPrintf(ctx, 4, "fdCount < 0 (%d)\n", ctx->fdCount); ctx->fdCount = 0; } /* We get here if the caller deselect()'s an FD. Gag me with a goto. */ goto again; } int evDispatch(evContext opaqueCtx, evEvent opaqueEv) { evContext_p *ctx = opaqueCtx.opaque; evEvent_p *ev = opaqueEv.opaque; #ifdef EVENTLIB_TIME_CHECKS void *func; struct timespec start_time; struct timespec interval; #endif #ifdef EVENTLIB_TIME_CHECKS if (ctx->debug > 0) start_time = evNowTime(); #endif ctx->cur = ev; switch (ev->type) { case Accept: { evAccept *this = ev->u.accept.this; evPrintf(ctx, 5, - "Dispatch.Accept: fd %d -> %d, func %#x, uap %#x\n", + "Dispatch.Accept: fd %d -> %d, func %p, uap %p\n", this->conn->fd, this->fd, this->conn->func, this->conn->uap); errno = this->ioErrno; (this->conn->func)(opaqueCtx, this->conn->uap, this->fd, &this->la, this->lalen, &this->ra, this->ralen); #ifdef EVENTLIB_TIME_CHECKS func = this->conn->func; #endif break; } case File: { evFile *this = ev->u.file.this; int eventmask = ev->u.file.eventmask; evPrintf(ctx, 5, - "Dispatch.File: fd %d, mask 0x%x, func %#x, uap %#x\n", + "Dispatch.File: fd %d, mask 0x%x, func %p, uap %p\n", this->fd, this->eventmask, this->func, this->uap); (this->func)(opaqueCtx, this->uap, this->fd, eventmask); #ifdef EVENTLIB_TIME_CHECKS func = this->func; #endif break; } case Stream: { evStream *this = ev->u.stream.this; evPrintf(ctx, 5, - "Dispatch.Stream: fd %d, func %#x, uap %#x\n", + "Dispatch.Stream: fd %d, func %p, uap %p\n", this->fd, this->func, this->uap); errno = this->ioErrno; (this->func)(opaqueCtx, this->uap, this->fd, this->ioDone); #ifdef EVENTLIB_TIME_CHECKS func = this->func; #endif break; } case Timer: { evTimer *this = ev->u.timer.this; - evPrintf(ctx, 5, "Dispatch.Timer: func %#x, uap %#x\n", + evPrintf(ctx, 5, "Dispatch.Timer: func %p, uap %p\n", this->func, this->uap); (this->func)(opaqueCtx, this->uap, this->due, this->inter); #ifdef EVENTLIB_TIME_CHECKS func = this->func; #endif break; } case Wait: { evWait *this = ev->u.wait.this; evPrintf(ctx, 5, - "Dispatch.Wait: tag %#x, func %#x, uap %#x\n", + "Dispatch.Wait: tag %p, func %p, uap %p\n", this->tag, this->func, this->uap); (this->func)(opaqueCtx, this->uap, this->tag); #ifdef EVENTLIB_TIME_CHECKS func = this->func; #endif break; } case Null: { /* No work. */ #ifdef EVENTLIB_TIME_CHECKS func = NULL; #endif break; } default: { abort(); } } #ifdef EVENTLIB_TIME_CHECKS if (ctx->debug > 0) { interval = evSubTime(evNowTime(), start_time); /* * Complain if it took longer than 50 milliseconds. * * We call getuid() to make an easy to find mark in a kernel * trace. */ if (interval.tv_sec > 0 || interval.tv_nsec > 50000000) evPrintf(ctx, 1, "dispatch interval %u.%09u uid %d type %d func %p\n", interval.tv_sec, interval.tv_nsec, getuid(), ev->type, func); } #endif ctx->cur = NULL; evDrop(opaqueCtx, opaqueEv); return (0); } void evDrop(evContext opaqueCtx, evEvent opaqueEv) { evContext_p *ctx = opaqueCtx.opaque; evEvent_p *ev = opaqueEv.opaque; switch (ev->type) { case Accept: { FREE(ev->u.accept.this); break; } case File: { /* No work. */ break; } case Stream: { evStreamID id; id.opaque = ev->u.stream.this; (void) evCancelRW(opaqueCtx, id); break; } case Timer: { evTimer *this = ev->u.timer.this; evTimerID opaque; /* Check to see whether the user func cleared the timer. */ if (heap_element(ctx->timers, this->index) != this) { evPrintf(ctx, 5, "Dispatch.Timer: timer rm'd?\n"); break; } /* * Timer is still there. Delete it if it has expired, * otherwise set it according to its next interval. */ if (this->inter.tv_sec == 0 && this->inter.tv_nsec == 0L) { opaque.opaque = this; (void) evClearTimer(opaqueCtx, opaque); } else { opaque.opaque = this; (void) evResetTimer(opaqueCtx, opaque, this->func, this->uap, evAddTime(ctx->lastEventTime, this->inter), this->inter); } break; } case Wait: { FREE(ev->u.wait.this); break; } case Null: { /* No work. */ break; } default: { abort(); } } FREE(ev); } int evMainLoop(evContext opaqueCtx) { evEvent event; int x; while ((x = evGetNext(opaqueCtx, &event, EV_WAIT)) == 0) if ((x = evDispatch(opaqueCtx, event)) < 0) break; return (x); } int evHighestFD(evContext opaqueCtx) { evContext_p *ctx = opaqueCtx.opaque; return (ctx->highestFD); } void evPrintf(const evContext_p *ctx, int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (ctx->output != NULL && ctx->debug >= level) { vfprintf(ctx->output, fmt, ap); fflush(ctx->output); } va_end(ap); } #ifdef NEED_PSELECT /* XXX needs to move to the porting library. */ static int pselect(int nfds, void *rfds, void *wfds, void *efds, struct timespec *tsp, const sigset_t *sigmask) { struct timeval tv, *tvp; sigset_t sigs; int n; if (tsp) { tvp = &tv; tv = evTimeVal(*tsp); } else tvp = NULL; if (sigmask) sigprocmask(SIG_SETMASK, sigmask, &sigs); n = select(nfds, rfds, wfds, efds, tvp); if (sigmask) sigprocmask(SIG_SETMASK, &sigs, NULL); if (tsp) *tsp = evTimeSpec(tv); return (n); } #endif diff --git a/contrib/bind/lib/isc/eventlib_p.h b/contrib/bind/lib/isc/eventlib_p.h index 78f010016f7e..71b95eda9ff8 100644 --- a/contrib/bind/lib/isc/eventlib_p.h +++ b/contrib/bind/lib/isc/eventlib_p.h @@ -1,213 +1,214 @@ /* * Copyright (c) 1995-1999 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* eventlib_p.h - private interfaces for eventlib * vix 09sep95 [initial] * - * $Id: eventlib_p.h,v 1.30 2001/11/01 05:35:50 marka Exp $ + * $Id: eventlib_p.h,v 1.31 2003/04/03 05:37:56 marka Exp $ */ #ifndef _EVENTLIB_P_H #define _EVENTLIB_P_H #include #include #include #include #include #define EVENTLIB_DEBUG 1 #include #include #include #include #include #include #include #include #define EV_MASK_ALL (EV_READ | EV_WRITE | EV_EXCEPT) #define EV_ERR(e) return (errno = (e), -1) #define OK(x) if ((x) < 0) EV_ERR(errno); else (void)NULL #define NEW(p) if (((p) = memget(sizeof *(p))) != NULL) \ FILL(p); \ else \ (void)NULL; #define OKNEW(p) if (!((p) = memget(sizeof *(p)))) { \ errno = ENOMEM; \ return (-1); \ } else \ FILL(p) #define FREE(p) memput((p), sizeof *(p)) #if EVENTLIB_DEBUG #define FILL(p) memset((p), 0xF5, sizeof *(p)) #else #define FILL(p) #endif typedef struct evConn { evConnFunc func; void * uap; int fd; int flags; #define EV_CONN_LISTEN 0x0001 /* Connection is a listener. */ #define EV_CONN_SELECTED 0x0002 /* evSelectFD(conn->file). */ #define EV_CONN_BLOCK 0x0004 /* Listener fd was blocking. */ evFileID file; struct evConn * prev; struct evConn * next; } evConn; typedef struct evAccept { int fd; union { struct sockaddr sa; struct sockaddr_in in; #ifndef NO_SOCKADDR_UN struct sockaddr_un un; #endif } la; ISC_SOCKLEN_T lalen; union { struct sockaddr sa; struct sockaddr_in in; #ifndef NO_SOCKADDR_UN struct sockaddr_un un; #endif } ra; ISC_SOCKLEN_T ralen; int ioErrno; evConn * conn; LINK(struct evAccept) link; } evAccept; typedef struct evFile { evFileFunc func; void * uap; int fd; int eventmask; int preemptive; struct evFile * prev; struct evFile * next; struct evFile * fdprev; struct evFile * fdnext; } evFile; typedef struct evStream { evStreamFunc func; void * uap; evFileID file; evTimerID timer; int flags; #define EV_STR_TIMEROK 0x0001 /* IFF timer valid. */ int fd; struct iovec * iovOrig; int iovOrigCount; struct iovec * iovCur; int iovCurCount; int ioTotal; int ioDone; int ioErrno; struct evStream *prevDone, *nextDone; struct evStream *prev, *next; } evStream; typedef struct evTimer { evTimerFunc func; void * uap; struct timespec due, inter; int index; } evTimer; typedef struct evWait { evWaitFunc func; void * uap; const void * tag; struct evWait * next; } evWait; typedef struct evWaitList { evWait * first; evWait * last; struct evWaitList * prev; struct evWaitList * next; } evWaitList; typedef struct evEvent_p { enum { Accept, File, Stream, Timer, Wait, Free, Null } type; union { struct { evAccept *this; } accept; struct { evFile *this; int eventmask; } file; struct { evStream *this; } stream; struct { evTimer *this; } timer; struct { evWait *this; } wait; struct { struct evEvent_p *next; } free; struct { const void *placeholder; } null; } u; } evEvent_p; typedef struct { /* Global. */ const evEvent_p *cur; /* Debugging. */ int debug; FILE *output; /* Connections. */ evConn *conns; LIST(evAccept) accepts; /* Files. */ evFile *files, *fdNext; fd_set rdLast, rdNext; fd_set wrLast, wrNext; fd_set exLast, exNext; fd_set nonblockBefore; int fdMax, fdCount, highestFD; evFile *fdTable[FD_SETSIZE]; #ifdef EVENTLIB_TIME_CHECKS struct timespec lastSelectTime; int lastFdCount; #endif /* Streams. */ evStream *streams; evStream *strDone, *strLast; /* Timers. */ struct timespec lastEventTime; heap_context timers; /* Waits. */ evWaitList *waitLists; evWaitList waitDone; } evContext_p; /* eventlib.c */ #define evPrintf __evPrintf -void evPrintf(const evContext_p *ctx, int level, const char *fmt, ...); +void evPrintf(const evContext_p *ctx, int level, const char *fmt, ...) + ISC_FORMAT_PRINTF(3, 4); /* ev_timers.c */ #define evCreateTimers __evCreateTimers heap_context evCreateTimers(const evContext_p *); #define evDestroyTimers __evDestroyTimers void evDestroyTimers(const evContext_p *); /* ev_waits.c */ #define evFreeWait __evFreeWait evWait *evFreeWait(evContext_p *ctx, evWait *old); #endif /*_EVENTLIB_P_H*/ diff --git a/contrib/bind/lib/isc/logging.c b/contrib/bind/lib/isc/logging.c index ca7ea04c0bcf..f0a4940a1184 100644 --- a/contrib/bind/lib/isc/logging.c +++ b/contrib/bind/lib/isc/logging.c @@ -1,715 +1,720 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if !defined(LINT) && !defined(CODECENTER) -static const char rcsid[] = "$Id: logging.c,v 8.31 2001/06/18 14:44:03 marka Exp $"; +static const char rcsid[] = "$Id: logging.c,v 8.32 2003/01/02 00:35:42 marka Exp $"; #endif /* not lint */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" #ifdef VSPRINTF_CHAR # define VSPRINTF(x) strlen(vsprintf/**/x) #else # define VSPRINTF(x) ((size_t)vsprintf x) #endif #include "logging_p.h" static const int syslog_priority[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, LOG_CRIT }; static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static const char *level_text[] = { "info: ", "notice: ", "warning: ", "error: ", "critical: " }; static void version_rename(log_channel chan) { unsigned int ver; char old_name[PATH_MAX+1]; char new_name[PATH_MAX+1]; ver = chan->out.file.versions; if (ver < 1) return; if (ver > LOG_MAX_VERSIONS) ver = LOG_MAX_VERSIONS; /* * Need to have room for '.nn' (XXX assumes LOG_MAX_VERSIONS < 100) */ if (strlen(chan->out.file.name) > (PATH_MAX-3)) return; for (ver--; ver > 0; ver--) { sprintf(old_name, "%s.%d", chan->out.file.name, ver-1); sprintf(new_name, "%s.%d", chan->out.file.name, ver); (void)isc_movefile(old_name, new_name); } sprintf(new_name, "%s.0", chan->out.file.name); (void)isc_movefile(chan->out.file.name, new_name); } FILE * log_open_stream(log_channel chan) { FILE *stream; int fd, flags; struct stat sb; int regular; if (chan == NULL || chan->type != log_file) { errno = EINVAL; return (NULL); } /* * Don't open already open streams */ if (chan->out.file.stream != NULL) return (chan->out.file.stream); if (stat(chan->out.file.name, &sb) < 0) { if (errno != ENOENT) { syslog(LOG_ERR, "log_open_stream: stat of %s failed: %s", chan->out.file.name, strerror(errno)); chan->flags |= LOG_CHANNEL_BROKEN; return (NULL); } regular = 1; } else regular = (sb.st_mode & S_IFREG); if (chan->out.file.versions) { if (!regular) { syslog(LOG_ERR, "log_open_stream: want versions but %s isn't a regular file", chan->out.file.name); chan->flags |= LOG_CHANNEL_BROKEN; errno = EINVAL; return (NULL); } } flags = O_WRONLY|O_CREAT|O_APPEND; if ((chan->flags & LOG_TRUNCATE) != 0) { if (regular) { (void)unlink(chan->out.file.name); flags |= O_EXCL; } else { syslog(LOG_ERR, "log_open_stream: want truncation but %s isn't a regular file", chan->out.file.name); chan->flags |= LOG_CHANNEL_BROKEN; errno = EINVAL; return (NULL); } } fd = open(chan->out.file.name, flags, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if (fd < 0) { syslog(LOG_ERR, "log_open_stream: open(%s) failed: %s", chan->out.file.name, strerror(errno)); chan->flags |= LOG_CHANNEL_BROKEN; return (NULL); } stream = fdopen(fd, "a"); if (stream == NULL) { syslog(LOG_ERR, "log_open_stream: fdopen() failed"); chan->flags |= LOG_CHANNEL_BROKEN; return (NULL); } (void) fchown(fd, chan->out.file.owner, chan->out.file.group); chan->out.file.stream = stream; return (stream); } int log_close_stream(log_channel chan) { FILE *stream; if (chan == NULL || chan->type != log_file) { errno = EINVAL; return (0); } stream = chan->out.file.stream; chan->out.file.stream = NULL; if (stream != NULL && fclose(stream) == EOF) return (-1); return (0); } void log_close_debug_channels(log_context lc) { log_channel_list lcl; int i; for (i = 0; i < lc->num_categories; i++) for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl->next) if (lcl->channel->type == log_file && lcl->channel->out.file.stream != NULL && lcl->channel->flags & LOG_REQUIRE_DEBUG) (void)log_close_stream(lcl->channel); } FILE * log_get_stream(log_channel chan) { if (chan == NULL || chan->type != log_file) { errno = EINVAL; return (NULL); } return (chan->out.file.stream); } char * log_get_filename(log_channel chan) { if (chan == NULL || chan->type != log_file) { errno = EINVAL; return (NULL); } return (chan->out.file.name); } int log_check_channel(log_context lc, int level, log_channel chan) { int debugging, chan_level; REQUIRE(lc != NULL); debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0); /* * If not debugging, short circuit debugging messages very early. */ if (level > 0 && !debugging) return (0); if ((chan->flags & (LOG_CHANNEL_BROKEN|LOG_CHANNEL_OFF)) != 0) return (0); /* Some channels only log when debugging is on. */ if ((chan->flags & LOG_REQUIRE_DEBUG) && !debugging) return (0); /* Some channels use the global level. */ if ((chan->flags & LOG_USE_CONTEXT_LEVEL) != 0) { chan_level = lc->level; } else chan_level = chan->level; if (level > chan_level) return (0); return (1); } int log_check(log_context lc, int category, int level) { log_channel_list lcl; int debugging; REQUIRE(lc != NULL); debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0); /* * If not debugging, short circuit debugging messages very early. */ if (level > 0 && !debugging) return (0); if (category < 0 || category > lc->num_categories) category = 0; /* use default */ lcl = lc->categories[category]; if (lcl == NULL) { category = 0; lcl = lc->categories[0]; } for ( /* nothing */; lcl != NULL; lcl = lcl->next) { if (log_check_channel(lc, level, lcl->channel)) return (1); } return (0); } void log_vwrite(log_context lc, int category, int level, const char *format, va_list args) { log_channel_list lcl; int pri, debugging, did_vsprintf = 0; int original_category; FILE *stream; log_channel chan; struct timeval tv; struct tm *local_tm; +#ifdef HAVE_TIME_R + struct tm tm_tmp; +#endif + time_t tt; const char *category_name; const char *level_str; char time_buf[256]; char level_buf[256]; REQUIRE(lc != NULL); debugging = (lc->flags & LOG_OPTION_DEBUG); /* * If not debugging, short circuit debugging messages very early. */ if (level > 0 && !debugging) return; if (category < 0 || category > lc->num_categories) category = 0; /* use default */ original_category = category; lcl = lc->categories[category]; if (lcl == NULL) { category = 0; lcl = lc->categories[0]; } /* * Get the current time and format it. */ time_buf[0]='\0'; if (gettimeofday(&tv, NULL) < 0) { syslog(LOG_INFO, "gettimeofday failed in log_vwrite()"); } else { + tt = tv.tv_sec; #ifdef HAVE_TIME_R - localtime_r((time_t *)&tv.tv_sec, &local_tm); + local_tm = localtime_r(&tt, &tm_tmp); #else - local_tm = localtime((time_t *)&tv.tv_sec); + local_tm = localtime(&tt); #endif if (local_tm != NULL) { sprintf(time_buf, "%02d-%s-%4d %02d:%02d:%02d.%03ld ", local_tm->tm_mday, months[local_tm->tm_mon], local_tm->tm_year+1900, local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec, (long)tv.tv_usec/1000); } } /* * Make a string representation of the current category and level */ if (lc->category_names != NULL && lc->category_names[original_category] != NULL) category_name = lc->category_names[original_category]; else category_name = ""; if (level >= log_critical) { if (level >= 0) { sprintf(level_buf, "debug %d: ", level); level_str = level_buf; } else level_str = level_text[-level-1]; } else { sprintf(level_buf, "level %d: ", level); level_str = level_buf; } /* * Write the message to channels. */ for ( /* nothing */; lcl != NULL; lcl = lcl->next) { chan = lcl->channel; if (!log_check_channel(lc, level, chan)) continue; if (!did_vsprintf) { if (VSPRINTF((lc->buffer, format, args)) > LOG_BUFFER_SIZE) { syslog(LOG_CRIT, "memory overrun in log_vwrite()"); exit(1); } did_vsprintf = 1; } switch (chan->type) { case log_syslog: if (level >= log_critical) pri = (level >= 0) ? 0 : -level; else pri = -log_critical; syslog(chan->out.facility|syslog_priority[pri], "%s%s%s%s", (chan->flags & LOG_TIMESTAMP) ? time_buf : "", (chan->flags & LOG_PRINT_CATEGORY) ? category_name : "", (chan->flags & LOG_PRINT_LEVEL) ? level_str : "", lc->buffer); break; case log_file: stream = chan->out.file.stream; if (stream == NULL) { stream = log_open_stream(chan); if (stream == NULL) break; } if (chan->out.file.max_size != ULONG_MAX) { long pos; pos = ftell(stream); if (pos >= 0 && (unsigned long)pos > chan->out.file.max_size) { /* * try to roll over the log files, * ignoring all all return codes * except the open (we don't want * to write any more anyway) */ log_close_stream(chan); version_rename(chan); stream = log_open_stream(chan); if (stream == NULL) break; } } fprintf(stream, "%s%s%s%s\n", (chan->flags & LOG_TIMESTAMP) ? time_buf : "", (chan->flags & LOG_PRINT_CATEGORY) ? category_name : "", (chan->flags & LOG_PRINT_LEVEL) ? level_str : "", lc->buffer); fflush(stream); break; case log_null: break; default: syslog(LOG_ERR, "unknown channel type in log_vwrite()"); } } } void log_write(log_context lc, int category, int level, const char *format, ...) { va_list args; va_start(args, format); log_vwrite(lc, category, level, format, args); va_end(args); } /* * Functions to create, set, or destroy contexts */ int log_new_context(int num_categories, char **category_names, log_context *lc) { log_context nlc; nlc = memget(sizeof (struct log_context)); if (nlc == NULL) { errno = ENOMEM; return (-1); } nlc->num_categories = num_categories; nlc->category_names = category_names; nlc->categories = memget(num_categories * sizeof (log_channel_list)); if (nlc->categories == NULL) { memput(nlc, sizeof (struct log_context)); errno = ENOMEM; return (-1); } memset(nlc->categories, '\0', num_categories * sizeof (log_channel_list)); nlc->flags = 0U; nlc->level = 0; *lc = nlc; return (0); } void log_free_context(log_context lc) { log_channel_list lcl, lcl_next; log_channel chan; int i; REQUIRE(lc != NULL); for (i = 0; i < lc->num_categories; i++) for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl_next) { lcl_next = lcl->next; chan = lcl->channel; (void)log_free_channel(chan); memput(lcl, sizeof (struct log_channel_list)); } memput(lc->categories, lc->num_categories * sizeof (log_channel_list)); memput(lc, sizeof (struct log_context)); } int log_add_channel(log_context lc, int category, log_channel chan) { log_channel_list lcl; if (lc == NULL || category < 0 || category >= lc->num_categories) { errno = EINVAL; return (-1); } lcl = memget(sizeof (struct log_channel_list)); if (lcl == NULL) { errno = ENOMEM; return(-1); } lcl->channel = chan; lcl->next = lc->categories[category]; lc->categories[category] = lcl; chan->references++; return (0); } int log_remove_channel(log_context lc, int category, log_channel chan) { log_channel_list lcl, prev_lcl, next_lcl; int found = 0; if (lc == NULL || category < 0 || category >= lc->num_categories) { errno = EINVAL; return (-1); } for (prev_lcl = NULL, lcl = lc->categories[category]; lcl != NULL; lcl = next_lcl) { next_lcl = lcl->next; if (lcl->channel == chan) { log_free_channel(chan); if (prev_lcl != NULL) prev_lcl->next = next_lcl; else lc->categories[category] = next_lcl; memput(lcl, sizeof (struct log_channel_list)); /* * We just set found instead of returning because * the channel might be on the list more than once. */ found = 1; } else prev_lcl = lcl; } if (!found) { errno = ENOENT; return (-1); } return (0); } int log_option(log_context lc, int option, int value) { if (lc == NULL) { errno = EINVAL; return (-1); } switch (option) { case LOG_OPTION_DEBUG: if (value) lc->flags |= option; else lc->flags &= ~option; break; case LOG_OPTION_LEVEL: lc->level = value; break; default: errno = EINVAL; return (-1); } return (0); } int log_category_is_active(log_context lc, int category) { if (lc == NULL) { errno = EINVAL; return (-1); } if (category >= 0 && category < lc->num_categories && lc->categories[category] != NULL) return (1); return (0); } log_channel log_new_syslog_channel(unsigned int flags, int level, int facility) { log_channel chan; chan = memget(sizeof (struct log_channel)); if (chan == NULL) { errno = ENOMEM; return (NULL); } chan->type = log_syslog; chan->flags = flags; chan->level = level; chan->out.facility = facility; chan->references = 0; return (chan); } log_channel log_new_file_channel(unsigned int flags, int level, const char *name, FILE *stream, unsigned int versions, unsigned long max_size) { log_channel chan; chan = memget(sizeof (struct log_channel)); if (chan == NULL) { errno = ENOMEM; return (NULL); } chan->type = log_file; chan->flags = flags; chan->level = level; if (name != NULL) { size_t len; len = strlen(name); /* * Quantize length to a multiple of 256. There's space for the * NUL, since if len is a multiple of 256, the size chosen will * be the next multiple. */ chan->out.file.name_size = ((len / 256) + 1) * 256; chan->out.file.name = memget(chan->out.file.name_size); if (chan->out.file.name == NULL) { memput(chan, sizeof (struct log_channel)); errno = ENOMEM; return (NULL); } /* This is safe. */ strcpy(chan->out.file.name, name); } else { chan->out.file.name_size = 0; chan->out.file.name = NULL; } chan->out.file.stream = stream; chan->out.file.versions = versions; chan->out.file.max_size = max_size; chan->out.file.owner = getuid(); chan->out.file.group = getgid(); chan->references = 0; return (chan); } int log_set_file_owner(log_channel chan, uid_t owner, gid_t group) { if (chan->type != log_file) { errno = EBADF; return (-1); } chan->out.file.owner = owner; chan->out.file.group = group; return (0); } log_channel log_new_null_channel() { log_channel chan; chan = memget(sizeof (struct log_channel)); if (chan == NULL) { errno = ENOMEM; return (NULL); } chan->type = log_null; chan->flags = LOG_CHANNEL_OFF; chan->level = log_info; chan->references = 0; return (chan); } int log_inc_references(log_channel chan) { if (chan == NULL) { errno = EINVAL; return (-1); } chan->references++; return (0); } int log_dec_references(log_channel chan) { if (chan == NULL || chan->references <= 0) { errno = EINVAL; return (-1); } chan->references--; return (0); } log_channel_type log_get_channel_type(log_channel chan) { REQUIRE(chan != NULL); return (chan->type); } int log_free_channel(log_channel chan) { if (chan == NULL || chan->references <= 0) { errno = EINVAL; return (-1); } chan->references--; if (chan->references == 0) { if (chan->type == log_file) { if ((chan->flags & LOG_CLOSE_STREAM) && chan->out.file.stream != NULL) (void)fclose(chan->out.file.stream); if (chan->out.file.name != NULL) memput(chan->out.file.name, chan->out.file.name_size); } memput(chan, sizeof (struct log_channel)); } return (0); } diff --git a/contrib/bind/lib/nameser/ns_name.c b/contrib/bind/lib/nameser/ns_name.c index fff96f494007..c57ac3bd3a61 100644 --- a/contrib/bind/lib/nameser/ns_name.c +++ b/contrib/bind/lib/nameser/ns_name.c @@ -1,947 +1,963 @@ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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 lint -static const char rcsid[] = "$Id: ns_name.c,v 8.18.4.1 2002/11/14 13:32:08 marka Exp $"; +static const char rcsid[] = "$Id: ns_name.c,v 8.20 2003/04/03 06:00:07 marka Exp $"; #endif #include "port_before.h" #include #include #include #include #include #include #include #include #include #include "port_after.h" #ifdef SPRINTF_CHAR # define SPRINTF(x) strlen(sprintf/**/x) #else # define SPRINTF(x) ((size_t)sprintf x) #endif #define NS_TYPE_ELT 0x40 /* EDNS0 extended label type */ #define DNS_LABELTYPE_BITSTRING 0x41 /* Data. */ static const char digits[] = "0123456789"; static const char digitvalue[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/ }; /* Forward. */ static int special(int); static int printable(int); static int dn_find(const u_char *, const u_char *, const u_char * const *, const u_char * const *); static int encode_bitsring(const char **, const char *, char **, char **, const char *); static int labellen(const u_char *); static int decode_bitstring(const char **, char *, const char *); /* Public. */ /* * ns_name_ntop(src, dst, dstsiz) * Convert an encoded domain name to printable ascii as per RFC1035. * return: * Number of bytes written to buffer, or -1 (with errno set) * notes: * The root is returned as "." * All other domains are returned in non absolute form */ int ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) { const u_char *cp; char *dn, *eom; u_char c; u_int n; int l; cp = src; dn = dst; eom = dst + dstsiz; while ((n = *cp++) != 0) { if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { /* Some kind of compression pointer. */ errno = EMSGSIZE; return (-1); } if (dn != dst) { if (dn >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '.'; } if ((l = labellen(cp - 1)) < 0) { errno = EMSGSIZE; /* XXX */ return(-1); } if (dn + l >= eom) { errno = EMSGSIZE; return (-1); } if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) { int m; if (n != DNS_LABELTYPE_BITSTRING) { /* XXX: labellen should reject this case */ errno = EINVAL; return(-1); } if ((m = decode_bitstring((const char **)&cp, dn, eom)) < 0) { errno = EMSGSIZE; return(-1); } dn += m; continue; } for ((void)NULL; l > 0; l--) { c = *cp++; if (special(c)) { if (dn + 1 >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '\\'; *dn++ = (char)c; } else if (!printable(c)) { if (dn + 3 >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '\\'; *dn++ = digits[c / 100]; *dn++ = digits[(c % 100) / 10]; *dn++ = digits[c % 10]; } else { if (dn >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = (char)c; } } } if (dn == dst) { if (dn >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '.'; } if (dn >= eom) { errno = EMSGSIZE; return (-1); } *dn++ = '\0'; return (dn - dst); } /* * ns_name_pton(src, dst, dstsiz) * Convert a ascii string into an encoded domain name as per RFC1035. * return: * -1 if it fails * 1 if string was fully qualified * 0 is string was not fully qualified * notes: * Enforces label and domain length limits. */ int ns_name_pton(const char *src, u_char *dst, size_t dstsiz) { u_char *label, *bp, *eom; int c, n, escaped, e = 0; char *cp; escaped = 0; bp = dst; eom = dst + dstsiz; label = bp++; while ((c = *src++) != 0) { if (escaped) { if (c == '[') { /* start a bit string label */ if ((cp = strchr(src, ']')) == NULL) { errno = EINVAL; /* ??? */ return(-1); } if ((e = encode_bitsring(&src, cp + 2, (char **)&label, (char **)&bp, (const char *)eom)) != 0) { errno = e; return(-1); } escaped = 0; label = bp++; if ((c = *src++) == 0) goto done; else if (c != '.') { errno = EINVAL; return(-1); } continue; } else if ((cp = strchr(digits, c)) != NULL) { n = (cp - digits) * 100; if ((c = *src++) == 0 || (cp = strchr(digits, c)) == NULL) { errno = EMSGSIZE; return (-1); } n += (cp - digits) * 10; if ((c = *src++) == 0 || (cp = strchr(digits, c)) == NULL) { errno = EMSGSIZE; return (-1); } n += (cp - digits); if (n > 255) { errno = EMSGSIZE; return (-1); } c = n; } escaped = 0; } else if (c == '\\') { escaped = 1; continue; } else if (c == '.') { c = (bp - label - 1); if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ errno = EMSGSIZE; return (-1); } if (label >= eom) { errno = EMSGSIZE; return (-1); } *label = c; /* Fully qualified ? */ if (*src == '\0') { if (c != 0) { if (bp >= eom) { errno = EMSGSIZE; return (-1); } *bp++ = '\0'; } if ((bp - dst) > MAXCDNAME) { errno = EMSGSIZE; return (-1); } return (1); } if (c == 0 || *src == '.') { errno = EMSGSIZE; return (-1); } label = bp++; continue; } if (bp >= eom) { errno = EMSGSIZE; return (-1); } *bp++ = (u_char)c; } c = (bp - label - 1); if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ errno = EMSGSIZE; return (-1); } done: if (label >= eom) { errno = EMSGSIZE; return (-1); } *label = c; if (c != 0) { if (bp >= eom) { errno = EMSGSIZE; return (-1); } *bp++ = 0; } if ((bp - dst) > MAXCDNAME) { /* src too big */ errno = EMSGSIZE; return (-1); } return (0); } /* * ns_name_ntol(src, dst, dstsiz) * Convert a network strings labels into all lowercase. * return: * Number of bytes written to buffer, or -1 (with errno set) * notes: * Enforces label and domain length limits. */ int ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) { const u_char *cp; u_char *dn, *eom; u_char c; u_int n; int l; cp = src; dn = dst; eom = dst + dstsiz; if (dn >= eom) { errno = EMSGSIZE; return (-1); } while ((n = *cp++) != 0) { if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { /* Some kind of compression pointer. */ errno = EMSGSIZE; return (-1); } *dn++ = n; if ((l = labellen(cp - 1)) < 0) { errno = EMSGSIZE; return (-1); } if (dn + l >= eom) { errno = EMSGSIZE; return (-1); } for ((void)NULL; l > 0; l--) { c = *cp++; if (isupper(c)) *dn++ = tolower(c); else *dn++ = c; } } *dn++ = '\0'; return (dn - dst); } /* * ns_name_unpack(msg, eom, src, dst, dstsiz) * Unpack a domain name from a message, source may be compressed. * return: * -1 if it fails, or consumed octets if it succeeds. */ int ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, u_char *dst, size_t dstsiz) { const u_char *srcp, *dstlim; u_char *dstp; int n, len, checked, l; len = -1; checked = 0; dstp = dst; srcp = src; dstlim = dst + dstsiz; if (srcp < msg || srcp >= eom) { errno = EMSGSIZE; return (-1); } /* Fetch next label in domain name. */ while ((n = *srcp++) != 0) { /* Check for indirection. */ switch (n & NS_CMPRSFLGS) { case 0: case NS_TYPE_ELT: /* Limit checks. */ if ((l = labellen(srcp - 1)) < 0) { errno = EMSGSIZE; return(-1); } if (dstp + l + 1 >= dstlim || srcp + l >= eom) { errno = EMSGSIZE; return (-1); } checked += l + 1; *dstp++ = n; memcpy(dstp, srcp, l); dstp += l; srcp += l; break; case NS_CMPRSFLGS: if (srcp >= eom) { errno = EMSGSIZE; return (-1); } if (len < 0) len = srcp - src + 1; srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff)); if (srcp < msg || srcp >= eom) { /* Out of range. */ errno = EMSGSIZE; return (-1); } checked += 2; /* * Check for loops in the compressed name; * if we've looked at the whole message, * there must be a loop. */ if (checked >= eom - msg) { errno = EMSGSIZE; return (-1); } break; default: errno = EMSGSIZE; return (-1); /* flag error */ } } *dstp = '\0'; if (len < 0) len = srcp - src; return (len); } /* * ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr) * Pack domain name 'domain' into 'comp_dn'. * return: * Size of the compressed name, or -1. * notes: * 'dnptrs' is an array of pointers to previous compressed names. * dnptrs[0] is a pointer to the beginning of the message. The array * ends with NULL. * 'lastdnptr' is a pointer to the end of the array pointed to * by 'dnptrs'. * Side effects: * The list of pointers in dnptrs is updated for labels inserted into * the message as we compress the name. If 'dnptr' is NULL, we don't * try to compress names. If 'lastdnptr' is NULL, we don't update the * list. */ int ns_name_pack(const u_char *src, u_char *dst, int dstsiz, const u_char **dnptrs, const u_char **lastdnptr) { u_char *dstp; const u_char **cpp, **lpp, *eob, *msg; const u_char *srcp; int n, l, first = 1; srcp = src; dstp = dst; eob = dstp + dstsiz; lpp = cpp = NULL; if (dnptrs != NULL) { if ((msg = *dnptrs++) != NULL) { for (cpp = dnptrs; *cpp != NULL; cpp++) (void)NULL; lpp = cpp; /* end of list to search */ } } else msg = NULL; /* make sure the domain we are about to add is legal */ l = 0; do { int l0; n = *srcp; if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { errno = EMSGSIZE; return (-1); } if ((l0 = labellen(srcp)) < 0) { errno = EINVAL; return(-1); } l += l0 + 1; if (l > MAXCDNAME) { errno = EMSGSIZE; return (-1); } srcp += l0 + 1; } while (n != 0); /* from here on we need to reset compression pointer array on error */ srcp = src; do { /* Look to see if we can use pointers. */ n = *srcp; if (n != 0 && msg != NULL) { l = dn_find(srcp, msg, (const u_char * const *)dnptrs, (const u_char * const *)lpp); if (l >= 0) { if (dstp + 1 >= eob) { goto cleanup; } *dstp++ = (l >> 8) | NS_CMPRSFLGS; *dstp++ = l % 256; return (dstp - dst); } /* Not found, save it. */ if (lastdnptr != NULL && cpp < lastdnptr - 1 && (dstp - msg) < 0x4000 && first) { *cpp++ = dstp; *cpp = NULL; first = 0; } } /* copy label to buffer */ if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { /* Should not happen. */ goto cleanup; } n = labellen(srcp); if (dstp + 1 + n >= eob) { goto cleanup; } memcpy(dstp, srcp, n + 1); srcp += n + 1; dstp += n + 1; } while (n != 0); if (dstp > eob) { cleanup: if (msg != NULL) *lpp = NULL; errno = EMSGSIZE; return (-1); } return (dstp - dst); } /* * ns_name_uncompress(msg, eom, src, dst, dstsiz) * Expand compressed domain name to presentation format. * return: * Number of bytes read out of `src', or -1 (with errno set). * note: * Root domain returns as "." not "". */ int ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, char *dst, size_t dstsiz) { u_char tmp[NS_MAXCDNAME]; int n; if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1) return (-1); if (ns_name_ntop(tmp, dst, dstsiz) == -1) return (-1); return (n); } /* * ns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr) * Compress a domain name into wire format, using compression pointers. * return: * Number of bytes consumed in `dst' or -1 (with errno set). * notes: * 'dnptrs' is an array of pointers to previous compressed names. * dnptrs[0] is a pointer to the beginning of the message. * The list ends with NULL. 'lastdnptr' is a pointer to the end of the * array pointed to by 'dnptrs'. Side effect is to update the list of * pointers for labels inserted into the message as we compress the name. * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' * is NULL, we don't update the list. */ int ns_name_compress(const char *src, u_char *dst, size_t dstsiz, const u_char **dnptrs, const u_char **lastdnptr) { u_char tmp[NS_MAXCDNAME]; if (ns_name_pton(src, tmp, sizeof tmp) == -1) return (-1); return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr)); } /* * Reset dnptrs so that there are no active references to pointers at or * after src. */ void ns_name_rollback(const u_char *src, const u_char **dnptrs, const u_char **lastdnptr) { while (dnptrs < lastdnptr && *dnptrs != NULL) { if (*dnptrs >= src) { *dnptrs = NULL; break; } dnptrs++; } } /* * ns_name_skip(ptrptr, eom) * Advance *ptrptr to skip over the compressed name it points at. * return: * 0 on success, -1 (with errno set) on failure. */ int ns_name_skip(const u_char **ptrptr, const u_char *eom) { const u_char *cp; u_int n; int l; cp = *ptrptr; while (cp < eom && (n = *cp++) != 0) { /* Check for indirection. */ switch (n & NS_CMPRSFLGS) { case 0: /* normal case, n == len */ cp += n; continue; case NS_TYPE_ELT: /* EDNS0 extended label */ if ((l = labellen(cp - 1)) < 0) { errno = EMSGSIZE; /* XXX */ return(-1); } cp += l; continue; case NS_CMPRSFLGS: /* indirection */ cp++; break; default: /* illegal type */ errno = EMSGSIZE; return (-1); } break; } if (cp > eom) { errno = EMSGSIZE; return (-1); } *ptrptr = cp; return (0); } /* Private. */ /* * special(ch) * Thinking in noninternationalized USASCII (per the DNS spec), * is this characted special ("in need of quoting") ? * return: * boolean. */ static int special(int ch) { switch (ch) { case 0x22: /* '"' */ case 0x2E: /* '.' */ case 0x3B: /* ';' */ case 0x5C: /* '\\' */ case 0x28: /* '(' */ case 0x29: /* ')' */ /* Special modifiers in zone files. */ case 0x40: /* '@' */ case 0x24: /* '$' */ return (1); default: return (0); } } /* * printable(ch) * Thinking in noninternationalized USASCII (per the DNS spec), * is this character visible and not a space when printed ? * return: * boolean. */ static int printable(int ch) { return (ch > 0x20 && ch < 0x7f); } /* * Thinking in noninternationalized USASCII (per the DNS spec), * convert this character to lower case if it's upper case. */ static int mklower(int ch) { if (ch >= 0x41 && ch <= 0x5A) return (ch + 0x20); return (ch); } /* * dn_find(domain, msg, dnptrs, lastdnptr) * Search for the counted-label name in an array of compressed names. * return: * offset from msg if found, or -1. * notes: * dnptrs is the pointer to the first name on the list, * not the pointer to the start of the message. */ static int dn_find(const u_char *domain, const u_char *msg, const u_char * const *dnptrs, const u_char * const *lastdnptr) { const u_char *dn, *cp, *sp; const u_char * const *cpp; u_int n; for (cpp = dnptrs; cpp < lastdnptr; cpp++) { sp = *cpp; /* * terminate search on: * root label * compression pointer * unusable offset */ while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 && (sp - msg) < 0x4000) { dn = domain; cp = sp; while ((n = *cp++) != 0) { /* * check for indirection */ switch (n & NS_CMPRSFLGS) { case 0: /* normal case, n == len */ n = labellen(cp - 1); /* XXX */ if (n != *dn++) goto next; for ((void)NULL; n > 0; n--) if (mklower(*dn++) != mklower(*cp++)) goto next; /* Is next root for both ? */ if (*dn == '\0' && *cp == '\0') return (sp - msg); if (*dn) continue; goto next; case NS_CMPRSFLGS: /* indirection */ cp = msg + (((n & 0x3f) << 8) | *cp); break; default: /* illegal type */ errno = EMSGSIZE; return (-1); } } next: ; sp += *sp + 1; } } errno = ENOENT; return (-1); } static int decode_bitstring(const char **cpp, char *dn, const char *eom) { const char *cp = *cpp; char *beg = dn, tc; - int b, blen, plen; + int b, blen, plen, i; if ((blen = (*cp & 0xff)) == 0) blen = 256; plen = (blen + 3) / 4; plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1); if (dn + plen >= eom) return(-1); cp++; - dn += SPRINTF((dn, "\\[x")); - for (b = blen; b > 7; b -= 8, cp++) - dn += SPRINTF((dn, "%02x", *cp & 0xff)); + i = SPRINTF((dn, "\\[x")); + if (i < 0) + return (-1); + dn += i; + for (b = blen; b > 7; b -= 8, cp++) { + i = SPRINTF((dn, "%02x", *cp & 0xff)); + if (i < 0) + return (-1); + dn += i; + } if (b > 4) { tc = *cp++; - dn += SPRINTF((dn, "%02x", tc & (0xff << (8 - b)))); + i = SPRINTF((dn, "%02x", tc & (0xff << (8 - b)))); + if (i < 0) + return (-1); + dn += i; } else if (b > 0) { tc = *cp++; - dn += SPRINTF((dn, "%1x", + i = SPRINTF((dn, "%1x", ((tc >> 4) & 0x0f) & (0x0f << (4 - b)))); + if (i < 0) + return (-1); + dn += i; } - dn += SPRINTF((dn, "/%d]", blen)); + i = SPRINTF((dn, "/%d]", blen)); + if (i < 0) + return (-1); + dn += i; *cpp = cp; return(dn - beg); } static int encode_bitsring(const char **bp, const char *end, char **labelp, char ** dst, const char *eom) { int afterslash = 0; const char *cp = *bp; char *tp, c; const char *beg_blen; char *end_blen = NULL; int value = 0, count = 0, tbcount = 0, blen = 0; beg_blen = end_blen = NULL; /* a bitstring must contain at least 2 characters */ if (end - cp < 2) return(EINVAL); /* XXX: currently, only hex strings are supported */ if (*cp++ != 'x') return(EINVAL); if (!isxdigit((*cp) & 0xff)) /* reject '\[x/BLEN]' */ return(EINVAL); for (tp = *dst + 1; cp < end && tp < eom; cp++) { switch((c = *cp)) { case ']': /* end of the bitstring */ if (afterslash) { if (beg_blen == NULL) return(EINVAL); blen = (int)strtol(beg_blen, &end_blen, 10); if (*end_blen != ']') return(EINVAL); } if (count) *tp++ = ((value << 4) & 0xff); cp++; /* skip ']' */ goto done; case '/': afterslash = 1; break; default: if (afterslash) { if (!isdigit(c&0xff)) return(EINVAL); if (beg_blen == NULL) { if (c == '0') { /* blen never begings with 0 */ return(EINVAL); } beg_blen = cp; } } else { if (!isxdigit(c&0xff)) return(EINVAL); value <<= 4; value += digitvalue[(int)c]; count += 4; tbcount += 4; if (tbcount > 256) return(EINVAL); if (count == 8) { *tp++ = value; count = 0; } } break; } } done: if (cp >= end || tp >= eom) return(EMSGSIZE); /* * bit length validation: * If a is present, the number of digits in the * MUST be just sufficient to contain the number of bits specified * by the . If there are insignificant bits in a final * hexadecimal or octal digit, they MUST be zero. * RFC 2673, Section 3.2. */ if (blen > 0) { int traillen; if (((blen + 3) & ~3) != tbcount) return(EINVAL); traillen = tbcount - blen; /* between 0 and 3 */ if (((value << (8 - traillen)) & 0xff) != 0) return(EINVAL); } else blen = tbcount; if (blen == 256) blen = 0; /* encode the type and the significant bit fields */ **labelp = DNS_LABELTYPE_BITSTRING; **dst = blen; *bp = cp; *dst = tp; return(0); } static int labellen(const u_char *lp) { int bitlen; u_char l = *lp; if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) { /* should be avoided by the caller */ return(-1); } if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) { if (l == DNS_LABELTYPE_BITSTRING) { if ((bitlen = *(lp + 1)) == 0) bitlen = 256; return((bitlen + 7 ) / 8 + 1); } return(-1); /* unknwon ELT */ } return(l); } diff --git a/contrib/bind/lib/nameser/ns_parse.c b/contrib/bind/lib/nameser/ns_parse.c index f3f92c68d7ce..abd48f2edeae 100644 --- a/contrib/bind/lib/nameser/ns_parse.c +++ b/contrib/bind/lib/nameser/ns_parse.c @@ -1,202 +1,203 @@ /* * Copyright (c) 1996,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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 lint -static const char rcsid[] = "$Id: ns_parse.c,v 8.17 2001/06/20 02:50:49 marka Exp $"; +static const char rcsid[] = "$Id: ns_parse.c,v 8.18 2003/04/03 06:10:10 marka Exp $"; #endif /* Import. */ #include "port_before.h" #include #include #include #include #include #include #include "port_after.h" /* Forward. */ static void setsection(ns_msg *msg, ns_sect sect); /* Macros. */ #define RETERR(err) do { errno = (err); return (-1); } while (0) /* Public. */ /* These need to be in the same order as the nres.h:ns_flag enum. */ struct _ns_flagdata _ns_flagdata[16] = { { 0x8000, 15 }, /* qr. */ { 0x7800, 11 }, /* opcode. */ { 0x0400, 10 }, /* aa. */ { 0x0200, 9 }, /* tc. */ { 0x0100, 8 }, /* rd. */ { 0x0080, 7 }, /* ra. */ { 0x0040, 6 }, /* z. */ { 0x0020, 5 }, /* ad. */ { 0x0010, 4 }, /* cd. */ { 0x000f, 0 }, /* rcode. */ { 0x0000, 0 }, /* expansion (1/6). */ { 0x0000, 0 }, /* expansion (2/6). */ { 0x0000, 0 }, /* expansion (3/6). */ { 0x0000, 0 }, /* expansion (4/6). */ { 0x0000, 0 }, /* expansion (5/6). */ { 0x0000, 0 }, /* expansion (6/6). */ }; int ns_msg_getflag(ns_msg handle, int flag) { return(((handle)._flags & _ns_flagdata[flag].mask) >> _ns_flagdata[flag].shift); } int ns_skiprr(const u_char *ptr, const u_char *eom, ns_sect section, int count) { const u_char *optr = ptr; for ((void)NULL; count > 0; count--) { int b, rdlength; b = dn_skipname(ptr, eom); if (b < 0) RETERR(EMSGSIZE); ptr += b/*Name*/ + NS_INT16SZ/*Type*/ + NS_INT16SZ/*Class*/; if (section != ns_s_qd) { if (ptr + NS_INT32SZ + NS_INT16SZ > eom) RETERR(EMSGSIZE); ptr += NS_INT32SZ/*TTL*/; NS_GET16(rdlength, ptr); ptr += rdlength/*RData*/; } } if (ptr > eom) RETERR(EMSGSIZE); return (ptr - optr); } int ns_initparse(const u_char *msg, int msglen, ns_msg *handle) { const u_char *eom = msg + msglen; int i; memset(handle, 0x5e, sizeof *handle); handle->_msg = msg; handle->_eom = eom; if (msg + NS_INT16SZ > eom) RETERR(EMSGSIZE); NS_GET16(handle->_id, msg); if (msg + NS_INT16SZ > eom) RETERR(EMSGSIZE); NS_GET16(handle->_flags, msg); for (i = 0; i < ns_s_max; i++) { if (msg + NS_INT16SZ > eom) RETERR(EMSGSIZE); NS_GET16(handle->_counts[i], msg); } for (i = 0; i < ns_s_max; i++) if (handle->_counts[i] == 0) handle->_sections[i] = NULL; else { int b = ns_skiprr(msg, eom, (ns_sect)i, handle->_counts[i]); if (b < 0) return (-1); handle->_sections[i] = msg; msg += b; } if (msg != eom) RETERR(EMSGSIZE); setsection(handle, ns_s_max); return (0); } int ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr) { int b; + int tmp; /* Make section right. */ - if (section >= ns_s_max) + if ((tmp = section) < 0 || section >= ns_s_max) RETERR(ENODEV); if (section != handle->_sect) setsection(handle, section); /* Make rrnum right. */ if (rrnum == -1) rrnum = handle->_rrnum; if (rrnum < 0 || rrnum >= handle->_counts[(int)section]) RETERR(ENODEV); if (rrnum < handle->_rrnum) setsection(handle, section); if (rrnum > handle->_rrnum) { b = ns_skiprr(handle->_msg_ptr, handle->_eom, section, rrnum - handle->_rrnum); if (b < 0) return (-1); handle->_msg_ptr += b; handle->_rrnum = rrnum; } /* Do the parse. */ b = dn_expand(handle->_msg, handle->_eom, handle->_msg_ptr, rr->name, NS_MAXDNAME); if (b < 0) return (-1); handle->_msg_ptr += b; if (handle->_msg_ptr + NS_INT16SZ + NS_INT16SZ > handle->_eom) RETERR(EMSGSIZE); NS_GET16(rr->type, handle->_msg_ptr); NS_GET16(rr->rr_class, handle->_msg_ptr); if (section == ns_s_qd) { rr->ttl = 0; rr->rdlength = 0; rr->rdata = NULL; } else { if (handle->_msg_ptr + NS_INT32SZ + NS_INT16SZ > handle->_eom) RETERR(EMSGSIZE); NS_GET32(rr->ttl, handle->_msg_ptr); NS_GET16(rr->rdlength, handle->_msg_ptr); if (handle->_msg_ptr + rr->rdlength > handle->_eom) RETERR(EMSGSIZE); rr->rdata = handle->_msg_ptr; handle->_msg_ptr += rr->rdlength; } if (++handle->_rrnum > handle->_counts[(int)section]) setsection(handle, (ns_sect)((int)section + 1)); /* All done. */ return (0); } /* Private. */ static void setsection(ns_msg *msg, ns_sect sect) { msg->_sect = sect; if (sect == ns_s_max) { msg->_rrnum = -1; msg->_msg_ptr = NULL; } else { msg->_rrnum = 0; msg->_msg_ptr = msg->_sections[(int)sect]; } } diff --git a/contrib/bind/lib/nameser/ns_print.c b/contrib/bind/lib/nameser/ns_print.c index b23f805ac8b1..f8a85e7b5277 100644 --- a/contrib/bind/lib/nameser/ns_print.c +++ b/contrib/bind/lib/nameser/ns_print.c @@ -1,901 +1,899 @@ /* * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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 lint -static const char rcsid[] = "$Id: ns_print.c,v 8.24 2001/06/18 06:40:45 marka Exp $"; +static const char rcsid[] = "$Id: ns_print.c,v 8.26 2003/02/24 23:56:35 vixie Exp $"; #endif /* Import. */ #include "port_before.h" #include #include #include #include #include #include +#include #include #include #include #include #include "port_after.h" #ifdef SPRINTF_CHAR # define SPRINTF(x) strlen(sprintf/**/x) #else # define SPRINTF(x) ((size_t)sprintf x) #endif /* Forward. */ static size_t prune_origin(const char *name, const char *origin); static int charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen); static int addname(const u_char *msg, size_t msglen, const u_char **p, const char *origin, char **buf, size_t *buflen); static void addlen(size_t len, char **buf, size_t *buflen); static int addstr(const char *src, size_t len, char **buf, size_t *buflen); static int addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen); -/* Proto. */ - -u_int16_t dst_s_dns_key_id(const u_char *, const int); - /* Macros. */ #define T(x) \ do { \ if ((x) < 0) \ return (-1); \ } while (0) /* Public. */ /* * int * ns_sprintrr(handle, rr, name_ctx, origin, buf, buflen) * Convert an RR to presentation format. * return: * Number of characters written to buf, or -1 (check errno). */ int ns_sprintrr(const ns_msg *handle, const ns_rr *rr, const char *name_ctx, const char *origin, char *buf, size_t buflen) { int n; n = ns_sprintrrf(ns_msg_base(*handle), ns_msg_size(*handle), ns_rr_name(*rr), ns_rr_class(*rr), ns_rr_type(*rr), ns_rr_ttl(*rr), ns_rr_rdata(*rr), ns_rr_rdlen(*rr), name_ctx, origin, buf, buflen); return (n); } /* * int * ns_sprintrrf(msg, msglen, name, class, type, ttl, rdata, rdlen, * name_ctx, origin, buf, buflen) * Convert the fields of an RR into presentation format. * return: * Number of characters written to buf, or -1 (check errno). */ int ns_sprintrrf(const u_char *msg, size_t msglen, const char *name, ns_class class, ns_type type, u_long ttl, const u_char *rdata, size_t rdlen, const char *name_ctx, const char *origin, char *buf, size_t buflen) { const char *obuf = buf; const u_char *edata = rdata + rdlen; int spaced = 0; const char *comment; char tmp[100]; int len, x; /* * Owner. */ if (name_ctx != NULL && ns_samename(name_ctx, name) == 1) { T(addstr("\t\t\t", 3, &buf, &buflen)); } else { len = prune_origin(name, origin); if (*name == '\0') { goto root; } else if (len == 0) { T(addstr("@\t\t\t", 4, &buf, &buflen)); } else { T(addstr(name, len, &buf, &buflen)); /* Origin not used or not root, and no trailing dot? */ if (((origin == NULL || origin[0] == '\0') || (origin[0] != '.' && origin[1] != '\0' && name[len] == '\0')) && name[len - 1] != '.') { root: T(addstr(".", 1, &buf, &buflen)); len++; } T(spaced = addtab(len, 24, spaced, &buf, &buflen)); } } /* * TTL, Class, Type. */ T(x = ns_format_ttl(ttl, buf, buflen)); addlen(x, &buf, &buflen); len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type))); T(addstr(tmp, len, &buf, &buflen)); if (rdlen == 0) return (buf - obuf); T(spaced = addtab(x + len, 16, spaced, &buf, &buflen)); /* * RData. */ switch (type) { case ns_t_a: if (rdlen != NS_INADDRSZ) goto formerr; (void) inet_ntop(AF_INET, rdata, buf, buflen); addlen(strlen(buf), &buf, &buflen); break; case ns_t_cname: case ns_t_mb: case ns_t_mg: case ns_t_mr: case ns_t_ns: case ns_t_ptr: case ns_t_dname: T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; case ns_t_hinfo: case ns_t_isdn: /* First word. */ T(len = charstr(rdata, edata, &buf, &buflen)); if (len == 0) goto formerr; rdata += len; T(addstr(" ", 1, &buf, &buflen)); /* Second word, optional in ISDN records. */ if (type == ns_t_isdn && rdata == edata) break; T(len = charstr(rdata, edata, &buf, &buflen)); if (len == 0) goto formerr; rdata += len; break; case ns_t_soa: { u_long t; /* Server name. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); T(addstr(" ", 1, &buf, &buflen)); /* Administrator name. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); T(addstr(" (\n", 3, &buf, &buflen)); spaced = 0; if ((edata - rdata) != 5*NS_INT32SZ) goto formerr; /* Serial number. */ t = ns_get32(rdata); rdata += NS_INT32SZ; T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); len = SPRINTF((tmp, "%lu", t)); T(addstr(tmp, len, &buf, &buflen)); T(spaced = addtab(len, 16, spaced, &buf, &buflen)); T(addstr("; serial\n", 9, &buf, &buflen)); spaced = 0; /* Refresh interval. */ t = ns_get32(rdata); rdata += NS_INT32SZ; T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); T(len = ns_format_ttl(t, buf, buflen)); addlen(len, &buf, &buflen); T(spaced = addtab(len, 16, spaced, &buf, &buflen)); T(addstr("; refresh\n", 10, &buf, &buflen)); spaced = 0; /* Retry interval. */ t = ns_get32(rdata); rdata += NS_INT32SZ; T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); T(len = ns_format_ttl(t, buf, buflen)); addlen(len, &buf, &buflen); T(spaced = addtab(len, 16, spaced, &buf, &buflen)); T(addstr("; retry\n", 8, &buf, &buflen)); spaced = 0; /* Expiry. */ t = ns_get32(rdata); rdata += NS_INT32SZ; T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); T(len = ns_format_ttl(t, buf, buflen)); addlen(len, &buf, &buflen); T(spaced = addtab(len, 16, spaced, &buf, &buflen)); T(addstr("; expiry\n", 9, &buf, &buflen)); spaced = 0; /* Minimum TTL. */ t = ns_get32(rdata); rdata += NS_INT32SZ; T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); T(len = ns_format_ttl(t, buf, buflen)); addlen(len, &buf, &buflen); T(addstr(" )", 2, &buf, &buflen)); T(spaced = addtab(len, 16, spaced, &buf, &buflen)); T(addstr("; minimum\n", 10, &buf, &buflen)); break; } case ns_t_mx: case ns_t_afsdb: case ns_t_rt: { u_int t; if (rdlen < NS_INT16SZ) goto formerr; /* Priority. */ t = ns_get16(rdata); rdata += NS_INT16SZ; len = SPRINTF((tmp, "%u ", t)); T(addstr(tmp, len, &buf, &buflen)); /* Target. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; } case ns_t_px: { u_int t; if (rdlen < NS_INT16SZ) goto formerr; /* Priority. */ t = ns_get16(rdata); rdata += NS_INT16SZ; len = SPRINTF((tmp, "%u ", t)); T(addstr(tmp, len, &buf, &buflen)); /* Name1. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); T(addstr(" ", 1, &buf, &buflen)); /* Name2. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; } case ns_t_x25: T(len = charstr(rdata, edata, &buf, &buflen)); if (len == 0) goto formerr; rdata += len; break; case ns_t_txt: while (rdata < edata) { T(len = charstr(rdata, edata, &buf, &buflen)); if (len == 0) goto formerr; rdata += len; if (rdata < edata) T(addstr(" ", 1, &buf, &buflen)); } break; case ns_t_nsap: { char t[2+255*3]; (void) inet_nsap_ntoa(rdlen, rdata, t); T(addstr(t, strlen(t), &buf, &buflen)); break; } case ns_t_aaaa: if (rdlen != NS_IN6ADDRSZ) goto formerr; (void) inet_ntop(AF_INET6, rdata, buf, buflen); addlen(strlen(buf), &buf, &buflen); break; case ns_t_loc: { char t[255]; /* XXX protocol format checking? */ (void) loc_ntoa(rdata, t); T(addstr(t, strlen(t), &buf, &buflen)); break; } case ns_t_naptr: { u_int order, preference; char t[50]; if (rdlen < 2*NS_INT16SZ) goto formerr; /* Order, Precedence. */ order = ns_get16(rdata); rdata += NS_INT16SZ; preference = ns_get16(rdata); rdata += NS_INT16SZ; len = SPRINTF((t, "%u %u ", order, preference)); T(addstr(t, len, &buf, &buflen)); /* Flags. */ T(len = charstr(rdata, edata, &buf, &buflen)); if (len == 0) goto formerr; rdata += len; T(addstr(" ", 1, &buf, &buflen)); /* Service. */ T(len = charstr(rdata, edata, &buf, &buflen)); if (len == 0) goto formerr; rdata += len; T(addstr(" ", 1, &buf, &buflen)); /* Regexp. */ T(len = charstr(rdata, edata, &buf, &buflen)); if (len < 0) return (-1); if (len == 0) goto formerr; rdata += len; T(addstr(" ", 1, &buf, &buflen)); /* Server. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; } case ns_t_srv: { u_int priority, weight, port; char t[50]; if (rdlen < NS_INT16SZ*3) goto formerr; /* Priority, Weight, Port. */ priority = ns_get16(rdata); rdata += NS_INT16SZ; weight = ns_get16(rdata); rdata += NS_INT16SZ; port = ns_get16(rdata); rdata += NS_INT16SZ; len = SPRINTF((t, "%u %u %u ", priority, weight, port)); T(addstr(t, len, &buf, &buflen)); /* Server. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; } case ns_t_minfo: case ns_t_rp: /* Name1. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); T(addstr(" ", 1, &buf, &buflen)); /* Name2. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; case ns_t_wks: { int n, lcnt; if (rdlen < NS_INT32SZ + 1) goto formerr; /* Address. */ (void) inet_ntop(AF_INET, rdata, buf, buflen); addlen(strlen(buf), &buf, &buflen); rdata += NS_INADDRSZ; /* Protocol. */ len = SPRINTF((tmp, " %u ( ", *rdata)); T(addstr(tmp, len, &buf, &buflen)); rdata += NS_INT8SZ; /* Bit map. */ n = 0; lcnt = 0; while (rdata < edata) { u_int c = *rdata++; do { if (c & 0200) { if (lcnt == 0) { T(addstr("\n\t\t\t\t", 5, &buf, &buflen)); lcnt = 10; spaced = 0; } len = SPRINTF((tmp, "%d ", n)); T(addstr(tmp, len, &buf, &buflen)); lcnt--; } c <<= 1; } while (++n & 07); } T(addstr(")", 1, &buf, &buflen)); break; } case ns_t_key: { char base64_key[NS_MD5RSA_MAX_BASE64]; u_int keyflags, protocol, algorithm, key_id; const char *leader; int n; if (rdlen < NS_INT16SZ + NS_INT8SZ + NS_INT8SZ) goto formerr; /* Key flags, Protocol, Algorithm. */ key_id = dst_s_dns_key_id(rdata, edata-rdata); keyflags = ns_get16(rdata); rdata += NS_INT16SZ; protocol = *rdata++; algorithm = *rdata++; len = SPRINTF((tmp, "0x%04x %u %u", keyflags, protocol, algorithm)); T(addstr(tmp, len, &buf, &buflen)); /* Public key data. */ len = b64_ntop(rdata, edata - rdata, base64_key, sizeof base64_key); if (len < 0) goto formerr; if (len > 15) { T(addstr(" (", 2, &buf, &buflen)); leader = "\n\t\t"; spaced = 0; } else leader = " "; for (n = 0; n < len; n += 48) { T(addstr(leader, strlen(leader), &buf, &buflen)); T(addstr(base64_key + n, MIN(len - n, 48), &buf, &buflen)); } if (len > 15) T(addstr(" )", 2, &buf, &buflen)); n = SPRINTF((tmp, " ; key_tag= %u", key_id)); T(addstr(tmp, n, &buf, &buflen)); break; } case ns_t_sig: { char base64_key[NS_MD5RSA_MAX_BASE64]; u_int type, algorithm, labels, footprint; const char *leader; u_long t; int n; if (rdlen < 22) goto formerr; /* Type covered, Algorithm, Label count, Original TTL. */ type = ns_get16(rdata); rdata += NS_INT16SZ; algorithm = *rdata++; labels = *rdata++; t = ns_get32(rdata); rdata += NS_INT32SZ; len = SPRINTF((tmp, "%s %d %d %lu ", p_type(type), algorithm, labels, t)); T(addstr(tmp, len, &buf, &buflen)); if (labels > (u_int)dn_count_labels(name)) goto formerr; /* Signature expiry. */ t = ns_get32(rdata); rdata += NS_INT32SZ; len = SPRINTF((tmp, "%s ", p_secstodate(t))); T(addstr(tmp, len, &buf, &buflen)); /* Time signed. */ t = ns_get32(rdata); rdata += NS_INT32SZ; len = SPRINTF((tmp, "%s ", p_secstodate(t))); T(addstr(tmp, len, &buf, &buflen)); /* Signature Footprint. */ footprint = ns_get16(rdata); rdata += NS_INT16SZ; len = SPRINTF((tmp, "%u ", footprint)); T(addstr(tmp, len, &buf, &buflen)); /* Signer's name. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); /* Signature. */ len = b64_ntop(rdata, edata - rdata, base64_key, sizeof base64_key); if (len > 15) { T(addstr(" (", 2, &buf, &buflen)); leader = "\n\t\t"; spaced = 0; } else leader = " "; if (len < 0) goto formerr; for (n = 0; n < len; n += 48) { T(addstr(leader, strlen(leader), &buf, &buflen)); T(addstr(base64_key + n, MIN(len - n, 48), &buf, &buflen)); } if (len > 15) T(addstr(" )", 2, &buf, &buflen)); break; } case ns_t_nxt: { int n, c; /* Next domain name. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); /* Type bit map. */ n = edata - rdata; for (c = 0; c < n*8; c++) if (NS_NXT_BIT_ISSET(c, rdata)) { len = SPRINTF((tmp, " %s", p_type(c))); T(addstr(tmp, len, &buf, &buflen)); } break; } case ns_t_cert: { u_int c_type, key_tag, alg; int n; unsigned int siz; char base64_cert[8192], tmp[40]; const char *leader; c_type = ns_get16(rdata); rdata += NS_INT16SZ; key_tag = ns_get16(rdata); rdata += NS_INT16SZ; alg = (u_int) *rdata++; len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg)); T(addstr(tmp, len, &buf, &buflen)); siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */ if (siz > sizeof(base64_cert) * 3/4) { const char *str = "record too long to print"; T(addstr(str, strlen(str), &buf, &buflen)); } else { len = b64_ntop(rdata, edata-rdata, base64_cert, siz); if (len < 0) goto formerr; else if (len > 15) { T(addstr(" (", 2, &buf, &buflen)); leader = "\n\t\t"; spaced = 0; } else leader = " "; for (n = 0; n < len; n += 48) { T(addstr(leader, strlen(leader), &buf, &buflen)); T(addstr(base64_cert + n, MIN(len - n, 48), &buf, &buflen)); } if (len > 15) T(addstr(" )", 2, &buf, &buflen)); } break; } case ns_t_tkey: { /* KJD - need to complete this */ u_long t; int mode, err, keysize; /* Algorithm name. */ T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); T(addstr(" ", 1, &buf, &buflen)); /* Inception. */ t = ns_get32(rdata); rdata += NS_INT32SZ; len = SPRINTF((tmp, "%s ", p_secstodate(t))); T(addstr(tmp, len, &buf, &buflen)); /* Experation. */ t = ns_get32(rdata); rdata += NS_INT32SZ; len = SPRINTF((tmp, "%s ", p_secstodate(t))); T(addstr(tmp, len, &buf, &buflen)); /* Mode , Error, Key Size. */ /* Priority, Weight, Port. */ mode = ns_get16(rdata); rdata += NS_INT16SZ; err = ns_get16(rdata); rdata += NS_INT16SZ; keysize = ns_get16(rdata); rdata += NS_INT16SZ; len = SPRINTF((tmp, "%u %u %u ", mode, err, keysize)); T(addstr(tmp, len, &buf, &buflen)); - /* needs to dump key, print otherdata length & other data */ + /* XXX need to dump key, print otherdata length & other data */ break; } + case ns_t_tsig: { /* BEW - need to complete this */ int n; T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen)); T(addstr(" ", 1, &buf, &buflen)); rdata += 8; /* time */ n = ns_get16(rdata); rdata += INT16SZ; rdata += n; /* sig */ n = ns_get16(rdata); rdata += INT16SZ; /* original id */ sprintf(buf, "%d", ns_get16(rdata)); rdata += INT16SZ; addlen(strlen(buf), &buf, &buflen); break; } case ns_t_a6: { struct in6_addr a; int pbyte, pbit; /* prefix length */ if (rdlen == 0) goto formerr; len = SPRINTF((tmp, "%d ", *rdata)); T(addstr(tmp, len, &buf, &buflen)); pbit = *rdata; if (pbit > 128) goto formerr; pbyte = (pbit & ~7) / 8; rdata++; /* address suffix: provided only when prefix len != 128 */ if (pbit < 128) { if (rdata + pbyte >= edata) goto formerr; memset(&a, 0, sizeof(a)); memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte); (void) inet_ntop(AF_INET6, &a, buf, buflen); addlen(strlen(buf), &buf, &buflen); rdata += sizeof(a) - pbyte; } /* prefix name: provided only when prefix len > 0 */ if (pbit == 0) break; if (rdata >= edata) goto formerr; T(addstr(" ", 1, &buf, &buflen)); T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); break; - } + } case ns_t_opt: { len = SPRINTF((tmp, "%u bytes", class)); T(addstr(tmp, len, &buf, &buflen)); break; - } + } default: comment = "unknown RR type"; goto hexify; } return (buf - obuf); formerr: comment = "RR format error"; hexify: { int n, m; char *p; len = SPRINTF((tmp, "\\# %u (\t; %s", edata - rdata, comment)); T(addstr(tmp, len, &buf, &buflen)); while (rdata < edata) { p = tmp; p += SPRINTF((p, "\n\t")); spaced = 0; n = MIN(16, edata - rdata); for (m = 0; m < n; m++) p += SPRINTF((p, "%02x ", rdata[m])); T(addstr(tmp, p - tmp, &buf, &buflen)); if (n < 16) { T(addstr(")", 1, &buf, &buflen)); T(addtab(p - tmp + 1, 48, spaced, &buf, &buflen)); } p = tmp; p += SPRINTF((p, "; ")); for (m = 0; m < n; m++) *p++ = (isascii(rdata[m]) && isprint(rdata[m])) ? rdata[m] : '.'; T(addstr(tmp, p - tmp, &buf, &buflen)); rdata += n; } return (buf - obuf); } } /* Private. */ /* * size_t * prune_origin(name, origin) * Find out if the name is at or under the current origin. * return: * Number of characters in name before start of origin, * or length of name if origin does not match. * notes: * This function should share code with samedomain(). */ static size_t prune_origin(const char *name, const char *origin) { const char *oname = name; while (*name != '\0') { if (origin != NULL && ns_samename(name, origin) == 1) return (name - oname - (name > oname)); while (*name != '\0') { if (*name == '\\') { name++; /* XXX need to handle \nnn form. */ if (*name == '\0') break; } else if (*name == '.') { name++; break; } name++; } } return (name - oname); } /* * int * charstr(rdata, edata, buf, buflen) * Format a into the presentation buffer. * return: * Number of rdata octets consumed * 0 for protocol format error * -1 for output buffer error * side effects: * buffer is advanced on success. */ static int charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) { const u_char *odata = rdata; size_t save_buflen = *buflen; char *save_buf = *buf; if (addstr("\"", 1, buf, buflen) < 0) goto enospc; if (rdata < edata) { int n = *rdata; if (rdata + 1 + n <= edata) { rdata++; while (n-- > 0) { if (strchr("\n\"\\", *rdata) != NULL) if (addstr("\\", 1, buf, buflen) < 0) goto enospc; if (addstr((const char *)rdata, 1, buf, buflen) < 0) goto enospc; rdata++; } } } if (addstr("\"", 1, buf, buflen) < 0) goto enospc; return (rdata - odata); enospc: errno = ENOSPC; *buf = save_buf; *buflen = save_buflen; return (-1); } static int addname(const u_char *msg, size_t msglen, const u_char **pp, const char *origin, char **buf, size_t *buflen) { size_t newlen, save_buflen = *buflen; char *save_buf = *buf; int n; n = dn_expand(msg, msg + msglen, *pp, *buf, *buflen); if (n < 0) goto enospc; /* Guess. */ newlen = prune_origin(*buf, origin); if (**buf == '\0') { goto root; } else if (newlen == 0) { /* Use "@" instead of name. */ if (newlen + 2 > *buflen) goto enospc; /* No room for "@\0". */ (*buf)[newlen++] = '@'; (*buf)[newlen] = '\0'; } else { if (((origin == NULL || origin[0] == '\0') || (origin[0] != '.' && origin[1] != '\0' && (*buf)[newlen] == '\0')) && (*buf)[newlen - 1] != '.') { /* No trailing dot. */ root: if (newlen + 2 > *buflen) goto enospc; /* No room for ".\0". */ (*buf)[newlen++] = '.'; (*buf)[newlen] = '\0'; } } *pp += n; addlen(newlen, buf, buflen); **buf = '\0'; return (newlen); enospc: errno = ENOSPC; *buf = save_buf; *buflen = save_buflen; return (-1); } static void addlen(size_t len, char **buf, size_t *buflen) { INSIST(len <= *buflen); *buf += len; *buflen -= len; } static int addstr(const char *src, size_t len, char **buf, size_t *buflen) { if (len >= *buflen) { errno = ENOSPC; return (-1); } memcpy(*buf, src, len); addlen(len, buf, buflen); **buf = '\0'; return (0); } static int addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) { size_t save_buflen = *buflen; char *save_buf = *buf; int t; if (spaced || len >= target - 1) { T(addstr(" ", 2, buf, buflen)); spaced = 1; } else { for (t = (target - len - 1) / 8; t >= 0; t--) if (addstr("\t", 1, buf, buflen) < 0) { *buflen = save_buflen; *buf = save_buf; return (-1); } spaced = 0; } return (spaced); } diff --git a/contrib/bind/lib/nameser/ns_samedomain.c b/contrib/bind/lib/nameser/ns_samedomain.c index da6f3b629e98..6394701f4cb5 100644 --- a/contrib/bind/lib/nameser/ns_samedomain.c +++ b/contrib/bind/lib/nameser/ns_samedomain.c @@ -1,206 +1,206 @@ /* * Copyright (c) 1995,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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 lint -static const char rcsid[] = "$Id: ns_samedomain.c,v 8.9.6.2 2002/11/14 22:36:46 marka Exp $"; +static const char rcsid[] = "$Id: ns_samedomain.c,v 8.10.8.1 2003/06/02 05:05:05 marka Exp $"; #endif #include "port_before.h" #include #include #include #include #include "port_after.h" /* * int * ns_samedomain(a, b) * Check whether a name belongs to a domain. * Inputs: * a - the domain whose ancestory is being verified * b - the potential ancestor we're checking against * Return: * boolean - is a at or below b? * Notes: * Trailing dots are first removed from name and domain. * Always compare complete subdomains, not only whether the * domain name is the trailing string of the given name. * * "host.foobar.top" lies in "foobar.top" and in "top" and in "" * but NOT in "bar.top" */ int ns_samedomain(const char *a, const char *b) { size_t la, lb; int diff, i, escaped; const char *cp; la = strlen(a); lb = strlen(b); /* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */ if (la != 0 && a[la - 1] == '.') { escaped = 0; /* Note this loop doesn't get executed if la==1. */ for (i = la - 2; i >= 0; i--) if (a[i] == '\\') { if (escaped) escaped = 0; else escaped = 1; } else break; if (!escaped) la--; } /* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */ if (lb != 0 && b[lb - 1] == '.') { escaped = 0; /* note this loop doesn't get executed if lb==1 */ for (i = lb - 2; i >= 0; i--) if (b[i] == '\\') { if (escaped) escaped = 0; else escaped = 1; } else break; if (!escaped) lb--; } /* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */ if (lb == 0) return (1); /* 'b' longer than 'a' means 'a' can't be in 'b'. */ if (lb > la) return (0); /* 'a' and 'b' being equal at this point indicates sameness. */ if (lb == la) return (strncasecmp(a, b, lb) == 0); /* Ok, we know la > lb. */ diff = la - lb; /* * If 'a' is only 1 character longer than 'b', then it can't be * a subdomain of 'b' (because of the need for the '.' label * separator). */ if (diff < 2) return (0); /* * If the character before the last 'lb' characters of 'b' * isn't '.', then it can't be a match (this lets us avoid * having "foobar.com" match "bar.com"). */ if (a[diff - 1] != '.') return (0); /* * We're not sure about that '.', however. It could be escaped * and thus not a really a label separator. */ escaped = 0; for (i = diff - 2; i >= 0; i--) - if (a[i] == '\\') + if (a[i] == '\\') { if (escaped) escaped = 0; else escaped = 1; - else + } else break; if (escaped) return (0); /* Now compare aligned trailing substring. */ cp = a + diff; return (strncasecmp(cp, b, lb) == 0); } /* * int * ns_subdomain(a, b) * is "a" a subdomain of "b"? */ int ns_subdomain(const char *a, const char *b) { return (ns_samename(a, b) != 1 && ns_samedomain(a, b)); } /* * int * ns_makecanon(src, dst, dstsize) * make a canonical copy of domain name "src" * notes: * foo -> foo. * foo. -> foo. * foo.. -> foo. * foo\. -> foo\.. * foo\\. -> foo\\. */ int ns_makecanon(const char *src, char *dst, size_t dstsize) { size_t n = strlen(src); - if (n + sizeof "." > dstsize) { + if (n + sizeof "." > dstsize) { /* Note: sizeof == 2 */ errno = EMSGSIZE; return (-1); } strcpy(dst, src); - while (n > 0 && dst[n - 1] == '.') /* Ends in "." */ - if (n > 1 && dst[n - 2] == '\\' && /* Ends in "\." */ - (n < 2 || dst[n - 3] != '\\')) /* But not "\\." */ + while (n >= 1 && dst[n - 1] == '.') /* Ends in "." */ + if (n >= 2 && dst[n - 2] == '\\' && /* Ends in "\." */ + (n < 3 || dst[n - 3] != '\\')) /* But not "\\." */ break; else dst[--n] = '\0'; dst[n++] = '.'; dst[n] = '\0'; return (0); } /* * int * ns_samename(a, b) * determine whether domain name "a" is the same as domain name "b" * return: * -1 on error * 0 if names differ * 1 if names are the same */ int ns_samename(const char *a, const char *b) { char ta[NS_MAXDNAME], tb[NS_MAXDNAME]; if (ns_makecanon(a, ta, sizeof ta) < 0 || ns_makecanon(b, tb, sizeof tb) < 0) return (-1); if (strcasecmp(ta, tb) == 0) return (1); else return (0); } diff --git a/contrib/bind/lib/nameser/ns_sign.c b/contrib/bind/lib/nameser/ns_sign.c index 8c5fe1d79f3f..de8cd1418fdf 100644 --- a/contrib/bind/lib/nameser/ns_sign.c +++ b/contrib/bind/lib/nameser/ns_sign.c @@ -1,369 +1,380 @@ /* * Copyright (c) 1999 by Internet Software Consortium, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER 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 lint -static const char rcsid[] = "$Id: ns_sign.c,v 8.11 2002/04/30 03:43:55 marka Exp $"; +static const char rcsid[] = "$Id: ns_sign.c,v 8.12 2002/10/01 06:48:37 marka Exp $"; #endif /* Import. */ #include "port_before.h" #include "fd_setsize.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "port_after.h" #define BOUNDS_CHECK(ptr, count) \ do { \ if ((ptr) + (count) > eob) { \ errno = EMSGSIZE; \ return(NS_TSIG_ERROR_NO_SPACE); \ } \ } while (0) /* ns_sign * Parameters: * msg message to be sent * msglen input - length of message * output - length of signed message * msgsize length of buffer containing message * error value to put in the error field * key tsig key used for signing * querysig (response), the signature in the query * querysiglen (response), the length of the signature in the query * sig a buffer to hold the generated signature * siglen input - length of signature buffer * output - length of signature * * Errors: * - bad input data (-1) * - bad key / sign failed (-BADKEY) * - not enough space (NS_TSIG_ERROR_NO_SPACE) */ int ns_sign(u_char *msg, int *msglen, int msgsize, int error, void *k, const u_char *querysig, int querysiglen, u_char *sig, int *siglen, time_t in_timesigned) { return(ns_sign2(msg, msglen, msgsize, error, k, querysig, querysiglen, sig, siglen, in_timesigned, NULL, NULL)); } int ns_sign2(u_char *msg, int *msglen, int msgsize, int error, void *k, const u_char *querysig, int querysiglen, u_char *sig, int *siglen, time_t in_timesigned, u_char **dnptrs, u_char **lastdnptr) { HEADER *hp = (HEADER *)msg; DST_KEY *key = (DST_KEY *)k; u_char *cp = msg + *msglen, *eob = msg + msgsize; u_char *lenp; - u_char *name, *alg; + u_char *alg; int n; time_t timesigned; + u_char name[NS_MAXCDNAME]; dst_init(); if (msg == NULL || msglen == NULL || sig == NULL || siglen == NULL) return (-1); /* Name. */ - if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) - n = dn_comp(key->dk_key_name, cp, eob - cp, dnptrs, lastdnptr); - else - n = dn_comp("", cp, eob - cp, NULL, NULL); + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { + n = ns_name_pton(key->dk_key_name, name, sizeof name); + if (n != -1) + n = ns_name_pack(name, cp, eob - cp, + (const u_char **)dnptrs, + (const u_char **)lastdnptr); + + } else { + n = ns_name_pton("", name, sizeof name); + if (n != -1) + n = ns_name_pack(name, cp, eob - cp, NULL, NULL); + } if (n < 0) return (NS_TSIG_ERROR_NO_SPACE); - name = cp; cp += n; /* Type, class, ttl, length (not filled in yet). */ BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ); PUTSHORT(ns_t_tsig, cp); PUTSHORT(ns_c_any, cp); PUTLONG(0, cp); /* TTL */ lenp = cp; cp += 2; /* Alg. */ if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { if (key->dk_alg != KEY_HMAC_MD5) return (-ns_r_badkey); n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL); } else n = dn_comp("", cp, eob - cp, NULL, NULL); if (n < 0) return (NS_TSIG_ERROR_NO_SPACE); alg = cp; cp += n; /* Time. */ BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); PUTSHORT(0, cp); timesigned = time(NULL); if (error != ns_r_badtime) PUTLONG(timesigned, cp); else PUTLONG(in_timesigned, cp); PUTSHORT(NS_TSIG_FUDGE, cp); /* Compute the signature. */ if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { void *ctx; - u_char buf[MAXDNAME], *cp2; + u_char buf[NS_MAXCDNAME], *cp2; int n; dst_sign_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0); /* Digest the query signature, if this is a response. */ if (querysiglen > 0 && querysig != NULL) { u_int16_t len_n = htons(querysiglen); dst_sign_data(SIG_MODE_UPDATE, key, &ctx, (u_char *)&len_n, INT16SZ, NULL, 0); dst_sign_data(SIG_MODE_UPDATE, key, &ctx, querysig, querysiglen, NULL, 0); } /* Digest the message. */ dst_sign_data(SIG_MODE_UPDATE, key, &ctx, msg, *msglen, NULL, 0); /* Digest the key name. */ n = ns_name_ntol(name, buf, sizeof(buf)); + INSIST(n > 0); dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); /* Digest the class and TTL. */ cp2 = buf; PUTSHORT(ns_c_any, cp2); PUTLONG(0, cp2); dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf, NULL, 0); /* Digest the algorithm. */ n = ns_name_ntol(alg, buf, sizeof(buf)); + INSIST(n > 0); dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); /* Digest the time signed, fudge, error, and other data */ cp2 = buf; PUTSHORT(0, cp2); /* Top 16 bits of time */ if (error != ns_r_badtime) PUTLONG(timesigned, cp2); else PUTLONG(in_timesigned, cp2); PUTSHORT(NS_TSIG_FUDGE, cp2); PUTSHORT(error, cp2); /* Error */ if (error != ns_r_badtime) PUTSHORT(0, cp2); /* Other data length */ else { PUTSHORT(INT16SZ+INT32SZ, cp2); /* Other data length */ PUTSHORT(0, cp2); /* Top 16 bits of time */ PUTLONG(timesigned, cp2); } dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf, NULL, 0); n = dst_sign_data(SIG_MODE_FINAL, key, &ctx, NULL, 0, sig, *siglen); if (n < 0) return (-ns_r_badkey); *siglen = n; } else *siglen = 0; /* Add the signature. */ BOUNDS_CHECK(cp, INT16SZ + (*siglen)); PUTSHORT(*siglen, cp); memcpy(cp, sig, *siglen); cp += (*siglen); /* The original message ID & error. */ BOUNDS_CHECK(cp, INT16SZ + INT16SZ); PUTSHORT(ntohs(hp->id), cp); /* already in network order */ PUTSHORT(error, cp); /* Other data. */ BOUNDS_CHECK(cp, INT16SZ); if (error != ns_r_badtime) PUTSHORT(0, cp); /* Other data length */ else { PUTSHORT(INT16SZ+INT32SZ, cp); /* Other data length */ BOUNDS_CHECK(cp, INT32SZ+INT16SZ); PUTSHORT(0, cp); /* Top 16 bits of time */ PUTLONG(timesigned, cp); } /* Go back and fill in the length. */ PUTSHORT(cp - lenp - INT16SZ, lenp); hp->arcount = htons(ntohs(hp->arcount) + 1); *msglen = (cp - msg); return (0); } int ns_sign_tcp_init(void *k, const u_char *querysig, int querysiglen, ns_tcp_tsig_state *state) { dst_init(); if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0) return (-1); state->counter = -1; state->key = k; if (state->key->dk_alg != KEY_HMAC_MD5) return (-ns_r_badkey); if (querysiglen > (int)sizeof(state->sig)) return (-1); memcpy(state->sig, querysig, querysiglen); state->siglen = querysiglen; return (0); } int ns_sign_tcp(u_char *msg, int *msglen, int msgsize, int error, ns_tcp_tsig_state *state, int done) { return (ns_sign_tcp2(msg, msglen, msgsize, error, state, done, NULL, NULL)); } int ns_sign_tcp2(u_char *msg, int *msglen, int msgsize, int error, ns_tcp_tsig_state *state, int done, u_char **dnptrs, u_char **lastdnptr) { u_char *cp, *eob, *lenp; u_char buf[MAXDNAME], *cp2; HEADER *hp = (HEADER *)msg; time_t timesigned; int n; if (msg == NULL || msglen == NULL || state == NULL) return (-1); state->counter++; if (state->counter == 0) return (ns_sign2(msg, msglen, msgsize, error, state->key, state->sig, state->siglen, state->sig, &state->siglen, 0, dnptrs, lastdnptr)); if (state->siglen > 0) { u_int16_t siglen_n = htons(state->siglen); dst_sign_data(SIG_MODE_INIT, state->key, &state->ctx, NULL, 0, NULL, 0); dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, (u_char *)&siglen_n, INT16SZ, NULL, 0); dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, state->sig, state->siglen, NULL, 0); state->siglen = 0; } dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, msg, *msglen, NULL, 0); if (done == 0 && (state->counter % 100 != 0)) return (0); cp = msg + *msglen; eob = msg + msgsize; /* Name. */ n = dn_comp(state->key->dk_key_name, cp, eob - cp, dnptrs, lastdnptr); if (n < 0) return (NS_TSIG_ERROR_NO_SPACE); cp += n; /* Type, class, ttl, length (not filled in yet). */ BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ); PUTSHORT(ns_t_tsig, cp); PUTSHORT(ns_c_any, cp); PUTLONG(0, cp); /* TTL */ lenp = cp; cp += 2; /* Alg. */ n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL); if (n < 0) return (NS_TSIG_ERROR_NO_SPACE); cp += n; /* Time. */ BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); PUTSHORT(0, cp); timesigned = time(NULL); PUTLONG(timesigned, cp); PUTSHORT(NS_TSIG_FUDGE, cp); /* * Compute the signature. */ /* Digest the time signed and fudge. */ cp2 = buf; PUTSHORT(0, cp2); /* Top 16 bits of time */ PUTLONG(timesigned, cp2); PUTSHORT(NS_TSIG_FUDGE, cp2); dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, buf, cp2 - buf, NULL, 0); n = dst_sign_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0, state->sig, sizeof(state->sig)); if (n < 0) return (-ns_r_badkey); state->siglen = n; /* Add the signature. */ BOUNDS_CHECK(cp, INT16SZ + state->siglen); PUTSHORT(state->siglen, cp); memcpy(cp, state->sig, state->siglen); cp += state->siglen; /* The original message ID & error. */ BOUNDS_CHECK(cp, INT16SZ + INT16SZ); PUTSHORT(ntohs(hp->id), cp); /* already in network order */ PUTSHORT(error, cp); /* Other data. */ BOUNDS_CHECK(cp, INT16SZ); PUTSHORT(0, cp); /* Go back and fill in the length. */ PUTSHORT(cp - lenp - INT16SZ, lenp); hp->arcount = htons(ntohs(hp->arcount) + 1); *msglen = (cp - msg); return (0); } diff --git a/contrib/bind/lib/resolv/res_comp.c b/contrib/bind/lib/resolv/res_comp.c index d9728482e788..15de91a7b1bc 100644 --- a/contrib/bind/lib/resolv/res_comp.c +++ b/contrib/bind/lib/resolv/res_comp.c @@ -1,251 +1,251 @@ /* * Copyright (c) 1985, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "@(#)res_comp.c 8.1 (Berkeley) 6/4/93"; -static const char rcsid[] = "$Id: res_comp.c,v 8.15 1999/10/13 16:39:39 vixie Exp $"; +static const char rcsid[] = "$Id: res_comp.c,v 8.16 2002/08/07 03:47:34 marka Exp $"; #endif /* LIBC_SCCS and not lint */ #include "port_before.h" #include #include #include #include #include #include #include #include #include #include "port_after.h" /* - * Expand compressed domain name 'comp_dn' to full domain name. + * Expand compressed domain name 'src' to full domain name. * 'msg' is a pointer to the begining of the message, - * 'eomorig' points to the first location after the message, - * 'exp_dn' is a pointer to a buffer of size 'length' for the result. + * 'eom' points to the first location after the message, + * 'dst' is a pointer to a buffer of size 'dstsiz' for the result. * Return size of compressed name or -1 if there was an error. */ int dn_expand(const u_char *msg, const u_char *eom, const u_char *src, char *dst, int dstsiz) { int n = ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz); if (n > 0 && dst[0] == '.') dst[0] = '\0'; return (n); } /* * Pack domain name 'exp_dn' in presentation form into 'comp_dn'. * Return the size of the compressed name or -1. * 'length' is the size of the array pointed to by 'comp_dn'. */ int dn_comp(const char *src, u_char *dst, int dstsiz, u_char **dnptrs, u_char **lastdnptr) { return (ns_name_compress(src, dst, (size_t)dstsiz, (const u_char **)dnptrs, (const u_char **)lastdnptr)); } /* * Skip over a compressed domain name. Return the size or -1. */ int dn_skipname(const u_char *ptr, const u_char *eom) { const u_char *saveptr = ptr; if (ns_name_skip(&ptr, eom) == -1) return (-1); return (ptr - saveptr); } /* * Verify that a domain name uses an acceptable character set. */ /* * Note the conspicuous absence of ctype macros in these definitions. On * non-ASCII hosts, we can't depend on string literals or ctype macros to * tell us anything about network-format data. The rest of the BIND system * is not careful about this, but for some reason, we're doing it right here. */ #define PERIOD 0x2e #define hyphenchar(c) ((c) == 0x2d) #define bslashchar(c) ((c) == 0x5c) #define periodchar(c) ((c) == PERIOD) #define asterchar(c) ((c) == 0x2a) #define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \ || ((c) >= 0x61 && (c) <= 0x7a)) #define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) #define borderchar(c) (alphachar(c) || digitchar(c)) #define middlechar(c) (borderchar(c) || hyphenchar(c)) #define domainchar(c) ((c) > 0x20 && (c) < 0x7f) int res_hnok(const char *dn) { int ppch = '\0', pch = PERIOD, ch = *dn++; while (ch != '\0') { int nch = *dn++; if (periodchar(ch)) { (void)NULL; } else if (periodchar(pch)) { if (!borderchar(ch)) return (0); } else if (periodchar(nch) || nch == '\0') { if (!borderchar(ch)) return (0); } else { if (!middlechar(ch)) return (0); } ppch = pch, pch = ch, ch = nch; } return (1); } /* * hostname-like (A, MX, WKS) owners can have "*" as their first label * but must otherwise be as a host name. */ int res_ownok(const char *dn) { if (asterchar(dn[0])) { if (periodchar(dn[1])) return (res_hnok(dn+2)); if (dn[1] == '\0') return (1); } return (res_hnok(dn)); } /* * SOA RNAMEs and RP RNAMEs can have any printable character in their first * label, but the rest of the name has to look like a host name. */ int res_mailok(const char *dn) { int ch, escaped = 0; /* "." is a valid missing representation */ if (*dn == '\0') return (1); /* otherwise