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