]>
Commit | Line | Data |
---|---|---|
89c4ed63 A |
1 | /* |
2 | * dns64/dns64.c - DNS64 module | |
3 | * | |
4 | * Copyright (c) 2009, Viagénie. All rights reserved. | |
5 | * | |
6 | * This software is open source. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * | |
12 | * Redistributions of source code must retain the above copyright notice, | |
13 | * this list of conditions and the following disclaimer. | |
14 | * | |
15 | * Redistributions in binary form must reproduce the above copyright notice, | |
16 | * this list of conditions and the following disclaimer in the documentation | |
17 | * and/or other materials provided with the distribution. | |
18 | * | |
19 | * Neither the name of Viagénie nor the names of its contributors may | |
20 | * be used to endorse or promote products derived from this software without | |
21 | * specific prior written permission. | |
22 | * | |
23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
25 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
26 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE | |
27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
33 | * POSSIBILITY OF SUCH DAMAGE. | |
34 | */ | |
35 | ||
36 | /** | |
37 | * \file | |
38 | * | |
39 | * This file contains a module that performs DNS64 query processing. | |
40 | */ | |
41 | ||
42 | #include "config.h" | |
43 | #include "dns64/dns64.h" | |
44 | #include "services/cache/dns.h" | |
45 | #include "services/cache/rrset.h" | |
46 | #include "util/config_file.h" | |
47 | #include "util/data/msgreply.h" | |
48 | #include "util/fptr_wlist.h" | |
49 | #include "util/net_help.h" | |
50 | #include "util/regional.h" | |
51 | ||
52 | /****************************************************************************** | |
53 | * * | |
54 | * STATIC CONSTANTS * | |
55 | * * | |
56 | ******************************************************************************/ | |
57 | ||
58 | /** | |
59 | * This is the default DNS64 prefix that is used whent he dns64 module is listed | |
60 | * in module-config but when the dns64-prefix variable is not present. | |
61 | */ | |
62 | static const char DEFAULT_DNS64_PREFIX[] = "64:ff9b::/96"; | |
63 | ||
64 | /** | |
65 | * Maximum length of a domain name in a PTR query in the .in-addr.arpa tree. | |
66 | */ | |
67 | #define MAX_PTR_QNAME_IPV4 30 | |
68 | ||
69 | /** | |
70 | * Per-query module-specific state. This is usually a dynamically-allocated | |
71 | * structure, but in our case we only need to store one variable describing the | |
72 | * state the query is in. So we repurpose the minfo pointer by storing an | |
73 | * integer in there. | |
74 | */ | |
75 | enum dns64_qstate { | |
76 | DNS64_INTERNAL_QUERY, /**< Internally-generated query, no DNS64 | |
77 | processing. */ | |
78 | DNS64_NEW_QUERY, /**< Query for which we're the first module in | |
79 | line. */ | |
80 | DNS64_SUBQUERY_FINISHED /**< Query for which we generated a sub-query, and | |
81 | for which this sub-query is finished. */ | |
82 | }; | |
83 | ||
84 | ||
85 | /****************************************************************************** | |
86 | * * | |
87 | * STRUCTURES * | |
88 | * * | |
89 | ******************************************************************************/ | |
90 | ||
91 | /** | |
92 | * This structure contains module configuration information. One instance of | |
93 | * this structure exists per instance of the module. Normally there is only one | |
94 | * instance of the module. | |
95 | */ | |
96 | struct dns64_env { | |
97 | /** | |
98 | * DNS64 prefix address. We're using a full sockaddr instead of just an | |
99 | * in6_addr because we can reuse Unbound's generic string parsing functions. | |
100 | * It will always contain a sockaddr_in6, and only the sin6_addr member will | |
101 | * ever be used. | |
102 | */ | |
103 | struct sockaddr_storage prefix_addr; | |
104 | ||
105 | /** | |
106 | * This is always sizeof(sockaddr_in6). | |
107 | */ | |
108 | socklen_t prefix_addrlen; | |
109 | ||
110 | /** | |
111 | * This is the CIDR length of the prefix. It needs to be between 0 and 96. | |
112 | */ | |
113 | int prefix_net; | |
114 | }; | |
115 | ||
116 | ||
117 | /****************************************************************************** | |
118 | * * | |
119 | * UTILITY FUNCTIONS * | |
120 | * * | |
121 | ******************************************************************************/ | |
122 | ||
123 | /** | |
124 | * Generic macro for swapping two variables. | |
125 | * | |
126 | * \param t Type of the variables. (e.g. int) | |
127 | * \param a First variable. | |
128 | * \param b Second variable. | |
129 | * | |
130 | * \warning Do not attempt something foolish such as swap(int,a++,b++)! | |
131 | */ | |
132 | #define swap(t,a,b) do {t x = a; a = b; b = x;} while(0) | |
133 | ||
134 | /** | |
135 | * Reverses a string. | |
136 | * | |
137 | * \param begin Points to the first character of the string. | |
138 | * \param end Points one past the last character of the string. | |
139 | */ | |
140 | static void | |
141 | reverse(char* begin, char* end) | |
142 | { | |
143 | while ( begin < --end ) { | |
144 | swap(char, *begin, *end); | |
145 | ++begin; | |
146 | } | |
147 | } | |
148 | ||
149 | /** | |
150 | * Convert an unsigned integer to a string. The point of this function is that | |
151 | * of being faster than sprintf(). | |
152 | * | |
153 | * \param n The number to be converted. | |
154 | * \param s The result will be written here. Must be large enough, be careful! | |
155 | * | |
156 | * \return The number of characters written. | |
157 | */ | |
158 | static int | |
159 | uitoa(unsigned n, char* s) | |
160 | { | |
161 | char* ss = s; | |
162 | do { | |
163 | *ss++ = '0' + n % 10; | |
164 | } while (n /= 10); | |
165 | reverse(s, ss); | |
166 | return ss - s; | |
167 | } | |
168 | ||
169 | /** | |
170 | * Extract an IPv4 address embedded in the IPv6 address \a ipv6 at offset \a | |
171 | * offset (in bits). Note that bits are not necessarily aligned on bytes so we | |
172 | * need to be careful. | |
173 | * | |
174 | * \param ipv6 IPv6 address represented as a 128-bit array in big-endian | |
175 | * order. | |
176 | * \param offset Index of the MSB of the IPv4 address embedded in the IPv6 | |
177 | * address. | |
178 | */ | |
179 | static uint32_t | |
180 | extract_ipv4(const uint8_t ipv6[16], const int offset) | |
181 | { | |
182 | uint32_t ipv4 = (uint32_t)ipv6[offset/8+0] << (24 + (offset%8)) | |
183 | | (uint32_t)ipv6[offset/8+1] << (16 + (offset%8)) | |
184 | | (uint32_t)ipv6[offset/8+2] << ( 8 + (offset%8)) | |
185 | | (uint32_t)ipv6[offset/8+3] << ( 0 + (offset%8)); | |
186 | if (offset/8+4 < 16) | |
187 | ipv4 |= (uint32_t)ipv6[offset/8+4] >> (8 - offset%8); | |
188 | return ipv4; | |
189 | } | |
190 | ||
191 | /** | |
192 | * Builds the PTR query name corresponding to an IPv4 address. For example, | |
193 | * given the number 3,464,175,361, this will build the string | |
194 | * "\03206\03123\0231\011\07in-addr\04arpa". | |
195 | * | |
196 | * \param ipv4 IPv4 address represented as an unsigned 32-bit number. | |
197 | * \param ptr The result will be written here. Must be large enough, be | |
198 | * careful! | |
199 | * | |
200 | * \return The number of characters written. | |
201 | */ | |
202 | static size_t | |
203 | ipv4_to_ptr(uint32_t ipv4, char ptr[MAX_PTR_QNAME_IPV4]) | |
204 | { | |
205 | static const char IPV4_PTR_SUFFIX[] = "\07in-addr\04arpa"; | |
206 | int i; | |
207 | char* c = ptr; | |
208 | ||
209 | for (i = 0; i < 4; ++i) { | |
210 | *c = uitoa((unsigned int)(ipv4 % 256), c + 1); | |
211 | c += *c + 1; | |
212 | ipv4 /= 256; | |
213 | } | |
214 | ||
215 | memmove(c, IPV4_PTR_SUFFIX, sizeof(IPV4_PTR_SUFFIX)); | |
216 | ||
217 | return c + sizeof(IPV4_PTR_SUFFIX) - ptr; | |
218 | } | |
219 | ||
220 | /** | |
221 | * Converts an IPv6-related domain name string from a PTR query into an IPv6 | |
222 | * address represented as a 128-bit array. | |
223 | * | |
224 | * \param ptr The domain name. (e.g. "\011[...]\010\012\016\012\03ip6\04arpa") | |
225 | * \param ipv6 The result will be written here, in network byte order. | |
226 | * | |
227 | * \return 1 on success, 0 on failure. | |
228 | */ | |
229 | static int | |
230 | ptr_to_ipv6(const char* ptr, uint8_t ipv6[16]) | |
231 | { | |
232 | int i; | |
233 | ||
234 | for (i = 0; i < 64; i++) { | |
235 | int x; | |
236 | ||
237 | if (ptr[i++] != 1) | |
238 | return 0; | |
239 | ||
240 | if (ptr[i] >= '0' && ptr[i] <= '9') { | |
241 | x = ptr[i] - '0'; | |
242 | } else if (ptr[i] >= 'a' && ptr[i] <= 'f') { | |
243 | x = ptr[i] - 'a' + 10; | |
244 | } else if (ptr[i] >= 'A' && ptr[i] <= 'F') { | |
245 | x = ptr[i] - 'A' + 10; | |
246 | } else { | |
247 | return 0; | |
248 | } | |
249 | ||
250 | ipv6[15-i/4] |= x << (2 * ((i-1) % 4)); | |
251 | } | |
252 | ||
253 | return 1; | |
254 | } | |
255 | ||
256 | /** | |
257 | * Synthesize an IPv6 address based on an IPv4 address and the DNS64 prefix. | |
258 | * | |
259 | * \param prefix_addr DNS64 prefix address. | |
260 | * \param prefix_net CIDR length of the DNS64 prefix. Must be between 0 and 96. | |
261 | * \param a IPv4 address. | |
262 | * \param aaaa IPv6 address. The result will be written here. | |
263 | */ | |
264 | static void | |
265 | synthesize_aaaa(const uint8_t prefix_addr[16], int prefix_net, | |
266 | const uint8_t a[4], uint8_t aaaa[16]) | |
267 | { | |
268 | memcpy(aaaa, prefix_addr, 16); | |
269 | aaaa[prefix_net/8+0] |= a[0] >> (0+prefix_net%8); | |
270 | aaaa[prefix_net/8+1] |= a[0] << (8-prefix_net%8); | |
271 | aaaa[prefix_net/8+1] |= a[1] >> (0+prefix_net%8); | |
272 | aaaa[prefix_net/8+2] |= a[1] << (8-prefix_net%8); | |
273 | aaaa[prefix_net/8+2] |= a[2] >> (0+prefix_net%8); | |
274 | aaaa[prefix_net/8+3] |= a[2] << (8-prefix_net%8); | |
275 | aaaa[prefix_net/8+3] |= a[3] >> (0+prefix_net%8); | |
276 | if (prefix_net/8+4 < 16) /* <-- my beautiful symmetry is destroyed! */ | |
277 | aaaa[prefix_net/8+4] |= a[3] << (8-prefix_net%8); | |
278 | } | |
279 | ||
280 | ||
281 | /****************************************************************************** | |
282 | * * | |
283 | * DNS64 MODULE FUNCTIONS * | |
284 | * * | |
285 | ******************************************************************************/ | |
286 | ||
287 | /** | |
288 | * This function applies the configuration found in the parsed configuration | |
289 | * file \a cfg to this instance of the dns64 module. Currently only the DNS64 | |
290 | * prefix (a.k.a. Pref64) is configurable. | |
291 | * | |
292 | * \param dns64_env Module-specific global parameters. | |
293 | * \param cfg Parsed configuration file. | |
294 | */ | |
295 | static int | |
296 | dns64_apply_cfg(struct dns64_env* dns64_env, struct config_file* cfg) | |
297 | { | |
298 | verbose(VERB_ALGO, "dns64-prefix: %s", cfg->dns64_prefix); | |
299 | if (!netblockstrtoaddr(cfg->dns64_prefix ? cfg->dns64_prefix : | |
300 | DEFAULT_DNS64_PREFIX, 0, &dns64_env->prefix_addr, | |
301 | &dns64_env->prefix_addrlen, &dns64_env->prefix_net)) { | |
302 | log_err("cannot parse dns64-prefix netblock: %s", cfg->dns64_prefix); | |
303 | return 0; | |
304 | } | |
305 | if (!addr_is_ip6(&dns64_env->prefix_addr, dns64_env->prefix_addrlen)) { | |
306 | log_err("dns64_prefix is not IPv6: %s", cfg->dns64_prefix); | |
307 | return 0; | |
308 | } | |
309 | if (dns64_env->prefix_net < 0 || dns64_env->prefix_net > 96) { | |
310 | log_err("dns64-prefix length it not between 0 and 96: %s", | |
311 | cfg->dns64_prefix); | |
312 | return 0; | |
313 | } | |
314 | return 1; | |
315 | } | |
316 | ||
317 | /** | |
318 | * Initializes this instance of the dns64 module. | |
319 | * | |
320 | * \param env Global state of all module instances. | |
321 | * \param id This instance's ID number. | |
322 | */ | |
323 | int | |
324 | dns64_init(struct module_env* env, int id) | |
325 | { | |
326 | struct dns64_env* dns64_env = | |
327 | (struct dns64_env*)calloc(1, sizeof(struct dns64_env)); | |
328 | if (!dns64_env) { | |
329 | log_err("malloc failure"); | |
330 | return 0; | |
331 | } | |
332 | env->modinfo[id] = (void*)dns64_env; | |
333 | if (!dns64_apply_cfg(dns64_env, env->cfg)) { | |
334 | log_err("dns64: could not apply configuration settings."); | |
335 | return 0; | |
336 | } | |
337 | return 1; | |
338 | } | |
339 | ||
340 | /** | |
341 | * Deinitializes this instance of the dns64 module. | |
342 | * | |
343 | * \param env Global state of all module instances. | |
344 | * \param id This instance's ID number. | |
345 | */ | |
346 | void | |
347 | dns64_deinit(struct module_env* env, int id) | |
348 | { | |
349 | if (!env) | |
350 | return; | |
351 | free(env->modinfo[id]); | |
352 | env->modinfo[id] = NULL; | |
353 | } | |
354 | ||
355 | /** | |
356 | * Handle PTR queries for IPv6 addresses. If the address belongs to the DNS64 | |
357 | * prefix, we must do a PTR query for the corresponding IPv4 address instead. | |
358 | * | |
359 | * \param qstate Query state structure. | |
360 | * \param id This module instance's ID number. | |
361 | * | |
362 | * \return The new state of the query. | |
363 | */ | |
364 | static enum module_ext_state | |
365 | handle_ipv6_ptr(struct module_qstate* qstate, int id) | |
366 | { | |
367 | struct dns64_env* dns64_env = (struct dns64_env*)qstate->env->modinfo[id]; | |
368 | struct module_qstate* subq = NULL; | |
369 | struct query_info qinfo; | |
370 | struct sockaddr_in6 sin6; | |
371 | ||
372 | /* Convert the PTR query string to an IPv6 address. */ | |
373 | memset(&sin6, 0, sizeof(sin6)); | |
374 | sin6.sin6_family = AF_INET6; | |
375 | if (!ptr_to_ipv6((char*)qstate->qinfo.qname, sin6.sin6_addr.s6_addr)) | |
376 | return module_wait_module; /* Let other module handle this. */ | |
377 | ||
378 | /* | |
379 | * If this IPv6 address is not part of our DNS64 prefix, then we don't need | |
380 | * to do anything. Let another module handle the query. | |
381 | */ | |
382 | if (addr_in_common((struct sockaddr_storage*)&sin6, 128, | |
383 | &dns64_env->prefix_addr, dns64_env->prefix_net, | |
384 | (socklen_t)sizeof(sin6)) != dns64_env->prefix_net) | |
385 | return module_wait_module; | |
386 | ||
387 | verbose(VERB_ALGO, "dns64: rewrite PTR record"); | |
388 | ||
389 | /* | |
390 | * Create a new PTR query info for the domain name corresponding to the IPv4 | |
391 | * address corresponding to the IPv6 address corresponding to the original | |
392 | * PTR query domain name. | |
393 | */ | |
394 | qinfo = qstate->qinfo; | |
395 | if (!(qinfo.qname = regional_alloc(qstate->region, MAX_PTR_QNAME_IPV4))) | |
396 | return module_error; | |
397 | qinfo.qname_len = ipv4_to_ptr(extract_ipv4(sin6.sin6_addr.s6_addr, | |
398 | dns64_env->prefix_net), (char*)qinfo.qname); | |
399 | ||
400 | /* Create the new sub-query. */ | |
401 | fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); | |
402 | if(!(*qstate->env->attach_sub)(qstate, &qinfo, qstate->query_flags, 0, 0, | |
403 | &subq)) | |
404 | return module_error; | |
405 | if (subq) { | |
406 | subq->curmod = id; | |
407 | subq->ext_state[id] = module_state_initial; | |
408 | subq->minfo[id] = NULL; | |
409 | } | |
410 | ||
411 | return module_wait_subquery; | |
412 | } | |
413 | ||
414 | /** allocate (special) rrset keys, return 0 on error */ | |
415 | static int | |
416 | repinfo_alloc_rrset_keys(struct reply_info* rep, | |
417 | struct regional* region) | |
418 | { | |
419 | size_t i; | |
420 | for(i=0; i<rep->rrset_count; i++) { | |
421 | if(region) { | |
422 | rep->rrsets[i] = (struct ub_packed_rrset_key*) | |
423 | regional_alloc(region, | |
424 | sizeof(struct ub_packed_rrset_key)); | |
425 | if(rep->rrsets[i]) { | |
426 | memset(rep->rrsets[i], 0, | |
427 | sizeof(struct ub_packed_rrset_key)); | |
428 | rep->rrsets[i]->entry.key = rep->rrsets[i]; | |
429 | } | |
430 | } | |
431 | else return 0;/* rep->rrsets[i] = alloc_special_obtain(alloc);*/ | |
432 | if(!rep->rrsets[i]) | |
433 | return 0; | |
434 | rep->rrsets[i]->entry.data = NULL; | |
435 | } | |
436 | return 1; | |
437 | } | |
438 | ||
439 | static enum module_ext_state | |
440 | generate_type_A_query(struct module_qstate* qstate, int id) | |
441 | { | |
442 | struct module_qstate* subq = NULL; | |
443 | struct query_info qinfo; | |
444 | ||
445 | verbose(VERB_ALGO, "dns64: query A record"); | |
446 | ||
447 | /* Create a new query info. */ | |
448 | qinfo = qstate->qinfo; | |
449 | qinfo.qtype = LDNS_RR_TYPE_A; | |
450 | ||
451 | /* Start the sub-query. */ | |
452 | fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); | |
453 | if(!(*qstate->env->attach_sub)(qstate, &qinfo, qstate->query_flags, 0, | |
454 | 0, &subq)) | |
455 | { | |
456 | verbose(VERB_ALGO, "dns64: sub-query creation failed"); | |
457 | return module_error; | |
458 | } | |
459 | if (subq) { | |
460 | subq->curmod = id; | |
461 | subq->ext_state[id] = module_state_initial; | |
462 | subq->minfo[id] = NULL; | |
463 | } | |
464 | ||
465 | return module_wait_subquery; | |
466 | } | |
467 | ||
468 | /** | |
469 | * Handles the "pass" event for a query. This event is received when a new query | |
470 | * is received by this module. The query may have been generated internally by | |
471 | * another module, in which case we don't want to do any special processing | |
472 | * (this is an interesting discussion topic), or it may be brand new, e.g. | |
473 | * received over a socket, in which case we do want to apply DNS64 processing. | |
474 | * | |
475 | * \param qstate A structure representing the state of the query that has just | |
476 | * received the "pass" event. | |
477 | * \param id This module's instance ID. | |
478 | * | |
479 | * \return The new state of the query. | |
480 | */ | |
481 | static enum module_ext_state | |
482 | handle_event_pass(struct module_qstate* qstate, int id) | |
483 | { | |
484 | if ((uintptr_t)qstate->minfo[id] == DNS64_NEW_QUERY | |
485 | && qstate->qinfo.qtype == LDNS_RR_TYPE_PTR | |
486 | && qstate->qinfo.qname_len == 74 | |
487 | && !strcmp((char*)&qstate->qinfo.qname[64], "\03ip6\04arpa")) | |
488 | /* Handle PTR queries for IPv6 addresses. */ | |
489 | return handle_ipv6_ptr(qstate, id); | |
490 | ||
491 | if (qstate->env->cfg->dns64_synthall && | |
492 | (uintptr_t)qstate->minfo[id] == DNS64_NEW_QUERY | |
493 | && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) | |
494 | return generate_type_A_query(qstate, id); | |
495 | ||
496 | /* We are finished when our sub-query is finished. */ | |
497 | if ((uintptr_t)qstate->minfo[id] == DNS64_SUBQUERY_FINISHED) | |
498 | return module_finished; | |
499 | ||
500 | /* Otherwise, pass request to next module. */ | |
501 | verbose(VERB_ALGO, "dns64: pass to next module"); | |
502 | return module_wait_module; | |
503 | } | |
504 | ||
505 | /** | |
506 | * Handles the "done" event for a query. We need to analyze the response and | |
507 | * maybe issue a new sub-query for the A record. | |
508 | * | |
509 | * \param qstate A structure representing the state of the query that has just | |
510 | * received the "pass" event. | |
511 | * \param id This module's instance ID. | |
512 | * | |
513 | * \return The new state of the query. | |
514 | */ | |
515 | static enum module_ext_state | |
516 | handle_event_moddone(struct module_qstate* qstate, int id) | |
517 | { | |
518 | /* | |
519 | * In many cases we have nothing special to do. From most to least common: | |
520 | * | |
521 | * - An internal query. | |
522 | * - A query for a record type other than AAAA. | |
523 | * - CD FLAG was set on querier | |
524 | * - An AAAA query for which an error was returned. | |
525 | * - A successful AAAA query with an answer. | |
526 | */ | |
527 | if ( (enum dns64_qstate)qstate->minfo[id] == DNS64_INTERNAL_QUERY | |
528 | || qstate->qinfo.qtype != LDNS_RR_TYPE_AAAA | |
529 | || (qstate->query_flags & BIT_CD) | |
530 | || qstate->return_rcode != LDNS_RCODE_NOERROR | |
531 | || (qstate->return_msg && | |
532 | qstate->return_msg->rep && | |
533 | reply_find_answer_rrset(&qstate->qinfo, | |
534 | qstate->return_msg->rep))) | |
535 | return module_finished; | |
536 | ||
537 | /* So, this is a AAAA noerror/nodata answer */ | |
538 | return generate_type_A_query(qstate, id); | |
539 | } | |
540 | ||
541 | /** | |
542 | * This is the module's main() function. It gets called each time a query | |
543 | * receives an event which we may need to handle. We respond by updating the | |
544 | * state of the query. | |
545 | * | |
546 | * \param qstate Structure containing the state of the query. | |
547 | * \param event Event that has just been received. | |
548 | * \param id This module's instance ID. | |
549 | * \param outbound State of a DNS query on an authoritative server. We never do | |
550 | * our own queries ourselves (other modules do it for us), so | |
551 | * this is unused. | |
552 | */ | |
553 | void | |
554 | dns64_operate(struct module_qstate* qstate, enum module_ev event, int id, | |
555 | struct outbound_entry* outbound) | |
556 | { | |
557 | (void)outbound; | |
558 | verbose(VERB_QUERY, "dns64[module %d] operate: extstate:%s event:%s", | |
559 | id, strextstate(qstate->ext_state[id]), | |
560 | strmodulevent(event)); | |
561 | log_query_info(VERB_QUERY, "dns64 operate: query", &qstate->qinfo); | |
562 | ||
563 | switch(event) { | |
564 | case module_event_new: | |
565 | /* Tag this query as being new and fall through. */ | |
566 | qstate->minfo[id] = (void*)DNS64_NEW_QUERY; | |
567 | case module_event_pass: | |
568 | qstate->ext_state[id] = handle_event_pass(qstate, id); | |
569 | break; | |
570 | case module_event_moddone: | |
571 | qstate->ext_state[id] = handle_event_moddone(qstate, id); | |
572 | break; | |
573 | default: | |
574 | qstate->ext_state[id] = module_finished; | |
575 | break; | |
576 | } | |
577 | } | |
578 | ||
579 | static void | |
580 | dns64_synth_aaaa_data(const struct ub_packed_rrset_key* fk, | |
581 | const struct packed_rrset_data* fd, | |
582 | struct ub_packed_rrset_key *dk, | |
583 | struct packed_rrset_data **dd_out, struct regional *region, | |
584 | struct dns64_env* dns64_env ) | |
585 | { | |
586 | struct packed_rrset_data *dd; | |
587 | size_t i; | |
588 | /* | |
589 | * Create synthesized AAAA RR set data. We need to allocated extra memory | |
590 | * for the RRs themselves. Each RR has a length, TTL, pointer to wireformat | |
591 | * data, 2 bytes of data length, and 16 bytes of IPv6 address. | |
592 | */ | |
593 | if (!(dd = *dd_out = regional_alloc(region, | |
594 | sizeof(struct packed_rrset_data) | |
595 | + fd->count * (sizeof(size_t) + sizeof(time_t) + | |
596 | sizeof(uint8_t*) + 2 + 16)))) { | |
597 | log_err("out of memory"); | |
598 | return; | |
599 | } | |
600 | ||
601 | /* Copy attributes from A RR set. */ | |
602 | dd->ttl = fd->ttl; | |
603 | dd->count = fd->count; | |
604 | dd->rrsig_count = 0; | |
605 | dd->trust = fd->trust; | |
606 | dd->security = fd->security; | |
607 | ||
608 | /* | |
609 | * Synthesize AAAA records. Adjust pointers in structure. | |
610 | */ | |
611 | dd->rr_len = | |
612 | (size_t*)((uint8_t*)dd + sizeof(struct packed_rrset_data)); | |
613 | dd->rr_data = (uint8_t**)&dd->rr_len[dd->count]; | |
614 | dd->rr_ttl = (time_t*)&dd->rr_data[dd->count]; | |
615 | for(i = 0; i < fd->count; ++i) { | |
616 | if (fd->rr_len[i] != 6 || fd->rr_data[i][0] != 0 | |
617 | || fd->rr_data[i][1] != 4) | |
618 | return; | |
619 | dd->rr_len[i] = 18; | |
620 | dd->rr_data[i] = | |
621 | (uint8_t*)&dd->rr_ttl[dd->count] + 18*i; | |
622 | dd->rr_data[i][0] = 0; | |
623 | dd->rr_data[i][1] = 16; | |
624 | synthesize_aaaa( | |
625 | ((struct sockaddr_in6*)&dns64_env->prefix_addr)->sin6_addr.s6_addr, | |
626 | dns64_env->prefix_net, &fd->rr_data[i][2], | |
627 | &dd->rr_data[i][2] ); | |
628 | dd->rr_ttl[i] = fd->rr_ttl[i]; | |
629 | } | |
630 | ||
631 | /* | |
632 | * Create synthesized AAAA RR set key. This is mostly just bookkeeping, | |
633 | * nothing interesting here. | |
634 | */ | |
635 | if(!dk) { | |
636 | log_err("no key"); | |
637 | return; | |
638 | } | |
639 | ||
640 | dk->rk.dname = (uint8_t*)regional_alloc_init(region, | |
641 | fk->rk.dname, fk->rk.dname_len); | |
642 | ||
643 | if(!dk->rk.dname) { | |
644 | log_err("out of memory"); | |
645 | return; | |
646 | } | |
647 | ||
648 | dk->rk.type = htons(LDNS_RR_TYPE_AAAA); | |
649 | memset(&dk->entry, 0, sizeof(dk->entry)); | |
650 | dk->entry.key = dk; | |
651 | dk->entry.hash = rrset_key_hash(&dk->rk); | |
652 | dk->entry.data = dd; | |
653 | ||
654 | } | |
655 | ||
656 | /** | |
657 | * Synthesize an AAAA RR set from an A sub-query's answer and add it to the | |
658 | * original empty response. | |
659 | * | |
660 | * \param id This module's instance ID. | |
661 | * \param super Original AAAA query. | |
662 | * \param qstate A query. | |
663 | */ | |
664 | static void | |
665 | dns64_adjust_a(int id, struct module_qstate* super, struct module_qstate* qstate) | |
666 | { | |
667 | struct dns64_env* dns64_env = (struct dns64_env*)super->env->modinfo[id]; | |
668 | struct reply_info *rep, *cp; | |
669 | size_t i, s; | |
670 | struct packed_rrset_data* fd, *dd; | |
671 | struct ub_packed_rrset_key* fk, *dk; | |
672 | ||
673 | verbose(VERB_ALGO, "converting A answers to AAAA answers"); | |
674 | ||
675 | log_assert(super->region); | |
676 | log_assert(qstate->return_msg); | |
677 | log_assert(qstate->return_msg->rep); | |
678 | ||
679 | /* If dns64-synthall is enabled, return_msg is not initialized */ | |
680 | if(!super->return_msg) { | |
681 | super->return_msg = (struct dns_msg*)regional_alloc( | |
682 | super->region, sizeof(struct dns_msg)); | |
683 | if(!super->return_msg) | |
684 | return; | |
685 | memset(super->return_msg, 0, sizeof(*super->return_msg)); | |
686 | super->return_msg->qinfo = super->qinfo; | |
687 | } | |
688 | ||
689 | rep = qstate->return_msg->rep; | |
690 | ||
691 | /* | |
692 | * Build the actual reply. | |
693 | */ | |
694 | cp = construct_reply_info_base(super->region, rep->flags, rep->qdcount, | |
695 | rep->ttl, rep->prefetch_ttl, rep->an_numrrsets, rep->ns_numrrsets, | |
696 | rep->ar_numrrsets, rep->rrset_count, rep->security); | |
697 | if(!cp) | |
698 | return; | |
699 | ||
700 | /* allocate ub_key structures special or not */ | |
701 | if(!repinfo_alloc_rrset_keys(cp, super->region)) { | |
702 | return; | |
703 | } | |
704 | ||
705 | /* copy everything and replace A by AAAA */ | |
706 | for(i=0; i<cp->rrset_count; i++) { | |
707 | fk = rep->rrsets[i]; | |
708 | dk = cp->rrsets[i]; | |
709 | fd = (struct packed_rrset_data*)fk->entry.data; | |
710 | dk->rk = fk->rk; | |
711 | dk->id = fk->id; | |
712 | ||
713 | if(i<rep->an_numrrsets && fk->rk.type == htons(LDNS_RR_TYPE_A)) { | |
714 | /* also sets dk->entry.hash */ | |
715 | dns64_synth_aaaa_data(fk, fd, dk, &dd, super->region, dns64_env); | |
716 | /* Delete negative AAAA record from cache stored by | |
717 | * the iterator module */ | |
718 | rrset_cache_remove(super->env->rrset_cache, dk->rk.dname, | |
719 | dk->rk.dname_len, LDNS_RR_TYPE_AAAA, | |
720 | LDNS_RR_CLASS_IN, 0); | |
721 | } else { | |
722 | dk->entry.hash = fk->entry.hash; | |
723 | dk->rk.dname = (uint8_t*)regional_alloc_init(super->region, | |
724 | fk->rk.dname, fk->rk.dname_len); | |
725 | ||
726 | if(!dk->rk.dname) | |
727 | return; | |
728 | ||
729 | s = packed_rrset_sizeof(fd); | |
730 | dd = (struct packed_rrset_data*)regional_alloc_init( | |
731 | super->region, fd, s); | |
732 | ||
733 | if(!dd) | |
734 | return; | |
735 | } | |
736 | ||
737 | packed_rrset_ptr_fixup(dd); | |
738 | dk->entry.data = (void*)dd; | |
739 | } | |
740 | ||
741 | /* Commit changes. */ | |
742 | super->return_msg->rep = cp; | |
743 | } | |
744 | ||
745 | /** | |
746 | * Generate a response for the original IPv6 PTR query based on an IPv4 PTR | |
747 | * sub-query's response. | |
748 | * | |
749 | * \param qstate IPv4 PTR sub-query. | |
750 | * \param super Original IPv6 PTR query. | |
751 | */ | |
752 | static void | |
753 | dns64_adjust_ptr(struct module_qstate* qstate, struct module_qstate* super) | |
754 | { | |
755 | struct ub_packed_rrset_key* answer; | |
756 | ||
757 | verbose(VERB_ALGO, "adjusting PTR reply"); | |
758 | ||
759 | /* Copy the sub-query's reply to the parent. */ | |
760 | if (!(super->return_msg = (struct dns_msg*)regional_alloc(super->region, | |
761 | sizeof(struct dns_msg)))) | |
762 | return; | |
763 | super->return_msg->qinfo = super->qinfo; | |
764 | super->return_msg->rep = reply_info_copy(qstate->return_msg->rep, NULL, | |
765 | super->region); | |
766 | ||
767 | /* | |
768 | * Adjust the domain name of the answer RR set so that it matches the | |
769 | * initial query's domain name. | |
770 | */ | |
771 | answer = reply_find_answer_rrset(&qstate->qinfo, super->return_msg->rep); | |
772 | log_assert(answer); | |
773 | answer->rk.dname = super->qinfo.qname; | |
774 | answer->rk.dname_len = super->qinfo.qname_len; | |
775 | } | |
776 | ||
777 | /** | |
778 | * This function is called when a sub-query finishes to inform the parent query. | |
779 | * | |
780 | * We issue two kinds of sub-queries: PTR and A. | |
781 | * | |
782 | * \param qstate State of the sub-query. | |
783 | * \param id This module's instance ID. | |
784 | * \param super State of the super-query. | |
785 | */ | |
786 | void | |
787 | dns64_inform_super(struct module_qstate* qstate, int id, | |
788 | struct module_qstate* super) | |
789 | { | |
790 | log_query_info(VERB_ALGO, "dns64: inform_super, sub is", | |
791 | &qstate->qinfo); | |
792 | log_query_info(VERB_ALGO, "super is", &super->qinfo); | |
793 | ||
794 | /* | |
795 | * Signal that the sub-query is finished, no matter whether we are | |
796 | * successful or not. This lets the state machine terminate. | |
797 | */ | |
798 | super->minfo[id] = (void*)DNS64_SUBQUERY_FINISHED; | |
799 | ||
800 | /* If there is no successful answer, we're done. */ | |
801 | if (qstate->return_rcode != LDNS_RCODE_NOERROR | |
802 | || !qstate->return_msg | |
803 | || !qstate->return_msg->rep | |
804 | || !reply_find_answer_rrset(&qstate->qinfo, | |
805 | qstate->return_msg->rep)) | |
806 | return; | |
807 | ||
808 | /* Generate a response suitable for the original query. */ | |
809 | if (qstate->qinfo.qtype == LDNS_RR_TYPE_A) { | |
810 | dns64_adjust_a(id, super, qstate); | |
811 | } else { | |
812 | log_assert(qstate->qinfo.qtype == LDNS_RR_TYPE_PTR); | |
813 | dns64_adjust_ptr(qstate, super); | |
814 | } | |
815 | ||
816 | /* Store the generated response in cache. */ | |
817 | if (!dns_cache_store(super->env, &super->qinfo, super->return_msg->rep, | |
818 | 0, 0, 0, NULL, super->query_flags)) | |
819 | log_err("out of memory"); | |
820 | } | |
821 | ||
822 | /** | |
823 | * Clear module-specific data from query state. Since we do not allocate memory, | |
824 | * it's just a matter of setting a pointer to NULL. | |
825 | * | |
826 | * \param qstate Query state. | |
827 | * \param id This module's instance ID. | |
828 | */ | |
829 | void | |
830 | dns64_clear(struct module_qstate* qstate, int id) | |
831 | { | |
832 | qstate->minfo[id] = NULL; | |
833 | } | |
834 | ||
835 | /** | |
836 | * Returns the amount of global memory that this module uses, not including | |
837 | * per-query data. | |
838 | * | |
839 | * \param env Module environment. | |
840 | * \param id This module's instance ID. | |
841 | */ | |
842 | size_t | |
843 | dns64_get_mem(struct module_env* env, int id) | |
844 | { | |
845 | struct dns64_env* dns64_env = (struct dns64_env*)env->modinfo[id]; | |
846 | if (!dns64_env) | |
847 | return 0; | |
848 | return sizeof(*dns64_env); | |
849 | } | |
850 | ||
851 | /** | |
852 | * The dns64 function block. | |
853 | */ | |
854 | static struct module_func_block dns64_block = { | |
855 | "dns64", | |
856 | &dns64_init, &dns64_deinit, &dns64_operate, &dns64_inform_super, | |
857 | &dns64_clear, &dns64_get_mem | |
858 | }; | |
859 | ||
860 | /** | |
861 | * Function for returning the above function block. | |
862 | */ | |
863 | struct module_func_block * | |
864 | dns64_get_funcblock() | |
865 | { | |
866 | return &dns64_block; | |
867 | } |