1238104Sdes/*
2238104Sdes * special zone file structures and functions for better dnssec handling
3238104Sdes */
4238104Sdes
5238104Sdes#include <ldns/config.h>
6238104Sdes
7238104Sdes#include <ldns/ldns.h>
8238104Sdes
9238104Sdesldns_dnssec_rrs *
10246827Sdesldns_dnssec_rrs_new(void)
11238104Sdes{
12238104Sdes	ldns_dnssec_rrs *new_rrs;
13238104Sdes	new_rrs = LDNS_MALLOC(ldns_dnssec_rrs);
14238104Sdes        if(!new_rrs) return NULL;
15238104Sdes	new_rrs->rr = NULL;
16238104Sdes	new_rrs->next = NULL;
17238104Sdes	return new_rrs;
18238104Sdes}
19238104Sdes
20238104SdesINLINE void
21238104Sdesldns_dnssec_rrs_free_internal(ldns_dnssec_rrs *rrs, int deep)
22238104Sdes{
23238104Sdes	ldns_dnssec_rrs *next;
24238104Sdes	while (rrs) {
25238104Sdes		next = rrs->next;
26238104Sdes		if (deep) {
27238104Sdes			ldns_rr_free(rrs->rr);
28238104Sdes		}
29238104Sdes		LDNS_FREE(rrs);
30238104Sdes		rrs = next;
31238104Sdes	}
32238104Sdes}
33238104Sdes
34238104Sdesvoid
35238104Sdesldns_dnssec_rrs_free(ldns_dnssec_rrs *rrs)
36238104Sdes{
37238104Sdes	ldns_dnssec_rrs_free_internal(rrs, 0);
38238104Sdes}
39238104Sdes
40238104Sdesvoid
41238104Sdesldns_dnssec_rrs_deep_free(ldns_dnssec_rrs *rrs)
42238104Sdes{
43238104Sdes	ldns_dnssec_rrs_free_internal(rrs, 1);
44238104Sdes}
45238104Sdes
46238104Sdesldns_status
47238104Sdesldns_dnssec_rrs_add_rr(ldns_dnssec_rrs *rrs, ldns_rr *rr)
48238104Sdes{
49238104Sdes	int cmp;
50238104Sdes	ldns_dnssec_rrs *new_rrs;
51238104Sdes	if (!rrs || !rr) {
52238104Sdes		return LDNS_STATUS_ERR;
53238104Sdes	}
54238104Sdes
55238104Sdes	/* this could be done more efficiently; name and type should already
56238104Sdes	   be equal */
57269257Sdes	cmp = ldns_rr_compare(rrs->rr, rr);
58269257Sdes	if (cmp < 0) {
59238104Sdes		if (rrs->next) {
60238104Sdes			return ldns_dnssec_rrs_add_rr(rrs->next, rr);
61238104Sdes		} else {
62238104Sdes			new_rrs = ldns_dnssec_rrs_new();
63238104Sdes			new_rrs->rr = rr;
64238104Sdes			rrs->next = new_rrs;
65238104Sdes		}
66238104Sdes	} else if (cmp > 0) {
67238104Sdes		/* put the current old rr in the new next, put the new
68238104Sdes		   rr in the current container */
69238104Sdes		new_rrs = ldns_dnssec_rrs_new();
70238104Sdes		new_rrs->rr = rrs->rr;
71238104Sdes		new_rrs->next = rrs->next;
72238104Sdes		rrs->rr = rr;
73238104Sdes		rrs->next = new_rrs;
74238104Sdes	}
75269257Sdes	/* Silently ignore equal rr's */
76238104Sdes	return LDNS_STATUS_OK;
77238104Sdes}
78238104Sdes
79238104Sdesvoid
80238104Sdesldns_dnssec_rrs_print_fmt(FILE *out, const ldns_output_format *fmt,
81238104Sdes	       ldns_dnssec_rrs *rrs)
82238104Sdes{
83238104Sdes	if (!rrs) {
84238104Sdes		if ((fmt->flags & LDNS_COMMENT_LAYOUT))
85238104Sdes			fprintf(out, "; <void>");
86238104Sdes	} else {
87238104Sdes		if (rrs->rr) {
88238104Sdes			ldns_rr_print_fmt(out, fmt, rrs->rr);
89238104Sdes		}
90238104Sdes		if (rrs->next) {
91238104Sdes			ldns_dnssec_rrs_print_fmt(out, fmt, rrs->next);
92238104Sdes		}
93238104Sdes	}
94238104Sdes}
95238104Sdes
96238104Sdesvoid
97238104Sdesldns_dnssec_rrs_print(FILE *out, ldns_dnssec_rrs *rrs)
98238104Sdes{
99238104Sdes	ldns_dnssec_rrs_print_fmt(out, ldns_output_format_default, rrs);
100238104Sdes}
101238104Sdes
102238104Sdes
103238104Sdesldns_dnssec_rrsets *
104246827Sdesldns_dnssec_rrsets_new(void)
105238104Sdes{
106238104Sdes	ldns_dnssec_rrsets *new_rrsets;
107238104Sdes	new_rrsets = LDNS_MALLOC(ldns_dnssec_rrsets);
108238104Sdes        if(!new_rrsets) return NULL;
109238104Sdes	new_rrsets->rrs = NULL;
110238104Sdes	new_rrsets->type = 0;
111238104Sdes	new_rrsets->signatures = NULL;
112238104Sdes	new_rrsets->next = NULL;
113238104Sdes	return new_rrsets;
114238104Sdes}
115238104Sdes
116238104SdesINLINE void
117238104Sdesldns_dnssec_rrsets_free_internal(ldns_dnssec_rrsets *rrsets, int deep)
118238104Sdes{
119238104Sdes	if (rrsets) {
120238104Sdes		if (rrsets->rrs) {
121238104Sdes			ldns_dnssec_rrs_free_internal(rrsets->rrs, deep);
122238104Sdes		}
123238104Sdes		if (rrsets->next) {
124238104Sdes			ldns_dnssec_rrsets_free_internal(rrsets->next, deep);
125238104Sdes		}
126238104Sdes		if (rrsets->signatures) {
127238104Sdes			ldns_dnssec_rrs_free_internal(rrsets->signatures, deep);
128238104Sdes		}
129238104Sdes		LDNS_FREE(rrsets);
130238104Sdes	}
131238104Sdes}
132238104Sdes
133238104Sdesvoid
134238104Sdesldns_dnssec_rrsets_free(ldns_dnssec_rrsets *rrsets)
135238104Sdes{
136238104Sdes	ldns_dnssec_rrsets_free_internal(rrsets, 0);
137238104Sdes}
138238104Sdes
139238104Sdesvoid
140238104Sdesldns_dnssec_rrsets_deep_free(ldns_dnssec_rrsets *rrsets)
141238104Sdes{
142238104Sdes	ldns_dnssec_rrsets_free_internal(rrsets, 1);
143238104Sdes}
144238104Sdes
145238104Sdesldns_rr_type
146238104Sdesldns_dnssec_rrsets_type(ldns_dnssec_rrsets *rrsets)
147238104Sdes{
148238104Sdes	if (rrsets) {
149238104Sdes		return rrsets->type;
150238104Sdes	} else {
151238104Sdes		return 0;
152238104Sdes	}
153238104Sdes}
154238104Sdes
155238104Sdesldns_status
156238104Sdesldns_dnssec_rrsets_set_type(ldns_dnssec_rrsets *rrsets,
157238104Sdes					   ldns_rr_type type)
158238104Sdes{
159238104Sdes	if (rrsets) {
160238104Sdes		rrsets->type = type;
161238104Sdes		return LDNS_STATUS_OK;
162238104Sdes	}
163238104Sdes	return LDNS_STATUS_ERR;
164238104Sdes}
165238104Sdes
166246827Sdesstatic ldns_dnssec_rrsets *
167238104Sdesldns_dnssec_rrsets_new_frm_rr(ldns_rr *rr)
168238104Sdes{
169238104Sdes	ldns_dnssec_rrsets *new_rrsets;
170238104Sdes	ldns_rr_type rr_type;
171238104Sdes	bool rrsig;
172238104Sdes
173238104Sdes	new_rrsets = ldns_dnssec_rrsets_new();
174238104Sdes	rr_type = ldns_rr_get_type(rr);
175238104Sdes	if (rr_type == LDNS_RR_TYPE_RRSIG) {
176238104Sdes		rrsig = true;
177238104Sdes		rr_type = ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(rr));
178238104Sdes	} else {
179238104Sdes		rrsig = false;
180238104Sdes	}
181238104Sdes	if (!rrsig) {
182238104Sdes		new_rrsets->rrs = ldns_dnssec_rrs_new();
183238104Sdes		new_rrsets->rrs->rr = rr;
184238104Sdes	} else {
185238104Sdes		new_rrsets->signatures = ldns_dnssec_rrs_new();
186238104Sdes		new_rrsets->signatures->rr = rr;
187238104Sdes	}
188238104Sdes	new_rrsets->type = rr_type;
189238104Sdes	return new_rrsets;
190238104Sdes}
191238104Sdes
192238104Sdesldns_status
193238104Sdesldns_dnssec_rrsets_add_rr(ldns_dnssec_rrsets *rrsets, ldns_rr *rr)
194238104Sdes{
195238104Sdes	ldns_dnssec_rrsets *new_rrsets;
196238104Sdes	ldns_rr_type rr_type;
197238104Sdes	bool rrsig = false;
198238104Sdes	ldns_status result = LDNS_STATUS_OK;
199238104Sdes
200238104Sdes	if (!rrsets || !rr) {
201238104Sdes		return LDNS_STATUS_ERR;
202238104Sdes	}
203238104Sdes
204238104Sdes	rr_type = ldns_rr_get_type(rr);
205238104Sdes
206238104Sdes	if (rr_type == LDNS_RR_TYPE_RRSIG) {
207238104Sdes		rrsig = true;
208238104Sdes		rr_type = ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(rr));
209238104Sdes	}
210238104Sdes
211238104Sdes	if (!rrsets->rrs && rrsets->type == 0 && !rrsets->signatures) {
212238104Sdes		if (!rrsig) {
213238104Sdes			rrsets->rrs = ldns_dnssec_rrs_new();
214238104Sdes			rrsets->rrs->rr = rr;
215238104Sdes			rrsets->type = rr_type;
216238104Sdes		} else {
217238104Sdes			rrsets->signatures = ldns_dnssec_rrs_new();
218238104Sdes			rrsets->signatures->rr = rr;
219238104Sdes			rrsets->type = rr_type;
220238104Sdes		}
221238104Sdes		return LDNS_STATUS_OK;
222238104Sdes	}
223238104Sdes
224238104Sdes	if (rr_type > ldns_dnssec_rrsets_type(rrsets)) {
225238104Sdes		if (rrsets->next) {
226238104Sdes			result = ldns_dnssec_rrsets_add_rr(rrsets->next, rr);
227238104Sdes		} else {
228238104Sdes			new_rrsets = ldns_dnssec_rrsets_new_frm_rr(rr);
229238104Sdes			rrsets->next = new_rrsets;
230238104Sdes		}
231238104Sdes	} else if (rr_type < ldns_dnssec_rrsets_type(rrsets)) {
232238104Sdes		/* move the current one into the new next,
233238104Sdes		   replace field of current with data from new rr */
234238104Sdes		new_rrsets = ldns_dnssec_rrsets_new();
235238104Sdes		new_rrsets->rrs = rrsets->rrs;
236238104Sdes		new_rrsets->type = rrsets->type;
237238104Sdes		new_rrsets->signatures = rrsets->signatures;
238238104Sdes		new_rrsets->next = rrsets->next;
239238104Sdes		if (!rrsig) {
240238104Sdes			rrsets->rrs = ldns_dnssec_rrs_new();
241238104Sdes			rrsets->rrs->rr = rr;
242238104Sdes			rrsets->signatures = NULL;
243238104Sdes		} else {
244238104Sdes			rrsets->rrs = NULL;
245238104Sdes			rrsets->signatures = ldns_dnssec_rrs_new();
246238104Sdes			rrsets->signatures->rr = rr;
247238104Sdes		}
248238104Sdes		rrsets->type = rr_type;
249238104Sdes		rrsets->next = new_rrsets;
250238104Sdes	} else {
251238104Sdes		/* equal, add to current rrsets */
252238104Sdes		if (rrsig) {
253238104Sdes			if (rrsets->signatures) {
254238104Sdes				result = ldns_dnssec_rrs_add_rr(rrsets->signatures, rr);
255238104Sdes			} else {
256238104Sdes				rrsets->signatures = ldns_dnssec_rrs_new();
257238104Sdes				rrsets->signatures->rr = rr;
258238104Sdes			}
259238104Sdes		} else {
260238104Sdes			if (rrsets->rrs) {
261238104Sdes				result = ldns_dnssec_rrs_add_rr(rrsets->rrs, rr);
262238104Sdes			} else {
263238104Sdes				rrsets->rrs = ldns_dnssec_rrs_new();
264238104Sdes				rrsets->rrs->rr = rr;
265238104Sdes			}
266238104Sdes		}
267238104Sdes	}
268238104Sdes
269238104Sdes	return result;
270238104Sdes}
271238104Sdes
272246827Sdesstatic void
273238104Sdesldns_dnssec_rrsets_print_soa_fmt(FILE *out, const ldns_output_format *fmt,
274238104Sdes		ldns_dnssec_rrsets *rrsets,
275238104Sdes		bool follow,
276238104Sdes		bool show_soa)
277238104Sdes{
278238104Sdes	if (!rrsets) {
279238104Sdes		if ((fmt->flags & LDNS_COMMENT_LAYOUT))
280238104Sdes			fprintf(out, "; <void>\n");
281238104Sdes	} else {
282238104Sdes		if (rrsets->rrs &&
283238104Sdes		    (show_soa ||
284238104Sdes			ldns_rr_get_type(rrsets->rrs->rr) != LDNS_RR_TYPE_SOA
285238104Sdes		    )
286238104Sdes		   ) {
287238104Sdes			ldns_dnssec_rrs_print_fmt(out, fmt, rrsets->rrs);
288238104Sdes			if (rrsets->signatures) {
289238104Sdes				ldns_dnssec_rrs_print_fmt(out, fmt,
290238104Sdes						rrsets->signatures);
291238104Sdes			}
292238104Sdes		}
293238104Sdes		if (follow && rrsets->next) {
294238104Sdes			ldns_dnssec_rrsets_print_soa_fmt(out, fmt,
295238104Sdes					rrsets->next, follow, show_soa);
296238104Sdes		}
297238104Sdes	}
298238104Sdes}
299238104Sdes
300238104Sdes
301238104Sdesvoid
302238104Sdesldns_dnssec_rrsets_print_fmt(FILE *out, const ldns_output_format *fmt,
303238104Sdes		ldns_dnssec_rrsets *rrsets,
304238104Sdes		bool follow)
305238104Sdes{
306238104Sdes	ldns_dnssec_rrsets_print_soa_fmt(out, fmt, rrsets, follow, true);
307238104Sdes}
308238104Sdes
309238104Sdesvoid
310238104Sdesldns_dnssec_rrsets_print(FILE *out, ldns_dnssec_rrsets *rrsets, bool follow)
311238104Sdes{
312238104Sdes	ldns_dnssec_rrsets_print_fmt(out, ldns_output_format_default,
313238104Sdes			rrsets, follow);
314238104Sdes}
315238104Sdes
316238104Sdesldns_dnssec_name *
317246827Sdesldns_dnssec_name_new(void)
318238104Sdes{
319238104Sdes	ldns_dnssec_name *new_name;
320238104Sdes
321238104Sdes	new_name = LDNS_CALLOC(ldns_dnssec_name, 1);
322238104Sdes	if (!new_name) {
323238104Sdes		return NULL;
324238104Sdes	}
325238104Sdes	/*
326238104Sdes	 * not needed anymore because CALLOC initalizes everything to zero.
327238104Sdes
328238104Sdes	new_name->name = NULL;
329238104Sdes	new_name->rrsets = NULL;
330238104Sdes	new_name->name_alloced = false;
331238104Sdes	new_name->nsec = NULL;
332238104Sdes	new_name->nsec_signatures = NULL;
333238104Sdes
334238104Sdes	new_name->is_glue = false;
335238104Sdes	new_name->hashed_name = NULL;
336238104Sdes
337238104Sdes	 */
338238104Sdes	return new_name;
339238104Sdes}
340238104Sdes
341238104Sdesldns_dnssec_name *
342238104Sdesldns_dnssec_name_new_frm_rr(ldns_rr *rr)
343238104Sdes{
344238104Sdes	ldns_dnssec_name *new_name = ldns_dnssec_name_new();
345238104Sdes
346238104Sdes	new_name->name = ldns_rr_owner(rr);
347238104Sdes	if(ldns_dnssec_name_add_rr(new_name, rr) != LDNS_STATUS_OK) {
348238104Sdes		ldns_dnssec_name_free(new_name);
349238104Sdes		return NULL;
350238104Sdes	}
351238104Sdes
352238104Sdes	return new_name;
353238104Sdes}
354238104Sdes
355238104SdesINLINE void
356238104Sdesldns_dnssec_name_free_internal(ldns_dnssec_name *name,
357238104Sdes                               int deep)
358238104Sdes{
359238104Sdes	if (name) {
360238104Sdes		if (name->name_alloced) {
361238104Sdes			ldns_rdf_deep_free(name->name);
362238104Sdes		}
363238104Sdes		if (name->rrsets) {
364238104Sdes			ldns_dnssec_rrsets_free_internal(name->rrsets, deep);
365238104Sdes		}
366238104Sdes		if (name->nsec && deep) {
367238104Sdes			ldns_rr_free(name->nsec);
368238104Sdes		}
369238104Sdes		if (name->nsec_signatures) {
370238104Sdes			ldns_dnssec_rrs_free_internal(name->nsec_signatures, deep);
371238104Sdes		}
372238104Sdes		if (name->hashed_name) {
373238104Sdes			if (deep) {
374238104Sdes				ldns_rdf_deep_free(name->hashed_name);
375238104Sdes			}
376238104Sdes		}
377238104Sdes		LDNS_FREE(name);
378238104Sdes	}
379238104Sdes}
380238104Sdes
381238104Sdesvoid
382238104Sdesldns_dnssec_name_free(ldns_dnssec_name *name)
383238104Sdes{
384238104Sdes  ldns_dnssec_name_free_internal(name, 0);
385238104Sdes}
386238104Sdes
387238104Sdesvoid
388238104Sdesldns_dnssec_name_deep_free(ldns_dnssec_name *name)
389238104Sdes{
390238104Sdes  ldns_dnssec_name_free_internal(name, 1);
391238104Sdes}
392238104Sdes
393238104Sdesldns_rdf *
394238104Sdesldns_dnssec_name_name(ldns_dnssec_name *name)
395238104Sdes{
396238104Sdes	if (name) {
397238104Sdes		return name->name;
398238104Sdes	}
399238104Sdes	return NULL;
400238104Sdes}
401238104Sdes
402238104Sdesbool
403238104Sdesldns_dnssec_name_is_glue(ldns_dnssec_name *name)
404238104Sdes{
405238104Sdes	if (name) {
406238104Sdes		return name->is_glue;
407238104Sdes	}
408238104Sdes	return false;
409238104Sdes}
410238104Sdes
411238104Sdesvoid
412238104Sdesldns_dnssec_name_set_name(ldns_dnssec_name *rrset,
413238104Sdes					 ldns_rdf *dname)
414238104Sdes{
415238104Sdes	if (rrset && dname) {
416238104Sdes		rrset->name = dname;
417238104Sdes	}
418238104Sdes}
419238104Sdes
420238104Sdes
421238104Sdesvoid
422238104Sdesldns_dnssec_name_set_nsec(ldns_dnssec_name *rrset, ldns_rr *nsec)
423238104Sdes{
424238104Sdes	if (rrset && nsec) {
425238104Sdes		rrset->nsec = nsec;
426238104Sdes	}
427238104Sdes}
428238104Sdes
429238104Sdesint
430238104Sdesldns_dnssec_name_cmp(const void *a, const void *b)
431238104Sdes{
432238104Sdes	ldns_dnssec_name *na = (ldns_dnssec_name *) a;
433238104Sdes	ldns_dnssec_name *nb = (ldns_dnssec_name *) b;
434238104Sdes
435238104Sdes	if (na && nb) {
436238104Sdes		return ldns_dname_compare(ldns_dnssec_name_name(na),
437238104Sdes							 ldns_dnssec_name_name(nb));
438238104Sdes	} else if (na) {
439238104Sdes		return 1;
440238104Sdes	} else if (nb) {
441238104Sdes		return -1;
442238104Sdes	} else {
443238104Sdes		return 0;
444238104Sdes	}
445238104Sdes}
446238104Sdes
447238104Sdesldns_status
448238104Sdesldns_dnssec_name_add_rr(ldns_dnssec_name *name,
449238104Sdes				    ldns_rr *rr)
450238104Sdes{
451238104Sdes	ldns_status result = LDNS_STATUS_OK;
452238104Sdes	ldns_rr_type rr_type;
453238104Sdes	ldns_rr_type typecovered = 0;
454238104Sdes
455238104Sdes	/* special handling for NSEC3 and NSECX covering RRSIGS */
456238104Sdes
457238104Sdes	if (!name || !rr) {
458238104Sdes		return LDNS_STATUS_ERR;
459238104Sdes	}
460238104Sdes
461238104Sdes	rr_type = ldns_rr_get_type(rr);
462238104Sdes
463238104Sdes	if (rr_type == LDNS_RR_TYPE_RRSIG) {
464238104Sdes		typecovered = ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(rr));
465238104Sdes	}
466238104Sdes
467238104Sdes	if (rr_type == LDNS_RR_TYPE_NSEC ||
468238104Sdes	    rr_type == LDNS_RR_TYPE_NSEC3) {
469238104Sdes		/* XX check if is already set (and error?) */
470238104Sdes		name->nsec = rr;
471238104Sdes	} else if (typecovered == LDNS_RR_TYPE_NSEC ||
472238104Sdes			 typecovered == LDNS_RR_TYPE_NSEC3) {
473238104Sdes		if (name->nsec_signatures) {
474238104Sdes			result = ldns_dnssec_rrs_add_rr(name->nsec_signatures, rr);
475238104Sdes		} else {
476238104Sdes			name->nsec_signatures = ldns_dnssec_rrs_new();
477238104Sdes			name->nsec_signatures->rr = rr;
478238104Sdes		}
479238104Sdes	} else {
480238104Sdes		/* it's a 'normal' RR, add it to the right rrset */
481238104Sdes		if (name->rrsets) {
482238104Sdes			result = ldns_dnssec_rrsets_add_rr(name->rrsets, rr);
483238104Sdes		} else {
484238104Sdes			name->rrsets = ldns_dnssec_rrsets_new();
485238104Sdes			result = ldns_dnssec_rrsets_add_rr(name->rrsets, rr);
486238104Sdes		}
487238104Sdes	}
488238104Sdes	return result;
489238104Sdes}
490238104Sdes
491238104Sdesldns_dnssec_rrsets *
492238104Sdesldns_dnssec_name_find_rrset(ldns_dnssec_name *name,
493238104Sdes					   ldns_rr_type type) {
494238104Sdes	ldns_dnssec_rrsets *result;
495238104Sdes
496238104Sdes	result = name->rrsets;
497238104Sdes	while (result) {
498238104Sdes		if (result->type == type) {
499238104Sdes			return result;
500238104Sdes		} else {
501238104Sdes			result = result->next;
502238104Sdes		}
503238104Sdes	}
504238104Sdes	return NULL;
505238104Sdes}
506238104Sdes
507238104Sdesldns_dnssec_rrsets *
508238104Sdesldns_dnssec_zone_find_rrset(ldns_dnssec_zone *zone,
509238104Sdes					   ldns_rdf *dname,
510238104Sdes					   ldns_rr_type type)
511238104Sdes{
512238104Sdes	ldns_rbnode_t *node;
513238104Sdes
514238104Sdes	if (!zone || !dname) {
515238104Sdes		return NULL;
516238104Sdes	}
517238104Sdes
518238104Sdes	node = ldns_rbtree_search(zone->names, dname);
519238104Sdes	if (node) {
520238104Sdes		return ldns_dnssec_name_find_rrset((ldns_dnssec_name *)node->data,
521238104Sdes									type);
522238104Sdes	} else {
523238104Sdes		return NULL;
524238104Sdes	}
525238104Sdes}
526238104Sdes
527246827Sdesstatic void
528238104Sdesldns_dnssec_name_print_soa_fmt(FILE *out, const ldns_output_format *fmt,
529238104Sdes		ldns_dnssec_name *name,
530238104Sdes		bool show_soa)
531238104Sdes{
532238104Sdes	if (name) {
533238104Sdes		if(name->rrsets) {
534238104Sdes			ldns_dnssec_rrsets_print_soa_fmt(out, fmt,
535238104Sdes					name->rrsets, true, show_soa);
536238104Sdes		} else if ((fmt->flags & LDNS_COMMENT_LAYOUT)) {
537238104Sdes			fprintf(out, ";; Empty nonterminal: ");
538238104Sdes			ldns_rdf_print(out, name->name);
539238104Sdes			fprintf(out, "\n");
540238104Sdes		}
541238104Sdes		if(name->nsec) {
542238104Sdes			ldns_rr_print_fmt(out, fmt, name->nsec);
543238104Sdes		}
544238104Sdes		if (name->nsec_signatures) {
545238104Sdes			ldns_dnssec_rrs_print_fmt(out, fmt,
546238104Sdes					name->nsec_signatures);
547238104Sdes		}
548238104Sdes	} else if ((fmt->flags & LDNS_COMMENT_LAYOUT)) {
549238104Sdes		fprintf(out, "; <void>\n");
550238104Sdes	}
551238104Sdes}
552238104Sdes
553238104Sdes
554238104Sdesvoid
555238104Sdesldns_dnssec_name_print_fmt(FILE *out, const ldns_output_format *fmt,
556238104Sdes		ldns_dnssec_name *name)
557238104Sdes{
558238104Sdes	ldns_dnssec_name_print_soa_fmt(out, fmt, name, true);
559238104Sdes}
560238104Sdes
561238104Sdesvoid
562238104Sdesldns_dnssec_name_print(FILE *out, ldns_dnssec_name *name)
563238104Sdes{
564238104Sdes	ldns_dnssec_name_print_fmt(out, ldns_output_format_default, name);
565238104Sdes}
566238104Sdes
567238104Sdes
568238104Sdesldns_dnssec_zone *
569246827Sdesldns_dnssec_zone_new(void)
570238104Sdes{
571238104Sdes	ldns_dnssec_zone *zone = LDNS_MALLOC(ldns_dnssec_zone);
572238104Sdes        if(!zone) return NULL;
573238104Sdes	zone->soa = NULL;
574238104Sdes	zone->names = NULL;
575269257Sdes	zone->hashed_names = NULL;
576269257Sdes	zone->_nsec3params = NULL;
577238104Sdes
578238104Sdes	return zone;
579238104Sdes}
580238104Sdes
581238104Sdesstatic bool
582238104Sdesrr_is_rrsig_covering(ldns_rr* rr, ldns_rr_type t)
583238104Sdes{
584238104Sdes	return     ldns_rr_get_type(rr) == LDNS_RR_TYPE_RRSIG
585238104Sdes		&& ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(rr)) == t;
586238104Sdes}
587238104Sdes
588238104Sdes/* When the zone is first read into an list and then inserted into an
589238104Sdes * ldns_dnssec_zone (rbtree) the nodes of the rbtree are allocated close (next)
590238104Sdes * to each other. Because ldns-verify-zone (the only program that uses this
591238104Sdes * function) uses the rbtree mostly for sequentual walking, this results
592238104Sdes * in a speed increase (of 15% on linux) because we have less CPU-cache misses.
593238104Sdes */
594238104Sdes#define FASTER_DNSSEC_ZONE_NEW_FRM_FP 1 /* Because of L2 cache efficiency */
595238104Sdes
596238104Sdesldns_status
597238104Sdesldns_dnssec_zone_new_frm_fp_l(ldns_dnssec_zone** z, FILE* fp, ldns_rdf* origin,
598238104Sdes	       	uint32_t ttl, ldns_rr_class ATTR_UNUSED(c), int* line_nr)
599238104Sdes{
600238104Sdes	ldns_rr* cur_rr;
601238104Sdes	size_t i;
602238104Sdes
603238104Sdes	ldns_rdf *my_origin = NULL;
604238104Sdes	ldns_rdf *my_prev = NULL;
605238104Sdes
606238104Sdes	ldns_dnssec_zone *newzone = ldns_dnssec_zone_new();
607238104Sdes	/* when reading NSEC3s, there is a chance that we encounter nsecs
608238104Sdes	   for empty nonterminals, whose nonterminals we cannot derive yet
609238104Sdes	   because the needed information is to be read later. in that case
610238104Sdes	   we keep a list of those nsec3's and retry to add them later */
611238104Sdes	ldns_rr_list* todo_nsec3s = ldns_rr_list_new();
612238104Sdes	ldns_rr_list* todo_nsec3_rrsigs = ldns_rr_list_new();
613238104Sdes
614238104Sdes	ldns_status status = LDNS_STATUS_MEM_ERR;
615238104Sdes
616238104Sdes#ifdef FASTER_DNSSEC_ZONE_NEW_FRM_FP
617238104Sdes	ldns_zone* zone = NULL;
618238104Sdes	if (ldns_zone_new_frm_fp_l(&zone, fp, origin,ttl, c, line_nr)
619238104Sdes			!= LDNS_STATUS_OK) goto error;
620238104Sdes#else
621238104Sdes	uint32_t  my_ttl = ttl;
622238104Sdes#endif
623238104Sdes
624238104Sdes	if (!newzone || !todo_nsec3s || !todo_nsec3_rrsigs ) goto error;
625238104Sdes
626238104Sdes	if (origin) {
627238104Sdes		if (!(my_origin = ldns_rdf_clone(origin))) goto error;
628238104Sdes		if (!(my_prev   = ldns_rdf_clone(origin))) goto error;
629238104Sdes	}
630238104Sdes
631238104Sdes#ifdef FASTER_DNSSEC_ZONE_NEW_FRM_FP
632238104Sdes	if (ldns_dnssec_zone_add_rr(newzone, ldns_zone_soa(zone))
633238104Sdes			!= LDNS_STATUS_OK) goto error;
634238104Sdes
635238104Sdes	for (i = 0; i < ldns_rr_list_rr_count(ldns_zone_rrs(zone)); i++) {
636238104Sdes		cur_rr = ldns_rr_list_rr(ldns_zone_rrs(zone), i);
637238104Sdes		status = LDNS_STATUS_OK;
638238104Sdes#else
639238104Sdes	while (!feof(fp)) {
640238104Sdes		status = ldns_rr_new_frm_fp_l(&cur_rr, fp, &my_ttl, &my_origin,
641238104Sdes				&my_prev, line_nr);
642238104Sdes
643238104Sdes#endif
644238104Sdes		switch (status) {
645238104Sdes		case LDNS_STATUS_OK:
646238104Sdes
647238104Sdes			status = ldns_dnssec_zone_add_rr(newzone, cur_rr);
648238104Sdes			if (status ==
649238104Sdes				LDNS_STATUS_DNSSEC_NSEC3_ORIGINAL_NOT_FOUND) {
650238104Sdes
651238104Sdes				if (rr_is_rrsig_covering(cur_rr,
652238104Sdes							LDNS_RR_TYPE_NSEC3)){
653238104Sdes					ldns_rr_list_push_rr(todo_nsec3_rrsigs,
654238104Sdes							cur_rr);
655238104Sdes				} else {
656238104Sdes					ldns_rr_list_push_rr(todo_nsec3s,
657238104Sdes						       	cur_rr);
658238104Sdes				}
659269257Sdes				status = LDNS_STATUS_OK;
660269257Sdes
661238104Sdes			} else if (status != LDNS_STATUS_OK)
662238104Sdes				goto error;
663238104Sdes
664238104Sdes			break;
665238104Sdes
666238104Sdes
667238104Sdes		case LDNS_STATUS_SYNTAX_EMPTY:	/* empty line was seen */
668238104Sdes		case LDNS_STATUS_SYNTAX_TTL:	/* the ttl was set*/
669238104Sdes		case LDNS_STATUS_SYNTAX_ORIGIN:	/* the origin was set*/
670246854Sdes			status = LDNS_STATUS_OK;
671238104Sdes			break;
672238104Sdes
673238104Sdes		case LDNS_STATUS_SYNTAX_INCLUDE:/* $include not implemented */
674238104Sdes			status =  LDNS_STATUS_SYNTAX_INCLUDE_ERR_NOTIMPL;
675238104Sdes			break;
676238104Sdes
677238104Sdes		default:
678238104Sdes			goto error;
679238104Sdes		}
680238104Sdes	}
681238104Sdes
682238104Sdes	if (ldns_rr_list_rr_count(todo_nsec3s) > 0) {
683238104Sdes		(void) ldns_dnssec_zone_add_empty_nonterminals(newzone);
684269257Sdes		for (i = 0; status == LDNS_STATUS_OK &&
685246854Sdes				i < ldns_rr_list_rr_count(todo_nsec3s); i++) {
686238104Sdes			cur_rr = ldns_rr_list_rr(todo_nsec3s, i);
687238104Sdes			status = ldns_dnssec_zone_add_rr(newzone, cur_rr);
688238104Sdes		}
689269257Sdes	}
690269257Sdes	if (ldns_rr_list_rr_count(todo_nsec3_rrsigs) > 0) {
691246854Sdes		for (i = 0; status == LDNS_STATUS_OK &&
692246854Sdes				i < ldns_rr_list_rr_count(todo_nsec3_rrsigs);
693246854Sdes				i++){
694238104Sdes			cur_rr = ldns_rr_list_rr(todo_nsec3_rrsigs, i);
695238104Sdes			status = ldns_dnssec_zone_add_rr(newzone, cur_rr);
696238104Sdes		}
697238104Sdes	}
698238104Sdes
699238104Sdes	if (z) {
700238104Sdes		*z = newzone;
701246854Sdes		newzone = NULL;
702238104Sdes	} else {
703238104Sdes		ldns_dnssec_zone_free(newzone);
704238104Sdes	}
705238104Sdes
706238104Sdeserror:
707238104Sdes#ifdef FASTER_DNSSEC_ZONE_NEW_FRM_FP
708238104Sdes	if (zone) {
709238104Sdes		ldns_zone_free(zone);
710238104Sdes	}
711238104Sdes#endif
712246854Sdes	ldns_rr_list_free(todo_nsec3_rrsigs);
713246854Sdes	ldns_rr_list_free(todo_nsec3s);
714246854Sdes
715238104Sdes	if (my_origin) {
716238104Sdes		ldns_rdf_deep_free(my_origin);
717238104Sdes	}
718238104Sdes	if (my_prev) {
719238104Sdes		ldns_rdf_deep_free(my_prev);
720238104Sdes	}
721238104Sdes	if (newzone) {
722238104Sdes		ldns_dnssec_zone_free(newzone);
723238104Sdes	}
724238104Sdes	return status;
725238104Sdes}
726238104Sdes
727238104Sdesldns_status
728238104Sdesldns_dnssec_zone_new_frm_fp(ldns_dnssec_zone** z, FILE* fp, ldns_rdf* origin,
729238104Sdes		uint32_t ttl, ldns_rr_class ATTR_UNUSED(c))
730238104Sdes{
731238104Sdes	return ldns_dnssec_zone_new_frm_fp_l(z, fp, origin, ttl, c, NULL);
732238104Sdes}
733238104Sdes
734246827Sdesstatic void
735238104Sdesldns_dnssec_name_node_free(ldns_rbnode_t *node, void *arg) {
736238104Sdes	(void) arg;
737238104Sdes	ldns_dnssec_name_free((ldns_dnssec_name *)node->data);
738238104Sdes	LDNS_FREE(node);
739238104Sdes}
740238104Sdes
741246827Sdesstatic void
742238104Sdesldns_dnssec_name_node_deep_free(ldns_rbnode_t *node, void *arg) {
743238104Sdes	(void) arg;
744238104Sdes	ldns_dnssec_name_deep_free((ldns_dnssec_name *)node->data);
745238104Sdes	LDNS_FREE(node);
746238104Sdes}
747238104Sdes
748238104Sdesvoid
749238104Sdesldns_dnssec_zone_free(ldns_dnssec_zone *zone)
750238104Sdes{
751238104Sdes	if (zone) {
752238104Sdes		if (zone->names) {
753238104Sdes			/* destroy all name structures within the tree */
754238104Sdes			ldns_traverse_postorder(zone->names,
755238104Sdes						    ldns_dnssec_name_node_free,
756238104Sdes						    NULL);
757238104Sdes			LDNS_FREE(zone->names);
758238104Sdes		}
759238104Sdes		LDNS_FREE(zone);
760238104Sdes	}
761238104Sdes}
762238104Sdes
763238104Sdesvoid
764238104Sdesldns_dnssec_zone_deep_free(ldns_dnssec_zone *zone)
765238104Sdes{
766238104Sdes	if (zone) {
767238104Sdes		if (zone->names) {
768238104Sdes			/* destroy all name structures within the tree */
769238104Sdes			ldns_traverse_postorder(zone->names,
770238104Sdes						    ldns_dnssec_name_node_deep_free,
771238104Sdes						    NULL);
772238104Sdes			LDNS_FREE(zone->names);
773238104Sdes		}
774238104Sdes		LDNS_FREE(zone);
775238104Sdes	}
776238104Sdes}
777238104Sdes
778238104Sdes/* use for dname comparison in tree */
779249307Sdesint
780238104Sdesldns_dname_compare_v(const void *a, const void *b) {
781238104Sdes	return ldns_dname_compare((ldns_rdf *)a, (ldns_rdf *)b);
782238104Sdes}
783238104Sdes
784269257Sdesstatic void
785269257Sdesldns_dnssec_name_make_hashed_name(ldns_dnssec_zone *zone,
786269257Sdes		ldns_dnssec_name* name, ldns_rr* nsec3rr);
787238104Sdes
788269257Sdesstatic void
789269257Sdesldns_hashed_names_node_free(ldns_rbnode_t *node, void *arg) {
790269257Sdes	(void) arg;
791269257Sdes	LDNS_FREE(node);
792269257Sdes}
793238104Sdes
794269257Sdesstatic void
795269257Sdesldns_dnssec_zone_hashed_names_from_nsec3(
796269257Sdes		ldns_dnssec_zone* zone, ldns_rr* nsec3rr)
797269257Sdes{
798269257Sdes	ldns_rbnode_t* current_node;
799269257Sdes	ldns_dnssec_name* current_name;
800269257Sdes
801269257Sdes	assert(zone != NULL);
802269257Sdes	assert(nsec3rr != NULL);
803269257Sdes
804269257Sdes	if (zone->hashed_names) {
805269257Sdes		ldns_traverse_postorder(zone->hashed_names,
806269257Sdes				ldns_hashed_names_node_free, NULL);
807269257Sdes		LDNS_FREE(zone->hashed_names);
808269257Sdes	}
809269257Sdes	zone->_nsec3params = nsec3rr;
810269257Sdes
811269257Sdes	/* So this is a NSEC3 zone.
812269257Sdes	* Calculate hashes for all names already in the zone
813269257Sdes	*/
814269257Sdes	zone->hashed_names = ldns_rbtree_create(ldns_dname_compare_v);
815269257Sdes	if (zone->hashed_names == NULL) {
816269257Sdes		return;
817269257Sdes	}
818269257Sdes	for ( current_node  = ldns_rbtree_first(zone->names)
819269257Sdes	    ; current_node != LDNS_RBTREE_NULL
820269257Sdes	    ; current_node  = ldns_rbtree_next(current_node)
821269257Sdes	    ) {
822238104Sdes		current_name = (ldns_dnssec_name *) current_node->data;
823269257Sdes		ldns_dnssec_name_make_hashed_name(zone, current_name, nsec3rr);
824269257Sdes
825269257Sdes	}
826269257Sdes}
827269257Sdes
828269257Sdesstatic void
829269257Sdesldns_dnssec_name_make_hashed_name(ldns_dnssec_zone *zone,
830269257Sdes		ldns_dnssec_name* name, ldns_rr* nsec3rr)
831269257Sdes{
832269257Sdes	ldns_rbnode_t* new_node;
833269257Sdes
834269257Sdes	assert(name != NULL);
835269257Sdes	if (! zone->_nsec3params) {
836269257Sdes		if (! nsec3rr) {
837269257Sdes			return;
838238104Sdes		}
839269257Sdes		ldns_dnssec_zone_hashed_names_from_nsec3(zone, nsec3rr);
840269257Sdes
841269257Sdes	} else if (! nsec3rr) {
842269257Sdes		nsec3rr = zone->_nsec3params;
843269257Sdes	}
844269257Sdes	name->hashed_name = ldns_nsec3_hash_name_frm_nsec3(nsec3rr, name->name);
845269257Sdes
846269257Sdes	/* Also store in zone->hashed_names */
847269257Sdes	if ((new_node = LDNS_MALLOC(ldns_rbnode_t))) {
848269257Sdes
849269257Sdes		new_node->key  = name->hashed_name;
850269257Sdes		new_node->data = name;
851269257Sdes
852269257Sdes		if (ldns_rbtree_insert(zone->hashed_names, new_node) == NULL) {
853269257Sdes
854269257Sdes				LDNS_FREE(new_node);
855238104Sdes		}
856238104Sdes	}
857238104Sdes}
858238104Sdes
859269257Sdes
860269257Sdesstatic ldns_rbnode_t *
861269257Sdesldns_dnssec_zone_find_nsec3_original(ldns_dnssec_zone *zone, ldns_rr *rr) {
862269257Sdes	ldns_rdf *hashed_name;
863269257Sdes
864269257Sdes	hashed_name = ldns_dname_label(ldns_rr_owner(rr), 0);
865269257Sdes	if (hashed_name == NULL) {
866269257Sdes		return NULL;
867269257Sdes	}
868269257Sdes	if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_NSEC3 && ! zone->_nsec3params){
869269257Sdes
870269257Sdes		ldns_dnssec_zone_hashed_names_from_nsec3(zone, rr);
871269257Sdes	}
872269257Sdes	if (zone->hashed_names == NULL) {
873269257Sdes		ldns_rdf_deep_free(hashed_name);
874269257Sdes		return NULL;
875269257Sdes	}
876269257Sdes	return  ldns_rbtree_search(zone->hashed_names, hashed_name);
877269257Sdes}
878269257Sdes
879238104Sdesldns_status
880238104Sdesldns_dnssec_zone_add_rr(ldns_dnssec_zone *zone, ldns_rr *rr)
881238104Sdes{
882238104Sdes	ldns_status result = LDNS_STATUS_OK;
883238104Sdes	ldns_dnssec_name *cur_name;
884238104Sdes	ldns_rbnode_t *cur_node;
885238104Sdes	ldns_rr_type type_covered = 0;
886238104Sdes
887238104Sdes	if (!zone || !rr) {
888238104Sdes		return LDNS_STATUS_ERR;
889238104Sdes	}
890238104Sdes
891238104Sdes	if (!zone->names) {
892238104Sdes		zone->names = ldns_rbtree_create(ldns_dname_compare_v);
893238104Sdes                if(!zone->names) return LDNS_STATUS_MEM_ERR;
894238104Sdes	}
895238104Sdes
896238104Sdes	/* we need the original of the hashed name if this is
897238104Sdes	   an NSEC3, or an RRSIG that covers an NSEC3 */
898238104Sdes	if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_RRSIG) {
899238104Sdes		type_covered = ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(rr));
900238104Sdes	}
901238104Sdes	if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_NSEC3 ||
902238104Sdes	    type_covered == LDNS_RR_TYPE_NSEC3) {
903269257Sdes		cur_node = ldns_dnssec_zone_find_nsec3_original(zone, rr);
904238104Sdes		if (!cur_node) {
905238104Sdes			return LDNS_STATUS_DNSSEC_NSEC3_ORIGINAL_NOT_FOUND;
906238104Sdes		}
907238104Sdes	} else {
908238104Sdes		cur_node = ldns_rbtree_search(zone->names, ldns_rr_owner(rr));
909238104Sdes	}
910238104Sdes	if (!cur_node) {
911238104Sdes		/* add */
912238104Sdes		cur_name = ldns_dnssec_name_new_frm_rr(rr);
913238104Sdes                if(!cur_name) return LDNS_STATUS_MEM_ERR;
914238104Sdes		cur_node = LDNS_MALLOC(ldns_rbnode_t);
915238104Sdes                if(!cur_node) {
916238104Sdes                        ldns_dnssec_name_free(cur_name);
917238104Sdes                        return LDNS_STATUS_MEM_ERR;
918238104Sdes                }
919238104Sdes		cur_node->key = ldns_rr_owner(rr);
920238104Sdes		cur_node->data = cur_name;
921238104Sdes		(void)ldns_rbtree_insert(zone->names, cur_node);
922269257Sdes		ldns_dnssec_name_make_hashed_name(zone, cur_name, NULL);
923238104Sdes	} else {
924238104Sdes		cur_name = (ldns_dnssec_name *) cur_node->data;
925238104Sdes		result = ldns_dnssec_name_add_rr(cur_name, rr);
926238104Sdes	}
927238104Sdes	if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) {
928238104Sdes		zone->soa = cur_name;
929238104Sdes	}
930238104Sdes	return result;
931238104Sdes}
932238104Sdes
933238104Sdesvoid
934238104Sdesldns_dnssec_zone_names_print_fmt(FILE *out, const ldns_output_format *fmt,
935238104Sdes		ldns_rbtree_t *tree,
936238104Sdes		bool print_soa)
937238104Sdes{
938238104Sdes	ldns_rbnode_t *node;
939238104Sdes	ldns_dnssec_name *name;
940238104Sdes
941238104Sdes	node = ldns_rbtree_first(tree);
942238104Sdes	while (node != LDNS_RBTREE_NULL) {
943238104Sdes		name = (ldns_dnssec_name *) node->data;
944238104Sdes		ldns_dnssec_name_print_soa_fmt(out, fmt, name, print_soa);
945238104Sdes		if ((fmt->flags & LDNS_COMMENT_LAYOUT))
946238104Sdes			fprintf(out, ";\n");
947238104Sdes		node = ldns_rbtree_next(node);
948238104Sdes	}
949238104Sdes}
950238104Sdes
951238104Sdesvoid
952238104Sdesldns_dnssec_zone_names_print(FILE *out, ldns_rbtree_t *tree, bool print_soa)
953238104Sdes{
954238104Sdes	ldns_dnssec_zone_names_print_fmt(out, ldns_output_format_default,
955238104Sdes		       tree, print_soa);
956238104Sdes}
957238104Sdes
958238104Sdesvoid
959238104Sdesldns_dnssec_zone_print_fmt(FILE *out, const ldns_output_format *fmt,
960238104Sdes	       ldns_dnssec_zone *zone)
961238104Sdes{
962238104Sdes	if (zone) {
963238104Sdes		if (zone->soa) {
964238104Sdes			if ((fmt->flags & LDNS_COMMENT_LAYOUT)) {
965238104Sdes				fprintf(out, ";; Zone: ");
966238104Sdes				ldns_rdf_print(out, ldns_dnssec_name_name(
967238104Sdes							zone->soa));
968238104Sdes				fprintf(out, "\n;\n");
969238104Sdes			}
970238104Sdes			ldns_dnssec_rrsets_print_fmt(out, fmt,
971238104Sdes					ldns_dnssec_name_find_rrset(
972238104Sdes						zone->soa,
973238104Sdes						LDNS_RR_TYPE_SOA),
974238104Sdes					false);
975238104Sdes			if ((fmt->flags & LDNS_COMMENT_LAYOUT))
976238104Sdes				fprintf(out, ";\n");
977238104Sdes		}
978238104Sdes
979238104Sdes		if (zone->names) {
980238104Sdes			ldns_dnssec_zone_names_print_fmt(out, fmt,
981238104Sdes					zone->names, false);
982238104Sdes		}
983238104Sdes	}
984238104Sdes}
985238104Sdes
986238104Sdesvoid
987238104Sdesldns_dnssec_zone_print(FILE *out, ldns_dnssec_zone *zone)
988238104Sdes{
989238104Sdes	ldns_dnssec_zone_print_fmt(out, ldns_output_format_default, zone);
990238104Sdes}
991238104Sdes
992238104Sdesldns_status
993238104Sdesldns_dnssec_zone_add_empty_nonterminals(ldns_dnssec_zone *zone)
994238104Sdes{
995238104Sdes	ldns_dnssec_name *new_name;
996238104Sdes	ldns_rdf *cur_name;
997238104Sdes	ldns_rdf *next_name;
998238104Sdes	ldns_rbnode_t *cur_node, *next_node, *new_node;
999238104Sdes
1000238104Sdes	/* for the detection */
1001238104Sdes	uint16_t i, cur_label_count, next_label_count;
1002238104Sdes	uint16_t soa_label_count = 0;
1003238104Sdes	ldns_rdf *l1, *l2;
1004238104Sdes	int lpos;
1005238104Sdes
1006238104Sdes	if (!zone) {
1007238104Sdes		return LDNS_STATUS_ERR;
1008238104Sdes	}
1009238104Sdes	if (zone->soa && zone->soa->name) {
1010238104Sdes		soa_label_count = ldns_dname_label_count(zone->soa->name);
1011238104Sdes	}
1012238104Sdes
1013238104Sdes	cur_node = ldns_rbtree_first(zone->names);
1014238104Sdes	while (cur_node != LDNS_RBTREE_NULL) {
1015238104Sdes		next_node = ldns_rbtree_next(cur_node);
1016238104Sdes
1017238104Sdes		/* skip glue */
1018238104Sdes		while (next_node != LDNS_RBTREE_NULL &&
1019238104Sdes		       next_node->data &&
1020238104Sdes		       ((ldns_dnssec_name *)next_node->data)->is_glue
1021238104Sdes		) {
1022238104Sdes			next_node = ldns_rbtree_next(next_node);
1023238104Sdes		}
1024238104Sdes
1025238104Sdes		if (next_node == LDNS_RBTREE_NULL) {
1026238104Sdes			next_node = ldns_rbtree_first(zone->names);
1027238104Sdes		}
1028246854Sdes		if (! cur_node->data || ! next_node->data) {
1029246854Sdes			return LDNS_STATUS_ERR;
1030246854Sdes		}
1031238104Sdes		cur_name = ((ldns_dnssec_name *)cur_node->data)->name;
1032238104Sdes		next_name = ((ldns_dnssec_name *)next_node->data)->name;
1033238104Sdes		cur_label_count = ldns_dname_label_count(cur_name);
1034238104Sdes		next_label_count = ldns_dname_label_count(next_name);
1035238104Sdes
1036238104Sdes		/* Since the names are in canonical order, we can
1037238104Sdes		 * recognize empty non-terminals by their labels;
1038238104Sdes		 * every label after the first one on the next owner
1039238104Sdes		 * name is a non-terminal if it either does not exist
1040238104Sdes		 * in the current name or is different from the same
1041238104Sdes		 * label in the current name (counting from the end)
1042238104Sdes		 */
1043238104Sdes		for (i = 1; i < next_label_count - soa_label_count; i++) {
1044238104Sdes			lpos = (int)cur_label_count - (int)next_label_count + (int)i;
1045238104Sdes			if (lpos >= 0) {
1046238104Sdes				l1 = ldns_dname_clone_from(cur_name, (uint8_t)lpos);
1047238104Sdes			} else {
1048238104Sdes				l1 = NULL;
1049238104Sdes			}
1050238104Sdes			l2 = ldns_dname_clone_from(next_name, i);
1051238104Sdes
1052238104Sdes			if (!l1 || ldns_dname_compare(l1, l2) != 0) {
1053238104Sdes				/* We have an empty nonterminal, add it to the
1054238104Sdes				 * tree
1055238104Sdes				 */
1056238104Sdes				new_name = ldns_dnssec_name_new();
1057238104Sdes				if (!new_name) {
1058238104Sdes					return LDNS_STATUS_MEM_ERR;
1059238104Sdes				}
1060238104Sdes				new_name->name = ldns_dname_clone_from(next_name,
1061238104Sdes				                                       i);
1062238104Sdes				if (!new_name->name) {
1063238104Sdes					ldns_dnssec_name_free(new_name);
1064238104Sdes					return LDNS_STATUS_MEM_ERR;
1065238104Sdes				}
1066238104Sdes				new_name->name_alloced = true;
1067238104Sdes				new_node = LDNS_MALLOC(ldns_rbnode_t);
1068238104Sdes				if (!new_node) {
1069238104Sdes					ldns_dnssec_name_free(new_name);
1070238104Sdes					return LDNS_STATUS_MEM_ERR;
1071238104Sdes				}
1072238104Sdes				new_node->key = new_name->name;
1073238104Sdes				new_node->data = new_name;
1074238104Sdes				(void)ldns_rbtree_insert(zone->names, new_node);
1075269257Sdes				ldns_dnssec_name_make_hashed_name(
1076269257Sdes						zone, new_name, NULL);
1077238104Sdes			}
1078238104Sdes			ldns_rdf_deep_free(l1);
1079238104Sdes			ldns_rdf_deep_free(l2);
1080238104Sdes		}
1081238104Sdes
1082238104Sdes		/* we might have inserted a new node after
1083238104Sdes		 * the current one so we can't just use next()
1084238104Sdes		 */
1085238104Sdes		if (next_node != ldns_rbtree_first(zone->names)) {
1086238104Sdes			cur_node = next_node;
1087238104Sdes		} else {
1088238104Sdes			cur_node = LDNS_RBTREE_NULL;
1089238104Sdes		}
1090238104Sdes	}
1091238104Sdes	return LDNS_STATUS_OK;
1092238104Sdes}
1093238104Sdes
1094238104Sdesbool
1095238104Sdesldns_dnssec_zone_is_nsec3_optout(ldns_dnssec_zone* zone)
1096238104Sdes{
1097238104Sdes	ldns_rr* nsec3;
1098238104Sdes	ldns_rbnode_t* node;
1099238104Sdes
1100238104Sdes	if (ldns_dnssec_name_find_rrset(zone->soa, LDNS_RR_TYPE_NSEC3PARAM)) {
1101238104Sdes		node = ldns_rbtree_first(zone->names);
1102238104Sdes		while (node != LDNS_RBTREE_NULL) {
1103238104Sdes			nsec3 = ((ldns_dnssec_name*)node->data)->nsec;
1104238104Sdes			if (nsec3 &&ldns_rr_get_type(nsec3)
1105238104Sdes					== LDNS_RR_TYPE_NSEC3 &&
1106238104Sdes					ldns_nsec3_optout(nsec3)) {
1107238104Sdes				return true;
1108238104Sdes			}
1109238104Sdes			node = ldns_rbtree_next(node);
1110238104Sdes		}
1111238104Sdes	}
1112238104Sdes	return false;
1113238104Sdes}
1114