1/*
2 * Copyright (C) 2004, 2005, 2007, 2008, 2010  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2002  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: rootns.c,v 1.40 2010/06/18 05:36:24 marka Exp $ */
19
20/*! \file */
21
22#include <config.h>
23
24#include <isc/buffer.h>
25#include <isc/string.h>		/* Required for HP/UX (and others?) */
26#include <isc/util.h>
27
28#include <dns/callbacks.h>
29#include <dns/db.h>
30#include <dns/dbiterator.h>
31#include <dns/fixedname.h>
32#include <dns/log.h>
33#include <dns/master.h>
34#include <dns/rdata.h>
35#include <dns/rdata.h>
36#include <dns/rdataset.h>
37#include <dns/rdatasetiter.h>
38#include <dns/rdatastruct.h>
39#include <dns/rdatatype.h>
40#include <dns/result.h>
41#include <dns/rootns.h>
42#include <dns/view.h>
43
44static char root_ns[] =
45";\n"
46"; Internet Root Nameservers\n"
47";\n"
48"$TTL 518400\n"
49".                       518400  IN      NS      A.ROOT-SERVERS.NET.\n"
50".                       518400  IN      NS      B.ROOT-SERVERS.NET.\n"
51".                       518400  IN      NS      C.ROOT-SERVERS.NET.\n"
52".                       518400  IN      NS      D.ROOT-SERVERS.NET.\n"
53".                       518400  IN      NS      E.ROOT-SERVERS.NET.\n"
54".                       518400  IN      NS      F.ROOT-SERVERS.NET.\n"
55".                       518400  IN      NS      G.ROOT-SERVERS.NET.\n"
56".                       518400  IN      NS      H.ROOT-SERVERS.NET.\n"
57".                       518400  IN      NS      I.ROOT-SERVERS.NET.\n"
58".                       518400  IN      NS      J.ROOT-SERVERS.NET.\n"
59".                       518400  IN      NS      K.ROOT-SERVERS.NET.\n"
60".                       518400  IN      NS      L.ROOT-SERVERS.NET.\n"
61".                       518400  IN      NS      M.ROOT-SERVERS.NET.\n"
62"A.ROOT-SERVERS.NET.     3600000 IN      A       198.41.0.4\n"
63"A.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:503:BA3E::2:30\n"
64"B.ROOT-SERVERS.NET.     3600000 IN      A       192.228.79.201\n"
65"C.ROOT-SERVERS.NET.     3600000 IN      A       192.33.4.12\n"
66"D.ROOT-SERVERS.NET.     3600000 IN      A       128.8.10.90\n"
67"E.ROOT-SERVERS.NET.     3600000 IN      A       192.203.230.10\n"
68"F.ROOT-SERVERS.NET.     3600000 IN      A       192.5.5.241\n"
69"F.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:500:2F::F\n"
70"G.ROOT-SERVERS.NET.     3600000 IN      A       192.112.36.4\n"
71"H.ROOT-SERVERS.NET.     3600000 IN      A       128.63.2.53\n"
72"H.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:500:1::803F:235\n"
73"I.ROOT-SERVERS.NET.     3600000 IN      A       192.36.148.17\n"
74"I.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:7fe::53\n"
75"J.ROOT-SERVERS.NET.     3600000 IN      A       192.58.128.30\n"
76"J.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:503:C27::2:30\n"
77"K.ROOT-SERVERS.NET.     3600000 IN      A       193.0.14.129\n"
78"K.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:7FD::1\n"
79"L.ROOT-SERVERS.NET.     3600000 IN      A       199.7.83.42\n"
80"L.ROOT-SERVERS.NET.     604800  IN      AAAA    2001:500:3::42\n"
81"M.ROOT-SERVERS.NET.     3600000 IN      A       202.12.27.33\n"
82"M.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:DC3::35\n";
83
84static isc_result_t
85in_rootns(dns_rdataset_t *rootns, dns_name_t *name) {
86	isc_result_t result;
87	dns_rdata_t rdata = DNS_RDATA_INIT;
88	dns_rdata_ns_t ns;
89
90	if (!dns_rdataset_isassociated(rootns))
91		return (ISC_R_NOTFOUND);
92
93	result = dns_rdataset_first(rootns);
94	while (result == ISC_R_SUCCESS) {
95		dns_rdataset_current(rootns, &rdata);
96		result = dns_rdata_tostruct(&rdata, &ns, NULL);
97		if (result != ISC_R_SUCCESS)
98			return (result);
99		if (dns_name_compare(name, &ns.name) == 0)
100			return (ISC_R_SUCCESS);
101		result = dns_rdataset_next(rootns);
102		dns_rdata_reset(&rdata);
103	}
104	if (result == ISC_R_NOMORE)
105		result = ISC_R_NOTFOUND;
106	return (result);
107}
108
109static isc_result_t
110check_node(dns_rdataset_t *rootns, dns_name_t *name,
111	   dns_rdatasetiter_t *rdsiter) {
112	isc_result_t result;
113	dns_rdataset_t rdataset;
114
115	dns_rdataset_init(&rdataset);
116	result = dns_rdatasetiter_first(rdsiter);
117	while (result == ISC_R_SUCCESS) {
118		dns_rdatasetiter_current(rdsiter, &rdataset);
119		switch (rdataset.type) {
120		case dns_rdatatype_a:
121		case dns_rdatatype_aaaa:
122			result = in_rootns(rootns, name);
123			if (result != ISC_R_SUCCESS)
124				goto cleanup;
125			break;
126		case dns_rdatatype_ns:
127			if (dns_name_compare(name, dns_rootname) == 0)
128				break;
129			/*FALLTHROUGH*/
130		default:
131			result = ISC_R_FAILURE;
132			goto cleanup;
133		}
134		dns_rdataset_disassociate(&rdataset);
135		result = dns_rdatasetiter_next(rdsiter);
136	}
137	if (result == ISC_R_NOMORE)
138		result = ISC_R_SUCCESS;
139 cleanup:
140	if (dns_rdataset_isassociated(&rdataset))
141		dns_rdataset_disassociate(&rdataset);
142	return (result);
143}
144
145static isc_result_t
146check_hints(dns_db_t *db) {
147	isc_result_t result;
148	dns_rdataset_t rootns;
149	dns_dbiterator_t *dbiter = NULL;
150	dns_dbnode_t *node = NULL;
151	isc_stdtime_t now;
152	dns_fixedname_t fixname;
153	dns_name_t *name;
154	dns_rdatasetiter_t *rdsiter = NULL;
155
156	isc_stdtime_get(&now);
157
158	dns_fixedname_init(&fixname);
159	name = dns_fixedname_name(&fixname);
160
161	dns_rdataset_init(&rootns);
162	(void)dns_db_find(db, dns_rootname, NULL, dns_rdatatype_ns, 0,
163			  now, NULL, name, &rootns, NULL);
164	result = dns_db_createiterator(db, 0, &dbiter);
165	if (result != ISC_R_SUCCESS)
166		goto cleanup;
167	result = dns_dbiterator_first(dbiter);
168	while (result == ISC_R_SUCCESS) {
169		result = dns_dbiterator_current(dbiter, &node, name);
170		if (result != ISC_R_SUCCESS)
171			goto cleanup;
172		result = dns_db_allrdatasets(db, node, NULL, now, &rdsiter);
173		if (result != ISC_R_SUCCESS)
174			goto cleanup;
175		result = check_node(&rootns, name, rdsiter);
176		if (result != ISC_R_SUCCESS)
177			goto cleanup;
178		dns_rdatasetiter_destroy(&rdsiter);
179		dns_db_detachnode(db, &node);
180		result = dns_dbiterator_next(dbiter);
181	}
182	if (result == ISC_R_NOMORE)
183		result = ISC_R_SUCCESS;
184
185 cleanup:
186	if (dns_rdataset_isassociated(&rootns))
187		dns_rdataset_disassociate(&rootns);
188	if (rdsiter != NULL)
189		dns_rdatasetiter_destroy(&rdsiter);
190	if (node != NULL)
191		dns_db_detachnode(db, &node);
192	if (dbiter != NULL)
193		dns_dbiterator_destroy(&dbiter);
194	return (result);
195}
196
197isc_result_t
198dns_rootns_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
199		  const char *filename, dns_db_t **target)
200{
201	isc_result_t result, eresult;
202	isc_buffer_t source;
203	size_t len;
204	dns_rdatacallbacks_t callbacks;
205	dns_db_t *db = NULL;
206
207	REQUIRE(target != NULL && *target == NULL);
208
209	result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
210			       rdclass, 0, NULL, &db);
211	if (result != ISC_R_SUCCESS)
212		return (result);
213
214	dns_rdatacallbacks_init(&callbacks);
215
216	len = strlen(root_ns);
217	isc_buffer_init(&source, root_ns, len);
218	isc_buffer_add(&source, len);
219
220	result = dns_db_beginload(db, &callbacks.add,
221				  &callbacks.add_private);
222	if (result != ISC_R_SUCCESS)
223		return (result);
224	if (filename != NULL) {
225		/*
226		 * Load the hints from the specified filename.
227		 */
228		result = dns_master_loadfile(filename, &db->origin,
229					     &db->origin, db->rdclass,
230					     DNS_MASTER_HINT,
231					     &callbacks, db->mctx);
232	} else if (rdclass == dns_rdataclass_in) {
233		/*
234		 * Default to using the Internet root servers.
235		 */
236		result = dns_master_loadbuffer(&source, &db->origin,
237					       &db->origin, db->rdclass,
238					       DNS_MASTER_HINT,
239					       &callbacks, db->mctx);
240	} else
241		result = ISC_R_NOTFOUND;
242	eresult = dns_db_endload(db, &callbacks.add_private);
243	if (result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE)
244		result = eresult;
245	if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
246		goto db_detach;
247	if (check_hints(db) != ISC_R_SUCCESS)
248		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
249			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
250			      "extra data in root hints '%s'",
251			      (filename != NULL) ? filename : "<BUILT-IN>");
252	*target = db;
253	return (ISC_R_SUCCESS);
254
255 db_detach:
256	dns_db_detach(&db);
257
258	return (result);
259}
260
261static void
262report(dns_view_t *view, dns_name_t *name, isc_boolean_t missing,
263       dns_rdata_t *rdata)
264{
265	const char *viewname = "", *sep = "";
266	char namebuf[DNS_NAME_FORMATSIZE];
267	char typebuf[DNS_RDATATYPE_FORMATSIZE];
268	char databuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
269	isc_buffer_t buffer;
270	isc_result_t result;
271
272	if (strcmp(view->name, "_bind") != 0 &&
273	    strcmp(view->name, "_default") != 0) {
274		viewname = view->name;
275		sep = ": view ";
276	}
277
278	dns_name_format(name, namebuf, sizeof(namebuf));
279	dns_rdatatype_format(rdata->type, typebuf, sizeof(typebuf));
280	isc_buffer_init(&buffer, databuf, sizeof(databuf) - 1);
281	result = dns_rdata_totext(rdata, NULL, &buffer);
282	RUNTIME_CHECK(result == ISC_R_SUCCESS);
283	databuf[isc_buffer_usedlength(&buffer)] = '\0';
284
285	if (missing)
286		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
287			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
288			      "checkhints%s%s: %s/%s (%s) missing from hints",
289			      sep, viewname, namebuf, typebuf, databuf);
290	else
291		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
292			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
293			      "checkhints%s%s: %s/%s (%s) extra record "
294			      "in hints", sep, viewname, namebuf, typebuf,
295			      databuf);
296}
297
298static isc_boolean_t
299inrrset(dns_rdataset_t *rrset, dns_rdata_t *rdata) {
300	isc_result_t result;
301	dns_rdata_t current = DNS_RDATA_INIT;
302
303	result = dns_rdataset_first(rrset);
304	while (result == ISC_R_SUCCESS) {
305		dns_rdataset_current(rrset, &current);
306		if (dns_rdata_compare(rdata, &current) == 0)
307			return (ISC_TRUE);
308		dns_rdata_reset(&current);
309		result = dns_rdataset_next(rrset);
310	}
311	return (ISC_FALSE);
312}
313
314/*
315 * Check that the address RRsets match.
316 *
317 * Note we don't complain about missing glue records.
318 */
319
320static void
321check_address_records(dns_view_t *view, dns_db_t *hints, dns_db_t *db,
322		      dns_name_t *name, isc_stdtime_t now)
323{
324	isc_result_t hresult, rresult, result;
325	dns_rdataset_t hintrrset, rootrrset;
326	dns_rdata_t rdata = DNS_RDATA_INIT;
327	dns_name_t *foundname;
328	dns_fixedname_t fixed;
329
330	dns_rdataset_init(&hintrrset);
331	dns_rdataset_init(&rootrrset);
332	dns_fixedname_init(&fixed);
333	foundname = dns_fixedname_name(&fixed);
334
335	hresult = dns_db_find(hints, name, NULL, dns_rdatatype_a, 0,
336			      now, NULL, foundname, &hintrrset, NULL);
337	rresult = dns_db_find(db, name, NULL, dns_rdatatype_a,
338			      DNS_DBFIND_GLUEOK, now, NULL, foundname,
339			      &rootrrset, NULL);
340	if (hresult == ISC_R_SUCCESS &&
341	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE)) {
342		result = dns_rdataset_first(&rootrrset);
343		while (result == ISC_R_SUCCESS) {
344			dns_rdata_reset(&rdata);
345			dns_rdataset_current(&rootrrset, &rdata);
346			if (!inrrset(&hintrrset, &rdata))
347				report(view, name, ISC_TRUE, &rdata);
348			result = dns_rdataset_next(&rootrrset);
349		}
350		result = dns_rdataset_first(&hintrrset);
351		while (result == ISC_R_SUCCESS) {
352			dns_rdata_reset(&rdata);
353			dns_rdataset_current(&hintrrset, &rdata);
354			if (!inrrset(&rootrrset, &rdata))
355				report(view, name, ISC_FALSE, &rdata);
356			result = dns_rdataset_next(&hintrrset);
357		}
358	}
359	if (hresult == ISC_R_NOTFOUND &&
360	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE)) {
361		result = dns_rdataset_first(&rootrrset);
362		while (result == ISC_R_SUCCESS) {
363			dns_rdata_reset(&rdata);
364			dns_rdataset_current(&rootrrset, &rdata);
365			report(view, name, ISC_TRUE, &rdata);
366			result = dns_rdataset_next(&rootrrset);
367		}
368	}
369	if (dns_rdataset_isassociated(&rootrrset))
370		dns_rdataset_disassociate(&rootrrset);
371	if (dns_rdataset_isassociated(&hintrrset))
372		dns_rdataset_disassociate(&hintrrset);
373
374	/*
375	 * Check AAAA records.
376	 */
377	hresult = dns_db_find(hints, name, NULL, dns_rdatatype_aaaa, 0,
378			      now, NULL, foundname, &hintrrset, NULL);
379	rresult = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
380			      DNS_DBFIND_GLUEOK, now, NULL, foundname,
381			      &rootrrset, NULL);
382	if (hresult == ISC_R_SUCCESS &&
383	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE)) {
384		result = dns_rdataset_first(&rootrrset);
385		while (result == ISC_R_SUCCESS) {
386			dns_rdata_reset(&rdata);
387			dns_rdataset_current(&rootrrset, &rdata);
388			if (!inrrset(&hintrrset, &rdata))
389				report(view, name, ISC_TRUE, &rdata);
390			dns_rdata_reset(&rdata);
391			result = dns_rdataset_next(&rootrrset);
392		}
393		result = dns_rdataset_first(&hintrrset);
394		while (result == ISC_R_SUCCESS) {
395			dns_rdata_reset(&rdata);
396			dns_rdataset_current(&hintrrset, &rdata);
397			if (!inrrset(&rootrrset, &rdata))
398				report(view, name, ISC_FALSE, &rdata);
399			dns_rdata_reset(&rdata);
400			result = dns_rdataset_next(&hintrrset);
401		}
402	}
403	if (hresult == ISC_R_NOTFOUND &&
404	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE)) {
405		result = dns_rdataset_first(&rootrrset);
406		while (result == ISC_R_SUCCESS) {
407			dns_rdata_reset(&rdata);
408			dns_rdataset_current(&rootrrset, &rdata);
409			report(view, name, ISC_TRUE, &rdata);
410			dns_rdata_reset(&rdata);
411			result = dns_rdataset_next(&rootrrset);
412		}
413	}
414	if (dns_rdataset_isassociated(&rootrrset))
415		dns_rdataset_disassociate(&rootrrset);
416	if (dns_rdataset_isassociated(&hintrrset))
417		dns_rdataset_disassociate(&hintrrset);
418}
419
420void
421dns_root_checkhints(dns_view_t *view, dns_db_t *hints, dns_db_t *db) {
422	isc_result_t result;
423	dns_rdata_t rdata = DNS_RDATA_INIT;
424	dns_rdata_ns_t ns;
425	dns_rdataset_t hintns, rootns;
426	const char *viewname = "", *sep = "";
427	isc_stdtime_t now;
428	dns_name_t *name;
429	dns_fixedname_t fixed;
430
431	REQUIRE(hints != NULL);
432	REQUIRE(db != NULL);
433	REQUIRE(view != NULL);
434
435	isc_stdtime_get(&now);
436
437	if (strcmp(view->name, "_bind") != 0 &&
438	    strcmp(view->name, "_default") != 0) {
439		viewname = view->name;
440		sep = ": view ";
441	}
442
443	dns_rdataset_init(&hintns);
444	dns_rdataset_init(&rootns);
445	dns_fixedname_init(&fixed);
446	name = dns_fixedname_name(&fixed);
447
448	result = dns_db_find(hints, dns_rootname, NULL, dns_rdatatype_ns, 0,
449			     now, NULL, name, &hintns, NULL);
450	if (result != ISC_R_SUCCESS) {
451		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
452			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
453			      "checkhints%s%s: unable to get root NS rrset "
454			      "from hints: %s", sep, viewname,
455			      dns_result_totext(result));
456		goto cleanup;
457	}
458
459	result = dns_db_find(db, dns_rootname, NULL, dns_rdatatype_ns, 0,
460			     now, NULL, name, &rootns, NULL);
461	if (result != ISC_R_SUCCESS) {
462		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
463			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
464			      "checkhints%s%s: unable to get root NS rrset "
465			      "from cache: %s", sep, viewname,
466			      dns_result_totext(result));
467		goto cleanup;
468	}
469
470	/*
471	 * Look for missing root NS names.
472	 */
473	result = dns_rdataset_first(&rootns);
474	while (result == ISC_R_SUCCESS) {
475		dns_rdataset_current(&rootns, &rdata);
476		result = dns_rdata_tostruct(&rdata, &ns, NULL);
477		RUNTIME_CHECK(result == ISC_R_SUCCESS);
478		result = in_rootns(&hintns, &ns.name);
479		if (result != ISC_R_SUCCESS) {
480			char namebuf[DNS_NAME_FORMATSIZE];
481			/* missing from hints */
482			dns_name_format(&ns.name, namebuf, sizeof(namebuf));
483			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
484				      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
485				      "checkhints%s%s: unable to find root "
486				      "NS '%s' in hints", sep, viewname,
487				      namebuf);
488		} else
489			check_address_records(view, hints, db, &ns.name, now);
490		dns_rdata_reset(&rdata);
491		result = dns_rdataset_next(&rootns);
492	}
493	if (result != ISC_R_NOMORE) {
494		goto cleanup;
495	}
496
497	/*
498	 * Look for extra root NS names.
499	 */
500	result = dns_rdataset_first(&hintns);
501	while (result == ISC_R_SUCCESS) {
502		dns_rdataset_current(&hintns, &rdata);
503		result = dns_rdata_tostruct(&rdata, &ns, NULL);
504		RUNTIME_CHECK(result == ISC_R_SUCCESS);
505		result = in_rootns(&rootns, &ns.name);
506		if (result != ISC_R_SUCCESS) {
507			char namebuf[DNS_NAME_FORMATSIZE];
508			/* extra entry in hints */
509			dns_name_format(&ns.name, namebuf, sizeof(namebuf));
510			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
511				      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
512				      "checkhints%s%s: extra NS '%s' in hints",
513				      sep, viewname, namebuf);
514		}
515		dns_rdata_reset(&rdata);
516		result = dns_rdataset_next(&hintns);
517	}
518	if (result != ISC_R_NOMORE) {
519		goto cleanup;
520	}
521
522 cleanup:
523	if (dns_rdataset_isassociated(&rootns))
524		dns_rdataset_disassociate(&rootns);
525	if (dns_rdataset_isassociated(&hintns))
526		dns_rdataset_disassociate(&hintns);
527}
528