1238104Sdes/* zone.c
2238104Sdes *
3238104Sdes * Functions for ldns_zone structure
4238104Sdes * a Net::DNS like library for C
5238104Sdes *
6238104Sdes * (c) NLnet Labs, 2005-2006
7238104Sdes * See the file LICENSE for the license
8238104Sdes */
9238104Sdes#include <ldns/config.h>
10238104Sdes
11238104Sdes#include <ldns/ldns.h>
12238104Sdes
13238104Sdes#include <strings.h>
14238104Sdes#include <limits.h>
15238104Sdes
16238104Sdesldns_rr *
17238104Sdesldns_zone_soa(const ldns_zone *z)
18238104Sdes{
19238104Sdes        return z->_soa;
20238104Sdes}
21238104Sdes
22238104Sdessize_t
23238104Sdesldns_zone_rr_count(const ldns_zone *z)
24238104Sdes{
25238104Sdes	return ldns_rr_list_rr_count(z->_rrs);
26238104Sdes}
27238104Sdes
28238104Sdesvoid
29238104Sdesldns_zone_set_soa(ldns_zone *z, ldns_rr *soa)
30238104Sdes{
31238104Sdes	z->_soa = soa;
32238104Sdes}
33238104Sdes
34238104Sdesldns_rr_list *
35238104Sdesldns_zone_rrs(const ldns_zone *z)
36238104Sdes{
37238104Sdes	return z->_rrs;
38238104Sdes}
39238104Sdes
40238104Sdesvoid
41238104Sdesldns_zone_set_rrs(ldns_zone *z, ldns_rr_list *rrlist)
42238104Sdes{
43238104Sdes	z->_rrs = rrlist;
44238104Sdes}
45238104Sdes
46238104Sdesbool
47238104Sdesldns_zone_push_rr_list(ldns_zone *z, ldns_rr_list *list)
48238104Sdes{
49238104Sdes	return ldns_rr_list_cat(ldns_zone_rrs(z), list);
50238104Sdes
51238104Sdes}
52238104Sdes
53238104Sdesbool
54238104Sdesldns_zone_push_rr(ldns_zone *z, ldns_rr *rr)
55238104Sdes{
56238104Sdes	return ldns_rr_list_push_rr( ldns_zone_rrs(z), rr);
57238104Sdes}
58238104Sdes
59238104Sdes
60238104Sdes/*
61238104Sdes * Get the list of glue records in a zone
62238104Sdes * XXX: there should be a way for this to return error, other than NULL,
63238104Sdes *      since NULL is a valid return
64238104Sdes */
65238104Sdesldns_rr_list *
66238104Sdesldns_zone_glue_rr_list(const ldns_zone *z)
67238104Sdes{
68238104Sdes	/* when do we find glue? It means we find an IP address
69238104Sdes	 * (AAAA/A) for a nameserver listed in the zone
70238104Sdes	 *
71238104Sdes	 * Alg used here:
72238104Sdes	 * first find all the zonecuts (NS records)
73238104Sdes	 * find all the AAAA or A records (can be done it the
74238104Sdes	 * above loop).
75238104Sdes	 *
76238104Sdes	 * Check if the aaaa/a list are subdomains under the
77238104Sdes	 * NS domains.
78238104Sdes	 * If yes -> glue, if no -> not glue
79238104Sdes	 */
80238104Sdes
81238104Sdes	ldns_rr_list *zone_cuts;
82238104Sdes	ldns_rr_list *addr;
83238104Sdes	ldns_rr_list *glue;
84238104Sdes	ldns_rr *r, *ns, *a;
85238104Sdes	ldns_rdf *dname_a, *ns_owner;
86238104Sdes	size_t i,j;
87238104Sdes
88238104Sdes	zone_cuts = NULL;
89238104Sdes	addr = NULL;
90238104Sdes	glue = NULL;
91238104Sdes
92238104Sdes	/* we cannot determine glue in a 'zone' without a SOA */
93238104Sdes	if (!ldns_zone_soa(z)) {
94238104Sdes		return NULL;
95238104Sdes	}
96238104Sdes
97238104Sdes	zone_cuts = ldns_rr_list_new();
98238104Sdes	if (!zone_cuts) goto memory_error;
99238104Sdes	addr = ldns_rr_list_new();
100238104Sdes	if (!addr) goto memory_error;
101238104Sdes	glue = ldns_rr_list_new();
102238104Sdes	if (!glue) goto memory_error;
103238104Sdes
104238104Sdes	for(i = 0; i < ldns_zone_rr_count(z); i++) {
105238104Sdes		r = ldns_rr_list_rr(ldns_zone_rrs(z), i);
106238104Sdes		if (ldns_rr_get_type(r) == LDNS_RR_TYPE_A ||
107238104Sdes				ldns_rr_get_type(r) == LDNS_RR_TYPE_AAAA) {
108238104Sdes			/* possibly glue */
109238104Sdes			if (!ldns_rr_list_push_rr(addr, r)) goto memory_error;
110238104Sdes			continue;
111238104Sdes		}
112238104Sdes		if (ldns_rr_get_type(r) == LDNS_RR_TYPE_NS) {
113238104Sdes			/* multiple zones will end up here -
114238104Sdes			 * for now; not a problem
115238104Sdes			 */
116238104Sdes			/* don't add NS records for the current zone itself */
117238104Sdes			if (ldns_rdf_compare(ldns_rr_owner(r),
118238104Sdes						ldns_rr_owner(ldns_zone_soa(z))) != 0) {
119238104Sdes				if (!ldns_rr_list_push_rr(zone_cuts, r)) goto memory_error;
120238104Sdes			}
121238104Sdes			continue;
122238104Sdes		}
123238104Sdes	}
124238104Sdes
125238104Sdes	/* will sorting make it quicker ?? */
126238104Sdes	for(i = 0; i < ldns_rr_list_rr_count(zone_cuts); i++) {
127238104Sdes		ns = ldns_rr_list_rr(zone_cuts, i);
128238104Sdes		ns_owner = ldns_rr_owner(ns);
129238104Sdes
130238104Sdes		for(j = 0; j < ldns_rr_list_rr_count(addr); j++) {
131238104Sdes			a = ldns_rr_list_rr(addr, j);
132238104Sdes			dname_a = ldns_rr_owner(a);
133238104Sdes
134238104Sdes			if (ldns_dname_is_subdomain(dname_a, ns_owner) ||
135238104Sdes				ldns_dname_compare(dname_a, ns_owner) == 0) {
136238104Sdes				/* GLUE! */
137238104Sdes				if (!ldns_rr_list_push_rr(glue, a)) goto memory_error;
138238104Sdes			}
139238104Sdes		}
140238104Sdes	}
141238104Sdes
142238104Sdes	ldns_rr_list_free(addr);
143238104Sdes	ldns_rr_list_free(zone_cuts);
144238104Sdes
145238104Sdes	if (ldns_rr_list_rr_count(glue) == 0) {
146238104Sdes		ldns_rr_list_free(glue);
147238104Sdes		return NULL;
148238104Sdes	} else {
149238104Sdes		return glue;
150238104Sdes	}
151238104Sdes
152238104Sdesmemory_error:
153238104Sdes	if (zone_cuts) {
154238104Sdes		LDNS_FREE(zone_cuts);
155238104Sdes	}
156238104Sdes	if (addr) {
157238104Sdes		ldns_rr_list_free(addr);
158238104Sdes	}
159238104Sdes	if (glue) {
160238104Sdes		ldns_rr_list_free(glue);
161238104Sdes	}
162238104Sdes	return NULL;
163238104Sdes}
164238104Sdes
165238104Sdesldns_zone *
166238104Sdesldns_zone_new(void)
167238104Sdes{
168238104Sdes	ldns_zone *z;
169238104Sdes
170238104Sdes	z = LDNS_MALLOC(ldns_zone);
171238104Sdes	if (!z) {
172238104Sdes		return NULL;
173238104Sdes	}
174238104Sdes
175238104Sdes	z->_rrs = ldns_rr_list_new();
176238104Sdes	if (!z->_rrs) {
177238104Sdes		LDNS_FREE(z);
178238104Sdes		return NULL;
179238104Sdes	}
180238104Sdes	ldns_zone_set_soa(z, NULL);
181238104Sdes	return z;
182238104Sdes}
183238104Sdes
184238104Sdes/* we regocnize:
185238104Sdes * $TTL, $ORIGIN
186238104Sdes */
187238104Sdesldns_status
188238104Sdesldns_zone_new_frm_fp(ldns_zone **z, FILE *fp, ldns_rdf *origin, uint32_t ttl, ldns_rr_class c)
189238104Sdes{
190238104Sdes	return ldns_zone_new_frm_fp_l(z, fp, origin, ttl, c, NULL);
191238104Sdes}
192238104Sdes
193238104Sdes/* XXX: class is never used */
194238104Sdesldns_status
195238104Sdesldns_zone_new_frm_fp_l(ldns_zone **z, FILE *fp, ldns_rdf *origin, uint32_t ttl,
196238104Sdes        ldns_rr_class ATTR_UNUSED(c), int *line_nr)
197238104Sdes{
198238104Sdes	ldns_zone *newzone;
199238104Sdes	ldns_rr *rr;
200238104Sdes	uint32_t my_ttl;
201238104Sdes	ldns_rdf *my_origin;
202238104Sdes	ldns_rdf *my_prev;
203238104Sdes	bool soa_seen = false; 	/* 2 soa are an error */
204238104Sdes	ldns_status s;
205238104Sdes	ldns_status ret;
206238104Sdes
207238104Sdes	/* most cases of error are memory problems */
208238104Sdes	ret = LDNS_STATUS_MEM_ERR;
209238104Sdes
210238104Sdes	newzone = NULL;
211238104Sdes	my_origin = NULL;
212238104Sdes	my_prev = NULL;
213238104Sdes
214238104Sdes	my_ttl    = ttl;
215238104Sdes
216238104Sdes	if (origin) {
217238104Sdes		my_origin = ldns_rdf_clone(origin);
218238104Sdes		if (!my_origin) goto error;
219238104Sdes		/* also set the prev */
220238104Sdes		my_prev   = ldns_rdf_clone(origin);
221238104Sdes		if (!my_prev) goto error;
222238104Sdes	}
223238104Sdes
224238104Sdes	newzone = ldns_zone_new();
225238104Sdes	if (!newzone) goto error;
226238104Sdes
227238104Sdes	while(!feof(fp)) {
228238104Sdes		s = ldns_rr_new_frm_fp_l(&rr, fp, &my_ttl, &my_origin, &my_prev, line_nr);
229238104Sdes		switch (s) {
230238104Sdes		case LDNS_STATUS_OK:
231238104Sdes			if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) {
232238104Sdes				if (soa_seen) {
233238104Sdes					/* second SOA
234238104Sdes					 * just skip, maybe we want to say
235238104Sdes					 * something??? */
236238104Sdes					ldns_rr_free(rr);
237238104Sdes					continue;
238238104Sdes				}
239238104Sdes				soa_seen = true;
240238104Sdes				ldns_zone_set_soa(newzone, rr);
241238104Sdes				/* set origin to soa if not specified */
242238104Sdes				if (!my_origin) {
243238104Sdes					my_origin = ldns_rdf_clone(ldns_rr_owner(rr));
244238104Sdes				}
245238104Sdes				continue;
246238104Sdes			}
247238104Sdes
248238104Sdes			/* a normal RR - as sofar the DNS is normal */
249238104Sdes			if (!ldns_zone_push_rr(newzone, rr)) goto error;
250238104Sdes
251238104Sdes		case LDNS_STATUS_SYNTAX_EMPTY:
252238104Sdes			/* empty line was seen */
253238104Sdes		case LDNS_STATUS_SYNTAX_TTL:
254238104Sdes			/* the function set the ttl */
255238104Sdes			break;
256238104Sdes		case LDNS_STATUS_SYNTAX_ORIGIN:
257238104Sdes			/* the function set the origin */
258238104Sdes			break;
259238104Sdes		case LDNS_STATUS_SYNTAX_INCLUDE:
260238104Sdes			ret = LDNS_STATUS_SYNTAX_INCLUDE_ERR_NOTIMPL;
261238104Sdes			break;
262238104Sdes		default:
263238104Sdes			ret = s;
264238104Sdes			goto error;
265238104Sdes		}
266238104Sdes	}
267238104Sdes
268238104Sdes	if (my_origin) {
269238104Sdes		ldns_rdf_deep_free(my_origin);
270238104Sdes	}
271238104Sdes	if (my_prev) {
272238104Sdes		ldns_rdf_deep_free(my_prev);
273238104Sdes	}
274238104Sdes	if (z) {
275238104Sdes		*z = newzone;
276238104Sdes	} else {
277238104Sdes		ldns_zone_free(newzone);
278238104Sdes	}
279238104Sdes
280238104Sdes	return LDNS_STATUS_OK;
281238104Sdes
282238104Sdeserror:
283238104Sdes	if (my_origin) {
284238104Sdes		ldns_rdf_deep_free(my_origin);
285238104Sdes	}
286238104Sdes	if (my_prev) {
287238104Sdes		ldns_rdf_deep_free(my_prev);
288238104Sdes	}
289238104Sdes	if (newzone) {
290238104Sdes		ldns_zone_free(newzone);
291238104Sdes	}
292238104Sdes	return ret;
293238104Sdes}
294238104Sdes
295238104Sdesvoid
296238104Sdesldns_zone_sort(ldns_zone *zone)
297238104Sdes{
298238104Sdes	ldns_rr_list *zrr;
299238104Sdes	assert(zone != NULL);
300238104Sdes
301238104Sdes	zrr = ldns_zone_rrs(zone);
302238104Sdes	ldns_rr_list_sort(zrr);
303238104Sdes}
304238104Sdes
305238104Sdesvoid
306238104Sdesldns_zone_free(ldns_zone *zone)
307238104Sdes{
308238104Sdes	ldns_rr_list_free(zone->_rrs);
309238104Sdes	LDNS_FREE(zone);
310238104Sdes}
311238104Sdes
312238104Sdesvoid
313238104Sdesldns_zone_deep_free(ldns_zone *zone)
314238104Sdes{
315238104Sdes	ldns_rr_free(zone->_soa);
316238104Sdes	ldns_rr_list_deep_free(zone->_rrs);
317238104Sdes	LDNS_FREE(zone);
318238104Sdes}
319