1 /*
2  * Copyright (c) 2009 NLNet Labs. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
19  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
21  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26 
27 /**
28  *
29  * The zonelist and all.
30  */
31 
32 #include "config.h"
33 #include "parser/confparser.h"
34 #include "parser/zonelistparser.h"
35 #include "duration.h"
36 #include "file.h"
37 #include "log.h"
38 #include "status.h"
39 #include "signer/zone.h"
40 #include "signer/zonelist.h"
41 
42 #include <ldns/ldns.h>
43 #include <stdlib.h>
44 
45 static const char* zl_str = "zonelist";
46 
47 
48 /**
49  * Compare two zones.
50  *
51  */
52 static int
zone_compare(const void * a,const void * b)53 zone_compare(const void* a, const void* b)
54 {
55     zone_type* x = (zone_type*)a;
56     zone_type* y = (zone_type*)b;
57     ods_log_assert(x);
58     ods_log_assert(y);
59     if (x->klass != y->klass) {
60         if (x->klass < y->klass) {
61             return -1;
62         }
63         return 1;
64     }
65     return ldns_dname_compare(x->apex, y->apex);
66 }
67 
68 
69 /**
70  * Create a new zone list.
71  *
72  */
73 zonelist_type*
zonelist_create()74 zonelist_create()
75 {
76     zonelist_type* zlist = NULL;
77         CHECKALLOC(zlist = (zonelist_type*) malloc(sizeof(zonelist_type)));
78     if (!zlist) {
79         ods_log_error("[%s] unable to create zonelist: allocator_alloc() "
80             "failed", zl_str);
81         return NULL;
82     }
83     zlist->zones = ldns_rbtree_create(zone_compare);
84     if (!zlist->zones) {
85         ods_log_error("[%s] unable to create zonelist: ldns_rbtree_create() "
86             "failed", zl_str);
87         free(zlist);
88         return NULL;
89     }
90     zlist->last_modified = 0;
91     pthread_mutex_init(&zlist->zl_lock, NULL);
92     return zlist;
93 }
94 
95 
96 /**
97  * Read a zonelist file.
98  *
99  */
100 static ods_status
zonelist_read(zonelist_type * zl,const char * zlfile)101 zonelist_read(zonelist_type* zl, const char* zlfile)
102 {
103     const char* rngfile = ODS_SE_RNGDIR "/zonelist.rng";
104     ods_status status = ODS_STATUS_OK;
105     ods_log_assert(zlfile);
106     ods_log_verbose("[%s] read file %s", zl_str, zlfile);
107     status = parse_file_check(zlfile, rngfile);
108     if (status != ODS_STATUS_OK) {
109         ods_log_error("[%s] unable to read file: parse error in %s", zl_str,
110             zlfile);
111         return status;
112     }
113     return parse_zonelist_zones((struct zonelist_struct*) zl, zlfile);
114 }
115 
116 
117 /**
118  * Convert a zone to a tree node.
119  *
120  */
121 static ldns_rbnode_t*
zone2node(zone_type * zone)122 zone2node(zone_type* zone)
123 {
124     ldns_rbnode_t* node = (ldns_rbnode_t*) malloc(sizeof(ldns_rbnode_t));
125     if (!node) {
126         return NULL;
127     }
128     node->key = zone;
129     node->data = zone;
130     return node;
131 }
132 
133 
134 /**
135  * Lookup zone.
136  *
137  */
138 static zone_type*
zonelist_lookup_zone(zonelist_type * zonelist,zone_type * zone)139 zonelist_lookup_zone(zonelist_type* zonelist, zone_type* zone)
140 {
141     ldns_rbnode_t* node = LDNS_RBTREE_NULL;
142     if (zonelist && zonelist->zones && zone) {
143         node = ldns_rbtree_search(zonelist->zones, zone);
144         if (node) {
145             return (zone_type*) node->data;
146         }
147     }
148     return NULL;
149 }
150 
151 
152 /**
153  * Lookup zone by name.
154  *
155  */
156 zone_type*
zonelist_lookup_zone_by_name(zonelist_type * zonelist,const char * name,ldns_rr_class klass)157 zonelist_lookup_zone_by_name(zonelist_type* zonelist, const char* name,
158     ldns_rr_class klass)
159 {
160     zone_type* zone = NULL;
161     zone_type* result = NULL;
162     if (zonelist && zonelist->zones && name  && klass) {
163         zone = zone_create((char*) name, klass);
164         if (!zone) {
165             ods_log_error("[%s] unable to lookup zone %s: "
166                 "zone_create() failed", zl_str, name);
167             /* result stays NULL */
168         } else {
169             result = zonelist_lookup_zone(zonelist, zone);
170             zone_cleanup(zone);
171         }
172     }
173     return result;
174 }
175 
176 
177 /**
178  * Lookup zone by dname.
179  *
180  */
181 zone_type*
zonelist_lookup_zone_by_dname(zonelist_type * zonelist,ldns_rdf * dname,ldns_rr_class klass)182 zonelist_lookup_zone_by_dname(zonelist_type* zonelist, ldns_rdf* dname,
183     ldns_rr_class klass)
184 {
185     char* name = NULL;
186     zone_type* result = NULL;
187     if (zonelist && zonelist->zones && dname && klass) {
188         name = ldns_rdf2str(dname);
189         result = zonelist_lookup_zone_by_name(zonelist, name, klass);
190         free((void*)name);
191     }
192     return result;
193 }
194 
195 
196 /**
197  * Add zone.
198  *
199  */
200 zone_type*
zonelist_add_zone(zonelist_type * zlist,zone_type * zone)201 zonelist_add_zone(zonelist_type* zlist, zone_type* zone)
202 {
203     ldns_rbnode_t* new_node = NULL;
204     if (!zone) {
205         return NULL;
206     }
207     if (!zlist || !zlist->zones) {
208         zone_cleanup(zone);
209         return NULL;
210     }
211     /* look up */
212     if (zonelist_lookup_zone(zlist, zone) != NULL) {
213         ods_log_warning("[%s] unable to add zone %s: already present", zl_str,
214             zone->name);
215         zone_cleanup(zone);
216         return NULL;
217     }
218     /* add */
219     new_node = zone2node(zone);
220     if (ldns_rbtree_insert(zlist->zones, new_node) == NULL) {
221         ods_log_error("[%s] unable to add zone %s: ldns_rbtree_insert() "
222             "failed", zl_str, zone->name);
223         free((void*) new_node);
224         zone_cleanup(zone);
225         return NULL;
226     }
227     zone->zl_status = ZONE_ZL_ADDED;
228     zlist->just_added++;
229     return zone;
230 }
231 
232 
233 /**
234  * Delete zone.
235  *
236  */
237 void
zonelist_del_zone(zonelist_type * zlist,zone_type * zone)238 zonelist_del_zone(zonelist_type* zlist, zone_type* zone)
239 {
240     ldns_rbnode_t* old_node = LDNS_RBTREE_NULL;
241     assert(zone);
242     if (!zlist || !zlist->zones) {
243         goto zone_not_present;
244     }
245     old_node = ldns_rbtree_delete(zlist->zones, zone);
246     if (!old_node) {
247         goto zone_not_present;
248     }
249     free((void*) old_node);
250     return;
251 
252 zone_not_present:
253     ods_log_warning("[%s] unable to delete zone %s: not present", zl_str,
254         zone->name);
255 }
256 
257 
258 /**
259  * Merge zone lists.
260  *
261  */
262 static void
zonelist_merge(zonelist_type * zl1,zonelist_type * zl2)263 zonelist_merge(zonelist_type* zl1, zonelist_type* zl2)
264 {
265     zone_type* z1 = NULL;
266     zone_type* z2 = NULL;
267     ldns_rbnode_t* n1 = LDNS_RBTREE_NULL;
268     ldns_rbnode_t* n2 = LDNS_RBTREE_NULL;
269     int ret = 0;
270 
271     ods_log_assert(zl1);
272     ods_log_assert(zl2);
273     ods_log_assert(zl1->zones);
274     ods_log_assert(zl2->zones);
275     ods_log_debug("[%s] merge two zone lists", zl_str);
276 
277     n1 = ldns_rbtree_first(zl1->zones);
278     n2 = ldns_rbtree_first(zl2->zones);
279     while (n2 && n2 != LDNS_RBTREE_NULL) {
280         z2 = (zone_type*) n2->data;
281         if (n1 && n1 != LDNS_RBTREE_NULL) {
282             z1 = (zone_type*) n1->data;
283         } else {
284             z1 = NULL;
285         }
286         if (!z2) {
287             /* no more zones to merge into zl1 */
288             return;
289         } else if (!z1) {
290             /* just add remaining zones from zl2 */
291             z2 = zonelist_add_zone(zl1, z2);
292             if (!z2) {
293                 ods_log_crit("[%s] merge failed: z2 not added", zl_str);
294                 return;
295             }
296             n2 = ldns_rbtree_next(n2);
297         } else {
298             /* compare the zones z1 and z2 */
299             ret = zone_compare(z1, z2);
300             if (ret < 0) {
301                 /* remove zone z1, it is not present in the new list zl2 */
302                 z1->zl_status = ZONE_ZL_REMOVED;
303                 zl1->just_removed++;
304                 n1 = ldns_rbtree_next(n1);
305             } else if (ret > 0) {
306                 /* add the new zone z2 */
307                 z2 = zonelist_add_zone(zl1, z2);
308                 if (!z2) {
309                     ods_log_crit("[%s] merge failed: z2 not added", zl_str);
310                     return;
311                 }
312                 n2 = ldns_rbtree_next(n2);
313             } else {
314                 /* just update zone z1 */
315                 n1 = ldns_rbtree_next(n1);
316                 n2 = ldns_rbtree_next(n2);
317                 zone_merge(z1, z2);
318                 zone_cleanup(z2);
319                 if (z1->zl_status == ZONE_ZL_UPDATED) {
320                     zl1->just_updated++;
321                 }
322                 z1->zl_status = ZONE_ZL_UPDATED;
323             }
324         }
325     }
326     /* remove remaining zones from z1 */
327     while (n1 && n1 != LDNS_RBTREE_NULL) {
328         z1 = (zone_type*) n1->data;
329         z1->zl_status = ZONE_ZL_REMOVED;
330         zl1->just_removed++;
331         n1 = ldns_rbtree_next(n1);
332     }
333     zl1->last_modified = zl2->last_modified;
334 }
335 
336 
337 /**
338  * Update zone list.
339  *
340  */
341 ods_status
zonelist_update(zonelist_type * zl,const char * zlfile)342 zonelist_update(zonelist_type* zl, const char* zlfile)
343 {
344     zonelist_type* new_zlist = NULL;
345     time_t st_mtime = 0;
346     ods_status status = ODS_STATUS_OK;
347     char* datestamp = NULL;
348 
349     ods_log_debug("[%s] update zone list", zl_str);
350     if (!zl|| !zl->zones || !zlfile) {
351         return ODS_STATUS_ASSERT_ERR;
352     }
353     /* is the file updated? */
354     /* OPENDNSSEC-686: changes happening within one second will not be
355      * seen
356      */
357     st_mtime = ods_file_lastmodified(zlfile);
358     if (st_mtime <= zl->last_modified) {
359         (void)time_datestamp(zl->last_modified, "%Y-%m-%d %T", &datestamp);
360         ods_log_debug("[%s] zonelist file %s is unchanged since %s",
361             zl_str, zlfile, datestamp?datestamp:"Unknown");
362         free((void*)datestamp);
363         return ODS_STATUS_UNCHANGED;
364     }
365     /* create new zonelist */
366     new_zlist = zonelist_create();
367     if (!new_zlist) {
368         ods_log_error("[%s] unable to update zonelist: zonelist_create() "
369             "failed", zl_str);
370         return ODS_STATUS_ERR;
371     }
372     /* read zonelist */
373     status = zonelist_read(new_zlist, zlfile);
374     if (status == ODS_STATUS_OK) {
375         zl->just_removed = 0;
376         zl->just_added = 0;
377         zl->just_updated = 0;
378         new_zlist->last_modified = st_mtime;
379         zonelist_merge(zl, new_zlist);
380         (void)time_datestamp(zl->last_modified, "%Y-%m-%d %T", &datestamp);
381         ods_log_debug("[%s] file %s is modified since %s", zl_str, zlfile,
382             datestamp?datestamp:"Unknown");
383         free((void*)datestamp);
384     } else {
385         ods_log_error("[%s] unable to update zonelist: read file %s failed "
386             "(%s)", zl_str, zlfile, ods_status2str(status));
387     }
388     zonelist_free(new_zlist);
389     return status;
390 }
391 
392 
393 /**
394  * Internal zone cleanup function.
395  *
396  */
397 static void
zone_delfunc(ldns_rbnode_t * elem)398 zone_delfunc(ldns_rbnode_t* elem)
399 {
400     zone_type* zone;
401     if (elem && elem != LDNS_RBTREE_NULL) {
402         zone = (zone_type*) elem->data;
403         zone_delfunc(elem->left);
404         zone_delfunc(elem->right);
405         ods_log_deeebug("[%s] cleanup zone %s", zl_str, zone->name);
406         zone_cleanup(zone);
407         free((void*)elem);
408     }
409 }
410 
411 
412 /**
413  * Internal node cleanup function.
414  *
415  */
416 static void
node_delfunc(ldns_rbnode_t * elem)417 node_delfunc(ldns_rbnode_t* elem)
418 {
419     if (elem && elem != LDNS_RBTREE_NULL) {
420         node_delfunc(elem->left);
421         node_delfunc(elem->right);
422         free((void*)elem);
423     }
424 }
425 
426 
427 /**
428  * Clean up a zonelist.
429  *
430  */
431 void
zonelist_cleanup(zonelist_type * zl)432 zonelist_cleanup(zonelist_type* zl)
433 {
434     if (!zl) {
435         return;
436     }
437     ods_log_debug("[%s] cleanup zonelist", zl_str);
438     if (zl->zones) {
439         zone_delfunc(zl->zones->root);
440         ldns_rbtree_free(zl->zones);
441         zl->zones = NULL;
442     }
443     pthread_mutex_destroy(&zl->zl_lock);
444     free(zl);
445 }
446 
447 
448 /**
449  * Free zonelist.
450  *
451  */
452 void
zonelist_free(zonelist_type * zl)453 zonelist_free(zonelist_type* zl)
454 {
455     if (!zl) {
456         return;
457     }
458     if (zl->zones) {
459         node_delfunc(zl->zones->root);
460         ldns_rbtree_free(zl->zones);
461         zl->zones = NULL;
462     }
463     pthread_mutex_destroy(&zl->zl_lock);
464     free(zl);
465 }
466