File: | src/usr.sbin/nsd/namedb.h |
Warning: | line 275, column 24 Access to field 'dname' results in a dereference of a null pointer (loaded from variable 'domain') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | ||||||||
2 | * query.c -- nsd(8) the resolver. | ||||||||
3 | * | ||||||||
4 | * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. | ||||||||
5 | * | ||||||||
6 | * See LICENSE for the license. | ||||||||
7 | * | ||||||||
8 | */ | ||||||||
9 | |||||||||
10 | #include "config.h" | ||||||||
11 | |||||||||
12 | #include <sys/types.h> | ||||||||
13 | #include <sys/socket.h> | ||||||||
14 | #include <netinet/in.h> | ||||||||
15 | #include <arpa/inet.h> | ||||||||
16 | #include <assert.h> | ||||||||
17 | #include <ctype.h> | ||||||||
18 | #include <errno(*__errno()).h> | ||||||||
19 | #include <limits.h> | ||||||||
20 | #include <stddef.h> | ||||||||
21 | #include <stdio.h> | ||||||||
22 | #include <stdlib.h> | ||||||||
23 | #include <string.h> | ||||||||
24 | #include <time.h> | ||||||||
25 | #include <unistd.h> | ||||||||
26 | #include <netdb.h> | ||||||||
27 | |||||||||
28 | #include "answer.h" | ||||||||
29 | #include "axfr.h" | ||||||||
30 | #include "dns.h" | ||||||||
31 | #include "dname.h" | ||||||||
32 | #include "nsd.h" | ||||||||
33 | #include "namedb.h" | ||||||||
34 | #include "query.h" | ||||||||
35 | #include "util.h" | ||||||||
36 | #include "options.h" | ||||||||
37 | #include "nsec3.h" | ||||||||
38 | #include "tsig.h" | ||||||||
39 | |||||||||
40 | /* [Bug #253] Adding unnecessary NS RRset may lead to undesired truncation. | ||||||||
41 | * This function determines if the final response packet needs the NS RRset | ||||||||
42 | * included. Currently, it will only return negative if QTYPE == DNSKEY|DS. | ||||||||
43 | * This way, resolvers won't fallback to TCP unnecessarily when priming | ||||||||
44 | * trust anchors. | ||||||||
45 | */ | ||||||||
46 | static int answer_needs_ns(struct query *query); | ||||||||
47 | |||||||||
48 | static int add_rrset(struct query *query, | ||||||||
49 | answer_type *answer, | ||||||||
50 | rr_section_type section, | ||||||||
51 | domain_type *owner, | ||||||||
52 | rrset_type *rrset); | ||||||||
53 | |||||||||
54 | static void answer_authoritative(struct nsd *nsd, | ||||||||
55 | struct query *q, | ||||||||
56 | answer_type *answer, | ||||||||
57 | size_t domain_number, | ||||||||
58 | int exact, | ||||||||
59 | domain_type *closest_match, | ||||||||
60 | domain_type *closest_encloser, | ||||||||
61 | const dname_type *qname); | ||||||||
62 | |||||||||
63 | static void answer_lookup_zone(struct nsd *nsd, struct query *q, | ||||||||
64 | answer_type *answer, size_t domain_number, | ||||||||
65 | int exact, domain_type *closest_match, | ||||||||
66 | domain_type *closest_encloser, | ||||||||
67 | const dname_type *qname); | ||||||||
68 | |||||||||
69 | void | ||||||||
70 | query_put_dname_offset(struct query *q, domain_type *domain, uint16_t offset) | ||||||||
71 | { | ||||||||
72 | assert(q)((void)0); | ||||||||
73 | assert(domain)((void)0); | ||||||||
74 | assert(domain->number > 0)((void)0); | ||||||||
75 | |||||||||
76 | if (offset > MAX_COMPRESSION_OFFSET16383) | ||||||||
77 | return; | ||||||||
78 | if (q->compressed_dname_count >= MAX_COMPRESSED_DNAMES10240) | ||||||||
79 | return; | ||||||||
80 | |||||||||
81 | q->compressed_dname_offsets[domain->number] = offset; | ||||||||
82 | q->compressed_dnames[q->compressed_dname_count] = domain; | ||||||||
83 | ++q->compressed_dname_count; | ||||||||
84 | } | ||||||||
85 | |||||||||
86 | void | ||||||||
87 | query_clear_dname_offsets(struct query *q, size_t max_offset) | ||||||||
88 | { | ||||||||
89 | while (q->compressed_dname_count > 0 | ||||||||
90 | && (q->compressed_dname_offsets[q->compressed_dnames[q->compressed_dname_count - 1]->number] | ||||||||
91 | >= max_offset)) | ||||||||
92 | { | ||||||||
93 | q->compressed_dname_offsets[q->compressed_dnames[q->compressed_dname_count - 1]->number] = 0; | ||||||||
94 | --q->compressed_dname_count; | ||||||||
95 | } | ||||||||
96 | } | ||||||||
97 | |||||||||
98 | void | ||||||||
99 | query_clear_compression_tables(struct query *q) | ||||||||
100 | { | ||||||||
101 | uint16_t i; | ||||||||
102 | |||||||||
103 | for (i = 0; i < q->compressed_dname_count; ++i) { | ||||||||
104 | assert(q->compressed_dnames)((void)0); | ||||||||
105 | q->compressed_dname_offsets[q->compressed_dnames[i]->number] = 0; | ||||||||
106 | } | ||||||||
107 | q->compressed_dname_count = 0; | ||||||||
108 | } | ||||||||
109 | |||||||||
110 | void | ||||||||
111 | query_add_compression_domain(struct query *q, domain_type *domain, uint16_t offset) | ||||||||
112 | { | ||||||||
113 | while (domain->parent) { | ||||||||
114 | DEBUG(DEBUG_NAME_COMPRESSION, 2, | ||||||||
115 | (LOG_INFO, "query dname: %s, number: %lu, offset: %u\n", | ||||||||
116 | domain_to_string(domain), | ||||||||
117 | (unsigned long) domain->number, | ||||||||
118 | offset)); | ||||||||
119 | query_put_dname_offset(q, domain, offset); | ||||||||
120 | offset += label_length(dname_name(domain_dname(domain))) + 1; | ||||||||
121 | domain = domain->parent; | ||||||||
122 | } | ||||||||
123 | } | ||||||||
124 | |||||||||
125 | /* | ||||||||
126 | * Generate an error response with the specified RCODE. | ||||||||
127 | */ | ||||||||
128 | query_state_type | ||||||||
129 | query_error (struct query *q, nsd_rc_type rcode) | ||||||||
130 | { | ||||||||
131 | if (rcode == NSD_RC_DISCARD) { | ||||||||
132 | return QUERY_DISCARDED; | ||||||||
133 | } | ||||||||
134 | |||||||||
135 | buffer_clear(q->packet); | ||||||||
136 | |||||||||
137 | QR_SET(q->packet)(*buffer_at((q->packet), 2) |= 0x80U); /* This is an answer. */ | ||||||||
138 | AD_CLR(q->packet)(*buffer_at((q->packet), 3) &= ~0x20U); | ||||||||
139 | RCODE_SET(q->packet, (int) rcode)(*buffer_at((q->packet), 3) = (*buffer_at((q->packet), 3 ) & ~0x0fU) | ((int) rcode)); /* Error code. */ | ||||||||
140 | |||||||||
141 | /* Truncate the question as well... */ | ||||||||
142 | QDCOUNT_SET(q->packet, 0)(buffer_write_u16_at((q->packet), 4, (0))); | ||||||||
143 | ANCOUNT_SET(q->packet, 0)(buffer_write_u16_at((q->packet), 6, (0))); | ||||||||
144 | NSCOUNT_SET(q->packet, 0)(buffer_write_u16_at((q->packet), 8, (0))); | ||||||||
145 | ARCOUNT_SET(q->packet, 0)(buffer_write_u16_at((q->packet), 10, (0))); | ||||||||
146 | buffer_set_position(q->packet, QHEADERSZ12); | ||||||||
147 | return QUERY_PROCESSED; | ||||||||
148 | } | ||||||||
149 | |||||||||
150 | static int | ||||||||
151 | query_ratelimit_err(nsd_type* nsd) | ||||||||
152 | { | ||||||||
153 | time_t now = time(NULL((void *)0)); | ||||||||
154 | if(nsd->err_limit_time == now) { | ||||||||
155 | /* see if limit is exceeded for this second */ | ||||||||
156 | if(nsd->err_limit_count++ > ERROR_RATELIMIT100) | ||||||||
157 | return 1; | ||||||||
158 | } else { | ||||||||
159 | /* new second, new limits */ | ||||||||
160 | nsd->err_limit_time = now; | ||||||||
161 | nsd->err_limit_count = 1; | ||||||||
162 | } | ||||||||
163 | return 0; | ||||||||
164 | } | ||||||||
165 | |||||||||
166 | static query_state_type | ||||||||
167 | query_formerr (struct query *query, nsd_type* nsd) | ||||||||
168 | { | ||||||||
169 | int opcode = OPCODE(query->packet)((*buffer_at((query->packet), 2) & 0x78U) >> 3); | ||||||||
170 | if(query_ratelimit_err(nsd)) | ||||||||
171 | return QUERY_DISCARDED; | ||||||||
172 | FLAGS_SET(query->packet, FLAGS(query->packet) & 0x0100U)(buffer_write_u16_at((query->packet), 2, ((buffer_read_u16_at ((query->packet), 2)) & 0x0100U))); | ||||||||
173 | /* Preserve the RD flag. Clear the rest. */ | ||||||||
174 | OPCODE_SET(query->packet, opcode)(*buffer_at((query->packet), 2) = (*buffer_at((query->packet ), 2) & ~0x78U) | ((opcode) << 3)); | ||||||||
175 | return query_error(query, NSD_RC_FORMAT); | ||||||||
176 | } | ||||||||
177 | |||||||||
178 | static void | ||||||||
179 | query_cleanup(void *data) | ||||||||
180 | { | ||||||||
181 | query_type *query = (query_type *) data; | ||||||||
182 | region_destroy(query->region); | ||||||||
183 | } | ||||||||
184 | |||||||||
185 | query_type * | ||||||||
186 | query_create(region_type *region, uint16_t *compressed_dname_offsets, | ||||||||
187 | size_t compressed_dname_size, domain_type **compressed_dnames) | ||||||||
188 | { | ||||||||
189 | query_type *query | ||||||||
190 | = (query_type *) region_alloc_zero(region, sizeof(query_type)); | ||||||||
191 | /* create region with large block size, because the initial chunk | ||||||||
192 | saves many mallocs in the server */ | ||||||||
193 | query->region = region_create_custom(xalloc, free, 16384, 16384/8, 32, 0); | ||||||||
194 | query->compressed_dname_offsets = compressed_dname_offsets; | ||||||||
195 | query->compressed_dnames = compressed_dnames; | ||||||||
196 | query->packet = buffer_create(region, QIOBUFSZ(65535 + (255 + sizeof(uint32_t) + 4*sizeof(uint16_t) + 65535 ))); | ||||||||
197 | region_add_cleanup(region, query_cleanup, query); | ||||||||
198 | query->compressed_dname_offsets_size = compressed_dname_size; | ||||||||
199 | tsig_create_record(&query->tsig, region); | ||||||||
200 | query->tsig_prepare_it = 1; | ||||||||
201 | query->tsig_update_it = 1; | ||||||||
202 | query->tsig_sign_it = 1; | ||||||||
203 | return query; | ||||||||
204 | } | ||||||||
205 | |||||||||
206 | void | ||||||||
207 | query_reset(query_type *q, size_t maxlen, int is_tcp) | ||||||||
208 | { | ||||||||
209 | /* | ||||||||
210 | * As long as less than 4Kb (region block size) has been used, | ||||||||
211 | * this call to free_all is free, the block is saved for re-use, | ||||||||
212 | * so no malloc() or free() calls are done. | ||||||||
213 | * at present use of the region is for: | ||||||||
214 | * o query qname dname_type (255 max). | ||||||||
215 | * o wildcard expansion domain_type (7*ptr+u32+2bytes)+(5*ptr nsec3) | ||||||||
216 | * o wildcard expansion for additional section domain_type. | ||||||||
217 | * o nsec3 hashed name(s) (3 dnames for a nonexist_proof, | ||||||||
218 | * one proof per wildcard and for nx domain). | ||||||||
219 | */ | ||||||||
220 | region_free_all(q->region); | ||||||||
221 | q->remote_addrlen = (socklen_t)sizeof(q->remote_addr); | ||||||||
222 | q->client_addrlen = (socklen_t)sizeof(q->client_addr); | ||||||||
223 | q->is_proxied = 0; | ||||||||
224 | q->maxlen = maxlen; | ||||||||
225 | q->reserved_space = 0; | ||||||||
226 | buffer_clear(q->packet); | ||||||||
227 | edns_init_record(&q->edns); | ||||||||
228 | tsig_init_record(&q->tsig, NULL((void *)0), NULL((void *)0)); | ||||||||
229 | q->tsig_prepare_it = 1; | ||||||||
230 | q->tsig_update_it = 1; | ||||||||
231 | q->tsig_sign_it = 1; | ||||||||
232 | q->tcp = is_tcp; | ||||||||
233 | q->qname = NULL((void *)0); | ||||||||
234 | q->qtype = 0; | ||||||||
235 | q->qclass = 0; | ||||||||
236 | q->zone = NULL((void *)0); | ||||||||
237 | q->opcode = 0; | ||||||||
238 | q->cname_count = 0; | ||||||||
239 | q->delegation_domain = NULL((void *)0); | ||||||||
240 | q->delegation_rrset = NULL((void *)0); | ||||||||
241 | q->compressed_dname_count = 0; | ||||||||
242 | q->number_temporary_domains = 0; | ||||||||
243 | |||||||||
244 | q->axfr_is_done = 0; | ||||||||
245 | q->axfr_zone = NULL((void *)0); | ||||||||
246 | q->axfr_current_domain = NULL((void *)0); | ||||||||
247 | q->axfr_current_rrset = NULL((void *)0); | ||||||||
248 | q->axfr_current_rr = 0; | ||||||||
249 | |||||||||
250 | q->ixfr_is_done = 0; | ||||||||
251 | q->ixfr_data = NULL((void *)0); | ||||||||
252 | q->ixfr_count_newsoa = 0; | ||||||||
253 | q->ixfr_count_oldsoa = 0; | ||||||||
254 | q->ixfr_count_del = 0; | ||||||||
255 | q->ixfr_count_add = 0; | ||||||||
256 | |||||||||
257 | #ifdef RATELIMIT | ||||||||
258 | q->wildcard_domain = NULL((void *)0); | ||||||||
259 | #endif | ||||||||
260 | } | ||||||||
261 | |||||||||
262 | /* get a temporary domain number (or 0=failure) */ | ||||||||
263 | static domain_type* | ||||||||
264 | query_get_tempdomain(struct query *q) | ||||||||
265 | { | ||||||||
266 | static domain_type d[EXTRA_DOMAIN_NUMBERS1024]; | ||||||||
267 | if(q->number_temporary_domains >= EXTRA_DOMAIN_NUMBERS1024) | ||||||||
268 | return 0; | ||||||||
269 | q->number_temporary_domains ++; | ||||||||
270 | memset(&d[q->number_temporary_domains-1], 0, sizeof(domain_type)); | ||||||||
271 | d[q->number_temporary_domains-1].number = q->compressed_dname_offsets_size + | ||||||||
272 | q->number_temporary_domains - 1; | ||||||||
273 | return &d[q->number_temporary_domains-1]; | ||||||||
274 | } | ||||||||
275 | |||||||||
276 | static void | ||||||||
277 | query_addtxt(struct query *q, | ||||||||
278 | const uint8_t *dname, | ||||||||
279 | uint16_t klass, | ||||||||
280 | uint32_t ttl, | ||||||||
281 | const char *txt) | ||||||||
282 | { | ||||||||
283 | size_t txt_length = strlen(txt); | ||||||||
284 | uint8_t len = (uint8_t) txt_length; | ||||||||
285 | |||||||||
286 | assert(txt_length <= UCHAR_MAX)((void)0); | ||||||||
287 | |||||||||
288 | /* Add the dname */ | ||||||||
289 | if (dname >= buffer_begin(q->packet) | ||||||||
290 | && dname <= buffer_current(q->packet)) | ||||||||
291 | { | ||||||||
292 | buffer_write_u16(q->packet, | ||||||||
293 | 0xc000 | (dname - buffer_begin(q->packet))); | ||||||||
294 | } else { | ||||||||
295 | buffer_write(q->packet, dname + 1, *dname); | ||||||||
296 | } | ||||||||
297 | |||||||||
298 | buffer_write_u16(q->packet, TYPE_TXT16); | ||||||||
299 | buffer_write_u16(q->packet, klass); | ||||||||
300 | buffer_write_u32(q->packet, ttl); | ||||||||
301 | buffer_write_u16(q->packet, len + 1); | ||||||||
302 | buffer_write_u8(q->packet, len); | ||||||||
303 | buffer_write(q->packet, txt, len); | ||||||||
304 | } | ||||||||
305 | |||||||||
306 | /* | ||||||||
307 | * Parse the question section of a query. The normalized query name | ||||||||
308 | * is stored in QUERY->name, the class in QUERY->klass, and the type | ||||||||
309 | * in QUERY->type. | ||||||||
310 | */ | ||||||||
311 | static int | ||||||||
312 | process_query_section(query_type *query) | ||||||||
313 | { | ||||||||
314 | uint8_t qnamebuf[MAXDOMAINLEN255]; | ||||||||
315 | |||||||||
316 | buffer_set_position(query->packet, QHEADERSZ12); | ||||||||
317 | /* Lets parse the query name and convert it to lower case. */ | ||||||||
318 | if(!packet_read_query_section(query->packet, qnamebuf, | ||||||||
319 | &query->qtype, &query->qclass)) | ||||||||
320 | return 0; | ||||||||
321 | query->qname = dname_make(query->region, qnamebuf, 1); | ||||||||
322 | return 1; | ||||||||
323 | } | ||||||||
324 | |||||||||
325 | |||||||||
326 | /* | ||||||||
327 | * Process an optional EDNS OPT record. Sets QUERY->EDNS to 0 if | ||||||||
328 | * there was no EDNS record, to -1 if there was an invalid or | ||||||||
329 | * unsupported EDNS record, and to 1 otherwise. Updates QUERY->MAXLEN | ||||||||
330 | * if the EDNS record specifies a maximum supported response length. | ||||||||
331 | * | ||||||||
332 | * Return NSD_RC_FORMAT on failure, NSD_RC_OK on success. | ||||||||
333 | */ | ||||||||
334 | static nsd_rc_type | ||||||||
335 | process_edns(nsd_type* nsd, struct query *q) | ||||||||
336 | { | ||||||||
337 | if (q->edns.status == EDNS_ERROR) { | ||||||||
338 | /* The only error is VERSION not implemented */ | ||||||||
339 | return NSD_RC_FORMAT; | ||||||||
340 | } | ||||||||
341 | |||||||||
342 | if (q->edns.status == EDNS_OK) { | ||||||||
343 | /* Only care about UDP size larger than normal... */ | ||||||||
344 | if (!q->tcp && q->edns.maxlen > UDP_MAX_MESSAGE_LEN512) { | ||||||||
345 | size_t edns_size; | ||||||||
346 | #if defined(INET6) | ||||||||
347 | if (q->client_addr.ss_family == AF_INET624) { | ||||||||
348 | edns_size = nsd->ipv6_edns_size; | ||||||||
349 | } else | ||||||||
350 | #endif | ||||||||
351 | edns_size = nsd->ipv4_edns_size; | ||||||||
352 | |||||||||
353 | if (q->edns.maxlen < edns_size) { | ||||||||
354 | q->maxlen = q->edns.maxlen; | ||||||||
355 | } else { | ||||||||
356 | q->maxlen = edns_size; | ||||||||
357 | } | ||||||||
358 | |||||||||
359 | #if defined(INET6) && !defined(IPV6_USE_MIN_MTU42) && !defined(IPV6_MTU) | ||||||||
360 | /* | ||||||||
361 | * Use IPv6 minimum MTU to avoid sending | ||||||||
362 | * packets that are too large for some links. | ||||||||
363 | * IPv6 will not automatically fragment in | ||||||||
364 | * this case (unlike IPv4). | ||||||||
365 | */ | ||||||||
366 | if (q->client_addr.ss_family == AF_INET624 | ||||||||
367 | && q->maxlen > IPV6_MIN_MTU1280) | ||||||||
368 | { | ||||||||
369 | q->maxlen = IPV6_MIN_MTU1280; | ||||||||
370 | } | ||||||||
371 | #endif | ||||||||
372 | } | ||||||||
373 | |||||||||
374 | /* Strip the OPT resource record off... */ | ||||||||
375 | buffer_set_position(q->packet, q->edns.position); | ||||||||
376 | buffer_set_limit(q->packet, q->edns.position); | ||||||||
377 | ARCOUNT_SET(q->packet, ARCOUNT(q->packet) - 1)(buffer_write_u16_at((q->packet), 10, ((buffer_read_u16_at ((q->packet), 10)) - 1))); | ||||||||
378 | } | ||||||||
379 | return NSD_RC_OK; | ||||||||
380 | } | ||||||||
381 | |||||||||
382 | /* | ||||||||
383 | * Processes TSIG. | ||||||||
384 | * Sets error when tsig does not verify on the query. | ||||||||
385 | */ | ||||||||
386 | static nsd_rc_type | ||||||||
387 | process_tsig(struct query* q) | ||||||||
388 | { | ||||||||
389 | if(q->tsig.status == TSIG_ERROR) | ||||||||
390 | return NSD_RC_FORMAT; | ||||||||
391 | if(q->tsig.status == TSIG_OK) { | ||||||||
392 | if(!tsig_from_query(&q->tsig)) { | ||||||||
393 | char a[128]; | ||||||||
394 | addr2str(&q->client_addr, a, sizeof(a)); | ||||||||
395 | log_msg(LOG_ERR3, "query: bad tsig (%s) for key %s from %s", | ||||||||
396 | tsig_error(q->tsig.error_code), | ||||||||
397 | dname_to_string(q->tsig.key_name, NULL((void *)0)), a); | ||||||||
398 | return NSD_RC_NOTAUTH; | ||||||||
399 | } | ||||||||
400 | buffer_set_limit(q->packet, q->tsig.position); | ||||||||
401 | ARCOUNT_SET(q->packet, ARCOUNT(q->packet) - 1)(buffer_write_u16_at((q->packet), 10, ((buffer_read_u16_at ((q->packet), 10)) - 1))); | ||||||||
402 | tsig_prepare(&q->tsig); | ||||||||
403 | tsig_update(&q->tsig, q->packet, buffer_limit(q->packet)); | ||||||||
404 | if(!tsig_verify(&q->tsig)) { | ||||||||
405 | char a[128]; | ||||||||
406 | addr2str(&q->client_addr, a, sizeof(a)); | ||||||||
407 | log_msg(LOG_ERR3, "query: bad tsig signature for key %s from %s", | ||||||||
408 | dname_to_string(q->tsig.key->name, NULL((void *)0)), a); | ||||||||
409 | return NSD_RC_NOTAUTH; | ||||||||
410 | } | ||||||||
411 | DEBUG(DEBUG_XFRD,1, (LOG_INFO, "query good tsig signature for %s", | ||||||||
412 | dname_to_string(q->tsig.key->name, NULL))); | ||||||||
413 | } | ||||||||
414 | return NSD_RC_OK; | ||||||||
415 | } | ||||||||
416 | |||||||||
417 | /* | ||||||||
418 | * Check notify acl and forward to xfrd (or return an error). | ||||||||
419 | */ | ||||||||
420 | static query_state_type | ||||||||
421 | answer_notify(struct nsd* nsd, struct query *query) | ||||||||
422 | { | ||||||||
423 | int acl_num, acl_num_xfr; | ||||||||
424 | struct acl_options *why; | ||||||||
425 | nsd_rc_type rc; | ||||||||
426 | |||||||||
427 | struct zone_options* zone_opt; | ||||||||
428 | DEBUG(DEBUG_XFRD,1, (LOG_INFO, "got notify %s processing acl", | ||||||||
429 | dname_to_string(query->qname, NULL))); | ||||||||
430 | |||||||||
431 | zone_opt = zone_options_find(nsd->options, query->qname); | ||||||||
432 | if(!zone_opt) | ||||||||
433 | return query_error(query, NSD_RC_NXDOMAIN); | ||||||||
434 | |||||||||
435 | if(!nsd->this_child) /* we are in debug mode or something */ | ||||||||
436 | return query_error(query, NSD_RC_SERVFAIL); | ||||||||
437 | |||||||||
438 | if(!tsig_find_rr(&query->tsig, query->packet)) { | ||||||||
439 | DEBUG(DEBUG_XFRD,2, (LOG_ERR, "bad tsig RR format")); | ||||||||
440 | return query_error(query, NSD_RC_FORMAT); | ||||||||
441 | } | ||||||||
442 | rc = process_tsig(query); | ||||||||
443 | if(rc != NSD_RC_OK) | ||||||||
444 | return query_error(query, rc); | ||||||||
445 | |||||||||
446 | /* check if it passes acl */ | ||||||||
447 | if(query->is_proxied && acl_check_incoming_block_proxy( | ||||||||
448 | zone_opt->pattern->allow_notify, query, &why) == -1) { | ||||||||
449 | /* the proxy address is blocked */ | ||||||||
450 | if (verbosity >= 2) { | ||||||||
451 | char address[128], proxy[128]; | ||||||||
452 | addr2str(&query->client_addr, address, sizeof(address)); | ||||||||
453 | addr2str(&query->remote_addr, proxy, sizeof(proxy)); | ||||||||
454 | VERBOSITY(2, (LOG_INFO, "notify for %s from %s via proxy %s refused because of proxy, %s %s",do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(query->qname, ((void *)0)), address, proxy , (why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches")) ; } } while (0) | ||||||||
455 | dname_to_string(query->qname, NULL),do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(query->qname, ((void *)0)), address, proxy , (why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches")) ; } } while (0) | ||||||||
456 | address, proxy,do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(query->qname, ((void *)0)), address, proxy , (why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches")) ; } } while (0) | ||||||||
457 | (why?why->ip_address_spec:"."),do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(query->qname, ((void *)0)), address, proxy , (why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches")) ; } } while (0) | ||||||||
458 | (why ? ( why->nokey ? "NOKEY"do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(query->qname, ((void *)0)), address, proxy , (why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches")) ; } } while (0) | ||||||||
459 | : why->blocked ? "BLOCKED"do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(query->qname, ((void *)0)), address, proxy , (why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches")) ; } } while (0) | ||||||||
460 | : why->key_name )do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(query->qname, ((void *)0)), address, proxy , (why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches")) ; } } while (0) | ||||||||
461 | : "no acl matches")))do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(query->qname, ((void *)0)), address, proxy , (why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches")) ; } } while (0); | ||||||||
462 | } | ||||||||
463 | return query_error(query, NSD_RC_REFUSE); | ||||||||
464 | } | ||||||||
465 | if((acl_num = acl_check_incoming(zone_opt->pattern->allow_notify, query, | ||||||||
466 | &why)) != -1) | ||||||||
467 | { | ||||||||
468 | sig_atomic_t mode = NSD_PASS_TO_XFRD6; | ||||||||
469 | int s = nsd->this_child->parent_fd; | ||||||||
470 | uint16_t sz; | ||||||||
471 | uint32_t acl_send = htonl(acl_num)(__uint32_t)(__builtin_constant_p(acl_num) ? (__uint32_t)(((__uint32_t )(acl_num) & 0xff) << 24 | ((__uint32_t)(acl_num) & 0xff00) << 8 | ((__uint32_t)(acl_num) & 0xff0000) >> 8 | ((__uint32_t)(acl_num) & 0xff000000) >> 24) : __swap32md (acl_num)); | ||||||||
472 | uint32_t acl_xfr; | ||||||||
473 | size_t pos; | ||||||||
474 | |||||||||
475 | /* Find priority candidate for request XFR. -1 if no match */ | ||||||||
476 | acl_num_xfr = acl_check_incoming( | ||||||||
477 | zone_opt->pattern->request_xfr, query, NULL((void *)0)); | ||||||||
478 | |||||||||
479 | acl_xfr = htonl(acl_num_xfr)(__uint32_t)(__builtin_constant_p(acl_num_xfr) ? (__uint32_t) (((__uint32_t)(acl_num_xfr) & 0xff) << 24 | ((__uint32_t )(acl_num_xfr) & 0xff00) << 8 | ((__uint32_t)(acl_num_xfr ) & 0xff0000) >> 8 | ((__uint32_t)(acl_num_xfr) & 0xff000000) >> 24) : __swap32md(acl_num_xfr)); | ||||||||
480 | |||||||||
481 | assert(why)((void)0); | ||||||||
482 | DEBUG(DEBUG_XFRD,1, (LOG_INFO, "got notify %s passed acl %s %s", | ||||||||
483 | dname_to_string(query->qname, NULL), | ||||||||
484 | why->ip_address_spec, | ||||||||
485 | why->nokey?"NOKEY": | ||||||||
486 | (why->blocked?"BLOCKED":why->key_name))); | ||||||||
487 | sz = buffer_limit(query->packet); | ||||||||
488 | if(buffer_limit(query->packet) > MAX_PACKET_SIZE65535) | ||||||||
489 | return query_error(query, NSD_RC_SERVFAIL); | ||||||||
490 | /* forward to xfrd for processing | ||||||||
491 | Note. Blocking IPC I/O, but acl is OK. */ | ||||||||
492 | sz = htons(sz)(__uint16_t)(__builtin_constant_p(sz) ? (__uint16_t)(((__uint16_t )(sz) & 0xffU) << 8 | ((__uint16_t)(sz) & 0xff00U ) >> 8) : __swap16md(sz)); | ||||||||
493 | if(!write_socket(s, &mode, sizeof(mode)) || | ||||||||
494 | !write_socket(s, &sz, sizeof(sz)) || | ||||||||
495 | !write_socket(s, buffer_begin(query->packet), | ||||||||
496 | buffer_limit(query->packet)) || | ||||||||
497 | !write_socket(s, &acl_send, sizeof(acl_send)) || | ||||||||
498 | !write_socket(s, &acl_xfr, sizeof(acl_xfr))) { | ||||||||
499 | log_msg(LOG_ERR3, "error in IPC notify server2main, %s", | ||||||||
500 | strerror(errno(*__errno()))); | ||||||||
501 | return query_error(query, NSD_RC_SERVFAIL); | ||||||||
502 | } | ||||||||
503 | if(verbosity >= 1) { | ||||||||
504 | uint32_t serial = 0; | ||||||||
505 | char address[128]; | ||||||||
506 | addr2str(&query->client_addr, address, sizeof(address)); | ||||||||
507 | if(packet_find_notify_serial(query->packet, &serial)) | ||||||||
508 | VERBOSITY(1, (LOG_INFO, "notify for %s from %s serial %u",do { if ((1) <= verbosity) { log_msg (6, "notify for %s from %s serial %u" , dname_to_string(query->qname, ((void *)0)), address, (unsigned )serial) ; } } while (0) | ||||||||
509 | dname_to_string(query->qname, NULL), address,do { if ((1) <= verbosity) { log_msg (6, "notify for %s from %s serial %u" , dname_to_string(query->qname, ((void *)0)), address, (unsigned )serial) ; } } while (0) | ||||||||
510 | (unsigned)serial))do { if ((1) <= verbosity) { log_msg (6, "notify for %s from %s serial %u" , dname_to_string(query->qname, ((void *)0)), address, (unsigned )serial) ; } } while (0); | ||||||||
511 | else | ||||||||
512 | VERBOSITY(1, (LOG_INFO, "notify for %s from %s",do { if ((1) <= verbosity) { log_msg (6, "notify for %s from %s" , dname_to_string(query->qname, ((void *)0)), address) ; } } while (0) | ||||||||
513 | dname_to_string(query->qname, NULL), address))do { if ((1) <= verbosity) { log_msg (6, "notify for %s from %s" , dname_to_string(query->qname, ((void *)0)), address) ; } } while (0); | ||||||||
514 | } | ||||||||
515 | |||||||||
516 | /* create notify reply - keep same query contents */ | ||||||||
517 | QR_SET(query->packet)(*buffer_at((query->packet), 2) |= 0x80U); /* This is an answer. */ | ||||||||
518 | AA_SET(query->packet)(*buffer_at((query->packet), 2) |= 0x04U); /* we are authoritative. */ | ||||||||
519 | ANCOUNT_SET(query->packet, 0)(buffer_write_u16_at((query->packet), 6, (0))); | ||||||||
520 | NSCOUNT_SET(query->packet, 0)(buffer_write_u16_at((query->packet), 8, (0))); | ||||||||
521 | ARCOUNT_SET(query->packet, 0)(buffer_write_u16_at((query->packet), 10, (0))); | ||||||||
522 | RCODE_SET(query->packet, RCODE_OK)(*buffer_at((query->packet), 3) = (*buffer_at((query->packet ), 3) & ~0x0fU) | (0)); /* Error code. */ | ||||||||
523 | /* position is right after the query */ | ||||||||
524 | pos = buffer_position(query->packet); | ||||||||
525 | buffer_clear(query->packet); | ||||||||
526 | buffer_set_position(query->packet, pos); | ||||||||
527 | /* tsig is added in add_additional later (if needed) */ | ||||||||
528 | return QUERY_PROCESSED; | ||||||||
529 | } | ||||||||
530 | |||||||||
531 | if (verbosity >= 2) { | ||||||||
532 | char address[128]; | ||||||||
533 | addr2str(&query->client_addr, address, sizeof(address)); | ||||||||
534 | VERBOSITY(2, (LOG_INFO, "notify for %s from %s refused, %s %s",do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s refused, %s %s" , dname_to_string(query->qname, ((void *)0)), address, (why ?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
535 | dname_to_string(query->qname, NULL),do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s refused, %s %s" , dname_to_string(query->qname, ((void *)0)), address, (why ?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
536 | address,do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s refused, %s %s" , dname_to_string(query->qname, ((void *)0)), address, (why ?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
537 | (why?why->ip_address_spec:"."),do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s refused, %s %s" , dname_to_string(query->qname, ((void *)0)), address, (why ?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
538 | (why ? ( why->nokey ? "NOKEY"do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s refused, %s %s" , dname_to_string(query->qname, ((void *)0)), address, (why ?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
539 | : why->blocked ? "BLOCKED"do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s refused, %s %s" , dname_to_string(query->qname, ((void *)0)), address, (why ?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
540 | : why->key_name )do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s refused, %s %s" , dname_to_string(query->qname, ((void *)0)), address, (why ?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
541 | : "no acl matches")))do { if ((2) <= verbosity) { log_msg (6, "notify for %s from %s refused, %s %s" , dname_to_string(query->qname, ((void *)0)), address, (why ?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0); | ||||||||
542 | } | ||||||||
543 | |||||||||
544 | return query_error(query, NSD_RC_REFUSE); | ||||||||
545 | } | ||||||||
546 | |||||||||
547 | |||||||||
548 | /* | ||||||||
549 | * Answer a query in the CHAOS class. | ||||||||
550 | */ | ||||||||
551 | static query_state_type | ||||||||
552 | answer_chaos(struct nsd *nsd, query_type *q) | ||||||||
553 | { | ||||||||
554 | AA_CLR(q->packet)(*buffer_at((q->packet), 2) &= ~0x04U); | ||||||||
555 | switch (q->qtype) { | ||||||||
556 | case TYPE_ANY255: | ||||||||
557 | case TYPE_TXT16: | ||||||||
558 | if ((q->qname->name_size == 11 | ||||||||
559 | && memcmp(dname_name(q->qname), "\002id\006server", 11) == 0) || | ||||||||
560 | (q->qname->name_size == 15 | ||||||||
561 | && memcmp(dname_name(q->qname), "\010hostname\004bind", 15) == 0)) | ||||||||
562 | { | ||||||||
563 | if(!nsd->options->hide_identity) { | ||||||||
564 | /* Add ID */ | ||||||||
565 | query_addtxt(q, | ||||||||
566 | buffer_begin(q->packet) + QHEADERSZ12, | ||||||||
567 | CLASS_CH3, | ||||||||
568 | 0, | ||||||||
569 | nsd->identity); | ||||||||
570 | ANCOUNT_SET(q->packet, ANCOUNT(q->packet) + 1)(buffer_write_u16_at((q->packet), 6, ((buffer_read_u16_at( (q->packet), 6)) + 1))); | ||||||||
571 | } else { | ||||||||
572 | RCODE_SET(q->packet, RCODE_REFUSE)(*buffer_at((q->packet), 3) = (*buffer_at((q->packet), 3 ) & ~0x0fU) | (5)); | ||||||||
573 | /* RFC8914 - Extended DNS Errors | ||||||||
574 | * 4.19. Extended DNS Error Code 18 - Prohibited */ | ||||||||
575 | q->edns.ede = EDE_PROHIBITED18; | ||||||||
576 | } | ||||||||
577 | } else if ((q->qname->name_size == 16 | ||||||||
578 | && memcmp(dname_name(q->qname), "\007version\006server", 16) == 0) || | ||||||||
579 | (q->qname->name_size == 14 | ||||||||
580 | && memcmp(dname_name(q->qname), "\007version\004bind", 14) == 0)) | ||||||||
581 | { | ||||||||
582 | if(!nsd->options->hide_version) { | ||||||||
583 | /* Add version */ | ||||||||
584 | query_addtxt(q, | ||||||||
585 | buffer_begin(q->packet) + QHEADERSZ12, | ||||||||
586 | CLASS_CH3, | ||||||||
587 | 0, | ||||||||
588 | nsd->version); | ||||||||
589 | ANCOUNT_SET(q->packet, ANCOUNT(q->packet) + 1)(buffer_write_u16_at((q->packet), 6, ((buffer_read_u16_at( (q->packet), 6)) + 1))); | ||||||||
590 | } else { | ||||||||
591 | RCODE_SET(q->packet, RCODE_REFUSE)(*buffer_at((q->packet), 3) = (*buffer_at((q->packet), 3 ) & ~0x0fU) | (5)); | ||||||||
592 | /* RFC8914 - Extended DNS Errors | ||||||||
593 | * 4.19. Extended DNS Error Code 18 - Prohibited */ | ||||||||
594 | q->edns.ede = EDE_PROHIBITED18; | ||||||||
595 | } | ||||||||
596 | } else { | ||||||||
597 | RCODE_SET(q->packet, RCODE_REFUSE)(*buffer_at((q->packet), 3) = (*buffer_at((q->packet), 3 ) & ~0x0fU) | (5)); | ||||||||
598 | /* RFC8914 - Extended DNS Errors | ||||||||
599 | * 4.22. Extended DNS Error Code 21 - Not Supported */ | ||||||||
600 | q->edns.ede = EDE_NOT_SUPPORTED21; | ||||||||
601 | |||||||||
602 | } | ||||||||
603 | break; | ||||||||
604 | default: | ||||||||
605 | RCODE_SET(q->packet, RCODE_REFUSE)(*buffer_at((q->packet), 3) = (*buffer_at((q->packet), 3 ) & ~0x0fU) | (5)); | ||||||||
606 | /* RFC8914 - Extended DNS Errors | ||||||||
607 | * 4.22. Extended DNS Error Code 21 - Not Supported */ | ||||||||
608 | q->edns.ede = EDE_NOT_SUPPORTED21; | ||||||||
609 | break; | ||||||||
610 | } | ||||||||
611 | |||||||||
612 | return QUERY_PROCESSED; | ||||||||
613 | } | ||||||||
614 | |||||||||
615 | |||||||||
616 | /* | ||||||||
617 | * Find the covering NSEC for a non-existent domain name. Normally | ||||||||
618 | * the NSEC will be located at CLOSEST_MATCH, except when it is an | ||||||||
619 | * empty non-terminal. In this case the NSEC may be located at the | ||||||||
620 | * previous domain name (in canonical ordering). | ||||||||
621 | */ | ||||||||
622 | static domain_type * | ||||||||
623 | find_covering_nsec(domain_type *closest_match, | ||||||||
624 | zone_type *zone, | ||||||||
625 | rrset_type **nsec_rrset) | ||||||||
626 | { | ||||||||
627 | assert(closest_match)((void)0); | ||||||||
628 | assert(nsec_rrset)((void)0); | ||||||||
629 | |||||||||
630 | /* loop away temporary created domains. For real ones it is &RBTREE_NULL */ | ||||||||
631 | #ifdef USE_RADIX_TREE | ||||||||
632 | while (closest_match->rnode == NULL((void *)0)) | ||||||||
633 | #else | ||||||||
634 | while (closest_match->node.parent == NULL((void *)0)) | ||||||||
635 | #endif | ||||||||
636 | closest_match = closest_match->parent; | ||||||||
637 | while (closest_match) { | ||||||||
638 | *nsec_rrset = domain_find_rrset(closest_match, zone, TYPE_NSEC47); | ||||||||
639 | if (*nsec_rrset) { | ||||||||
640 | return closest_match; | ||||||||
641 | } | ||||||||
642 | if (closest_match == zone->apex) { | ||||||||
643 | /* Don't look outside the current zone. */ | ||||||||
644 | return NULL((void *)0); | ||||||||
645 | } | ||||||||
646 | closest_match = domain_previous(closest_match); | ||||||||
647 | } | ||||||||
648 | return NULL((void *)0); | ||||||||
649 | } | ||||||||
650 | |||||||||
651 | |||||||||
652 | struct additional_rr_types | ||||||||
653 | { | ||||||||
654 | uint16_t rr_type; | ||||||||
655 | rr_section_type rr_section; | ||||||||
656 | }; | ||||||||
657 | |||||||||
658 | struct additional_rr_types default_additional_rr_types[] = { | ||||||||
659 | { TYPE_A1, ADDITIONAL_A_SECTION }, | ||||||||
660 | { TYPE_AAAA28, ADDITIONAL_AAAA_SECTION }, | ||||||||
661 | { 0, (rr_section_type) 0 } | ||||||||
662 | }; | ||||||||
663 | |||||||||
664 | struct additional_rr_types swap_aaaa_additional_rr_types[] = { | ||||||||
665 | { TYPE_AAAA28, ADDITIONAL_A_SECTION }, | ||||||||
666 | { TYPE_A1, ADDITIONAL_AAAA_SECTION }, | ||||||||
667 | { 0, (rr_section_type) 0 } | ||||||||
668 | }; | ||||||||
669 | |||||||||
670 | struct additional_rr_types rt_additional_rr_types[] = { | ||||||||
671 | { TYPE_A1, ADDITIONAL_A_SECTION }, | ||||||||
672 | { TYPE_AAAA28, ADDITIONAL_AAAA_SECTION }, | ||||||||
673 | { TYPE_X2519, ADDITIONAL_OTHER_SECTION }, | ||||||||
674 | { TYPE_ISDN20, ADDITIONAL_OTHER_SECTION }, | ||||||||
675 | { 0, (rr_section_type) 0 } | ||||||||
676 | }; | ||||||||
677 | |||||||||
678 | static void | ||||||||
679 | add_additional_rrsets(struct query *query, answer_type *answer, | ||||||||
680 | rrset_type *master_rrset, size_t rdata_index, | ||||||||
681 | int allow_glue, struct additional_rr_types types[]) | ||||||||
682 | { | ||||||||
683 | size_t i; | ||||||||
684 | |||||||||
685 | assert(query)((void)0); | ||||||||
686 | assert(answer)((void)0); | ||||||||
687 | assert(master_rrset)((void)0); | ||||||||
688 | assert(rdata_atom_is_domain(rrset_rrtype(master_rrset), rdata_index))((void)0); | ||||||||
689 | |||||||||
690 | for (i = 0; i < master_rrset->rr_count; ++i) { | ||||||||
691 | int j; | ||||||||
692 | domain_type *additional = rdata_atom_domain(master_rrset->rrs[i].rdatas[rdata_index]); | ||||||||
693 | domain_type *match = additional; | ||||||||
694 | |||||||||
695 | assert(additional)((void)0); | ||||||||
696 | |||||||||
697 | if (!allow_glue && domain_is_glue(match, query->zone)) | ||||||||
698 | continue; | ||||||||
699 | |||||||||
700 | /* | ||||||||
701 | * Check to see if we need to generate the dependent | ||||||||
702 | * based on a wildcard domain. | ||||||||
703 | */ | ||||||||
704 | while (!match->is_existing) { | ||||||||
705 | match = match->parent; | ||||||||
706 | } | ||||||||
707 | if (additional != match && domain_wildcard_child(match)) { | ||||||||
708 | domain_type *wildcard_child = domain_wildcard_child(match); | ||||||||
709 | domain_type *temp = (domain_type *) region_alloc( | ||||||||
710 | query->region, sizeof(domain_type)); | ||||||||
711 | #ifdef USE_RADIX_TREE | ||||||||
712 | temp->rnode = NULL((void *)0); | ||||||||
713 | temp->dname = additional->dname; | ||||||||
714 | #else | ||||||||
715 | memcpy(&temp->node, &additional->node, sizeof(rbnode_type)); | ||||||||
716 | temp->node.parent = NULL((void *)0); | ||||||||
717 | #endif | ||||||||
718 | temp->number = additional->number; | ||||||||
719 | temp->parent = match; | ||||||||
720 | temp->wildcard_child_closest_match = temp; | ||||||||
721 | temp->rrsets = wildcard_child->rrsets; | ||||||||
722 | temp->is_existing = wildcard_child->is_existing; | ||||||||
723 | additional = temp; | ||||||||
724 | } | ||||||||
725 | |||||||||
726 | for (j = 0; types[j].rr_type != 0; ++j) { | ||||||||
727 | rrset_type *rrset = domain_find_rrset( | ||||||||
728 | additional, query->zone, types[j].rr_type); | ||||||||
729 | if (rrset) { | ||||||||
730 | answer_add_rrset(answer, types[j].rr_section, | ||||||||
731 | additional, rrset); | ||||||||
732 | } | ||||||||
733 | } | ||||||||
734 | } | ||||||||
735 | } | ||||||||
736 | |||||||||
737 | static int | ||||||||
738 | answer_needs_ns(struct query* query) | ||||||||
739 | { | ||||||||
740 | assert(query)((void)0); | ||||||||
741 | /* Currently, only troublesome for DNSKEY and DS, | ||||||||
742 | * cuz their RRSETs are quite large. */ | ||||||||
743 | return (query->qtype != TYPE_DNSKEY48 && query->qtype != TYPE_DS43 | ||||||||
744 | && query->qtype != TYPE_ANY255); | ||||||||
745 | } | ||||||||
746 | |||||||||
747 | static int | ||||||||
748 | add_rrset(struct query *query, | ||||||||
749 | answer_type *answer, | ||||||||
750 | rr_section_type section, | ||||||||
751 | domain_type *owner, | ||||||||
752 | rrset_type *rrset) | ||||||||
753 | { | ||||||||
754 | int result; | ||||||||
755 | |||||||||
756 | assert(query)((void)0); | ||||||||
757 | assert(answer)((void)0); | ||||||||
758 | assert(owner)((void)0); | ||||||||
759 | assert(rrset)((void)0); | ||||||||
760 | assert(rrset_rrclass(rrset) == CLASS_IN)((void)0); | ||||||||
761 | |||||||||
762 | result = answer_add_rrset(answer, section, owner, rrset); | ||||||||
763 | if(minimal_responses && section != AUTHORITY_SECTION && | ||||||||
764 | query->qtype != TYPE_NS2) | ||||||||
765 | return result; | ||||||||
766 | switch (rrset_rrtype(rrset)) { | ||||||||
767 | case TYPE_NS2: | ||||||||
768 | #if defined(INET6) | ||||||||
769 | /* if query over IPv6, swap A and AAAA; put AAAA first */ | ||||||||
770 | add_additional_rrsets(query, answer, rrset, 0, 1, | ||||||||
771 | (query->client_addr.ss_family == AF_INET624)? | ||||||||
772 | swap_aaaa_additional_rr_types: | ||||||||
773 | default_additional_rr_types); | ||||||||
774 | #else | ||||||||
775 | add_additional_rrsets(query, answer, rrset, 0, 1, | ||||||||
776 | default_additional_rr_types); | ||||||||
777 | #endif | ||||||||
778 | break; | ||||||||
779 | case TYPE_MB7: | ||||||||
780 | add_additional_rrsets(query, answer, rrset, 0, 0, | ||||||||
781 | default_additional_rr_types); | ||||||||
782 | break; | ||||||||
783 | case TYPE_MX15: | ||||||||
784 | case TYPE_KX36: | ||||||||
785 | add_additional_rrsets(query, answer, rrset, 1, 0, | ||||||||
786 | default_additional_rr_types); | ||||||||
787 | break; | ||||||||
788 | case TYPE_RT21: | ||||||||
789 | add_additional_rrsets(query, answer, rrset, 1, 0, | ||||||||
790 | rt_additional_rr_types); | ||||||||
791 | break; | ||||||||
792 | case TYPE_SRV33: | ||||||||
793 | add_additional_rrsets(query, answer, rrset, 3, 0, | ||||||||
794 | default_additional_rr_types); | ||||||||
795 | break; | ||||||||
796 | default: | ||||||||
797 | break; | ||||||||
798 | } | ||||||||
799 | |||||||||
800 | return result; | ||||||||
801 | } | ||||||||
802 | |||||||||
803 | |||||||||
804 | /* returns 0 on error, or the domain number for to_name. | ||||||||
805 | from_name is changes to to_name by the DNAME rr. | ||||||||
806 | DNAME rr is from src to dest. | ||||||||
807 | closest encloser encloses the to_name. */ | ||||||||
808 | static size_t | ||||||||
809 | query_synthesize_cname(struct query* q, struct answer* answer, const dname_type* from_name, | ||||||||
810 | const dname_type* to_name, domain_type* src, domain_type* to_closest_encloser, | ||||||||
811 | domain_type** to_closest_match, uint32_t ttl) | ||||||||
812 | { | ||||||||
813 | /* add temporary domains for from_name and to_name and all | ||||||||
814 | their (not allocated yet) parents */ | ||||||||
815 | /* any domains below src are not_existing (because of DNAME at src) */ | ||||||||
816 | int i; | ||||||||
817 | size_t j; | ||||||||
818 | domain_type* cname_domain; | ||||||||
819 | domain_type* cname_dest; | ||||||||
820 | rrset_type* rrset; | ||||||||
821 | |||||||||
822 | domain_type* lastparent = src; | ||||||||
823 | assert(q && answer && from_name && to_name && src && to_closest_encloser)((void)0); | ||||||||
824 | assert(to_closest_match)((void)0); | ||||||||
825 | |||||||||
826 | /* check for loop by duplicate CNAME rrset synthesized */ | ||||||||
827 | for(j=0; j<answer->rrset_count; ++j) { | ||||||||
828 | if(answer->section[j] == ANSWER_SECTION && | ||||||||
829 | answer->rrsets[j]->rr_count == 1 && | ||||||||
830 | answer->rrsets[j]->rrs[0].type == TYPE_CNAME5 && | ||||||||
831 | dname_compare(domain_dname(answer->rrsets[j]->rrs[0].owner), from_name) == 0 && | ||||||||
832 | answer->rrsets[j]->rrs[0].rdata_count == 1 && | ||||||||
833 | dname_compare(domain_dname(answer->rrsets[j]->rrs[0].rdatas->domain), to_name) == 0) { | ||||||||
834 | DEBUG(DEBUG_QUERY,2, (LOG_INFO, "loop for synthesized CNAME rrset for query %s", dname_to_string(q->qname, NULL))); | ||||||||
835 | return 0; | ||||||||
836 | } | ||||||||
837 | } | ||||||||
838 | |||||||||
839 | /* allocate source part */ | ||||||||
840 | for(i=0; i < from_name->label_count - domain_dname(src)->label_count; i++) | ||||||||
841 | { | ||||||||
842 | domain_type* newdom = query_get_tempdomain(q); | ||||||||
843 | if(!newdom) | ||||||||
844 | return 0; | ||||||||
845 | newdom->is_existing = 1; | ||||||||
846 | newdom->parent = lastparent; | ||||||||
847 | #ifdef USE_RADIX_TREE | ||||||||
848 | newdom->dname | ||||||||
849 | #else | ||||||||
850 | newdom->node.key | ||||||||
851 | #endif | ||||||||
852 | = dname_partial_copy(q->region, | ||||||||
853 | from_name, domain_dname(src)->label_count + i + 1); | ||||||||
854 | if(dname_compare(domain_dname(newdom), q->qname) == 0) { | ||||||||
855 | /* 0 good for query name, otherwise new number */ | ||||||||
856 | newdom->number = 0; | ||||||||
857 | } | ||||||||
858 | DEBUG(DEBUG_QUERY,2, (LOG_INFO, "created temp domain src %d. %s nr %d", i, | ||||||||
859 | domain_to_string(newdom), (int)newdom->number)); | ||||||||
860 | lastparent = newdom; | ||||||||
861 | } | ||||||||
862 | cname_domain = lastparent; | ||||||||
863 | |||||||||
864 | /* allocate dest part */ | ||||||||
865 | lastparent = to_closest_encloser; | ||||||||
866 | for(i=0; i < to_name->label_count - domain_dname(to_closest_encloser)->label_count; | ||||||||
867 | i++) | ||||||||
868 | { | ||||||||
869 | domain_type* newdom = query_get_tempdomain(q); | ||||||||
870 | if(!newdom) | ||||||||
871 | return 0; | ||||||||
872 | newdom->is_existing = 0; | ||||||||
873 | newdom->parent = lastparent; | ||||||||
874 | #ifdef USE_RADIX_TREE | ||||||||
875 | newdom->dname | ||||||||
876 | #else | ||||||||
877 | newdom->node.key | ||||||||
878 | #endif | ||||||||
879 | = dname_partial_copy(q->region, | ||||||||
880 | to_name, domain_dname(to_closest_encloser)->label_count + i + 1); | ||||||||
881 | DEBUG(DEBUG_QUERY,2, (LOG_INFO, "created temp domain dest %d. %s nr %d", i, | ||||||||
882 | domain_to_string(newdom), (int)newdom->number)); | ||||||||
883 | lastparent = newdom; | ||||||||
884 | } | ||||||||
885 | cname_dest = lastparent; | ||||||||
886 | *to_closest_match = cname_dest; | ||||||||
887 | |||||||||
888 | /* allocate the CNAME RR */ | ||||||||
889 | rrset = (rrset_type*) region_alloc(q->region, sizeof(rrset_type)); | ||||||||
890 | memset(rrset, 0, sizeof(rrset_type)); | ||||||||
891 | rrset->zone = q->zone; | ||||||||
892 | rrset->rr_count = 1; | ||||||||
893 | rrset->rrs = (rr_type*) region_alloc(q->region, sizeof(rr_type)); | ||||||||
894 | memset(rrset->rrs, 0, sizeof(rr_type)); | ||||||||
895 | rrset->rrs->owner = cname_domain; | ||||||||
896 | rrset->rrs->ttl = ttl; | ||||||||
897 | rrset->rrs->type = TYPE_CNAME5; | ||||||||
898 | rrset->rrs->klass = CLASS_IN1; | ||||||||
899 | rrset->rrs->rdata_count = 1; | ||||||||
900 | rrset->rrs->rdatas = (rdata_atom_type*)region_alloc(q->region, | ||||||||
901 | sizeof(rdata_atom_type)); | ||||||||
902 | rrset->rrs->rdatas->domain = cname_dest; | ||||||||
903 | |||||||||
904 | if(!add_rrset(q, answer, ANSWER_SECTION, cname_domain, rrset)) { | ||||||||
905 | DEBUG(DEBUG_QUERY,2, (LOG_INFO, "could not add synthesized CNAME rrset to packet for query %s", dname_to_string(q->qname, NULL))); | ||||||||
906 | /* failure to add CNAME; likely is a loop, the same twice */ | ||||||||
907 | return 0; | ||||||||
908 | } | ||||||||
909 | |||||||||
910 | return cname_dest->number; | ||||||||
911 | } | ||||||||
912 | |||||||||
913 | /* | ||||||||
914 | * Answer delegation information. | ||||||||
915 | * | ||||||||
916 | * DNSSEC: Include the DS RRset if present. Otherwise include an NSEC | ||||||||
917 | * record proving the DS RRset does not exist. | ||||||||
918 | */ | ||||||||
919 | static void | ||||||||
920 | answer_delegation(query_type *query, answer_type *answer) | ||||||||
921 | { | ||||||||
922 | assert(answer)((void)0); | ||||||||
923 | assert(query->delegation_domain)((void)0); | ||||||||
924 | assert(query->delegation_rrset)((void)0); | ||||||||
925 | |||||||||
926 | if (query->cname_count == 0) { | ||||||||
927 | AA_CLR(query->packet)(*buffer_at((query->packet), 2) &= ~0x04U); | ||||||||
928 | } else { | ||||||||
929 | AA_SET(query->packet)(*buffer_at((query->packet), 2) |= 0x04U); | ||||||||
930 | } | ||||||||
931 | |||||||||
932 | add_rrset(query, | ||||||||
933 | answer, | ||||||||
934 | AUTHORITY_SECTION, | ||||||||
935 | query->delegation_domain, | ||||||||
936 | query->delegation_rrset); | ||||||||
937 | if (query->edns.dnssec_ok && zone_is_secure(query->zone)) { | ||||||||
938 | rrset_type *rrset; | ||||||||
939 | if ((rrset = domain_find_rrset(query->delegation_domain, query->zone, TYPE_DS43))) { | ||||||||
940 | add_rrset(query, answer, AUTHORITY_SECTION, | ||||||||
941 | query->delegation_domain, rrset); | ||||||||
942 | #ifdef NSEC3 | ||||||||
943 | } else if (query->zone->nsec3_param) { | ||||||||
944 | nsec3_answer_delegation(query, answer); | ||||||||
945 | #endif | ||||||||
946 | } else if ((rrset = domain_find_rrset(query->delegation_domain, query->zone, TYPE_NSEC47))) { | ||||||||
947 | add_rrset(query, answer, AUTHORITY_SECTION, | ||||||||
948 | query->delegation_domain, rrset); | ||||||||
949 | } | ||||||||
950 | } | ||||||||
951 | } | ||||||||
952 | |||||||||
953 | |||||||||
954 | /* | ||||||||
955 | * Answer SOA information. | ||||||||
956 | */ | ||||||||
957 | static void | ||||||||
958 | answer_soa(struct query *query, answer_type *answer) | ||||||||
959 | { | ||||||||
960 | if (query->qclass != CLASS_ANY255) { | ||||||||
961 | add_rrset(query, answer, | ||||||||
962 | AUTHORITY_SECTION, | ||||||||
963 | query->zone->apex, | ||||||||
964 | query->zone->soa_nx_rrset); | ||||||||
965 | } | ||||||||
966 | } | ||||||||
967 | |||||||||
968 | |||||||||
969 | /* | ||||||||
970 | * Answer that the domain name exists but there is no RRset with the | ||||||||
971 | * requested type. | ||||||||
972 | * | ||||||||
973 | * DNSSEC: Include the correct NSEC record proving that the type does | ||||||||
974 | * not exist. In the wildcard no data (3.1.3.4) case the wildcard IS | ||||||||
975 | * NOT expanded, so the ORIGINAL parameter must point to the original | ||||||||
976 | * wildcard entry, not to the generated entry. | ||||||||
977 | */ | ||||||||
978 | static void | ||||||||
979 | answer_nodata(struct query *query, answer_type *answer, domain_type *original) | ||||||||
980 | { | ||||||||
981 | answer_soa(query, answer); | ||||||||
982 | |||||||||
983 | #ifdef NSEC3 | ||||||||
984 | if (query->edns.dnssec_ok && query->zone->nsec3_param) { | ||||||||
985 | nsec3_answer_nodata(query, answer, original); | ||||||||
986 | } else | ||||||||
987 | #endif | ||||||||
988 | if (query->edns.dnssec_ok && zone_is_secure(query->zone)) { | ||||||||
989 | domain_type *nsec_domain; | ||||||||
990 | rrset_type *nsec_rrset; | ||||||||
991 | |||||||||
992 | nsec_domain = find_covering_nsec(original, query->zone, &nsec_rrset); | ||||||||
993 | if (nsec_domain) { | ||||||||
994 | add_rrset(query, answer, AUTHORITY_SECTION, nsec_domain, nsec_rrset); | ||||||||
995 | } | ||||||||
996 | } | ||||||||
997 | } | ||||||||
998 | |||||||||
999 | static void | ||||||||
1000 | answer_nxdomain(query_type *query, answer_type *answer) | ||||||||
1001 | { | ||||||||
1002 | RCODE_SET(query->packet, RCODE_NXDOMAIN)(*buffer_at((query->packet), 3) = (*buffer_at((query->packet ), 3) & ~0x0fU) | (3)); | ||||||||
1003 | answer_soa(query, answer); | ||||||||
1004 | } | ||||||||
1005 | |||||||||
1006 | |||||||||
1007 | /* | ||||||||
1008 | * Answer domain information (or SOA if we do not have an RRset for | ||||||||
1009 | * the type specified by the query). | ||||||||
1010 | */ | ||||||||
1011 | static void | ||||||||
1012 | answer_domain(struct nsd* nsd, struct query *q, answer_type *answer, | ||||||||
1013 | domain_type *domain, domain_type *original) | ||||||||
1014 | { | ||||||||
1015 | rrset_type *rrset; | ||||||||
1016 | |||||||||
1017 | if (q->qtype == TYPE_ANY255) { | ||||||||
1018 | rrset_type *preferred_rrset = NULL((void *)0); | ||||||||
1019 | rrset_type *normal_rrset = NULL((void *)0); | ||||||||
1020 | rrset_type *non_preferred_rrset = NULL((void *)0); | ||||||||
1021 | |||||||||
1022 | /* | ||||||||
1023 | * Minimize response size for ANY, with one RRset | ||||||||
1024 | * according to RFC 8482(4.1). | ||||||||
1025 | * Prefers popular and not large rtypes (A,AAAA,...) | ||||||||
1026 | * lowering large ones (DNSKEY,RRSIG,...). | ||||||||
1027 | */ | ||||||||
1028 | for (rrset = domain_find_any_rrset(domain, q->zone); rrset; rrset = rrset->next) { | ||||||||
1029 | if (rrset->zone == q->zone | ||||||||
1030 | #ifdef NSEC3 | ||||||||
1031 | && rrset_rrtype(rrset) != TYPE_NSEC350 | ||||||||
1032 | #endif | ||||||||
1033 | /* | ||||||||
1034 | * Don't include the RRSIG RRset when | ||||||||
1035 | * DNSSEC is used, because it is added | ||||||||
1036 | * automatically on an per-RRset basis. | ||||||||
1037 | */ | ||||||||
1038 | && !(q->edns.dnssec_ok | ||||||||
1039 | && zone_is_secure(q->zone) | ||||||||
1040 | && rrset_rrtype(rrset) == TYPE_RRSIG46)) | ||||||||
1041 | { | ||||||||
1042 | switch(rrset_rrtype(rrset)) { | ||||||||
1043 | case TYPE_A1: | ||||||||
1044 | case TYPE_AAAA28: | ||||||||
1045 | case TYPE_SOA6: | ||||||||
1046 | case TYPE_MX15: | ||||||||
1047 | case TYPE_PTR12: | ||||||||
1048 | preferred_rrset = rrset; | ||||||||
1049 | break; | ||||||||
1050 | case TYPE_DNSKEY48: | ||||||||
1051 | case TYPE_RRSIG46: | ||||||||
1052 | case TYPE_NSEC47: | ||||||||
1053 | non_preferred_rrset = rrset; | ||||||||
1054 | break; | ||||||||
1055 | default: | ||||||||
1056 | normal_rrset = rrset; | ||||||||
1057 | } | ||||||||
1058 | if (preferred_rrset) break; | ||||||||
1059 | } | ||||||||
1060 | } | ||||||||
1061 | if (preferred_rrset) { | ||||||||
1062 | add_rrset(q, answer, ANSWER_SECTION, domain, preferred_rrset); | ||||||||
1063 | } else if (normal_rrset) { | ||||||||
1064 | add_rrset(q, answer, ANSWER_SECTION, domain, normal_rrset); | ||||||||
1065 | } else if (non_preferred_rrset) { | ||||||||
1066 | add_rrset(q, answer, ANSWER_SECTION, domain, non_preferred_rrset); | ||||||||
1067 | } else { | ||||||||
1068 | answer_nodata(q, answer, original); | ||||||||
1069 | return; | ||||||||
1070 | } | ||||||||
1071 | #ifdef NSEC3 | ||||||||
1072 | } else if (q->qtype == TYPE_NSEC350) { | ||||||||
1073 | answer_nodata(q, answer, original); | ||||||||
1074 | return; | ||||||||
1075 | #endif | ||||||||
1076 | } else if ((rrset = domain_find_rrset(domain, q->zone, q->qtype))) { | ||||||||
1077 | add_rrset(q, answer, ANSWER_SECTION, domain, rrset); | ||||||||
1078 | } else if ((rrset = domain_find_rrset(domain, q->zone, TYPE_CNAME5))) { | ||||||||
1079 | int added; | ||||||||
1080 | |||||||||
1081 | /* | ||||||||
1082 | * If the CNAME is not added it is already in the | ||||||||
1083 | * answer, so we have a CNAME loop. Don't follow the | ||||||||
1084 | * CNAME target in this case. | ||||||||
1085 | */ | ||||||||
1086 | added = add_rrset(q, answer, ANSWER_SECTION, domain, rrset); | ||||||||
1087 | assert(rrset->rr_count > 0)((void)0); | ||||||||
1088 | if (added) { | ||||||||
1089 | /* only process first CNAME record */ | ||||||||
1090 | domain_type *closest_match = rdata_atom_domain(rrset->rrs[0].rdatas[0]); | ||||||||
1091 | domain_type *closest_encloser = closest_match; | ||||||||
1092 | zone_type* origzone = q->zone; | ||||||||
1093 | ++q->cname_count; | ||||||||
1094 | |||||||||
1095 | answer_lookup_zone(nsd, q, answer, closest_match->number, | ||||||||
1096 | closest_match == closest_encloser, | ||||||||
1097 | closest_match, closest_encloser, | ||||||||
1098 | domain_dname(closest_match)); | ||||||||
1099 | q->zone = origzone; | ||||||||
1100 | } | ||||||||
1101 | return; | ||||||||
1102 | } else { | ||||||||
1103 | answer_nodata(q, answer, original); | ||||||||
1104 | return; | ||||||||
1105 | } | ||||||||
1106 | |||||||||
1107 | if (q->qclass != CLASS_ANY255 && q->zone->ns_rrset && answer_needs_ns(q) | ||||||||
1108 | && !minimal_responses) { | ||||||||
1109 | add_rrset(q, answer, OPTIONAL_AUTHORITY_SECTION, q->zone->apex, | ||||||||
1110 | q->zone->ns_rrset); | ||||||||
1111 | } | ||||||||
1112 | } | ||||||||
1113 | |||||||||
1114 | |||||||||
1115 | /* | ||||||||
1116 | * Answer with authoritative data. If a wildcard is matched the owner | ||||||||
1117 | * name will be expanded to the domain name specified by | ||||||||
1118 | * DOMAIN_NUMBER. DOMAIN_NUMBER 0 (zero) is reserved for the original | ||||||||
1119 | * query name. | ||||||||
1120 | * | ||||||||
1121 | * DNSSEC: Include the necessary NSEC records in case the request | ||||||||
1122 | * domain name does not exist and/or a wildcard match does not exist. | ||||||||
1123 | */ | ||||||||
1124 | static void | ||||||||
1125 | answer_authoritative(struct nsd *nsd, | ||||||||
1126 | struct query *q, | ||||||||
1127 | answer_type *answer, | ||||||||
1128 | size_t domain_number, | ||||||||
1129 | int exact, | ||||||||
1130 | domain_type *closest_match, | ||||||||
1131 | domain_type *closest_encloser, | ||||||||
1132 | const dname_type *qname) | ||||||||
1133 | { | ||||||||
1134 | domain_type *match; | ||||||||
1135 | domain_type *original = closest_match; | ||||||||
1136 | domain_type *dname_ce; | ||||||||
1137 | domain_type *wildcard_child; | ||||||||
1138 | rrset_type *rrset; | ||||||||
1139 | |||||||||
1140 | #ifdef NSEC3 | ||||||||
1141 | if(exact
| ||||||||
1142 | exact = 0; /* pretend it does not exist */ | ||||||||
1143 | if(closest_encloser->parent) | ||||||||
1144 | closest_encloser = closest_encloser->parent; | ||||||||
1145 | } | ||||||||
1146 | #endif /* NSEC3 */ | ||||||||
1147 | if((dname_ce = find_dname_above(closest_encloser, q->zone)) != NULL((void *)0)) { | ||||||||
1148 | /* occlude the found data, the DNAME is closest_encloser */ | ||||||||
1149 | closest_encloser = dname_ce; | ||||||||
1150 | exact = 0; | ||||||||
1151 | } | ||||||||
1152 | |||||||||
1153 | if (exact
| ||||||||
1154 | match = closest_match; | ||||||||
1155 | } else if ((rrset=domain_find_rrset(closest_encloser, q->zone, TYPE_DNAME39))) { | ||||||||
1156 | /* process DNAME */ | ||||||||
1157 | const dname_type* name = qname; | ||||||||
1158 | domain_type* src = closest_encloser; | ||||||||
1159 | domain_type *dest = rdata_atom_domain(rrset->rrs[0].rdatas[0]); | ||||||||
1160 | const dname_type* newname; | ||||||||
1161 | size_t newnum = 0; | ||||||||
1162 | zone_type* origzone = q->zone; | ||||||||
1163 | assert(rrset->rr_count > 0)((void)0); | ||||||||
1164 | if(domain_number != 0) /* we followed CNAMEs or DNAMEs */ | ||||||||
1165 | name = domain_dname(closest_match); | ||||||||
1166 | DEBUG(DEBUG_QUERY,2, (LOG_INFO, "expanding DNAME for q=%s", dname_to_string(name, NULL))); | ||||||||
1167 | DEBUG(DEBUG_QUERY,2, (LOG_INFO, "->src is %s", | ||||||||
1168 | domain_to_string(closest_encloser))); | ||||||||
1169 | DEBUG(DEBUG_QUERY,2, (LOG_INFO, "->dest is %s", | ||||||||
1170 | domain_to_string(dest))); | ||||||||
1171 | if(!add_rrset(q, answer, ANSWER_SECTION, closest_encloser, rrset)) { | ||||||||
1172 | /* stop if DNAME loops, when added second time */ | ||||||||
1173 | if(dname_is_subdomain(domain_dname(dest), domain_dname(src))) { | ||||||||
1174 | return; | ||||||||
1175 | } | ||||||||
1176 | } | ||||||||
1177 | newname = dname_replace(q->region, name, | ||||||||
1178 | domain_dname(src), domain_dname(dest)); | ||||||||
1179 | ++q->cname_count; | ||||||||
1180 | if(!newname) { /* newname too long */ | ||||||||
1181 | RCODE_SET(q->packet, RCODE_YXDOMAIN)(*buffer_at((q->packet), 3) = (*buffer_at((q->packet), 3 ) & ~0x0fU) | (6)); | ||||||||
1182 | /* RFC 8914 - Extended DNS Errors | ||||||||
1183 | * 4.21. Extended DNS Error Code 0 - Other */ | ||||||||
1184 | ASSIGN_EDE_CODE_AND_STRING_LITERAL(q->edns.ede,do { q->edns.ede = (0); q->edns.ede_text = ("DNAME expansion became too large" ""); q->edns.ede_text_len = sizeof("DNAME expansion became too large" ) - 1; } while (0) | ||||||||
1185 | EDE_OTHER, "DNAME expansion became too large")do { q->edns.ede = (0); q->edns.ede_text = ("DNAME expansion became too large" ""); q->edns.ede_text_len = sizeof("DNAME expansion became too large" ) - 1; } while (0); | ||||||||
1186 | return; | ||||||||
1187 | } | ||||||||
1188 | DEBUG(DEBUG_QUERY,2, (LOG_INFO, "->result is %s", dname_to_string(newname, NULL))); | ||||||||
1189 | /* follow the DNAME */ | ||||||||
1190 | (void)namedb_lookup(nsd->db, newname, &closest_match, &closest_encloser); | ||||||||
1191 | /* synthesize CNAME record */ | ||||||||
1192 | newnum = query_synthesize_cname(q, answer, name, newname, | ||||||||
1193 | src, closest_encloser, &closest_match, rrset->rrs[0].ttl); | ||||||||
1194 | if(!newnum) { | ||||||||
1195 | /* could not synthesize the CNAME. */ | ||||||||
1196 | /* return previous CNAMEs to make resolver recurse for us */ | ||||||||
1197 | return; | ||||||||
1198 | } | ||||||||
1199 | if(q->qtype == TYPE_CNAME5) { | ||||||||
1200 | /* The synthesized CNAME is the answer to | ||||||||
1201 | * that query, same as BIND does for query | ||||||||
1202 | * of type CNAME */ | ||||||||
1203 | return; | ||||||||
1204 | } | ||||||||
1205 | |||||||||
1206 | answer_lookup_zone(nsd, q, answer, newnum, | ||||||||
1207 | closest_match == closest_encloser, | ||||||||
1208 | closest_match, closest_encloser, newname); | ||||||||
1209 | q->zone = origzone; | ||||||||
1210 | return; | ||||||||
1211 | } else if ((wildcard_child=domain_wildcard_child(closest_encloser))!=NULL((void *)0) && | ||||||||
1212 | wildcard_child->is_existing) { | ||||||||
1213 | /* Generate the domain from the wildcard. */ | ||||||||
1214 | #ifdef RATELIMIT | ||||||||
1215 | q->wildcard_domain = wildcard_child; | ||||||||
1216 | #endif | ||||||||
1217 | |||||||||
1218 | match = (domain_type *) region_alloc(q->region, | ||||||||
1219 | sizeof(domain_type)); | ||||||||
1220 | #ifdef USE_RADIX_TREE | ||||||||
1221 | match->rnode = NULL((void *)0); | ||||||||
1222 | match->dname = wildcard_child->dname; | ||||||||
1223 | #else | ||||||||
1224 | memcpy(&match->node, &wildcard_child->node, sizeof(rbnode_type)); | ||||||||
1225 | match->node.parent = NULL((void *)0); | ||||||||
1226 | #endif | ||||||||
1227 | match->parent = closest_encloser; | ||||||||
1228 | match->wildcard_child_closest_match = match; | ||||||||
1229 | match->number = domain_number; | ||||||||
1230 | match->rrsets = wildcard_child->rrsets; | ||||||||
1231 | match->is_existing = wildcard_child->is_existing; | ||||||||
1232 | #ifdef NSEC3 | ||||||||
1233 | match->nsec3 = wildcard_child->nsec3; | ||||||||
1234 | /* copy over these entries: | ||||||||
1235 | match->nsec3_is_exact = wildcard_child->nsec3_is_exact; | ||||||||
1236 | match->nsec3_cover = wildcard_child->nsec3_cover; | ||||||||
1237 | match->nsec3_wcard_child_cover = wildcard_child->nsec3_wcard_child_cover; | ||||||||
1238 | match->nsec3_ds_parent_is_exact = wildcard_child->nsec3_ds_parent_is_exact; | ||||||||
1239 | match->nsec3_ds_parent_cover = wildcard_child->nsec3_ds_parent_cover; | ||||||||
1240 | */ | ||||||||
1241 | |||||||||
1242 | if (q->edns.dnssec_ok && q->zone->nsec3_param) { | ||||||||
1243 | /* Only add nsec3 wildcard data when do bit is set */ | ||||||||
1244 | nsec3_answer_wildcard(q, answer, wildcard_child, qname); | ||||||||
1245 | } | ||||||||
1246 | #endif | ||||||||
1247 | |||||||||
1248 | /* | ||||||||
1249 | * Remember the original domain in case a Wildcard No | ||||||||
1250 | * Data (3.1.3.4) response needs to be generated. In | ||||||||
1251 | * this particular case the wildcard IS NOT | ||||||||
1252 | * expanded. | ||||||||
1253 | */ | ||||||||
1254 | original = wildcard_child; | ||||||||
1255 | } else { | ||||||||
1256 | match = NULL((void *)0); | ||||||||
1257 | } | ||||||||
1258 | |||||||||
1259 | /* Authoritative zone. */ | ||||||||
1260 | #ifdef NSEC3 | ||||||||
1261 | if (q->edns.dnssec_ok && q->zone->nsec3_param) { | ||||||||
1262 | nsec3_answer_authoritative(&match, q, answer, | ||||||||
1263 | closest_encloser, qname); | ||||||||
1264 | } else | ||||||||
1265 | #endif | ||||||||
1266 | if (q->edns.dnssec_ok && zone_is_secure(q->zone)) { | ||||||||
1267 | if (match != closest_encloser) { | ||||||||
1268 | domain_type *nsec_domain; | ||||||||
1269 | rrset_type *nsec_rrset; | ||||||||
1270 | |||||||||
1271 | /* | ||||||||
1272 | * No match found or generated from wildcard, | ||||||||
1273 | * include NSEC record. | ||||||||
1274 | */ | ||||||||
1275 | nsec_domain = find_covering_nsec(closest_match, q->zone, &nsec_rrset); | ||||||||
1276 | if (nsec_domain) { | ||||||||
1277 | add_rrset(q, answer, AUTHORITY_SECTION, nsec_domain, nsec_rrset); | ||||||||
1278 | } | ||||||||
1279 | } | ||||||||
1280 | if (!match) { | ||||||||
1281 | domain_type *nsec_domain; | ||||||||
1282 | rrset_type *nsec_rrset; | ||||||||
1283 | |||||||||
1284 | /* | ||||||||
1285 | * No match and no wildcard. Include NSEC | ||||||||
1286 | * proving there is no wildcard. | ||||||||
1287 | */ | ||||||||
1288 | if(closest_encloser && (nsec_domain = | ||||||||
1289 | find_covering_nsec(closest_encloser-> | ||||||||
1290 | wildcard_child_closest_match, q->zone, | ||||||||
1291 | &nsec_rrset)) != NULL((void *)0)) { | ||||||||
1292 | add_rrset(q, answer, AUTHORITY_SECTION, nsec_domain, nsec_rrset); | ||||||||
1293 | } | ||||||||
1294 | } | ||||||||
1295 | } | ||||||||
1296 | |||||||||
1297 | #ifdef NSEC3 | ||||||||
1298 | if (RCODE(q->packet)(*buffer_at((q->packet), 3) & 0x0fU)!=RCODE_OK0) { | ||||||||
1299 | return; /* nsec3 collision failure */ | ||||||||
1300 | } | ||||||||
1301 | #endif | ||||||||
1302 | if (match) { | ||||||||
1303 | answer_domain(nsd, q, answer, match, original); | ||||||||
1304 | } else { | ||||||||
1305 | answer_nxdomain(q, answer); | ||||||||
1306 | } | ||||||||
1307 | } | ||||||||
1308 | |||||||||
1309 | /* | ||||||||
1310 | * qname may be different after CNAMEs have been followed from query->qname. | ||||||||
1311 | */ | ||||||||
1312 | static void | ||||||||
1313 | answer_lookup_zone(struct nsd *nsd, struct query *q, answer_type *answer, | ||||||||
1314 | size_t domain_number, int exact, domain_type *closest_match, | ||||||||
1315 | domain_type *closest_encloser, const dname_type *qname) | ||||||||
1316 | { | ||||||||
1317 | zone_type* origzone = q->zone; | ||||||||
1318 | q->zone = domain_find_zone(nsd->db, closest_encloser); | ||||||||
1319 | if (!q->zone) { | ||||||||
| |||||||||
1320 | /* no zone for this */ | ||||||||
1321 | if(q->cname_count == 0) { | ||||||||
1322 | RCODE_SET(q->packet, RCODE_REFUSE)(*buffer_at((q->packet), 3) = (*buffer_at((q->packet), 3 ) & ~0x0fU) | (5)); | ||||||||
1323 | /* RFC 8914 - Extended DNS Errors | ||||||||
1324 | * 4.21. Extended DNS Error Code 20 - Not Authoritative */ | ||||||||
1325 | q->edns.ede = EDE_NOT_AUTHORITATIVE20; | ||||||||
1326 | } | ||||||||
1327 | return; | ||||||||
1328 | } | ||||||||
1329 | assert(closest_encloser)((void)0); /* otherwise, no q->zone would be found */ | ||||||||
1330 | if(q->zone->opts && q->zone->opts->pattern | ||||||||
1331 | && q->zone->opts->pattern->allow_query) { | ||||||||
1332 | struct acl_options *why = NULL((void *)0); | ||||||||
1333 | |||||||||
1334 | /* check if it passes acl */ | ||||||||
1335 | if(q->is_proxied && acl_check_incoming_block_proxy( | ||||||||
1336 | q->zone->opts->pattern->allow_query, q, &why) == -1) { | ||||||||
1337 | /* the proxy address is blocked */ | ||||||||
1338 | if (verbosity >= 2) { | ||||||||
1339 | char address[128], proxy[128]; | ||||||||
1340 | addr2str(&q->client_addr, address, sizeof(address)); | ||||||||
1341 | addr2str(&q->remote_addr, proxy, sizeof(proxy)); | ||||||||
1342 | VERBOSITY(2, (LOG_INFO, "query %s from %s via proxy %s refused because of proxy, %s %s",do { if ((2) <= verbosity) { log_msg (6, "query %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(q->qname, ((void *)0)), address, proxy, ( why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
1343 | dname_to_string(q->qname, NULL),do { if ((2) <= verbosity) { log_msg (6, "query %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(q->qname, ((void *)0)), address, proxy, ( why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
1344 | address, proxy,do { if ((2) <= verbosity) { log_msg (6, "query %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(q->qname, ((void *)0)), address, proxy, ( why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
1345 | (why?why->ip_address_spec:"."),do { if ((2) <= verbosity) { log_msg (6, "query %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(q->qname, ((void *)0)), address, proxy, ( why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
1346 | (why ? ( why->nokey ? "NOKEY"do { if ((2) <= verbosity) { log_msg (6, "query %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(q->qname, ((void *)0)), address, proxy, ( why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
1347 | : why->blocked ? "BLOCKED"do { if ((2) <= verbosity) { log_msg (6, "query %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(q->qname, ((void *)0)), address, proxy, ( why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
1348 | : why->key_name )do { if ((2) <= verbosity) { log_msg (6, "query %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(q->qname, ((void *)0)), address, proxy, ( why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0) | ||||||||
1349 | : "no acl matches")))do { if ((2) <= verbosity) { log_msg (6, "query %s from %s via proxy %s refused because of proxy, %s %s" , dname_to_string(q->qname, ((void *)0)), address, proxy, ( why?why->ip_address_spec:"."), (why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why->key_name ) : "no acl matches" )) ; } } while (0); | ||||||||
1350 | } | ||||||||
1351 | /* no zone for this */ | ||||||||
1352 | if(q->cname_count == 0) { | ||||||||
1353 | RCODE_SET(q->packet, RCODE_REFUSE)(*buffer_at((q->packet), 3) = (*buffer_at((q->packet), 3 ) & ~0x0fU) | (5)); | ||||||||
1354 | /* RFC8914 - Extended DNS Errors | ||||||||
1355 | * 4.19. Extended DNS Error Code 18 - Prohibited */ | ||||||||
1356 | q->edns.ede = EDE_PROHIBITED18; | ||||||||
1357 | } | ||||||||
1358 | return; | ||||||||
1359 | } | ||||||||
1360 | if(acl_check_incoming( | ||||||||
1361 | q->zone->opts->pattern->allow_query, q, &why) != -1) { | ||||||||
1362 | assert(why)((void)0); | ||||||||
1363 | DEBUG(DEBUG_QUERY,1, (LOG_INFO, "query %s passed acl %s %s", | ||||||||
1364 | dname_to_string(q->qname, NULL), | ||||||||
1365 | why->ip_address_spec, | ||||||||
1366 | why->nokey?"NOKEY": | ||||||||
1367 | (why->blocked?"BLOCKED":why->key_name))); | ||||||||
1368 | } else { | ||||||||
1369 | if (verbosity >= 2) { | ||||||||
1370 | char address[128]; | ||||||||
1371 | addr2str(&q->client_addr, address, sizeof(address)); | ||||||||
1372 | VERBOSITY(2, (LOG_INFO, "query %s from %s refused, %s %s",do { if ((2) <= verbosity) { log_msg (6, "query %s from %s refused, %s %s" , dname_to_string(q->qname, ((void *)0)), address, why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why-> key_name ) : "no acl matches", why?why->ip_address_spec:"." ) ; } } while (0) | ||||||||
1373 | dname_to_string(q->qname, NULL),do { if ((2) <= verbosity) { log_msg (6, "query %s from %s refused, %s %s" , dname_to_string(q->qname, ((void *)0)), address, why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why-> key_name ) : "no acl matches", why?why->ip_address_spec:"." ) ; } } while (0) | ||||||||
1374 | address,do { if ((2) <= verbosity) { log_msg (6, "query %s from %s refused, %s %s" , dname_to_string(q->qname, ((void *)0)), address, why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why-> key_name ) : "no acl matches", why?why->ip_address_spec:"." ) ; } } while (0) | ||||||||
1375 | why ? ( why->nokey ? "NOKEY"do { if ((2) <= verbosity) { log_msg (6, "query %s from %s refused, %s %s" , dname_to_string(q->qname, ((void *)0)), address, why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why-> key_name ) : "no acl matches", why?why->ip_address_spec:"." ) ; } } while (0) | ||||||||
1376 | : why->blocked ? "BLOCKED"do { if ((2) <= verbosity) { log_msg (6, "query %s from %s refused, %s %s" , dname_to_string(q->qname, ((void *)0)), address, why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why-> key_name ) : "no acl matches", why?why->ip_address_spec:"." ) ; } } while (0) | ||||||||
1377 | : why->key_name )do { if ((2) <= verbosity) { log_msg (6, "query %s from %s refused, %s %s" , dname_to_string(q->qname, ((void *)0)), address, why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why-> key_name ) : "no acl matches", why?why->ip_address_spec:"." ) ; } } while (0) | ||||||||
1378 | : "no acl matches",do { if ((2) <= verbosity) { log_msg (6, "query %s from %s refused, %s %s" , dname_to_string(q->qname, ((void *)0)), address, why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why-> key_name ) : "no acl matches", why?why->ip_address_spec:"." ) ; } } while (0) | ||||||||
1379 | why?why->ip_address_spec:"."))do { if ((2) <= verbosity) { log_msg (6, "query %s from %s refused, %s %s" , dname_to_string(q->qname, ((void *)0)), address, why ? ( why->nokey ? "NOKEY" : why->blocked ? "BLOCKED" : why-> key_name ) : "no acl matches", why?why->ip_address_spec:"." ) ; } } while (0); | ||||||||
1380 | } | ||||||||
1381 | /* no zone for this */ | ||||||||
1382 | if(q->cname_count == 0) { | ||||||||
1383 | RCODE_SET(q->packet, RCODE_REFUSE)(*buffer_at((q->packet), 3) = (*buffer_at((q->packet), 3 ) & ~0x0fU) | (5)); | ||||||||
1384 | /* RFC8914 - Extended DNS Errors | ||||||||
1385 | * 4.19. Extended DNS Error Code 18 - Prohibited */ | ||||||||
1386 | q->edns.ede = EDE_PROHIBITED18; | ||||||||
1387 | } | ||||||||
1388 | return; | ||||||||
1389 | } | ||||||||
1390 | } | ||||||||
1391 | if(!q->zone->apex || !q->zone->soa_rrset) { | ||||||||
1392 | /* zone is configured but not loaded */ | ||||||||
1393 | if(q->cname_count == 0) { | ||||||||
1394 | RCODE_SET(q->packet, RCODE_SERVFAIL)(*buffer_at((q->packet), 3) = (*buffer_at((q->packet), 3 ) & ~0x0fU) | (2)); | ||||||||
1395 | /* RFC 8914 - Extended DNS Errors | ||||||||
1396 | * 4.15. Extended DNS Error Code 14 - Not Ready */ | ||||||||
1397 | q->edns.ede = EDE_NOT_READY14; | ||||||||
1398 | ASSIGN_EDE_CODE_AND_STRING_LITERAL(q->edns.ede,do { q->edns.ede = (14); q->edns.ede_text = ("Zone is configured but not loaded" ""); q->edns.ede_text_len = sizeof("Zone is configured but not loaded" ) - 1; } while (0) | ||||||||
1399 | EDE_NOT_READY, "Zone is configured but not loaded")do { q->edns.ede = (14); q->edns.ede_text = ("Zone is configured but not loaded" ""); q->edns.ede_text_len = sizeof("Zone is configured but not loaded" ) - 1; } while (0); | ||||||||
1400 | } | ||||||||
1401 | return; | ||||||||
1402 | } | ||||||||
1403 | |||||||||
1404 | /* | ||||||||
1405 | * If confine-to-zone is set to yes do not return additional | ||||||||
1406 | * information for a zone with a different apex from the query zone. | ||||||||
1407 | */ | ||||||||
1408 | if (nsd->options->confine_to_zone && | ||||||||
1409 | (origzone != NULL((void *)0) && dname_compare(domain_dname(origzone->apex), domain_dname(q->zone->apex)) != 0)) { | ||||||||
1410 | return; | ||||||||
1411 | } | ||||||||
1412 | |||||||||
1413 | /* now move up the closest encloser until it exists, previous | ||||||||
1414 | * (possibly empty) closest encloser was useful to finding the zone | ||||||||
1415 | * (for empty zones too), but now we want actual data nodes */ | ||||||||
1416 | if (closest_encloser && !closest_encloser->is_existing) { | ||||||||
1417 | exact = 0; | ||||||||
1418 | while (closest_encloser != NULL((void *)0) && !closest_encloser->is_existing) | ||||||||
1419 | closest_encloser = closest_encloser->parent; | ||||||||
1420 | } | ||||||||
1421 | |||||||||
1422 | /* | ||||||||
1423 | * See RFC 4035 (DNSSEC protocol) section 3.1.4.1 Responding | ||||||||
1424 | * to Queries for DS RRs. | ||||||||
1425 | */ | ||||||||
1426 | if (exact && q->qtype == TYPE_DS43 && closest_encloser == q->zone->apex) { | ||||||||
1427 | /* | ||||||||
1428 | * Type DS query at a zone cut, use the responsible | ||||||||
1429 | * parent zone to generate the answer if we are | ||||||||
1430 | * authoritative for the parent zone. | ||||||||
1431 | */ | ||||||||
1432 | zone_type *zone = domain_find_parent_zone(nsd->db, q->zone); | ||||||||
1433 | if (zone) { | ||||||||
1434 | q->zone = zone; | ||||||||
1435 | if(!q->zone->apex || !q->zone->soa_rrset) { | ||||||||
1436 | /* zone is configured but not loaded */ | ||||||||
1437 | if(q->cname_count == 0) { | ||||||||
1438 | RCODE_SET(q->packet, RCODE_SERVFAIL)(*buffer_at((q->packet), 3) = (*buffer_at((q->packet), 3 ) & ~0x0fU) | (2)); | ||||||||
1439 | /* RFC 8914 - Extended DNS Errors | ||||||||
1440 | * 4.15. Extended DNS Error Code 14 - Not Ready */ | ||||||||
1441 | ASSIGN_EDE_CODE_AND_STRING_LITERAL(do { q->edns.ede = (14); q->edns.ede_text = ("Zone is configured but not loaded" ""); q->edns.ede_text_len = sizeof("Zone is configured but not loaded" ) - 1; } while (0) | ||||||||
1442 | q->edns.ede, EDE_NOT_READY,do { q->edns.ede = (14); q->edns.ede_text = ("Zone is configured but not loaded" ""); q->edns.ede_text_len = sizeof("Zone is configured but not loaded" ) - 1; } while (0) | ||||||||
1443 | "Zone is configured but not loaded")do { q->edns.ede = (14); q->edns.ede_text = ("Zone is configured but not loaded" ""); q->edns.ede_text_len = sizeof("Zone is configured but not loaded" ) - 1; } while (0); | ||||||||
1444 | } | ||||||||
1445 | return; | ||||||||
1446 | } | ||||||||
1447 | } | ||||||||
1448 | } | ||||||||
1449 | |||||||||
1450 | /* see if the zone has expired (for secondary zones) */ | ||||||||
1451 | if(q->zone
| ||||||||
1452 | q->zone->opts->pattern->request_xfr != 0 && !q->zone->is_ok) { | ||||||||
1453 | if(q->cname_count == 0) { | ||||||||
1454 | RCODE_SET(q->packet, RCODE_SERVFAIL)(*buffer_at((q->packet), 3) = (*buffer_at((q->packet), 3 ) & ~0x0fU) | (2)); | ||||||||
1455 | /* RFC 8914 - Extended DNS Errors | ||||||||
1456 | * 4.25. Extended DNS Error Code 24 - Invalid Data */ | ||||||||
1457 | ASSIGN_EDE_CODE_AND_STRING_LITERAL(q->edns.ede,do { q->edns.ede = (24); q->edns.ede_text = ("Zone has expired" ""); q->edns.ede_text_len = sizeof("Zone has expired") - 1 ; } while (0) | ||||||||
1458 | EDE_INVALID_DATA, "Zone has expired")do { q->edns.ede = (24); q->edns.ede_text = ("Zone has expired" ""); q->edns.ede_text_len = sizeof("Zone has expired") - 1 ; } while (0); | ||||||||
1459 | } | ||||||||
1460 | return; | ||||||||
1461 | } | ||||||||
1462 | |||||||||
1463 | if (exact
| ||||||||
1464 | /* | ||||||||
1465 | * Type DS query at the zone apex (and the server is | ||||||||
1466 | * not authoritative for the parent zone). | ||||||||
1467 | */ | ||||||||
1468 | if (q->qclass == CLASS_ANY255) { | ||||||||
1469 | AA_CLR(q->packet)(*buffer_at((q->packet), 2) &= ~0x04U); | ||||||||
1470 | } else { | ||||||||
1471 | AA_SET(q->packet)(*buffer_at((q->packet), 2) |= 0x04U); | ||||||||
1472 | } | ||||||||
1473 | answer_nodata(q, answer, closest_encloser); | ||||||||
1474 | } else { | ||||||||
1475 | q->delegation_domain = domain_find_ns_rrsets( | ||||||||
1476 | closest_encloser, q->zone, &q->delegation_rrset); | ||||||||
1477 | if(q->delegation_domain && find_dname_above(q->delegation_domain, q->zone)) { | ||||||||
1478 | q->delegation_domain = NULL((void *)0); /* use higher DNAME */ | ||||||||
1479 | } | ||||||||
1480 | |||||||||
1481 | if (!q->delegation_domain
|
9.1 | Field 'delegation_domain' is null |
1 | /* | |||
2 | * namedb.h -- nsd(8) internal namespace database definitions | |||
3 | * | |||
4 | * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. | |||
5 | * | |||
6 | * See LICENSE for the license. | |||
7 | * | |||
8 | */ | |||
9 | ||||
10 | #ifndef NAMEDB_H | |||
11 | #define NAMEDB_H | |||
12 | ||||
13 | #include <stdio.h> | |||
14 | ||||
15 | #include "dname.h" | |||
16 | #include "dns.h" | |||
17 | #include "radtree.h" | |||
18 | #include "rbtree.h" | |||
19 | struct zone_options; | |||
20 | struct nsd_options; | |||
21 | struct udb_base; | |||
22 | struct udb_ptr; | |||
23 | struct nsd; | |||
24 | struct zone_ixfr; | |||
25 | ||||
26 | typedef union rdata_atom rdata_atom_type; | |||
27 | typedef struct rrset rrset_type; | |||
28 | typedef struct rr rr_type; | |||
29 | ||||
30 | /* | |||
31 | * A domain name table supporting fast insert and search operations. | |||
32 | */ | |||
33 | typedef struct domain_table domain_table_type; | |||
34 | typedef struct domain domain_type; | |||
35 | typedef struct zone zone_type; | |||
36 | typedef struct namedb namedb_type; | |||
37 | ||||
38 | struct domain_table | |||
39 | { | |||
40 | region_type* region; | |||
41 | #ifdef USE_RADIX_TREE | |||
42 | struct radtree *nametree; | |||
43 | #else | |||
44 | rbtree_type *names_to_domains; | |||
45 | #endif | |||
46 | domain_type* root; | |||
47 | /* ptr to biggest domain.number and last in list. | |||
48 | * the root is the lowest and first in the list. */ | |||
49 | domain_type *numlist_last; | |||
50 | #ifdef NSEC3 | |||
51 | /* the prehash list, start of the list */ | |||
52 | domain_type* prehash_list; | |||
53 | #endif /* NSEC3 */ | |||
54 | }; | |||
55 | ||||
56 | #ifdef NSEC3 | |||
57 | typedef struct nsec3_hash_node nsec3_hash_node_type; | |||
58 | struct nsec3_hash_node { | |||
59 | /* hash value */ | |||
60 | uint8_t hash[NSEC3_HASH_LEN20]; | |||
61 | /* entry in the hashtree */ | |||
62 | rbnode_type node; | |||
63 | } ATTR_PACKED; | |||
64 | ||||
65 | typedef struct nsec3_hash_wc_node nsec3_hash_wc_node_type; | |||
66 | struct nsec3_hash_wc_node { | |||
67 | nsec3_hash_node_type hash; | |||
68 | nsec3_hash_node_type wc; | |||
69 | }; | |||
70 | ||||
71 | struct nsec3_domain_data { | |||
72 | /* (if nsec3 chain complete) always the covering nsec3 record */ | |||
73 | domain_type* nsec3_cover; | |||
74 | /* the nsec3 that covers the wildcard child of this domain. */ | |||
75 | domain_type* nsec3_wcard_child_cover; | |||
76 | /* for the DS case we must answer on the parent side of zone cut */ | |||
77 | domain_type* nsec3_ds_parent_cover; | |||
78 | /* NSEC3 domains to prehash, prev and next on the list or cleared */ | |||
79 | domain_type* prehash_prev, *prehash_next; | |||
80 | /* entry in the nsec3tree (for NSEC3s in the chain in use) */ | |||
81 | rbnode_type nsec3_node; | |||
82 | ||||
83 | /* node for the precompiled domain and the precompiled wildcard */ | |||
84 | nsec3_hash_wc_node_type* hash_wc; | |||
85 | ||||
86 | /* node for the precompiled parent ds */ | |||
87 | nsec3_hash_node_type* ds_parent_hash; | |||
88 | ||||
89 | /* if the domain has an NSEC3 for it, use cover ptr to get it. */ | |||
90 | unsigned nsec3_is_exact : 1; | |||
91 | /* same but on parent side */ | |||
92 | unsigned nsec3_ds_parent_is_exact : 1; | |||
93 | } ATTR_PACKED; | |||
94 | #endif /* NSEC3 */ | |||
95 | ||||
96 | struct domain | |||
97 | { | |||
98 | #ifdef USE_RADIX_TREE | |||
99 | struct radnode* rnode; | |||
100 | const dname_type* dname; | |||
101 | #else | |||
102 | rbnode_type node; | |||
103 | #endif | |||
104 | domain_type* parent; | |||
105 | domain_type* wildcard_child_closest_match; | |||
106 | rrset_type* rrsets; | |||
107 | #ifdef NSEC3 | |||
108 | struct nsec3_domain_data* nsec3; | |||
109 | #endif | |||
110 | /* double-linked list sorted by domain.number */ | |||
111 | domain_type* numlist_prev, *numlist_next; | |||
112 | uint32_t number; /* Unique domain name number. */ | |||
113 | uint32_t usage; /* number of ptrs to this from RRs(in rdata) and | |||
114 | from zone-apex pointers, also the root has one | |||
115 | more to make sure it cannot be deleted. */ | |||
116 | ||||
117 | /* | |||
118 | * This domain name exists (see wildcard clarification draft). | |||
119 | */ | |||
120 | unsigned is_existing : 1; | |||
121 | unsigned is_apex : 1; | |||
122 | } ATTR_PACKED; | |||
123 | ||||
124 | struct zone | |||
125 | { | |||
126 | struct radnode *node; /* this entry in zonetree */ | |||
127 | domain_type* apex; | |||
128 | rrset_type* soa_rrset; | |||
129 | rrset_type* soa_nx_rrset; /* see bug #103 */ | |||
130 | rrset_type* ns_rrset; | |||
131 | #ifdef NSEC3 | |||
132 | rr_type* nsec3_param; /* NSEC3PARAM RR of chain in use or NULL */ | |||
133 | domain_type* nsec3_last; /* last domain with nsec3, wraps */ | |||
134 | /* in these trees, the root contains an elem ptr to the radtree* */ | |||
135 | rbtree_type* nsec3tree; /* tree with relevant NSEC3 domains */ | |||
136 | rbtree_type* hashtree; /* tree, hashed NSEC3precompiled domains */ | |||
137 | rbtree_type* wchashtree; /* tree, wildcard hashed domains */ | |||
138 | rbtree_type* dshashtree; /* tree, ds-parent-hash domains */ | |||
139 | #endif | |||
140 | struct zone_options* opts; | |||
141 | struct zone_ixfr* ixfr; | |||
142 | char* filename; /* set if read from file, which file */ | |||
143 | char* logstr; /* set for zone xfer, the log string */ | |||
144 | struct timespec mtime; /* time of last modification */ | |||
145 | unsigned zonestatid; /* array index for zone stats */ | |||
146 | unsigned is_secure : 1; /* zone uses DNSSEC */ | |||
147 | unsigned is_ok : 1; /* zone has not expired */ | |||
148 | unsigned is_changed : 1; /* zone changes must be written to disk */ | |||
149 | unsigned is_updated : 1; /* zone was changed by XFR */ | |||
150 | unsigned is_skipped : 1; /* subsequent zone updates are skipped */ | |||
151 | unsigned is_checked : 1; /* zone already verified */ | |||
152 | unsigned is_bad : 1; /* zone failed verification */ | |||
153 | } ATTR_PACKED; | |||
154 | ||||
155 | /* a RR in DNS */ | |||
156 | struct rr { | |||
157 | domain_type* owner; | |||
158 | rdata_atom_type* rdatas; | |||
159 | uint32_t ttl; | |||
160 | uint16_t type; | |||
161 | uint16_t klass; | |||
162 | uint16_t rdata_count; | |||
163 | } ATTR_PACKED; | |||
164 | ||||
165 | /* | |||
166 | * An RRset consists of at least one RR. All RRs are from the same | |||
167 | * zone. | |||
168 | */ | |||
169 | struct rrset | |||
170 | { | |||
171 | rrset_type* next; | |||
172 | zone_type* zone; | |||
173 | rr_type* rrs; | |||
174 | uint16_t rr_count; | |||
175 | } ATTR_PACKED; | |||
176 | ||||
177 | /* | |||
178 | * The field used is based on the wireformat the atom is stored in. | |||
179 | * The allowed wireformats are defined by the rdata_wireformat_type | |||
180 | * enumeration. | |||
181 | */ | |||
182 | union rdata_atom | |||
183 | { | |||
184 | /* RDATA_WF_COMPRESSED_DNAME, RDATA_WF_UNCOMPRESSED_DNAME */ | |||
185 | domain_type* domain; | |||
186 | ||||
187 | /* Default. */ | |||
188 | uint16_t* data; | |||
189 | }; | |||
190 | ||||
191 | /* | |||
192 | * Create a new domain_table containing only the root domain. | |||
193 | */ | |||
194 | domain_table_type *domain_table_create(region_type *region); | |||
195 | ||||
196 | /* | |||
197 | * Search the domain table for a match and the closest encloser. | |||
198 | */ | |||
199 | int domain_table_search(domain_table_type* table, | |||
200 | const dname_type* dname, | |||
201 | domain_type **closest_match, | |||
202 | domain_type **closest_encloser); | |||
203 | ||||
204 | /* | |||
205 | * The number of domains stored in the table (minimum is one for the | |||
206 | * root domain). | |||
207 | */ | |||
208 | static inline uint32_t | |||
209 | domain_table_count(domain_table_type* table) | |||
210 | { | |||
211 | #ifdef USE_RADIX_TREE | |||
212 | return table->nametree->count; | |||
213 | #else | |||
214 | return table->names_to_domains->count; | |||
215 | #endif | |||
216 | } | |||
217 | ||||
218 | /* | |||
219 | * Find the specified dname in the domain_table. NULL is returned if | |||
220 | * there is no exact match. | |||
221 | */ | |||
222 | domain_type* domain_table_find(domain_table_type* table, | |||
223 | const dname_type* dname); | |||
224 | ||||
225 | /* | |||
226 | * Insert a domain name in the domain table. If the domain name is | |||
227 | * not yet present in the table it is copied and a new dname_info node | |||
228 | * is created (as well as for the missing parent domain names, if | |||
229 | * any). Otherwise the domain_type that is already in the | |||
230 | * domain_table is returned. | |||
231 | */ | |||
232 | domain_type *domain_table_insert(domain_table_type *table, | |||
233 | const dname_type *dname); | |||
234 | ||||
235 | /* put domain into nsec3 hash space tree */ | |||
236 | void zone_add_domain_in_hash_tree(region_type* region, rbtree_type** tree, | |||
237 | int (*cmpf)(const void*, const void*), domain_type* domain, | |||
238 | rbnode_type* node); | |||
239 | void zone_del_domain_in_hash_tree(rbtree_type* tree, rbnode_type* node); | |||
240 | void hash_tree_delete(region_type* region, rbtree_type* tree); | |||
241 | void prehash_clear(domain_table_type* table); | |||
242 | void prehash_add(domain_table_type* table, domain_type* domain); | |||
243 | void prehash_del(domain_table_type* table, domain_type* domain); | |||
244 | int domain_is_prehash(domain_table_type* table, domain_type* domain); | |||
245 | ||||
246 | /* | |||
247 | * Add an RRset to the specified domain. Updates the is_existing flag | |||
248 | * as required. | |||
249 | */ | |||
250 | void domain_add_rrset(domain_type* domain, rrset_type* rrset); | |||
251 | ||||
252 | rrset_type* domain_find_rrset(domain_type* domain, zone_type* zone, uint16_t type); | |||
253 | rrset_type* domain_find_any_rrset(domain_type* domain, zone_type* zone); | |||
254 | ||||
255 | zone_type* domain_find_zone(namedb_type* db, domain_type* domain); | |||
256 | zone_type* domain_find_parent_zone(namedb_type* db, zone_type* zone); | |||
257 | ||||
258 | domain_type* domain_find_ns_rrsets(domain_type* domain, zone_type* zone, rrset_type **ns); | |||
259 | /* find DNAME rrset in domain->parent or higher and return that domain */ | |||
260 | domain_type * find_dname_above(domain_type* domain, zone_type* zone); | |||
261 | ||||
262 | int domain_is_glue(domain_type* domain, zone_type* zone); | |||
263 | ||||
264 | rrset_type* domain_find_non_cname_rrset(domain_type* domain, zone_type* zone); | |||
265 | ||||
266 | domain_type* domain_wildcard_child(domain_type* domain); | |||
267 | domain_type *domain_previous_existing_child(domain_type* domain); | |||
268 | ||||
269 | int zone_is_secure(zone_type* zone); | |||
270 | ||||
271 | static inline dname_type * | |||
272 | domain_dname(domain_type* domain) | |||
273 | { | |||
274 | #ifdef USE_RADIX_TREE | |||
275 | return (dname_type *) domain->dname; | |||
| ||||
276 | #else | |||
277 | return (dname_type *) domain->node.key; | |||
278 | #endif | |||
279 | } | |||
280 | ||||
281 | static inline const dname_type * | |||
282 | domain_dname_const(const domain_type* domain) | |||
283 | { | |||
284 | #ifdef USE_RADIX_TREE | |||
285 | return domain->dname; | |||
286 | #else | |||
287 | return (const dname_type *) domain->node.key; | |||
288 | #endif | |||
289 | } | |||
290 | ||||
291 | static inline domain_type * | |||
292 | domain_previous(domain_type* domain) | |||
293 | { | |||
294 | #ifdef USE_RADIX_TREE | |||
295 | struct radnode* prev = radix_prev(domain->rnode); | |||
296 | return prev == NULL((void *)0) ? NULL((void *)0) : (domain_type*)prev->elem; | |||
297 | #else | |||
298 | rbnode_type *prev = rbtree_previous((rbnode_type *) domain); | |||
299 | return prev == RBTREE_NULL&rbtree_null_node ? NULL((void *)0) : (domain_type *) prev; | |||
300 | #endif | |||
301 | } | |||
302 | ||||
303 | static inline domain_type * | |||
304 | domain_next(domain_type* domain) | |||
305 | { | |||
306 | #ifdef USE_RADIX_TREE | |||
307 | struct radnode* next = radix_next(domain->rnode); | |||
308 | return next == NULL((void *)0) ? NULL((void *)0) : (domain_type*)next->elem; | |||
309 | #else | |||
310 | rbnode_type *next = rbtree_next((rbnode_type *) domain); | |||
311 | return next == RBTREE_NULL&rbtree_null_node ? NULL((void *)0) : (domain_type *) next; | |||
312 | #endif | |||
313 | } | |||
314 | ||||
315 | /* easy comparison for subdomain, true if d1 is subdomain of d2. */ | |||
316 | static inline int domain_is_subdomain(domain_type* d1, domain_type* d2) | |||
317 | { return dname_is_subdomain(domain_dname(d1), domain_dname(d2)); } | |||
318 | /* easy printout, to static buffer of dname_to_string, fqdn. */ | |||
319 | static inline const char* domain_to_string(domain_type* domain) | |||
320 | { return dname_to_string(domain_dname(domain), NULL((void *)0)); } | |||
321 | ||||
322 | /* | |||
323 | * The type covered by the signature in the specified RRSIG RR. | |||
324 | */ | |||
325 | uint16_t rr_rrsig_type_covered(rr_type* rr); | |||
326 | ||||
327 | struct namedb | |||
328 | { | |||
329 | region_type* region; | |||
330 | domain_table_type* domains; | |||
331 | struct radtree* zonetree; | |||
332 | /* the timestamp on the ixfr.db file */ | |||
333 | struct timeval diff_timestamp; | |||
334 | /* if diff_skip=1, diff_pos contains the nsd.diff place to continue */ | |||
335 | uint8_t diff_skip; | |||
336 | off_t diff_pos; | |||
337 | }; | |||
338 | ||||
339 | static inline int rdata_atom_is_domain(uint16_t type, size_t index); | |||
340 | static inline int rdata_atom_is_literal_domain(uint16_t type, size_t index); | |||
341 | ||||
342 | static inline domain_type * | |||
343 | rdata_atom_domain(rdata_atom_type atom) | |||
344 | { | |||
345 | return atom.domain; | |||
346 | } | |||
347 | ||||
348 | static inline uint16_t | |||
349 | rdata_atom_size(rdata_atom_type atom) | |||
350 | { | |||
351 | return *atom.data; | |||
352 | } | |||
353 | ||||
354 | static inline uint8_t * | |||
355 | rdata_atom_data(rdata_atom_type atom) | |||
356 | { | |||
357 | return (uint8_t *) (atom.data + 1); | |||
358 | } | |||
359 | ||||
360 | ||||
361 | /* Find the zone for the specified dname in DB. */ | |||
362 | zone_type *namedb_find_zone(namedb_type *db, const dname_type *dname); | |||
363 | /* | |||
364 | * Delete a domain name from the domain table. Removes dname_info node. | |||
365 | * Only deletes if usage is 0, has no rrsets and no children. Checks parents | |||
366 | * for deletion as well. Adjusts numberlist(domain.number), and | |||
367 | * wcard_child closest match. | |||
368 | */ | |||
369 | void domain_table_deldomain(namedb_type* db, domain_type* domain); | |||
370 | ||||
371 | /** dbcreate.c */ | |||
372 | int print_rrs(FILE* out, struct zone* zone); | |||
373 | /** marshal rdata into buffer, must be MAX_RDLENGTH in size */ | |||
374 | size_t rr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz); | |||
375 | /* dbaccess.c */ | |||
376 | int namedb_lookup (struct namedb* db, | |||
377 | const dname_type* dname, | |||
378 | domain_type **closest_match, | |||
379 | domain_type **closest_encloser); | |||
380 | /* pass number of children (to alloc in dirty array */ | |||
381 | struct namedb *namedb_open(struct nsd_options* opt); | |||
382 | void namedb_close(struct namedb* db); | |||
383 | /* free ixfr data stored for zones */ | |||
384 | void namedb_free_ixfr(struct namedb* db); | |||
385 | void namedb_check_zonefiles(struct nsd* nsd, struct nsd_options* opt, | |||
386 | struct udb_base* taskudb, struct udb_ptr* last_task); | |||
387 | void namedb_check_zonefile(struct nsd* nsd, struct udb_base* taskudb, | |||
388 | struct udb_ptr* last_task, struct zone_options* zo); | |||
389 | /** zone one zonefile into memory and revert on parse error, write to udb */ | |||
390 | void namedb_read_zonefile(struct nsd* nsd, struct zone* zone, | |||
391 | struct udb_base* taskudb, struct udb_ptr* last_task); | |||
392 | zone_type* namedb_zone_create(namedb_type* db, const dname_type* dname, | |||
393 | struct zone_options* zopt); | |||
394 | void namedb_zone_delete(namedb_type* db, zone_type* zone); | |||
395 | void namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt); | |||
396 | void namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options); | |||
397 | int create_dirs(const char* path); | |||
398 | int file_get_mtime(const char* file, struct timespec* mtime, int* nonexist); | |||
399 | void allocate_domain_nsec3(domain_table_type *table, domain_type *result); | |||
400 | ||||
401 | static inline int | |||
402 | rdata_atom_is_domain(uint16_t type, size_t index) | |||
403 | { | |||
404 | const rrtype_descriptor_type *descriptor | |||
405 | = rrtype_descriptor_by_type(type); | |||
406 | return (index < descriptor->maximum | |||
407 | && (descriptor->wireformat[index] == RDATA_WF_COMPRESSED_DNAME | |||
408 | || descriptor->wireformat[index] == RDATA_WF_UNCOMPRESSED_DNAME)); | |||
409 | } | |||
410 | ||||
411 | static inline int | |||
412 | rdata_atom_is_literal_domain(uint16_t type, size_t index) | |||
413 | { | |||
414 | const rrtype_descriptor_type *descriptor | |||
415 | = rrtype_descriptor_by_type(type); | |||
416 | return (index < descriptor->maximum | |||
417 | && (descriptor->wireformat[index] == RDATA_WF_LITERAL_DNAME)); | |||
418 | } | |||
419 | ||||
420 | static inline rdata_wireformat_type | |||
421 | rdata_atom_wireformat_type(uint16_t type, size_t index) | |||
422 | { | |||
423 | const rrtype_descriptor_type *descriptor | |||
424 | = rrtype_descriptor_by_type(type); | |||
425 | assert(index < descriptor->maximum)((void)0); | |||
426 | return (rdata_wireformat_type) descriptor->wireformat[index]; | |||
427 | } | |||
428 | ||||
429 | static inline uint16_t | |||
430 | rrset_rrtype(rrset_type* rrset) | |||
431 | { | |||
432 | assert(rrset)((void)0); | |||
433 | assert(rrset->rr_count > 0)((void)0); | |||
434 | return rrset->rrs[0].type; | |||
435 | } | |||
436 | ||||
437 | static inline uint16_t | |||
438 | rrset_rrclass(rrset_type* rrset) | |||
439 | { | |||
440 | assert(rrset)((void)0); | |||
441 | assert(rrset->rr_count > 0)((void)0); | |||
442 | return rrset->rrs[0].klass; | |||
443 | } | |||
444 | ||||
445 | /* | |||
446 | * zone_rr_iter can be used to iterate over all RRs in a given zone. the | |||
447 | * SOA RRSET is guaranteed to be returned first. | |||
448 | */ | |||
449 | typedef struct zone_rr_iter zone_rr_iter_type; | |||
450 | ||||
451 | struct zone_rr_iter { | |||
452 | zone_type *zone; | |||
453 | domain_type *domain; | |||
454 | rrset_type *rrset; | |||
455 | ssize_t index; | |||
456 | }; | |||
457 | ||||
458 | void zone_rr_iter_init(zone_rr_iter_type *iter, zone_type *zone); | |||
459 | ||||
460 | rr_type *zone_rr_iter_next(zone_rr_iter_type *iter); | |||
461 | ||||
462 | #endif /* NAMEDB_H */ |