]>
Commit | Line | Data |
---|---|---|
89c4ed63 A |
1 | /* |
2 | * services/localzone.c - local zones authority service. | |
3 | * | |
4 | * Copyright (c) 2007, NLnet Labs. All rights reserved. | |
5 | * | |
6 | * This software is open source. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * | |
12 | * Redistributions of source code must retain the above copyright notice, | |
13 | * this list of conditions and the following disclaimer. | |
14 | * | |
15 | * Redistributions in binary form must reproduce the above copyright notice, | |
16 | * this list of conditions and the following disclaimer in the documentation | |
17 | * and/or other materials provided with the distribution. | |
18 | * | |
19 | * Neither the name of the NLNET LABS nor the names of its contributors may | |
20 | * be used to endorse or promote products derived from this software without | |
21 | * specific prior written permission. | |
22 | * | |
23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
26 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
27 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | |
29 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
30 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
31 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
32 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
33 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
34 | */ | |
35 | ||
36 | /** | |
37 | * \file | |
38 | * | |
39 | * This file contains functions to enable local zone authority service. | |
40 | */ | |
41 | #include "config.h" | |
42 | #include "services/localzone.h" | |
43 | #include "ldns/str2wire.h" | |
44 | #include "ldns/sbuffer.h" | |
45 | #include "util/regional.h" | |
46 | #include "util/config_file.h" | |
47 | #include "util/data/dname.h" | |
48 | #include "util/data/packed_rrset.h" | |
49 | #include "util/data/msgencode.h" | |
50 | #include "util/net_help.h" | |
51 | #include "util/data/msgreply.h" | |
52 | #include "util/data/msgparse.h" | |
53 | ||
54 | struct local_zones* | |
55 | local_zones_create(void) | |
56 | { | |
57 | struct local_zones* zones = (struct local_zones*)calloc(1, | |
58 | sizeof(*zones)); | |
59 | if(!zones) | |
60 | return NULL; | |
61 | rbtree_init(&zones->ztree, &local_zone_cmp); | |
62 | lock_rw_init(&zones->lock); | |
63 | lock_protect(&zones->lock, &zones->ztree, sizeof(zones->ztree)); | |
64 | /* also lock protects the rbnode's in struct local_zone */ | |
65 | return zones; | |
66 | } | |
67 | ||
68 | /** helper traverse to delete zones */ | |
69 | static void | |
70 | lzdel(rbnode_t* n, void* ATTR_UNUSED(arg)) | |
71 | { | |
72 | struct local_zone* z = (struct local_zone*)n->key; | |
73 | local_zone_delete(z); | |
74 | } | |
75 | ||
76 | void | |
77 | local_zones_delete(struct local_zones* zones) | |
78 | { | |
79 | if(!zones) | |
80 | return; | |
81 | lock_rw_destroy(&zones->lock); | |
82 | /* walk through zones and delete them all */ | |
83 | traverse_postorder(&zones->ztree, lzdel, NULL); | |
84 | free(zones); | |
85 | } | |
86 | ||
87 | void | |
88 | local_zone_delete(struct local_zone* z) | |
89 | { | |
90 | if(!z) | |
91 | return; | |
92 | lock_rw_destroy(&z->lock); | |
93 | regional_destroy(z->region); | |
94 | free(z->name); | |
95 | free(z); | |
96 | } | |
97 | ||
98 | int | |
99 | local_zone_cmp(const void* z1, const void* z2) | |
100 | { | |
101 | /* first sort on class, so that hierarchy can be maintained within | |
102 | * a class */ | |
103 | struct local_zone* a = (struct local_zone*)z1; | |
104 | struct local_zone* b = (struct local_zone*)z2; | |
105 | int m; | |
106 | if(a->dclass != b->dclass) { | |
107 | if(a->dclass < b->dclass) | |
108 | return -1; | |
109 | return 1; | |
110 | } | |
111 | return dname_lab_cmp(a->name, a->namelabs, b->name, b->namelabs, &m); | |
112 | } | |
113 | ||
114 | int | |
115 | local_data_cmp(const void* d1, const void* d2) | |
116 | { | |
117 | struct local_data* a = (struct local_data*)d1; | |
118 | struct local_data* b = (struct local_data*)d2; | |
119 | int m; | |
120 | return dname_canon_lab_cmp(a->name, a->namelabs, b->name, | |
121 | b->namelabs, &m); | |
122 | } | |
123 | ||
124 | /* form wireformat from text format domain name */ | |
125 | int | |
126 | parse_dname(const char* str, uint8_t** res, size_t* len, int* labs) | |
127 | { | |
128 | *res = sldns_str2wire_dname(str, len); | |
129 | *labs = 0; | |
130 | if(!*res) { | |
131 | log_err("cannot parse name %s", str); | |
132 | return 0; | |
133 | } | |
134 | *labs = dname_count_size_labels(*res, len); | |
135 | return 1; | |
136 | } | |
137 | ||
138 | /** create a new localzone */ | |
139 | static struct local_zone* | |
140 | local_zone_create(uint8_t* nm, size_t len, int labs, | |
141 | enum localzone_type t, uint16_t dclass) | |
142 | { | |
143 | struct local_zone* z = (struct local_zone*)calloc(1, sizeof(*z)); | |
144 | if(!z) { | |
145 | return NULL; | |
146 | } | |
147 | z->node.key = z; | |
148 | z->dclass = dclass; | |
149 | z->type = t; | |
150 | z->name = nm; | |
151 | z->namelen = len; | |
152 | z->namelabs = labs; | |
153 | lock_rw_init(&z->lock); | |
154 | z->region = regional_create(); | |
155 | if(!z->region) { | |
156 | free(z); | |
157 | return NULL; | |
158 | } | |
159 | rbtree_init(&z->data, &local_data_cmp); | |
160 | lock_protect(&z->lock, &z->parent, sizeof(*z)-sizeof(rbnode_t)); | |
161 | /* also the zones->lock protects node, parent, name*, class */ | |
162 | return z; | |
163 | } | |
164 | ||
165 | /** enter a new zone with allocated dname returns with WRlock */ | |
166 | static struct local_zone* | |
167 | lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len, | |
168 | int labs, enum localzone_type t, uint16_t c) | |
169 | { | |
170 | struct local_zone* z = local_zone_create(nm, len, labs, t, c); | |
171 | if(!z) { | |
172 | log_err("out of memory"); | |
173 | return NULL; | |
174 | } | |
175 | ||
176 | /* add to rbtree */ | |
177 | lock_rw_wrlock(&zones->lock); | |
178 | lock_rw_wrlock(&z->lock); | |
179 | if(!rbtree_insert(&zones->ztree, &z->node)) { | |
180 | log_warn("duplicate local-zone"); | |
181 | lock_rw_unlock(&z->lock); | |
182 | local_zone_delete(z); | |
183 | lock_rw_unlock(&zones->lock); | |
184 | return NULL; | |
185 | } | |
186 | lock_rw_unlock(&zones->lock); | |
187 | return z; | |
188 | } | |
189 | ||
190 | /** enter a new zone */ | |
191 | static struct local_zone* | |
192 | lz_enter_zone(struct local_zones* zones, const char* name, const char* type, | |
193 | uint16_t dclass) | |
194 | { | |
195 | struct local_zone* z; | |
196 | enum localzone_type t; | |
197 | uint8_t* nm; | |
198 | size_t len; | |
199 | int labs; | |
200 | if(!parse_dname(name, &nm, &len, &labs)) { | |
201 | log_err("bad zone name %s %s", name, type); | |
202 | return NULL; | |
203 | } | |
204 | if(!local_zone_str2type(type, &t)) { | |
205 | log_err("bad lz_enter_zone type %s %s", name, type); | |
206 | free(nm); | |
207 | return NULL; | |
208 | } | |
209 | if(!(z=lz_enter_zone_dname(zones, nm, len, labs, t, dclass))) { | |
210 | log_err("could not enter zone %s %s", name, type); | |
211 | return NULL; | |
212 | } | |
213 | return z; | |
214 | } | |
215 | ||
216 | /** return name and class and rdata of rr; parses string */ | |
217 | static int | |
218 | get_rr_content(const char* str, uint8_t** nm, uint16_t* type, | |
219 | uint16_t* dclass, time_t* ttl, uint8_t* rr, size_t len, | |
220 | uint8_t** rdata, size_t* rdata_len) | |
221 | { | |
222 | size_t dname_len = 0; | |
223 | int e = sldns_str2wire_rr_buf(str, rr, &len, &dname_len, 3600, | |
224 | NULL, 0, NULL, 0); | |
225 | if(e) { | |
226 | log_err("error parsing local-data at %d: '%s': %s", | |
227 | LDNS_WIREPARSE_OFFSET(e), str, | |
228 | sldns_get_errorstr_parse(e)); | |
229 | return 0; | |
230 | } | |
231 | *nm = memdup(rr, dname_len); | |
232 | if(!*nm) { | |
233 | log_err("out of memory"); | |
234 | return 0; | |
235 | } | |
236 | *dclass = sldns_wirerr_get_class(rr, len, dname_len); | |
237 | *type = sldns_wirerr_get_type(rr, len, dname_len); | |
238 | *ttl = (time_t)sldns_wirerr_get_ttl(rr, len, dname_len); | |
239 | *rdata = sldns_wirerr_get_rdatawl(rr, len, dname_len); | |
240 | *rdata_len = sldns_wirerr_get_rdatalen(rr, len, dname_len)+2; | |
241 | return 1; | |
242 | } | |
243 | ||
244 | /** return name and class of rr; parses string */ | |
245 | static int | |
246 | get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass) | |
247 | { | |
248 | uint8_t rr[LDNS_RR_BUF_SIZE]; | |
249 | size_t len = sizeof(rr), dname_len = 0; | |
250 | int s = sldns_str2wire_rr_buf(str, rr, &len, &dname_len, 3600, | |
251 | NULL, 0, NULL, 0); | |
252 | if(s != 0) { | |
253 | log_err("error parsing local-data at %d '%s': %s", | |
254 | LDNS_WIREPARSE_OFFSET(s), str, | |
255 | sldns_get_errorstr_parse(s)); | |
256 | return 0; | |
257 | } | |
258 | *nm = memdup(rr, dname_len); | |
259 | *dclass = sldns_wirerr_get_class(rr, len, dname_len); | |
260 | if(!*nm) { | |
261 | log_err("out of memory"); | |
262 | return 0; | |
263 | } | |
264 | return 1; | |
265 | } | |
266 | ||
267 | /** | |
268 | * Find an rrset in local data structure. | |
269 | * @param data: local data domain name structure. | |
270 | * @param type: type to look for (host order). | |
271 | * @return rrset pointer or NULL if not found. | |
272 | */ | |
273 | static struct local_rrset* | |
274 | local_data_find_type(struct local_data* data, uint16_t type) | |
275 | { | |
276 | struct local_rrset* p; | |
277 | type = htons(type); | |
278 | for(p = data->rrsets; p; p = p->next) { | |
279 | if(p->rrset->rk.type == type) | |
280 | return p; | |
281 | } | |
282 | return NULL; | |
283 | } | |
284 | ||
285 | /** check for RR duplicates */ | |
286 | static int | |
287 | rr_is_duplicate(struct packed_rrset_data* pd, uint8_t* rdata, size_t rdata_len) | |
288 | { | |
289 | size_t i; | |
290 | for(i=0; i<pd->count; i++) { | |
291 | if(pd->rr_len[i] == rdata_len && | |
292 | memcmp(pd->rr_data[i], rdata, rdata_len) == 0) | |
293 | return 1; | |
294 | } | |
295 | return 0; | |
296 | } | |
297 | ||
298 | /** new local_rrset */ | |
299 | static struct local_rrset* | |
300 | new_local_rrset(struct regional* region, struct local_data* node, | |
301 | uint16_t rrtype, uint16_t rrclass) | |
302 | { | |
303 | struct packed_rrset_data* pd; | |
304 | struct local_rrset* rrset = (struct local_rrset*) | |
305 | regional_alloc_zero(region, sizeof(*rrset)); | |
306 | if(!rrset) { | |
307 | log_err("out of memory"); | |
308 | return NULL; | |
309 | } | |
310 | rrset->next = node->rrsets; | |
311 | node->rrsets = rrset; | |
312 | rrset->rrset = (struct ub_packed_rrset_key*) | |
313 | regional_alloc_zero(region, sizeof(*rrset->rrset)); | |
314 | if(!rrset->rrset) { | |
315 | log_err("out of memory"); | |
316 | return NULL; | |
317 | } | |
318 | rrset->rrset->entry.key = rrset->rrset; | |
319 | pd = (struct packed_rrset_data*)regional_alloc_zero(region, | |
320 | sizeof(*pd)); | |
321 | if(!pd) { | |
322 | log_err("out of memory"); | |
323 | return NULL; | |
324 | } | |
325 | pd->trust = rrset_trust_prim_noglue; | |
326 | pd->security = sec_status_insecure; | |
327 | rrset->rrset->entry.data = pd; | |
328 | rrset->rrset->rk.dname = node->name; | |
329 | rrset->rrset->rk.dname_len = node->namelen; | |
330 | rrset->rrset->rk.type = htons(rrtype); | |
331 | rrset->rrset->rk.rrset_class = htons(rrclass); | |
332 | return rrset; | |
333 | } | |
334 | ||
335 | /** insert RR into RRset data structure; Wastes a couple of bytes */ | |
336 | static int | |
337 | insert_rr(struct regional* region, struct packed_rrset_data* pd, | |
338 | uint8_t* rdata, size_t rdata_len, time_t ttl) | |
339 | { | |
340 | size_t* oldlen = pd->rr_len; | |
341 | time_t* oldttl = pd->rr_ttl; | |
342 | uint8_t** olddata = pd->rr_data; | |
343 | ||
344 | /* add RR to rrset */ | |
345 | pd->count++; | |
346 | pd->rr_len = regional_alloc(region, sizeof(*pd->rr_len)*pd->count); | |
347 | pd->rr_ttl = regional_alloc(region, sizeof(*pd->rr_ttl)*pd->count); | |
348 | pd->rr_data = regional_alloc(region, sizeof(*pd->rr_data)*pd->count); | |
349 | if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) { | |
350 | log_err("out of memory"); | |
351 | return 0; | |
352 | } | |
353 | if(pd->count > 1) { | |
354 | memcpy(pd->rr_len+1, oldlen, | |
355 | sizeof(*pd->rr_len)*(pd->count-1)); | |
356 | memcpy(pd->rr_ttl+1, oldttl, | |
357 | sizeof(*pd->rr_ttl)*(pd->count-1)); | |
358 | memcpy(pd->rr_data+1, olddata, | |
359 | sizeof(*pd->rr_data)*(pd->count-1)); | |
360 | } | |
361 | pd->rr_len[0] = rdata_len; | |
362 | pd->rr_ttl[0] = ttl; | |
363 | pd->rr_data[0] = regional_alloc_init(region, rdata, rdata_len); | |
364 | if(!pd->rr_data[0]) { | |
365 | log_err("out of memory"); | |
366 | return 0; | |
367 | } | |
368 | return 1; | |
369 | } | |
370 | ||
371 | /** find a data node by exact name */ | |
372 | static struct local_data* | |
373 | lz_find_node(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs) | |
374 | { | |
375 | struct local_data key; | |
376 | key.node.key = &key; | |
377 | key.name = nm; | |
378 | key.namelen = nmlen; | |
379 | key.namelabs = nmlabs; | |
380 | return (struct local_data*)rbtree_search(&z->data, &key.node); | |
381 | } | |
382 | ||
383 | /** find a node, create it if not and all its empty nonterminal parents */ | |
384 | static int | |
385 | lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen, | |
386 | int nmlabs, struct local_data** res) | |
387 | { | |
388 | struct local_data* ld = lz_find_node(z, nm, nmlen, nmlabs); | |
389 | if(!ld) { | |
390 | /* create a domain name to store rr. */ | |
391 | ld = (struct local_data*)regional_alloc_zero(z->region, | |
392 | sizeof(*ld)); | |
393 | if(!ld) { | |
394 | log_err("out of memory adding local data"); | |
395 | return 0; | |
396 | } | |
397 | ld->node.key = ld; | |
398 | ld->name = regional_alloc_init(z->region, nm, nmlen); | |
399 | if(!ld->name) { | |
400 | log_err("out of memory"); | |
401 | return 0; | |
402 | } | |
403 | ld->namelen = nmlen; | |
404 | ld->namelabs = nmlabs; | |
405 | if(!rbtree_insert(&z->data, &ld->node)) { | |
406 | log_assert(0); /* duplicate name */ | |
407 | } | |
408 | /* see if empty nonterminals need to be created */ | |
409 | if(nmlabs > z->namelabs) { | |
410 | dname_remove_label(&nm, &nmlen); | |
411 | if(!lz_find_create_node(z, nm, nmlen, nmlabs-1, res)) | |
412 | return 0; | |
413 | } | |
414 | } | |
415 | *res = ld; | |
416 | return 1; | |
417 | } | |
418 | ||
419 | /** enter data RR into auth zone */ | |
420 | static int | |
421 | lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr) | |
422 | { | |
423 | uint8_t* nm; | |
424 | size_t nmlen; | |
425 | int nmlabs; | |
426 | struct local_data* node; | |
427 | struct local_rrset* rrset; | |
428 | struct packed_rrset_data* pd; | |
429 | uint16_t rrtype = 0, rrclass = 0; | |
430 | time_t ttl = 0; | |
431 | uint8_t rr[LDNS_RR_BUF_SIZE]; | |
432 | uint8_t* rdata; | |
433 | size_t rdata_len; | |
434 | if(!get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr, sizeof(rr), | |
435 | &rdata, &rdata_len)) { | |
436 | log_err("bad local-data: %s", rrstr); | |
437 | return 0; | |
438 | } | |
439 | log_assert(z->dclass == rrclass); | |
440 | if(z->type == local_zone_redirect && | |
441 | query_dname_compare(z->name, nm) != 0) { | |
442 | log_err("local-data in redirect zone must reside at top of zone" | |
443 | ", not at %s", rrstr); | |
444 | free(nm); | |
445 | return 0; | |
446 | } | |
447 | nmlabs = dname_count_size_labels(nm, &nmlen); | |
448 | if(!lz_find_create_node(z, nm, nmlen, nmlabs, &node)) { | |
449 | free(nm); | |
450 | return 0; | |
451 | } | |
452 | log_assert(node); | |
453 | free(nm); | |
454 | ||
455 | rrset = local_data_find_type(node, rrtype); | |
456 | if(!rrset) { | |
457 | rrset = new_local_rrset(z->region, node, rrtype, rrclass); | |
458 | if(!rrset) | |
459 | return 0; | |
460 | if(query_dname_compare(node->name, z->name) == 0) { | |
461 | if(rrtype == LDNS_RR_TYPE_NSEC) | |
462 | rrset->rrset->rk.flags = PACKED_RRSET_NSEC_AT_APEX; | |
463 | if(rrtype == LDNS_RR_TYPE_SOA) | |
464 | z->soa = rrset->rrset; | |
465 | } | |
466 | } | |
467 | pd = (struct packed_rrset_data*)rrset->rrset->entry.data; | |
468 | log_assert(rrset && pd); | |
469 | ||
470 | /* check for duplicate RR */ | |
471 | if(rr_is_duplicate(pd, rdata, rdata_len)) { | |
472 | verbose(VERB_ALGO, "ignoring duplicate RR: %s", rrstr); | |
473 | return 1; | |
474 | } | |
475 | return insert_rr(z->region, pd, rdata, rdata_len, ttl); | |
476 | } | |
477 | ||
478 | /** enter a data RR into auth data; a zone for it must exist */ | |
479 | static int | |
480 | lz_enter_rr_str(struct local_zones* zones, const char* rr) | |
481 | { | |
482 | uint8_t* rr_name; | |
483 | uint16_t rr_class; | |
484 | size_t len; | |
485 | int labs; | |
486 | struct local_zone* z; | |
487 | int r; | |
488 | if(!get_rr_nameclass(rr, &rr_name, &rr_class)) { | |
489 | log_err("bad rr %s", rr); | |
490 | return 0; | |
491 | } | |
492 | labs = dname_count_size_labels(rr_name, &len); | |
493 | lock_rw_rdlock(&zones->lock); | |
494 | z = local_zones_lookup(zones, rr_name, len, labs, rr_class); | |
495 | if(!z) { | |
496 | lock_rw_unlock(&zones->lock); | |
497 | fatal_exit("internal error: no zone for rr %s", rr); | |
498 | } | |
499 | lock_rw_wrlock(&z->lock); | |
500 | lock_rw_unlock(&zones->lock); | |
501 | free(rr_name); | |
502 | r = lz_enter_rr_into_zone(z, rr); | |
503 | lock_rw_unlock(&z->lock); | |
504 | return r; | |
505 | } | |
506 | ||
507 | /** parse local-zone: statements */ | |
508 | static int | |
509 | lz_enter_zones(struct local_zones* zones, struct config_file* cfg) | |
510 | { | |
511 | struct config_str2list* p; | |
512 | struct local_zone* z; | |
513 | for(p = cfg->local_zones; p; p = p->next) { | |
514 | if(!(z=lz_enter_zone(zones, p->str, p->str2, | |
515 | LDNS_RR_CLASS_IN))) | |
516 | return 0; | |
517 | lock_rw_unlock(&z->lock); | |
518 | } | |
519 | return 1; | |
520 | } | |
521 | ||
522 | /** lookup a zone in rbtree; exact match only; SLOW due to parse */ | |
523 | static int | |
524 | lz_exists(struct local_zones* zones, const char* name) | |
525 | { | |
526 | struct local_zone z; | |
527 | z.node.key = &z; | |
528 | z.dclass = LDNS_RR_CLASS_IN; | |
529 | if(!parse_dname(name, &z.name, &z.namelen, &z.namelabs)) { | |
530 | log_err("bad name %s", name); | |
531 | return 0; | |
532 | } | |
533 | lock_rw_rdlock(&zones->lock); | |
534 | if(rbtree_search(&zones->ztree, &z.node)) { | |
535 | lock_rw_unlock(&zones->lock); | |
536 | free(z.name); | |
537 | return 1; | |
538 | } | |
539 | lock_rw_unlock(&zones->lock); | |
540 | free(z.name); | |
541 | return 0; | |
542 | } | |
543 | ||
544 | /** lookup a zone in cfg->nodefault list */ | |
545 | static int | |
546 | lz_nodefault(struct config_file* cfg, const char* name) | |
547 | { | |
548 | struct config_strlist* p; | |
549 | size_t len = strlen(name); | |
550 | if(len == 0) return 0; | |
551 | if(name[len-1] == '.') len--; | |
552 | ||
553 | for(p = cfg->local_zones_nodefault; p; p = p->next) { | |
554 | /* compare zone name, lowercase, compare without ending . */ | |
555 | if(strncasecmp(p->str, name, len) == 0 && | |
556 | (strlen(p->str) == len || (strlen(p->str)==len+1 && | |
557 | p->str[len] == '.'))) | |
558 | return 1; | |
559 | } | |
560 | return 0; | |
561 | } | |
562 | ||
563 | /** enter AS112 default zone */ | |
564 | static int | |
565 | add_as112_default(struct local_zones* zones, struct config_file* cfg, | |
566 | const char* name) | |
567 | { | |
568 | struct local_zone* z; | |
569 | char str[1024]; /* known long enough */ | |
570 | if(lz_exists(zones, name) || lz_nodefault(cfg, name)) | |
571 | return 1; /* do not enter default content */ | |
572 | if(!(z=lz_enter_zone(zones, name, "static", LDNS_RR_CLASS_IN))) | |
573 | return 0; | |
574 | snprintf(str, sizeof(str), "%s 10800 IN SOA localhost. " | |
575 | "nobody.invalid. 1 3600 1200 604800 10800", name); | |
576 | if(!lz_enter_rr_into_zone(z, str)) { | |
577 | lock_rw_unlock(&z->lock); | |
578 | return 0; | |
579 | } | |
580 | snprintf(str, sizeof(str), "%s 10800 IN NS localhost. ", name); | |
581 | if(!lz_enter_rr_into_zone(z, str)) { | |
582 | lock_rw_unlock(&z->lock); | |
583 | return 0; | |
584 | } | |
585 | lock_rw_unlock(&z->lock); | |
586 | return 1; | |
587 | } | |
588 | ||
589 | /** enter default zones */ | |
590 | static int | |
591 | lz_enter_defaults(struct local_zones* zones, struct config_file* cfg) | |
592 | { | |
593 | struct local_zone* z; | |
594 | ||
595 | /* this list of zones is from RFC 6303 */ | |
596 | ||
597 | /* block localhost level zones, first, later the LAN zones */ | |
598 | ||
599 | /* localhost. zone */ | |
600 | if(!lz_exists(zones, "localhost.") && | |
601 | !lz_nodefault(cfg, "localhost.")) { | |
602 | if(!(z=lz_enter_zone(zones, "localhost.", "static", | |
603 | LDNS_RR_CLASS_IN)) || | |
604 | !lz_enter_rr_into_zone(z, | |
605 | "localhost. 10800 IN NS localhost.") || | |
606 | !lz_enter_rr_into_zone(z, | |
607 | "localhost. 10800 IN SOA localhost. nobody.invalid. " | |
608 | "1 3600 1200 604800 10800") || | |
609 | !lz_enter_rr_into_zone(z, | |
610 | "localhost. 10800 IN A 127.0.0.1") || | |
611 | !lz_enter_rr_into_zone(z, | |
612 | "localhost. 10800 IN AAAA ::1")) { | |
613 | log_err("out of memory adding default zone"); | |
614 | if(z) { lock_rw_unlock(&z->lock); } | |
615 | return 0; | |
616 | } | |
617 | lock_rw_unlock(&z->lock); | |
618 | } | |
619 | /* reverse ip4 zone */ | |
620 | if(!lz_exists(zones, "127.in-addr.arpa.") && | |
621 | !lz_nodefault(cfg, "127.in-addr.arpa.")) { | |
622 | if(!(z=lz_enter_zone(zones, "127.in-addr.arpa.", "static", | |
623 | LDNS_RR_CLASS_IN)) || | |
624 | !lz_enter_rr_into_zone(z, | |
625 | "127.in-addr.arpa. 10800 IN NS localhost.") || | |
626 | !lz_enter_rr_into_zone(z, | |
627 | "127.in-addr.arpa. 10800 IN SOA localhost. " | |
628 | "nobody.invalid. 1 3600 1200 604800 10800") || | |
629 | !lz_enter_rr_into_zone(z, | |
630 | "1.0.0.127.in-addr.arpa. 10800 IN PTR localhost.")) { | |
631 | log_err("out of memory adding default zone"); | |
632 | if(z) { lock_rw_unlock(&z->lock); } | |
633 | return 0; | |
634 | } | |
635 | lock_rw_unlock(&z->lock); | |
636 | } | |
637 | /* reverse ip6 zone */ | |
638 | if(!lz_exists(zones, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.") && | |
639 | !lz_nodefault(cfg, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.")) { | |
640 | if(!(z=lz_enter_zone(zones, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", "static", | |
641 | LDNS_RR_CLASS_IN)) || | |
642 | !lz_enter_rr_into_zone(z, | |
643 | "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN NS localhost.") || | |
644 | !lz_enter_rr_into_zone(z, | |
645 | "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN SOA localhost. " | |
646 | "nobody.invalid. 1 3600 1200 604800 10800") || | |
647 | !lz_enter_rr_into_zone(z, | |
648 | "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN PTR localhost.")) { | |
649 | log_err("out of memory adding default zone"); | |
650 | if(z) { lock_rw_unlock(&z->lock); } | |
651 | return 0; | |
652 | } | |
653 | lock_rw_unlock(&z->lock); | |
654 | } | |
655 | ||
656 | /* if unblock lan-zones, then do not add the zones below. | |
657 | * we do add the zones above, about 127.0.0.1, because localhost is | |
658 | * not on the lan. */ | |
659 | if(cfg->unblock_lan_zones) | |
660 | return 1; | |
661 | ||
662 | /* block LAN level zones */ | |
663 | if ( !add_as112_default(zones, cfg, "10.in-addr.arpa.") || | |
664 | !add_as112_default(zones, cfg, "16.172.in-addr.arpa.") || | |
665 | !add_as112_default(zones, cfg, "17.172.in-addr.arpa.") || | |
666 | !add_as112_default(zones, cfg, "18.172.in-addr.arpa.") || | |
667 | !add_as112_default(zones, cfg, "19.172.in-addr.arpa.") || | |
668 | !add_as112_default(zones, cfg, "20.172.in-addr.arpa.") || | |
669 | !add_as112_default(zones, cfg, "21.172.in-addr.arpa.") || | |
670 | !add_as112_default(zones, cfg, "22.172.in-addr.arpa.") || | |
671 | !add_as112_default(zones, cfg, "23.172.in-addr.arpa.") || | |
672 | !add_as112_default(zones, cfg, "24.172.in-addr.arpa.") || | |
673 | !add_as112_default(zones, cfg, "25.172.in-addr.arpa.") || | |
674 | !add_as112_default(zones, cfg, "26.172.in-addr.arpa.") || | |
675 | !add_as112_default(zones, cfg, "27.172.in-addr.arpa.") || | |
676 | !add_as112_default(zones, cfg, "28.172.in-addr.arpa.") || | |
677 | !add_as112_default(zones, cfg, "29.172.in-addr.arpa.") || | |
678 | !add_as112_default(zones, cfg, "30.172.in-addr.arpa.") || | |
679 | !add_as112_default(zones, cfg, "31.172.in-addr.arpa.") || | |
680 | !add_as112_default(zones, cfg, "168.192.in-addr.arpa.") || | |
681 | !add_as112_default(zones, cfg, "0.in-addr.arpa.") || | |
682 | !add_as112_default(zones, cfg, "64.100.in-addr.arpa.") || | |
683 | !add_as112_default(zones, cfg, "65.100.in-addr.arpa.") || | |
684 | !add_as112_default(zones, cfg, "66.100.in-addr.arpa.") || | |
685 | !add_as112_default(zones, cfg, "67.100.in-addr.arpa.") || | |
686 | !add_as112_default(zones, cfg, "68.100.in-addr.arpa.") || | |
687 | !add_as112_default(zones, cfg, "69.100.in-addr.arpa.") || | |
688 | !add_as112_default(zones, cfg, "70.100.in-addr.arpa.") || | |
689 | !add_as112_default(zones, cfg, "71.100.in-addr.arpa.") || | |
690 | !add_as112_default(zones, cfg, "72.100.in-addr.arpa.") || | |
691 | !add_as112_default(zones, cfg, "73.100.in-addr.arpa.") || | |
692 | !add_as112_default(zones, cfg, "74.100.in-addr.arpa.") || | |
693 | !add_as112_default(zones, cfg, "75.100.in-addr.arpa.") || | |
694 | !add_as112_default(zones, cfg, "76.100.in-addr.arpa.") || | |
695 | !add_as112_default(zones, cfg, "77.100.in-addr.arpa.") || | |
696 | !add_as112_default(zones, cfg, "78.100.in-addr.arpa.") || | |
697 | !add_as112_default(zones, cfg, "79.100.in-addr.arpa.") || | |
698 | !add_as112_default(zones, cfg, "80.100.in-addr.arpa.") || | |
699 | !add_as112_default(zones, cfg, "81.100.in-addr.arpa.") || | |
700 | !add_as112_default(zones, cfg, "82.100.in-addr.arpa.") || | |
701 | !add_as112_default(zones, cfg, "83.100.in-addr.arpa.") || | |
702 | !add_as112_default(zones, cfg, "84.100.in-addr.arpa.") || | |
703 | !add_as112_default(zones, cfg, "85.100.in-addr.arpa.") || | |
704 | !add_as112_default(zones, cfg, "86.100.in-addr.arpa.") || | |
705 | !add_as112_default(zones, cfg, "87.100.in-addr.arpa.") || | |
706 | !add_as112_default(zones, cfg, "88.100.in-addr.arpa.") || | |
707 | !add_as112_default(zones, cfg, "89.100.in-addr.arpa.") || | |
708 | !add_as112_default(zones, cfg, "90.100.in-addr.arpa.") || | |
709 | !add_as112_default(zones, cfg, "91.100.in-addr.arpa.") || | |
710 | !add_as112_default(zones, cfg, "92.100.in-addr.arpa.") || | |
711 | !add_as112_default(zones, cfg, "93.100.in-addr.arpa.") || | |
712 | !add_as112_default(zones, cfg, "94.100.in-addr.arpa.") || | |
713 | !add_as112_default(zones, cfg, "95.100.in-addr.arpa.") || | |
714 | !add_as112_default(zones, cfg, "96.100.in-addr.arpa.") || | |
715 | !add_as112_default(zones, cfg, "97.100.in-addr.arpa.") || | |
716 | !add_as112_default(zones, cfg, "98.100.in-addr.arpa.") || | |
717 | !add_as112_default(zones, cfg, "99.100.in-addr.arpa.") || | |
718 | !add_as112_default(zones, cfg, "100.100.in-addr.arpa.") || | |
719 | !add_as112_default(zones, cfg, "101.100.in-addr.arpa.") || | |
720 | !add_as112_default(zones, cfg, "102.100.in-addr.arpa.") || | |
721 | !add_as112_default(zones, cfg, "103.100.in-addr.arpa.") || | |
722 | !add_as112_default(zones, cfg, "104.100.in-addr.arpa.") || | |
723 | !add_as112_default(zones, cfg, "105.100.in-addr.arpa.") || | |
724 | !add_as112_default(zones, cfg, "106.100.in-addr.arpa.") || | |
725 | !add_as112_default(zones, cfg, "107.100.in-addr.arpa.") || | |
726 | !add_as112_default(zones, cfg, "108.100.in-addr.arpa.") || | |
727 | !add_as112_default(zones, cfg, "109.100.in-addr.arpa.") || | |
728 | !add_as112_default(zones, cfg, "110.100.in-addr.arpa.") || | |
729 | !add_as112_default(zones, cfg, "111.100.in-addr.arpa.") || | |
730 | !add_as112_default(zones, cfg, "112.100.in-addr.arpa.") || | |
731 | !add_as112_default(zones, cfg, "113.100.in-addr.arpa.") || | |
732 | !add_as112_default(zones, cfg, "114.100.in-addr.arpa.") || | |
733 | !add_as112_default(zones, cfg, "115.100.in-addr.arpa.") || | |
734 | !add_as112_default(zones, cfg, "116.100.in-addr.arpa.") || | |
735 | !add_as112_default(zones, cfg, "117.100.in-addr.arpa.") || | |
736 | !add_as112_default(zones, cfg, "118.100.in-addr.arpa.") || | |
737 | !add_as112_default(zones, cfg, "119.100.in-addr.arpa.") || | |
738 | !add_as112_default(zones, cfg, "120.100.in-addr.arpa.") || | |
739 | !add_as112_default(zones, cfg, "121.100.in-addr.arpa.") || | |
740 | !add_as112_default(zones, cfg, "122.100.in-addr.arpa.") || | |
741 | !add_as112_default(zones, cfg, "123.100.in-addr.arpa.") || | |
742 | !add_as112_default(zones, cfg, "124.100.in-addr.arpa.") || | |
743 | !add_as112_default(zones, cfg, "125.100.in-addr.arpa.") || | |
744 | !add_as112_default(zones, cfg, "126.100.in-addr.arpa.") || | |
745 | !add_as112_default(zones, cfg, "127.100.in-addr.arpa.") || | |
746 | !add_as112_default(zones, cfg, "254.169.in-addr.arpa.") || | |
747 | !add_as112_default(zones, cfg, "2.0.192.in-addr.arpa.") || | |
748 | !add_as112_default(zones, cfg, "100.51.198.in-addr.arpa.") || | |
749 | !add_as112_default(zones, cfg, "113.0.203.in-addr.arpa.") || | |
750 | !add_as112_default(zones, cfg, "255.255.255.255.in-addr.arpa.") || | |
751 | !add_as112_default(zones, cfg, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.") || | |
752 | !add_as112_default(zones, cfg, "d.f.ip6.arpa.") || | |
753 | !add_as112_default(zones, cfg, "8.e.f.ip6.arpa.") || | |
754 | !add_as112_default(zones, cfg, "9.e.f.ip6.arpa.") || | |
755 | !add_as112_default(zones, cfg, "a.e.f.ip6.arpa.") || | |
756 | !add_as112_default(zones, cfg, "b.e.f.ip6.arpa.") || | |
757 | !add_as112_default(zones, cfg, "8.b.d.0.1.0.0.2.ip6.arpa.")) { | |
758 | log_err("out of memory adding default zone"); | |
759 | return 0; | |
760 | } | |
761 | return 1; | |
762 | } | |
763 | ||
764 | /** setup parent pointers, so that a lookup can be done for closest match */ | |
765 | static void | |
766 | init_parents(struct local_zones* zones) | |
767 | { | |
768 | struct local_zone* node, *prev = NULL, *p; | |
769 | int m; | |
770 | lock_rw_wrlock(&zones->lock); | |
771 | RBTREE_FOR(node, struct local_zone*, &zones->ztree) { | |
772 | lock_rw_wrlock(&node->lock); | |
773 | node->parent = NULL; | |
774 | if(!prev || prev->dclass != node->dclass) { | |
775 | prev = node; | |
776 | lock_rw_unlock(&node->lock); | |
777 | continue; | |
778 | } | |
779 | (void)dname_lab_cmp(prev->name, prev->namelabs, node->name, | |
780 | node->namelabs, &m); /* we know prev is smaller */ | |
781 | /* sort order like: . com. bla.com. zwb.com. net. */ | |
782 | /* find the previous, or parent-parent-parent */ | |
783 | for(p = prev; p; p = p->parent) | |
784 | /* looking for name with few labels, a parent */ | |
785 | if(p->namelabs <= m) { | |
786 | /* ==: since prev matched m, this is closest*/ | |
787 | /* <: prev matches more, but is not a parent, | |
788 | * this one is a (grand)parent */ | |
789 | node->parent = p; | |
790 | break; | |
791 | } | |
792 | prev = node; | |
793 | lock_rw_unlock(&node->lock); | |
794 | } | |
795 | lock_rw_unlock(&zones->lock); | |
796 | } | |
797 | ||
798 | /** enter implicit transparent zone for local-data: without local-zone: */ | |
799 | static int | |
800 | lz_setup_implicit(struct local_zones* zones, struct config_file* cfg) | |
801 | { | |
802 | /* walk over all items that have no parent zone and find | |
803 | * the name that covers them all (could be the root) and | |
804 | * add that as a transparent zone */ | |
805 | struct config_strlist* p; | |
806 | int have_name = 0; | |
807 | int have_other_classes = 0; | |
808 | uint16_t dclass = 0; | |
809 | uint8_t* nm = 0; | |
810 | size_t nmlen = 0; | |
811 | int nmlabs = 0; | |
812 | int match = 0; /* number of labels match count */ | |
813 | ||
814 | init_parents(zones); /* to enable local_zones_lookup() */ | |
815 | for(p = cfg->local_data; p; p = p->next) { | |
816 | uint8_t* rr_name; | |
817 | uint16_t rr_class; | |
818 | size_t len; | |
819 | int labs; | |
820 | if(!get_rr_nameclass(p->str, &rr_name, &rr_class)) { | |
821 | log_err("Bad local-data RR %s", p->str); | |
822 | return 0; | |
823 | } | |
824 | labs = dname_count_size_labels(rr_name, &len); | |
825 | lock_rw_rdlock(&zones->lock); | |
826 | if(!local_zones_lookup(zones, rr_name, len, labs, rr_class)) { | |
827 | if(!have_name) { | |
828 | dclass = rr_class; | |
829 | nm = rr_name; | |
830 | nmlen = len; | |
831 | nmlabs = labs; | |
832 | match = labs; | |
833 | have_name = 1; | |
834 | } else { | |
835 | int m; | |
836 | if(rr_class != dclass) { | |
837 | /* process other classes later */ | |
838 | free(rr_name); | |
839 | have_other_classes = 1; | |
840 | lock_rw_unlock(&zones->lock); | |
841 | continue; | |
842 | } | |
843 | /* find smallest shared topdomain */ | |
844 | (void)dname_lab_cmp(nm, nmlabs, | |
845 | rr_name, labs, &m); | |
846 | free(rr_name); | |
847 | if(m < match) | |
848 | match = m; | |
849 | } | |
850 | } else free(rr_name); | |
851 | lock_rw_unlock(&zones->lock); | |
852 | } | |
853 | if(have_name) { | |
854 | uint8_t* n2; | |
855 | struct local_zone* z; | |
856 | /* allocate zone of smallest shared topdomain to contain em */ | |
857 | n2 = nm; | |
858 | dname_remove_labels(&n2, &nmlen, nmlabs - match); | |
859 | n2 = memdup(n2, nmlen); | |
860 | free(nm); | |
861 | if(!n2) { | |
862 | log_err("out of memory"); | |
863 | return 0; | |
864 | } | |
865 | log_nametypeclass(VERB_ALGO, "implicit transparent local-zone", | |
866 | n2, 0, dclass); | |
867 | if(!(z=lz_enter_zone_dname(zones, n2, nmlen, match, | |
868 | local_zone_transparent, dclass))) { | |
869 | return 0; | |
870 | } | |
871 | lock_rw_unlock(&z->lock); | |
872 | } | |
873 | if(have_other_classes) { | |
874 | /* restart to setup other class */ | |
875 | return lz_setup_implicit(zones, cfg); | |
876 | } | |
877 | return 1; | |
878 | } | |
879 | ||
880 | /** enter auth data */ | |
881 | static int | |
882 | lz_enter_data(struct local_zones* zones, struct config_file* cfg) | |
883 | { | |
884 | struct config_strlist* p; | |
885 | for(p = cfg->local_data; p; p = p->next) { | |
886 | if(!lz_enter_rr_str(zones, p->str)) | |
887 | return 0; | |
888 | } | |
889 | return 1; | |
890 | } | |
891 | ||
892 | /** free memory from config */ | |
893 | static void | |
894 | lz_freeup_cfg(struct config_file* cfg) | |
895 | { | |
896 | config_deldblstrlist(cfg->local_zones); | |
897 | cfg->local_zones = NULL; | |
898 | config_delstrlist(cfg->local_zones_nodefault); | |
899 | cfg->local_zones_nodefault = NULL; | |
900 | config_delstrlist(cfg->local_data); | |
901 | cfg->local_data = NULL; | |
902 | } | |
903 | ||
904 | int | |
905 | local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg) | |
906 | { | |
907 | /* create zones from zone statements. */ | |
908 | if(!lz_enter_zones(zones, cfg)) { | |
909 | return 0; | |
910 | } | |
911 | /* apply default zones+content (unless disabled, or overridden) */ | |
912 | if(!lz_enter_defaults(zones, cfg)) { | |
913 | return 0; | |
914 | } | |
915 | /* create implicit transparent zone from data. */ | |
916 | if(!lz_setup_implicit(zones, cfg)) { | |
917 | return 0; | |
918 | } | |
919 | ||
920 | /* setup parent ptrs for lookup during data entry */ | |
921 | init_parents(zones); | |
922 | /* insert local data */ | |
923 | if(!lz_enter_data(zones, cfg)) { | |
924 | return 0; | |
925 | } | |
926 | /* freeup memory from cfg struct. */ | |
927 | lz_freeup_cfg(cfg); | |
928 | return 1; | |
929 | } | |
930 | ||
931 | struct local_zone* | |
932 | local_zones_lookup(struct local_zones* zones, | |
933 | uint8_t* name, size_t len, int labs, uint16_t dclass) | |
934 | { | |
935 | rbnode_t* res = NULL; | |
936 | struct local_zone *result; | |
937 | struct local_zone key; | |
938 | key.node.key = &key; | |
939 | key.dclass = dclass; | |
940 | key.name = name; | |
941 | key.namelen = len; | |
942 | key.namelabs = labs; | |
943 | if(rbtree_find_less_equal(&zones->ztree, &key, &res)) { | |
944 | /* exact */ | |
945 | return (struct local_zone*)res; | |
946 | } else { | |
947 | /* smaller element (or no element) */ | |
948 | int m; | |
949 | result = (struct local_zone*)res; | |
950 | if(!result || result->dclass != dclass) | |
951 | return NULL; | |
952 | /* count number of labels matched */ | |
953 | (void)dname_lab_cmp(result->name, result->namelabs, key.name, | |
954 | key.namelabs, &m); | |
955 | while(result) { /* go up until qname is subdomain of zone */ | |
956 | if(result->namelabs <= m) | |
957 | break; | |
958 | result = result->parent; | |
959 | } | |
960 | return result; | |
961 | } | |
962 | } | |
963 | ||
964 | struct local_zone* | |
965 | local_zones_find(struct local_zones* zones, | |
966 | uint8_t* name, size_t len, int labs, uint16_t dclass) | |
967 | { | |
968 | struct local_zone key; | |
969 | key.node.key = &key; | |
970 | key.dclass = dclass; | |
971 | key.name = name; | |
972 | key.namelen = len; | |
973 | key.namelabs = labs; | |
974 | /* exact */ | |
975 | return (struct local_zone*)rbtree_search(&zones->ztree, &key); | |
976 | } | |
977 | ||
978 | /** print all RRsets in local zone */ | |
979 | static void | |
980 | local_zone_out(struct local_zone* z) | |
981 | { | |
982 | struct local_data* d; | |
983 | struct local_rrset* p; | |
984 | RBTREE_FOR(d, struct local_data*, &z->data) { | |
985 | for(p = d->rrsets; p; p = p->next) { | |
986 | log_nametypeclass(0, "rrset", d->name, | |
987 | ntohs(p->rrset->rk.type), | |
988 | ntohs(p->rrset->rk.rrset_class)); | |
989 | } | |
990 | } | |
991 | } | |
992 | ||
993 | void local_zones_print(struct local_zones* zones) | |
994 | { | |
995 | struct local_zone* z; | |
996 | lock_rw_rdlock(&zones->lock); | |
997 | log_info("number of auth zones %u", (unsigned)zones->ztree.count); | |
998 | RBTREE_FOR(z, struct local_zone*, &zones->ztree) { | |
999 | lock_rw_rdlock(&z->lock); | |
1000 | switch(z->type) { | |
1001 | case local_zone_deny: | |
1002 | log_nametypeclass(0, "deny zone", | |
1003 | z->name, 0, z->dclass); | |
1004 | break; | |
1005 | case local_zone_refuse: | |
1006 | log_nametypeclass(0, "refuse zone", | |
1007 | z->name, 0, z->dclass); | |
1008 | break; | |
1009 | case local_zone_redirect: | |
1010 | log_nametypeclass(0, "redirect zone", | |
1011 | z->name, 0, z->dclass); | |
1012 | break; | |
1013 | case local_zone_transparent: | |
1014 | log_nametypeclass(0, "transparent zone", | |
1015 | z->name, 0, z->dclass); | |
1016 | break; | |
1017 | case local_zone_typetransparent: | |
1018 | log_nametypeclass(0, "typetransparent zone", | |
1019 | z->name, 0, z->dclass); | |
1020 | break; | |
1021 | case local_zone_static: | |
1022 | log_nametypeclass(0, "static zone", | |
1023 | z->name, 0, z->dclass); | |
1024 | break; | |
1025 | default: | |
1026 | log_nametypeclass(0, "badtyped zone", | |
1027 | z->name, 0, z->dclass); | |
1028 | break; | |
1029 | } | |
1030 | local_zone_out(z); | |
1031 | lock_rw_unlock(&z->lock); | |
1032 | } | |
1033 | lock_rw_unlock(&zones->lock); | |
1034 | } | |
1035 | ||
1036 | /** encode answer consisting of 1 rrset */ | |
1037 | static int | |
1038 | local_encode(struct query_info* qinfo, struct edns_data* edns, | |
1039 | sldns_buffer* buf, struct regional* temp, | |
1040 | struct ub_packed_rrset_key* rrset, int ansec, int rcode) | |
1041 | { | |
1042 | struct reply_info rep; | |
1043 | uint16_t udpsize; | |
1044 | /* make answer with time=0 for fixed TTL values */ | |
1045 | memset(&rep, 0, sizeof(rep)); | |
1046 | rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode); | |
1047 | rep.qdcount = 1; | |
1048 | if(ansec) | |
1049 | rep.an_numrrsets = 1; | |
1050 | else rep.ns_numrrsets = 1; | |
1051 | rep.rrset_count = 1; | |
1052 | rep.rrsets = &rrset; | |
1053 | udpsize = edns->udp_size; | |
1054 | edns->edns_version = EDNS_ADVERTISED_VERSION; | |
1055 | edns->udp_size = EDNS_ADVERTISED_SIZE; | |
1056 | edns->ext_rcode = 0; | |
1057 | edns->bits &= EDNS_DO; | |
1058 | if(!reply_info_answer_encode(qinfo, &rep, | |
1059 | *(uint16_t*)sldns_buffer_begin(buf), | |
1060 | sldns_buffer_read_u16_at(buf, 2), | |
1061 | buf, 0, 0, temp, udpsize, edns, | |
1062 | (int)(edns->bits&EDNS_DO), 0)) | |
1063 | error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, | |
1064 | *(uint16_t*)sldns_buffer_begin(buf), | |
1065 | sldns_buffer_read_u16_at(buf, 2), edns); | |
1066 | return 1; | |
1067 | } | |
1068 | ||
1069 | /** answer local data match */ | |
1070 | static int | |
1071 | local_data_answer(struct local_zone* z, struct query_info* qinfo, | |
1072 | struct edns_data* edns, sldns_buffer* buf, struct regional* temp, | |
1073 | int labs, struct local_data** ldp) | |
1074 | { | |
1075 | struct local_data key; | |
1076 | struct local_data* ld; | |
1077 | struct local_rrset* lr; | |
1078 | key.node.key = &key; | |
1079 | key.name = qinfo->qname; | |
1080 | key.namelen = qinfo->qname_len; | |
1081 | key.namelabs = labs; | |
1082 | if(z->type == local_zone_redirect) { | |
1083 | key.name = z->name; | |
1084 | key.namelen = z->namelen; | |
1085 | key.namelabs = z->namelabs; | |
1086 | } | |
1087 | ld = (struct local_data*)rbtree_search(&z->data, &key.node); | |
1088 | *ldp = ld; | |
1089 | if(!ld) { | |
1090 | return 0; | |
1091 | } | |
1092 | lr = local_data_find_type(ld, qinfo->qtype); | |
1093 | if(!lr) | |
1094 | return 0; | |
1095 | if(z->type == local_zone_redirect) { | |
1096 | /* convert rrset name to query name; like a wildcard */ | |
1097 | struct ub_packed_rrset_key r = *lr->rrset; | |
1098 | r.rk.dname = qinfo->qname; | |
1099 | r.rk.dname_len = qinfo->qname_len; | |
1100 | return local_encode(qinfo, edns, buf, temp, &r, 1, | |
1101 | LDNS_RCODE_NOERROR); | |
1102 | } | |
1103 | return local_encode(qinfo, edns, buf, temp, lr->rrset, 1, | |
1104 | LDNS_RCODE_NOERROR); | |
1105 | } | |
1106 | ||
1107 | /** | |
1108 | * answer in case where no exact match is found | |
1109 | * @param z: zone for query | |
1110 | * @param qinfo: query | |
1111 | * @param edns: edns from query | |
1112 | * @param buf: buffer for answer. | |
1113 | * @param temp: temp region for encoding | |
1114 | * @param ld: local data, if NULL, no such name exists in localdata. | |
1115 | * @return 1 if a reply is to be sent, 0 if not. | |
1116 | */ | |
1117 | static int | |
1118 | lz_zone_answer(struct local_zone* z, struct query_info* qinfo, | |
1119 | struct edns_data* edns, sldns_buffer* buf, struct regional* temp, | |
1120 | struct local_data* ld) | |
1121 | { | |
1122 | if(z->type == local_zone_deny) { | |
1123 | /** no reply at all, signal caller by clearing buffer. */ | |
1124 | sldns_buffer_clear(buf); | |
1125 | sldns_buffer_flip(buf); | |
1126 | return 1; | |
1127 | } else if(z->type == local_zone_refuse) { | |
1128 | error_encode(buf, (LDNS_RCODE_REFUSED|BIT_AA), qinfo, | |
1129 | *(uint16_t*)sldns_buffer_begin(buf), | |
1130 | sldns_buffer_read_u16_at(buf, 2), edns); | |
1131 | return 1; | |
1132 | } else if(z->type == local_zone_static || | |
1133 | z->type == local_zone_redirect) { | |
1134 | /* for static, reply nodata or nxdomain | |
1135 | * for redirect, reply nodata */ | |
1136 | /* no additional section processing, | |
1137 | * cname, dname or wildcard processing, | |
1138 | * or using closest match for NSEC. | |
1139 | * or using closest match for returning delegation downwards | |
1140 | */ | |
1141 | int rcode = ld?LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN; | |
1142 | if(z->soa) | |
1143 | return local_encode(qinfo, edns, buf, temp, | |
1144 | z->soa, 0, rcode); | |
1145 | error_encode(buf, (rcode|BIT_AA), qinfo, | |
1146 | *(uint16_t*)sldns_buffer_begin(buf), | |
1147 | sldns_buffer_read_u16_at(buf, 2), edns); | |
1148 | return 1; | |
1149 | } else if(z->type == local_zone_typetransparent) { | |
1150 | /* no NODATA or NXDOMAINS for this zone type */ | |
1151 | return 0; | |
1152 | } | |
1153 | /* else z->type == local_zone_transparent */ | |
1154 | ||
1155 | /* if the zone is transparent and the name exists, but the type | |
1156 | * does not, then we should make this noerror/nodata */ | |
1157 | if(ld && ld->rrsets) { | |
1158 | int rcode = LDNS_RCODE_NOERROR; | |
1159 | if(z->soa) | |
1160 | return local_encode(qinfo, edns, buf, temp, | |
1161 | z->soa, 0, rcode); | |
1162 | error_encode(buf, (rcode|BIT_AA), qinfo, | |
1163 | *(uint16_t*)sldns_buffer_begin(buf), | |
1164 | sldns_buffer_read_u16_at(buf, 2), edns); | |
1165 | return 1; | |
1166 | } | |
1167 | ||
1168 | /* stop here, and resolve further on */ | |
1169 | return 0; | |
1170 | } | |
1171 | ||
1172 | int | |
1173 | local_zones_answer(struct local_zones* zones, struct query_info* qinfo, | |
1174 | struct edns_data* edns, sldns_buffer* buf, struct regional* temp) | |
1175 | { | |
1176 | /* see if query is covered by a zone, | |
1177 | * if so: - try to match (exact) local data | |
1178 | * - look at zone type for negative response. */ | |
1179 | int labs = dname_count_labels(qinfo->qname); | |
1180 | struct local_data* ld; | |
1181 | struct local_zone* z; | |
1182 | int r; | |
1183 | lock_rw_rdlock(&zones->lock); | |
1184 | z = local_zones_lookup(zones, qinfo->qname, | |
1185 | qinfo->qname_len, labs, qinfo->qclass); | |
1186 | if(!z) { | |
1187 | lock_rw_unlock(&zones->lock); | |
1188 | return 0; | |
1189 | } | |
1190 | lock_rw_rdlock(&z->lock); | |
1191 | lock_rw_unlock(&zones->lock); | |
1192 | ||
1193 | if(local_data_answer(z, qinfo, edns, buf, temp, labs, &ld)) { | |
1194 | lock_rw_unlock(&z->lock); | |
1195 | return 1; | |
1196 | } | |
1197 | r = lz_zone_answer(z, qinfo, edns, buf, temp, ld); | |
1198 | lock_rw_unlock(&z->lock); | |
1199 | return r; | |
1200 | } | |
1201 | ||
1202 | const char* local_zone_type2str(enum localzone_type t) | |
1203 | { | |
1204 | switch(t) { | |
1205 | case local_zone_deny: return "deny"; | |
1206 | case local_zone_refuse: return "refuse"; | |
1207 | case local_zone_redirect: return "redirect"; | |
1208 | case local_zone_transparent: return "transparent"; | |
1209 | case local_zone_typetransparent: return "typetransparent"; | |
1210 | case local_zone_static: return "static"; | |
1211 | case local_zone_nodefault: return "nodefault"; | |
1212 | } | |
1213 | return "badtyped"; | |
1214 | } | |
1215 | ||
1216 | int local_zone_str2type(const char* type, enum localzone_type* t) | |
1217 | { | |
1218 | if(strcmp(type, "deny") == 0) | |
1219 | *t = local_zone_deny; | |
1220 | else if(strcmp(type, "refuse") == 0) | |
1221 | *t = local_zone_refuse; | |
1222 | else if(strcmp(type, "static") == 0) | |
1223 | *t = local_zone_static; | |
1224 | else if(strcmp(type, "transparent") == 0) | |
1225 | *t = local_zone_transparent; | |
1226 | else if(strcmp(type, "typetransparent") == 0) | |
1227 | *t = local_zone_typetransparent; | |
1228 | else if(strcmp(type, "redirect") == 0) | |
1229 | *t = local_zone_redirect; | |
1230 | else return 0; | |
1231 | return 1; | |
1232 | } | |
1233 | ||
1234 | /** iterate over the kiddies of the given name and set their parent ptr */ | |
1235 | static void | |
1236 | set_kiddo_parents(struct local_zone* z, struct local_zone* match, | |
1237 | struct local_zone* newp) | |
1238 | { | |
1239 | /* both zones and z are locked already */ | |
1240 | /* in the sorted rbtree, the kiddies of z are located after z */ | |
1241 | /* z must be present in the tree */ | |
1242 | struct local_zone* p = z; | |
1243 | p = (struct local_zone*)rbtree_next(&p->node); | |
1244 | while(p!=(struct local_zone*)RBTREE_NULL && | |
1245 | p->dclass == z->dclass && dname_strict_subdomain(p->name, | |
1246 | p->namelabs, z->name, z->namelabs)) { | |
1247 | /* update parent ptr */ | |
1248 | /* only when matches with existing parent pointer, so that | |
1249 | * deeper child structures are not touched, i.e. | |
1250 | * update of x, and a.x, b.x, f.b.x, g.b.x, c.x, y | |
1251 | * gets to update a.x, b.x and c.x */ | |
1252 | lock_rw_wrlock(&p->lock); | |
1253 | if(p->parent == match) | |
1254 | p->parent = newp; | |
1255 | lock_rw_unlock(&p->lock); | |
1256 | p = (struct local_zone*)rbtree_next(&p->node); | |
1257 | } | |
1258 | } | |
1259 | ||
1260 | struct local_zone* local_zones_add_zone(struct local_zones* zones, | |
1261 | uint8_t* name, size_t len, int labs, uint16_t dclass, | |
1262 | enum localzone_type tp) | |
1263 | { | |
1264 | /* create */ | |
1265 | struct local_zone* z = local_zone_create(name, len, labs, tp, dclass); | |
1266 | if(!z) return NULL; | |
1267 | lock_rw_wrlock(&z->lock); | |
1268 | ||
1269 | /* find the closest parent */ | |
1270 | z->parent = local_zones_find(zones, name, len, labs, dclass); | |
1271 | ||
1272 | /* insert into the tree */ | |
1273 | if(!rbtree_insert(&zones->ztree, &z->node)) { | |
1274 | /* duplicate entry! */ | |
1275 | lock_rw_unlock(&z->lock); | |
1276 | local_zone_delete(z); | |
1277 | log_err("internal: duplicate entry in local_zones_add_zone"); | |
1278 | return NULL; | |
1279 | } | |
1280 | ||
1281 | /* set parent pointers right */ | |
1282 | set_kiddo_parents(z, z->parent, z); | |
1283 | ||
1284 | lock_rw_unlock(&z->lock); | |
1285 | return z; | |
1286 | } | |
1287 | ||
1288 | void local_zones_del_zone(struct local_zones* zones, struct local_zone* z) | |
1289 | { | |
1290 | /* fix up parents in tree */ | |
1291 | lock_rw_wrlock(&z->lock); | |
1292 | set_kiddo_parents(z, z, z->parent); | |
1293 | ||
1294 | /* remove from tree */ | |
1295 | (void)rbtree_delete(&zones->ztree, z); | |
1296 | ||
1297 | /* delete the zone */ | |
1298 | lock_rw_unlock(&z->lock); | |
1299 | local_zone_delete(z); | |
1300 | } | |
1301 | ||
1302 | int | |
1303 | local_zones_add_RR(struct local_zones* zones, const char* rr) | |
1304 | { | |
1305 | uint8_t* rr_name; | |
1306 | uint16_t rr_class; | |
1307 | size_t len; | |
1308 | int labs; | |
1309 | struct local_zone* z; | |
1310 | int r; | |
1311 | if(!get_rr_nameclass(rr, &rr_name, &rr_class)) { | |
1312 | return 0; | |
1313 | } | |
1314 | labs = dname_count_size_labels(rr_name, &len); | |
1315 | /* could first try readlock then get writelock if zone does not exist, | |
1316 | * but we do not add enough RRs (from multiple threads) to optimize */ | |
1317 | lock_rw_wrlock(&zones->lock); | |
1318 | z = local_zones_lookup(zones, rr_name, len, labs, rr_class); | |
1319 | if(!z) { | |
1320 | z = local_zones_add_zone(zones, rr_name, len, labs, rr_class, | |
1321 | local_zone_transparent); | |
1322 | if(!z) { | |
1323 | lock_rw_unlock(&zones->lock); | |
1324 | return 0; | |
1325 | } | |
1326 | } else { | |
1327 | free(rr_name); | |
1328 | } | |
1329 | lock_rw_wrlock(&z->lock); | |
1330 | lock_rw_unlock(&zones->lock); | |
1331 | r = lz_enter_rr_into_zone(z, rr); | |
1332 | lock_rw_unlock(&z->lock); | |
1333 | return r; | |
1334 | } | |
1335 | ||
1336 | /** returns true if the node is terminal so no deeper domain names exist */ | |
1337 | static int | |
1338 | is_terminal(struct local_data* d) | |
1339 | { | |
1340 | /* for empty nonterminals, the deeper domain names are sorted | |
1341 | * right after them, so simply check the next name in the tree | |
1342 | */ | |
1343 | struct local_data* n = (struct local_data*)rbtree_next(&d->node); | |
1344 | if(n == (struct local_data*)RBTREE_NULL) | |
1345 | return 1; /* last in tree, no deeper node */ | |
1346 | if(dname_strict_subdomain(n->name, n->namelabs, d->name, d->namelabs)) | |
1347 | return 0; /* there is a deeper node */ | |
1348 | return 1; | |
1349 | } | |
1350 | ||
1351 | /** delete empty terminals from tree when final data is deleted */ | |
1352 | static void | |
1353 | del_empty_term(struct local_zone* z, struct local_data* d, | |
1354 | uint8_t* name, size_t len, int labs) | |
1355 | { | |
1356 | while(d && d->rrsets == NULL && is_terminal(d)) { | |
1357 | /* is this empty nonterminal? delete */ | |
1358 | /* note, no memory recycling in zone region */ | |
1359 | (void)rbtree_delete(&z->data, d); | |
1360 | ||
1361 | /* go up and to the next label */ | |
1362 | if(dname_is_root(name)) | |
1363 | return; | |
1364 | dname_remove_label(&name, &len); | |
1365 | labs--; | |
1366 | d = lz_find_node(z, name, len, labs); | |
1367 | } | |
1368 | } | |
1369 | ||
1370 | void local_zones_del_data(struct local_zones* zones, | |
1371 | uint8_t* name, size_t len, int labs, uint16_t dclass) | |
1372 | { | |
1373 | /* find zone */ | |
1374 | struct local_zone* z; | |
1375 | struct local_data* d; | |
1376 | lock_rw_rdlock(&zones->lock); | |
1377 | z = local_zones_lookup(zones, name, len, labs, dclass); | |
1378 | if(!z) { | |
1379 | /* no such zone, we're done */ | |
1380 | lock_rw_unlock(&zones->lock); | |
1381 | return; | |
1382 | } | |
1383 | lock_rw_wrlock(&z->lock); | |
1384 | lock_rw_unlock(&zones->lock); | |
1385 | ||
1386 | /* find the domain */ | |
1387 | d = lz_find_node(z, name, len, labs); | |
1388 | if(d) { | |
1389 | /* no memory recycling for zone deletions ... */ | |
1390 | d->rrsets = NULL; | |
1391 | /* did we delete the soa record ? */ | |
1392 | if(query_dname_compare(d->name, z->name) == 0) | |
1393 | z->soa = NULL; | |
1394 | ||
1395 | /* cleanup the empty nonterminals for this name */ | |
1396 | del_empty_term(z, d, name, len, labs); | |
1397 | } | |
1398 | ||
1399 | lock_rw_unlock(&z->lock); | |
1400 | } |