1/* zone.c
2 *
3 * Functions for ldns_zone structure
4 * a Net::DNS like library for C
5 *
6 * (c) NLnet Labs, 2005-2006
7 * See the file LICENSE for the license
8 */
9#include <ldns/config.h>
10
11#include <ldns/ldns.h>
12
13#include <strings.h>
14#include <limits.h>
15
16ldns_rr *
17ldns_zone_soa(const ldns_zone *z)
18{
19        return z->_soa;
20}
21
22size_t
23ldns_zone_rr_count(const ldns_zone *z)
24{
25	return ldns_rr_list_rr_count(z->_rrs);
26}
27
28void
29ldns_zone_set_soa(ldns_zone *z, ldns_rr *soa)
30{
31	z->_soa = soa;
32}
33
34ldns_rr_list *
35ldns_zone_rrs(const ldns_zone *z)
36{
37	return z->_rrs;
38}
39
40void
41ldns_zone_set_rrs(ldns_zone *z, ldns_rr_list *rrlist)
42{
43	z->_rrs = rrlist;
44}
45
46bool
47ldns_zone_push_rr_list(ldns_zone *z, const ldns_rr_list *list)
48{
49	return ldns_rr_list_cat(ldns_zone_rrs(z), list);
50}
51
52bool
53ldns_zone_push_rr(ldns_zone *z, ldns_rr *rr)
54{
55	return ldns_rr_list_push_rr(ldns_zone_rrs(z), rr);
56}
57
58
59/*
60 * Get the list of glue records in a zone
61 * XXX: there should be a way for this to return error, other than NULL,
62 *      since NULL is a valid return
63 */
64ldns_rr_list *
65ldns_zone_glue_rr_list(const ldns_zone *z)
66{
67	/* when do we find glue? It means we find an IP address
68	 * (AAAA/A) for a nameserver listed in the zone
69	 *
70	 * Alg used here:
71	 * first find all the zonecuts (NS records)
72	 * find all the AAAA or A records (can be done it the
73	 * above loop).
74	 *
75	 * Check if the aaaa/a list are subdomains under the
76	 * NS domains.
77	 * If yes -> glue, if no -> not glue
78	 */
79
80	ldns_rr_list *zone_cuts;
81	ldns_rr_list *addr;
82	ldns_rr_list *glue;
83	ldns_rr *r, *ns, *a;
84	ldns_rdf *dname_a, *ns_owner;
85	size_t i,j;
86
87	zone_cuts = NULL;
88	addr = NULL;
89	glue = NULL;
90
91	/* we cannot determine glue in a 'zone' without a SOA */
92	if (!ldns_zone_soa(z)) {
93		return NULL;
94	}
95
96	zone_cuts = ldns_rr_list_new();
97	if (!zone_cuts) goto memory_error;
98	addr = ldns_rr_list_new();
99	if (!addr) goto memory_error;
100	glue = ldns_rr_list_new();
101	if (!glue) goto memory_error;
102
103	for(i = 0; i < ldns_zone_rr_count(z); i++) {
104		r = ldns_rr_list_rr(ldns_zone_rrs(z), i);
105		if (ldns_rr_get_type(r) == LDNS_RR_TYPE_A ||
106				ldns_rr_get_type(r) == LDNS_RR_TYPE_AAAA) {
107			/* possibly glue */
108			if (!ldns_rr_list_push_rr(addr, r)) goto memory_error;
109			continue;
110		}
111		if (ldns_rr_get_type(r) == LDNS_RR_TYPE_NS) {
112			/* multiple zones will end up here -
113			 * for now; not a problem
114			 */
115			/* don't add NS records for the current zone itself */
116			if (ldns_rdf_compare(ldns_rr_owner(r),
117						ldns_rr_owner(ldns_zone_soa(z))) != 0) {
118				if (!ldns_rr_list_push_rr(zone_cuts, r)) goto memory_error;
119			}
120			continue;
121		}
122	}
123
124	/* will sorting make it quicker ?? */
125	for(i = 0; i < ldns_rr_list_rr_count(zone_cuts); i++) {
126		ns = ldns_rr_list_rr(zone_cuts, i);
127		ns_owner = ldns_rr_owner(ns);
128
129		for(j = 0; j < ldns_rr_list_rr_count(addr); j++) {
130			a = ldns_rr_list_rr(addr, j);
131			dname_a = ldns_rr_owner(a);
132
133			if (ldns_dname_is_subdomain(dname_a, ns_owner) ||
134				ldns_dname_compare(dname_a, ns_owner) == 0) {
135				/* GLUE! */
136				if (!ldns_rr_list_push_rr(glue, a)) goto memory_error;
137			}
138		}
139	}
140
141	ldns_rr_list_free(addr);
142	ldns_rr_list_free(zone_cuts);
143
144	if (ldns_rr_list_rr_count(glue) == 0) {
145		ldns_rr_list_free(glue);
146		return NULL;
147	} else {
148		return glue;
149	}
150
151memory_error:
152	if (zone_cuts) {
153		LDNS_FREE(zone_cuts);
154	}
155	if (addr) {
156		ldns_rr_list_free(addr);
157	}
158	if (glue) {
159		ldns_rr_list_free(glue);
160	}
161	return NULL;
162}
163
164ldns_zone *
165ldns_zone_new(void)
166{
167	ldns_zone *z;
168
169	z = LDNS_MALLOC(ldns_zone);
170	if (!z) {
171		return NULL;
172	}
173
174	z->_rrs = ldns_rr_list_new();
175	if (!z->_rrs) {
176		LDNS_FREE(z);
177		return NULL;
178	}
179	ldns_zone_set_soa(z, NULL);
180	return z;
181}
182
183/* we regocnize:
184 * $TTL, $ORIGIN
185 */
186ldns_status
187ldns_zone_new_frm_fp(ldns_zone **z, FILE *fp, const ldns_rdf *origin, uint32_t ttl, ldns_rr_class c)
188{
189	return ldns_zone_new_frm_fp_l(z, fp, origin, ttl, c, NULL);
190}
191
192/* XXX: class is never used */
193ldns_status
194ldns_zone_new_frm_fp_l(ldns_zone **z, FILE *fp, const ldns_rdf *origin, uint32_t ttl,
195        ldns_rr_class ATTR_UNUSED(c), int *line_nr)
196{
197	ldns_zone *newzone;
198	ldns_rr *rr;
199	uint32_t my_ttl;
200	ldns_rdf *my_origin;
201	ldns_rdf *my_prev;
202	bool soa_seen = false; 	/* 2 soa are an error */
203	ldns_status s;
204	ldns_status ret;
205
206	/* most cases of error are memory problems */
207	ret = LDNS_STATUS_MEM_ERR;
208
209	newzone = NULL;
210	my_origin = NULL;
211	my_prev = NULL;
212
213	my_ttl    = ttl;
214
215	if (origin) {
216		my_origin = ldns_rdf_clone(origin);
217		if (!my_origin) goto error;
218		/* also set the prev */
219		my_prev   = ldns_rdf_clone(origin);
220		if (!my_prev) goto error;
221	}
222
223	newzone = ldns_zone_new();
224	if (!newzone) goto error;
225
226	while(!feof(fp)) {
227		s = ldns_rr_new_frm_fp_l(&rr, fp, &my_ttl, &my_origin, &my_prev, line_nr);
228		switch (s) {
229		case LDNS_STATUS_OK:
230			if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) {
231				if (soa_seen) {
232					/* second SOA
233					 * just skip, maybe we want to say
234					 * something??? */
235					ldns_rr_free(rr);
236					continue;
237				}
238				soa_seen = true;
239				ldns_zone_set_soa(newzone, rr);
240				/* set origin to soa if not specified */
241				if (!my_origin) {
242					my_origin = ldns_rdf_clone(ldns_rr_owner(rr));
243				}
244				continue;
245			}
246
247			/* a normal RR - as sofar the DNS is normal */
248			if (!ldns_zone_push_rr(newzone, rr)) goto error;
249
250		case LDNS_STATUS_SYNTAX_EMPTY:
251			/* empty line was seen */
252		case LDNS_STATUS_SYNTAX_TTL:
253			/* the function set the ttl */
254			break;
255		case LDNS_STATUS_SYNTAX_ORIGIN:
256			/* the function set the origin */
257			break;
258		case LDNS_STATUS_SYNTAX_INCLUDE:
259			ret = LDNS_STATUS_SYNTAX_INCLUDE_ERR_NOTIMPL;
260			break;
261		default:
262			ret = s;
263			goto error;
264		}
265	}
266
267	if (my_origin) {
268		ldns_rdf_deep_free(my_origin);
269	}
270	if (my_prev) {
271		ldns_rdf_deep_free(my_prev);
272	}
273	if (z) {
274		*z = newzone;
275	} else {
276		ldns_zone_free(newzone);
277	}
278
279	return LDNS_STATUS_OK;
280
281error:
282	if (my_origin) {
283		ldns_rdf_deep_free(my_origin);
284	}
285	if (my_prev) {
286		ldns_rdf_deep_free(my_prev);
287	}
288	if (newzone) {
289		ldns_zone_free(newzone);
290	}
291	return ret;
292}
293
294void
295ldns_zone_sort(ldns_zone *zone)
296{
297	ldns_rr_list *zrr;
298	assert(zone != NULL);
299
300	zrr = ldns_zone_rrs(zone);
301	ldns_rr_list_sort(zrr);
302}
303
304void
305ldns_zone_free(ldns_zone *zone)
306{
307	ldns_rr_list_free(zone->_rrs);
308	LDNS_FREE(zone);
309}
310
311void
312ldns_zone_deep_free(ldns_zone *zone)
313{
314	ldns_rr_free(zone->_soa);
315	ldns_rr_list_deep_free(zone->_rrs);
316	LDNS_FREE(zone);
317}
318