/src/unbound/dns64/dns64.c
Line | Count | Source (jump to first uncovered line) |
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 | | #include "util/storage/dnstree.h" |
52 | | #include "util/data/dname.h" |
53 | | #include "sldns/str2wire.h" |
54 | | |
55 | | /****************************************************************************** |
56 | | * * |
57 | | * STATIC CONSTANTS * |
58 | | * * |
59 | | ******************************************************************************/ |
60 | | |
61 | | /** |
62 | | * This is the default DNS64 prefix that is used when the dns64 module is listed |
63 | | * in module-config but when the dns64-prefix variable is not present. |
64 | | */ |
65 | | static const char DEFAULT_DNS64_PREFIX[] = "64:ff9b::/96"; |
66 | | |
67 | | /** |
68 | | * Maximum length of a domain name in a PTR query in the .in-addr.arpa tree. |
69 | | */ |
70 | 0 | #define MAX_PTR_QNAME_IPV4 30 |
71 | | |
72 | | /** |
73 | | * State of DNS64 processing for a query. |
74 | | */ |
75 | | enum dns64_state { |
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 | | * Per-query module-specific state. For the DNS64 module. |
86 | | */ |
87 | | struct dns64_qstate { |
88 | | /** State of the DNS64 module. */ |
89 | | enum dns64_state state; |
90 | | /** If the dns64 module started with no_cache bool set in the qstate, |
91 | | * a message to tell it to not modify the cache contents, then this |
92 | | * is true. The dns64 module is then free to modify that flag for |
93 | | * its own purposes. |
94 | | * Otherwise, it is false, the dns64 module was not told to no_cache */ |
95 | | int started_no_cache_store; |
96 | | }; |
97 | | |
98 | | /****************************************************************************** |
99 | | * * |
100 | | * STRUCTURES * |
101 | | * * |
102 | | ******************************************************************************/ |
103 | | |
104 | | /** |
105 | | * This structure contains module configuration information. One instance of |
106 | | * this structure exists per instance of the module. Normally there is only one |
107 | | * instance of the module. |
108 | | */ |
109 | | struct dns64_env { |
110 | | /** |
111 | | * DNS64 prefix address. We're using a full sockaddr instead of just an |
112 | | * in6_addr because we can reuse Unbound's generic string parsing functions. |
113 | | * It will always contain a sockaddr_in6, and only the sin6_addr member will |
114 | | * ever be used. |
115 | | */ |
116 | | struct sockaddr_storage prefix_addr; |
117 | | |
118 | | /** |
119 | | * This is always sizeof(sockaddr_in6). |
120 | | */ |
121 | | socklen_t prefix_addrlen; |
122 | | |
123 | | /** |
124 | | * This is the CIDR length of the prefix. It needs to be between 0 and 96. |
125 | | */ |
126 | | int prefix_net; |
127 | | |
128 | | /** |
129 | | * Tree of names for which AAAA is ignored. always synthesize from A. |
130 | | */ |
131 | | rbtree_type ignore_aaaa; |
132 | | }; |
133 | | |
134 | | |
135 | | /****************************************************************************** |
136 | | * * |
137 | | * UTILITY FUNCTIONS * |
138 | | * * |
139 | | ******************************************************************************/ |
140 | | |
141 | | /** |
142 | | * Generic macro for swapping two variables. |
143 | | * |
144 | | * \param t Type of the variables. (e.g. int) |
145 | | * \param a First variable. |
146 | | * \param b Second variable. |
147 | | * |
148 | | * \warning Do not attempt something foolish such as swap(int,a++,b++)! |
149 | | */ |
150 | 0 | #define swap(t,a,b) do {t x = a; a = b; b = x;} while(0) |
151 | | |
152 | | /** |
153 | | * Reverses a string. |
154 | | * |
155 | | * \param begin Points to the first character of the string. |
156 | | * \param end Points one past the last character of the string. |
157 | | */ |
158 | | static void |
159 | | reverse(char* begin, char* end) |
160 | 0 | { |
161 | 0 | while ( begin < --end ) { |
162 | 0 | swap(char, *begin, *end); |
163 | 0 | ++begin; |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | | /** |
168 | | * Convert an unsigned integer to a string. The point of this function is that |
169 | | * of being faster than sprintf(). |
170 | | * |
171 | | * \param n The number to be converted. |
172 | | * \param s The result will be written here. Must be large enough, be careful! |
173 | | * |
174 | | * \return The number of characters written. |
175 | | */ |
176 | | static int |
177 | | uitoa(unsigned n, char* s) |
178 | 0 | { |
179 | 0 | char* ss = s; |
180 | 0 | do { |
181 | 0 | *ss++ = '0' + n % 10; |
182 | 0 | } while (n /= 10); |
183 | 0 | reverse(s, ss); |
184 | 0 | return ss - s; |
185 | 0 | } |
186 | | |
187 | | /** |
188 | | * Extract an IPv4 address embedded in the IPv6 address \a ipv6 at offset \a |
189 | | * offset (in bits). Note that bits are not necessarily aligned on bytes so we |
190 | | * need to be careful. |
191 | | * |
192 | | * \param ipv6 IPv6 address represented as a 128-bit array in big-endian |
193 | | * order. |
194 | | * \param ipv6_len length of the ipv6 byte array. |
195 | | * \param offset Index of the MSB of the IPv4 address embedded in the IPv6 |
196 | | * address. |
197 | | */ |
198 | | static uint32_t |
199 | | extract_ipv4(const uint8_t ipv6[], size_t ipv6_len, const int offset) |
200 | 0 | { |
201 | 0 | uint32_t ipv4 = 0; |
202 | 0 | int i, pos; |
203 | 0 | log_assert(ipv6_len == 16); (void)ipv6_len; |
204 | 0 | log_assert(offset == 32 || offset == 40 || offset == 48 || offset == 56 || |
205 | 0 | offset == 64 || offset == 96); |
206 | 0 | for(i = 0, pos = offset / 8; i < 4; i++, pos++) { |
207 | 0 | if (pos == 8) |
208 | 0 | pos++; |
209 | 0 | ipv4 = ipv4 << 8; |
210 | 0 | ipv4 |= ipv6[pos]; |
211 | 0 | } |
212 | 0 | return ipv4; |
213 | 0 | } |
214 | | |
215 | | /** |
216 | | * Builds the PTR query name corresponding to an IPv4 address. For example, |
217 | | * given the number 3,464,175,361, this will build the string |
218 | | * "\03206\03123\0231\011\07in-addr\04arpa". |
219 | | * |
220 | | * \param ipv4 IPv4 address represented as an unsigned 32-bit number. |
221 | | * \param ptr The result will be written here. Must be large enough, be |
222 | | * careful! |
223 | | * \param nm_len length of the ptr buffer. |
224 | | * |
225 | | * \return The number of characters written. |
226 | | */ |
227 | | static size_t |
228 | | ipv4_to_ptr(uint32_t ipv4, char ptr[], size_t nm_len) |
229 | 0 | { |
230 | 0 | static const char IPV4_PTR_SUFFIX[] = "\07in-addr\04arpa"; |
231 | 0 | int i; |
232 | 0 | char* c = ptr; |
233 | 0 | log_assert(nm_len == MAX_PTR_QNAME_IPV4); (void)nm_len; |
234 | |
|
235 | 0 | for (i = 0; i < 4; ++i) { |
236 | 0 | *c = uitoa((unsigned int)(ipv4 % 256), c + 1); |
237 | 0 | c += *c + 1; |
238 | 0 | log_assert(c < ptr+nm_len); |
239 | 0 | ipv4 /= 256; |
240 | 0 | } |
241 | |
|
242 | 0 | log_assert(c + sizeof(IPV4_PTR_SUFFIX) <= ptr+nm_len); |
243 | 0 | memmove(c, IPV4_PTR_SUFFIX, sizeof(IPV4_PTR_SUFFIX)); |
244 | |
|
245 | 0 | return c + sizeof(IPV4_PTR_SUFFIX) - ptr; |
246 | 0 | } |
247 | | |
248 | | /** |
249 | | * Converts an IPv6-related domain name string from a PTR query into an IPv6 |
250 | | * address represented as a 128-bit array. |
251 | | * |
252 | | * \param ptr The domain name. (e.g. "\011[...]\010\012\016\012\03ip6\04arpa") |
253 | | * \param ipv6 The result will be written here, in network byte order. |
254 | | * \param ipv6_len length of the ipv6 byte array. |
255 | | * |
256 | | * \return 1 on success, 0 on failure. |
257 | | */ |
258 | | static int |
259 | | ptr_to_ipv6(const char* ptr, uint8_t ipv6[], size_t ipv6_len) |
260 | 0 | { |
261 | 0 | int i; |
262 | 0 | log_assert(ipv6_len == 16); (void)ipv6_len; |
263 | |
|
264 | 0 | for (i = 0; i < 64; i++) { |
265 | 0 | int x; |
266 | |
|
267 | 0 | if (ptr[i++] != 1) |
268 | 0 | return 0; |
269 | | |
270 | 0 | if (ptr[i] >= '0' && ptr[i] <= '9') { |
271 | 0 | x = ptr[i] - '0'; |
272 | 0 | } else if (ptr[i] >= 'a' && ptr[i] <= 'f') { |
273 | 0 | x = ptr[i] - 'a' + 10; |
274 | 0 | } else if (ptr[i] >= 'A' && ptr[i] <= 'F') { |
275 | 0 | x = ptr[i] - 'A' + 10; |
276 | 0 | } else { |
277 | 0 | return 0; |
278 | 0 | } |
279 | | |
280 | 0 | ipv6[15-i/4] |= x << (2 * ((i-1) % 4)); |
281 | 0 | } |
282 | | |
283 | 0 | return 1; |
284 | 0 | } |
285 | | |
286 | | /** |
287 | | * Synthesize an IPv6 address based on an IPv4 address and the DNS64 prefix. |
288 | | * |
289 | | * \param prefix_addr DNS64 prefix address. |
290 | | * \param prefix_addr_len length of the prefix_addr buffer. |
291 | | * \param prefix_net CIDR length of the DNS64 prefix. Must be between 0 and 96. |
292 | | * \param a IPv4 address. |
293 | | * \param a_len length of the a buffer. |
294 | | * \param aaaa IPv6 address. The result will be written here. |
295 | | * \param aaaa_len length of the aaaa buffer. |
296 | | */ |
297 | | static void |
298 | | synthesize_aaaa(const uint8_t prefix_addr[], size_t prefix_addr_len, |
299 | | int prefix_net, const uint8_t a[], size_t a_len, uint8_t aaaa[], |
300 | | size_t aaaa_len) |
301 | 0 | { |
302 | 0 | size_t i; |
303 | 0 | int pos; |
304 | 0 | log_assert(prefix_addr_len == 16 && a_len == 4 && aaaa_len == 16); |
305 | 0 | log_assert(prefix_net == 32 || prefix_net == 40 || prefix_net == 48 || |
306 | 0 | prefix_net == 56 || prefix_net == 64 || prefix_net == 96); |
307 | 0 | (void)prefix_addr_len; (void)a_len; (void)aaaa_len; |
308 | 0 | memcpy(aaaa, prefix_addr, 16); |
309 | 0 | for(i = 0, pos = prefix_net / 8; i < a_len; i++, pos++) { |
310 | 0 | if(pos == 8) |
311 | 0 | aaaa[pos++] = 0; |
312 | 0 | aaaa[pos] = a[i]; |
313 | 0 | } |
314 | 0 | } |
315 | | |
316 | | |
317 | | /****************************************************************************** |
318 | | * * |
319 | | * DNS64 MODULE FUNCTIONS * |
320 | | * * |
321 | | ******************************************************************************/ |
322 | | |
323 | | /** |
324 | | * insert ignore_aaaa element into the tree |
325 | | * @param dns64_env: module env. |
326 | | * @param str: string with domain name. |
327 | | * @return false on failure. |
328 | | */ |
329 | | static int |
330 | | dns64_insert_ignore_aaaa(struct dns64_env* dns64_env, char* str) |
331 | 0 | { |
332 | | /* parse and insert element */ |
333 | 0 | struct name_tree_node* node; |
334 | 0 | node = (struct name_tree_node*)calloc(1, sizeof(*node)); |
335 | 0 | if(!node) { |
336 | 0 | log_err("out of memory"); |
337 | 0 | return 0; |
338 | 0 | } |
339 | 0 | node->name = sldns_str2wire_dname(str, &node->len); |
340 | 0 | if(!node->name) { |
341 | 0 | free(node); |
342 | 0 | log_err("cannot parse dns64-ignore-aaaa: %s", str); |
343 | 0 | return 0; |
344 | 0 | } |
345 | 0 | node->labs = dname_count_labels(node->name); |
346 | 0 | node->dclass = LDNS_RR_CLASS_IN; |
347 | 0 | if(!name_tree_insert(&dns64_env->ignore_aaaa, node, |
348 | 0 | node->name, node->len, node->labs, node->dclass)) { |
349 | | /* ignore duplicate element */ |
350 | 0 | free(node->name); |
351 | 0 | free(node); |
352 | 0 | return 1; |
353 | 0 | } |
354 | 0 | return 1; |
355 | 0 | } |
356 | | |
357 | | /** |
358 | | * This function applies the configuration found in the parsed configuration |
359 | | * file \a cfg to this instance of the dns64 module. Currently only the DNS64 |
360 | | * prefix (a.k.a. Pref64) is configurable. |
361 | | * |
362 | | * \param dns64_env Module-specific global parameters. |
363 | | * \param cfg Parsed configuration file. |
364 | | */ |
365 | | static int |
366 | | dns64_apply_cfg(struct dns64_env* dns64_env, struct config_file* cfg) |
367 | 0 | { |
368 | 0 | struct config_strlist* s; |
369 | 0 | verbose(VERB_ALGO, "dns64-prefix: %s", cfg->dns64_prefix); |
370 | 0 | if (!netblockstrtoaddr(cfg->dns64_prefix ? cfg->dns64_prefix : |
371 | 0 | DEFAULT_DNS64_PREFIX, 0, &dns64_env->prefix_addr, |
372 | 0 | &dns64_env->prefix_addrlen, &dns64_env->prefix_net)) { |
373 | 0 | log_err("cannot parse dns64-prefix netblock: %s", cfg->dns64_prefix); |
374 | 0 | return 0; |
375 | 0 | } |
376 | 0 | if (!addr_is_ip6(&dns64_env->prefix_addr, dns64_env->prefix_addrlen)) { |
377 | 0 | log_err("dns64_prefix is not IPv6: %s", cfg->dns64_prefix); |
378 | 0 | return 0; |
379 | 0 | } |
380 | 0 | if (dns64_env->prefix_net != 32 && dns64_env->prefix_net != 40 && |
381 | 0 | dns64_env->prefix_net != 48 && dns64_env->prefix_net != 56 && |
382 | 0 | dns64_env->prefix_net != 64 && dns64_env->prefix_net != 96 ) { |
383 | 0 | log_err("dns64-prefix length it not 32, 40, 48, 56, 64 or 96: %s", |
384 | 0 | cfg->dns64_prefix); |
385 | 0 | return 0; |
386 | 0 | } |
387 | 0 | for(s = cfg->dns64_ignore_aaaa; s; s = s->next) { |
388 | 0 | if(!dns64_insert_ignore_aaaa(dns64_env, s->str)) |
389 | 0 | return 0; |
390 | 0 | } |
391 | 0 | name_tree_init_parents(&dns64_env->ignore_aaaa); |
392 | 0 | return 1; |
393 | 0 | } |
394 | | |
395 | | /** |
396 | | * Initializes this instance of the dns64 module. |
397 | | * |
398 | | * \param env Global state of all module instances. |
399 | | * \param id This instance's ID number. |
400 | | */ |
401 | | int |
402 | | dns64_init(struct module_env* env, int id) |
403 | 0 | { |
404 | 0 | struct dns64_env* dns64_env = |
405 | 0 | (struct dns64_env*)calloc(1, sizeof(struct dns64_env)); |
406 | 0 | if (!dns64_env) { |
407 | 0 | log_err("malloc failure"); |
408 | 0 | return 0; |
409 | 0 | } |
410 | 0 | env->modinfo[id] = (void*)dns64_env; |
411 | 0 | name_tree_init(&dns64_env->ignore_aaaa); |
412 | 0 | if (!dns64_apply_cfg(dns64_env, env->cfg)) { |
413 | 0 | log_err("dns64: could not apply configuration settings."); |
414 | 0 | return 0; |
415 | 0 | } |
416 | 0 | return 1; |
417 | 0 | } |
418 | | |
419 | | /** free ignore AAAA elements */ |
420 | | static void |
421 | | free_ignore_aaaa_node(rbnode_type* node, void* ATTR_UNUSED(arg)) |
422 | 0 | { |
423 | 0 | struct name_tree_node* n = (struct name_tree_node*)node; |
424 | 0 | if(!n) return; |
425 | 0 | free(n->name); |
426 | 0 | free(n); |
427 | 0 | } |
428 | | |
429 | | /** |
430 | | * Deinitializes this instance of the dns64 module. |
431 | | * |
432 | | * \param env Global state of all module instances. |
433 | | * \param id This instance's ID number. |
434 | | */ |
435 | | void |
436 | | dns64_deinit(struct module_env* env, int id) |
437 | 0 | { |
438 | 0 | struct dns64_env* dns64_env; |
439 | 0 | if (!env) |
440 | 0 | return; |
441 | 0 | dns64_env = (struct dns64_env*)env->modinfo[id]; |
442 | 0 | if(dns64_env) { |
443 | 0 | traverse_postorder(&dns64_env->ignore_aaaa, free_ignore_aaaa_node, |
444 | 0 | NULL); |
445 | 0 | } |
446 | 0 | free(env->modinfo[id]); |
447 | 0 | env->modinfo[id] = NULL; |
448 | 0 | } |
449 | | |
450 | | /** |
451 | | * Handle PTR queries for IPv6 addresses. If the address belongs to the DNS64 |
452 | | * prefix, we must do a PTR query for the corresponding IPv4 address instead. |
453 | | * |
454 | | * \param qstate Query state structure. |
455 | | * \param id This module instance's ID number. |
456 | | * |
457 | | * \return The new state of the query. |
458 | | */ |
459 | | static enum module_ext_state |
460 | | handle_ipv6_ptr(struct module_qstate* qstate, int id) |
461 | 0 | { |
462 | 0 | struct dns64_env* dns64_env = (struct dns64_env*)qstate->env->modinfo[id]; |
463 | 0 | struct module_qstate* subq = NULL; |
464 | 0 | struct query_info qinfo; |
465 | 0 | struct sockaddr_in6 sin6; |
466 | | |
467 | | /* Convert the PTR query string to an IPv6 address. */ |
468 | 0 | memset(&sin6, 0, sizeof(sin6)); |
469 | 0 | sin6.sin6_family = AF_INET6; |
470 | 0 | if (!ptr_to_ipv6((char*)qstate->qinfo.qname, sin6.sin6_addr.s6_addr, |
471 | 0 | sizeof(sin6.sin6_addr.s6_addr))) |
472 | 0 | return module_wait_module; /* Let other module handle this. */ |
473 | | |
474 | | /* |
475 | | * If this IPv6 address is not part of our DNS64 prefix, then we don't need |
476 | | * to do anything. Let another module handle the query. |
477 | | */ |
478 | 0 | if (addr_in_common((struct sockaddr_storage*)&sin6, 128, |
479 | 0 | &dns64_env->prefix_addr, dns64_env->prefix_net, |
480 | 0 | (socklen_t)sizeof(sin6)) != dns64_env->prefix_net) |
481 | 0 | return module_wait_module; |
482 | | |
483 | 0 | verbose(VERB_ALGO, "dns64: rewrite PTR record"); |
484 | | |
485 | | /* |
486 | | * Create a new PTR query info for the domain name corresponding to the IPv4 |
487 | | * address corresponding to the IPv6 address corresponding to the original |
488 | | * PTR query domain name. |
489 | | */ |
490 | 0 | qinfo = qstate->qinfo; |
491 | 0 | if (!(qinfo.qname = regional_alloc(qstate->region, MAX_PTR_QNAME_IPV4))) |
492 | 0 | return module_error; |
493 | 0 | qinfo.qname_len = ipv4_to_ptr(extract_ipv4(sin6.sin6_addr.s6_addr, |
494 | 0 | sizeof(sin6.sin6_addr.s6_addr), dns64_env->prefix_net), |
495 | 0 | (char*)qinfo.qname, MAX_PTR_QNAME_IPV4); |
496 | | |
497 | | /* Create the new sub-query. */ |
498 | 0 | fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); |
499 | 0 | if(!(*qstate->env->attach_sub)(qstate, &qinfo, qstate->query_flags, 0, 0, |
500 | 0 | &subq)) |
501 | 0 | return module_error; |
502 | 0 | if (subq) { |
503 | 0 | subq->curmod = id; |
504 | 0 | subq->ext_state[id] = module_state_initial; |
505 | 0 | subq->minfo[id] = NULL; |
506 | 0 | } |
507 | |
|
508 | 0 | return module_wait_subquery; |
509 | 0 | } |
510 | | |
511 | | static enum module_ext_state |
512 | | generate_type_A_query(struct module_qstate* qstate, int id) |
513 | 0 | { |
514 | 0 | struct module_qstate* subq = NULL; |
515 | 0 | struct query_info qinfo; |
516 | |
|
517 | 0 | verbose(VERB_ALGO, "dns64: query A record"); |
518 | | |
519 | | /* Create a new query info. */ |
520 | 0 | qinfo = qstate->qinfo; |
521 | 0 | qinfo.qtype = LDNS_RR_TYPE_A; |
522 | | |
523 | | /* Start the sub-query. */ |
524 | 0 | fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); |
525 | 0 | if(!(*qstate->env->attach_sub)(qstate, &qinfo, qstate->query_flags, 0, |
526 | 0 | 0, &subq)) |
527 | 0 | { |
528 | 0 | verbose(VERB_ALGO, "dns64: sub-query creation failed"); |
529 | 0 | return module_error; |
530 | 0 | } |
531 | 0 | if (subq) { |
532 | 0 | subq->curmod = id; |
533 | 0 | subq->ext_state[id] = module_state_initial; |
534 | 0 | subq->minfo[id] = NULL; |
535 | 0 | } |
536 | |
|
537 | 0 | return module_wait_subquery; |
538 | 0 | } |
539 | | |
540 | | /** |
541 | | * See if query name is in the always synth config. |
542 | | * The ignore-aaaa list has names for which the AAAA for the domain is |
543 | | * ignored and the A is always used to create the answer. |
544 | | * @param qstate: query state. |
545 | | * @param id: module id. |
546 | | * @return true if the name is covered by ignore-aaaa. |
547 | | */ |
548 | | static int |
549 | | dns64_always_synth_for_qname(struct module_qstate* qstate, int id) |
550 | 0 | { |
551 | 0 | struct dns64_env* dns64_env = (struct dns64_env*)qstate->env->modinfo[id]; |
552 | 0 | int labs = dname_count_labels(qstate->qinfo.qname); |
553 | 0 | struct name_tree_node* node = name_tree_lookup(&dns64_env->ignore_aaaa, |
554 | 0 | qstate->qinfo.qname, qstate->qinfo.qname_len, labs, |
555 | 0 | qstate->qinfo.qclass); |
556 | 0 | return (node != NULL); |
557 | 0 | } |
558 | | |
559 | | /** |
560 | | * Handles the "pass" event for a query. This event is received when a new query |
561 | | * is received by this module. The query may have been generated internally by |
562 | | * another module, in which case we don't want to do any special processing |
563 | | * (this is an interesting discussion topic), or it may be brand new, e.g. |
564 | | * received over a socket, in which case we do want to apply DNS64 processing. |
565 | | * |
566 | | * \param qstate A structure representing the state of the query that has just |
567 | | * received the "pass" event. |
568 | | * \param id This module's instance ID. |
569 | | * |
570 | | * \return The new state of the query. |
571 | | */ |
572 | | static enum module_ext_state |
573 | | handle_event_pass(struct module_qstate* qstate, int id) |
574 | 0 | { |
575 | 0 | struct dns64_qstate* iq = (struct dns64_qstate*)qstate->minfo[id]; |
576 | 0 | int synth_all_cfg = qstate->env->cfg->dns64_synthall; |
577 | 0 | int synth_qname = 0; |
578 | |
|
579 | 0 | if(iq && iq->state == DNS64_NEW_QUERY |
580 | 0 | && qstate->qinfo.qtype == LDNS_RR_TYPE_PTR |
581 | 0 | && qstate->qinfo.qname_len == 74 |
582 | 0 | && !strcmp((char*)&qstate->qinfo.qname[64], "\03ip6\04arpa")) { |
583 | | /* Handle PTR queries for IPv6 addresses. */ |
584 | 0 | return handle_ipv6_ptr(qstate, id); |
585 | 0 | } |
586 | | |
587 | 0 | if(iq && iq->state == DNS64_NEW_QUERY && |
588 | 0 | qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA && |
589 | 0 | (synth_all_cfg || |
590 | 0 | (synth_qname=(dns64_always_synth_for_qname(qstate, id) |
591 | 0 | && !(qstate->query_flags & BIT_CD))))) { |
592 | 0 | if(synth_qname) |
593 | 0 | verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway"); |
594 | 0 | return generate_type_A_query(qstate, id); |
595 | 0 | } |
596 | | |
597 | | /* We are finished when our sub-query is finished. */ |
598 | 0 | if(iq && iq->state == DNS64_SUBQUERY_FINISHED) |
599 | 0 | return module_finished; |
600 | | |
601 | | /* Otherwise, pass request to next module. */ |
602 | 0 | verbose(VERB_ALGO, "dns64: pass to next module"); |
603 | 0 | return module_wait_module; |
604 | 0 | } |
605 | | |
606 | | /** |
607 | | * Handles the "done" event for a query. We need to analyze the response and |
608 | | * maybe issue a new sub-query for the A record. |
609 | | * |
610 | | * \param qstate A structure representing the state of the query that has just |
611 | | * received the "pass" event. |
612 | | * \param id This module's instance ID. |
613 | | * |
614 | | * \return The new state of the query. |
615 | | */ |
616 | | static enum module_ext_state |
617 | | handle_event_moddone(struct module_qstate* qstate, int id) |
618 | 0 | { |
619 | 0 | struct dns64_qstate* iq = (struct dns64_qstate*)qstate->minfo[id]; |
620 | | /* |
621 | | * In many cases we have nothing special to do. From most to least common: |
622 | | * |
623 | | * - An internal query. |
624 | | * - A query for a record type other than AAAA. |
625 | | * - CD FLAG was set on querier |
626 | | * - An AAAA query for which an error was returned.(qstate.return_rcode) |
627 | | * -> treated as servfail thus synthesize (sec 5.1.3 6147), thus |
628 | | * synthesize in (sec 5.1.2 of RFC6147). |
629 | | * - A successful AAAA query with an answer. |
630 | | */ |
631 | | |
632 | | /* When an AAAA query completes check if we want to perform DNS64 |
633 | | * synthesis. We skip queries with DNSSEC enabled (!CD) and |
634 | | * ones generated by us to retrieve the A/PTR record to use for |
635 | | * synth. */ |
636 | 0 | int could_synth = |
637 | 0 | qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA && |
638 | 0 | (!iq || iq->state != DNS64_INTERNAL_QUERY) && |
639 | 0 | !(qstate->query_flags & BIT_CD); |
640 | 0 | int has_data = /* whether query returned non-empty rrset */ |
641 | 0 | qstate->return_msg && |
642 | 0 | qstate->return_msg->rep && |
643 | 0 | reply_find_answer_rrset(&qstate->qinfo, qstate->return_msg->rep); |
644 | 0 | int synth_qname = 0; |
645 | |
|
646 | 0 | if(could_synth && |
647 | 0 | (!has_data || |
648 | 0 | (synth_qname=dns64_always_synth_for_qname(qstate, id)))) { |
649 | 0 | if(synth_qname) |
650 | 0 | verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway"); |
651 | 0 | return generate_type_A_query(qstate, id); |
652 | 0 | } |
653 | | |
654 | | /* Store the response in cache. */ |
655 | 0 | if( (!iq || !iq->started_no_cache_store) && |
656 | 0 | qstate->return_msg && |
657 | 0 | qstate->return_msg->rep && |
658 | 0 | !dns_cache_store( |
659 | 0 | qstate->env, &qstate->qinfo, qstate->return_msg->rep, |
660 | 0 | 0, qstate->prefetch_leeway, 0, NULL, |
661 | 0 | qstate->query_flags, qstate->qstarttime, |
662 | 0 | qstate->is_valrec)) |
663 | 0 | log_err("out of memory"); |
664 | | |
665 | | /* do nothing */ |
666 | 0 | return module_finished; |
667 | 0 | } |
668 | | |
669 | | /** |
670 | | * This is the module's main() function. It gets called each time a query |
671 | | * receives an event which we may need to handle. We respond by updating the |
672 | | * state of the query. |
673 | | * |
674 | | * \param qstate Structure containing the state of the query. |
675 | | * \param event Event that has just been received. |
676 | | * \param id This module's instance ID. |
677 | | * \param outbound State of a DNS query on an authoritative server. We never do |
678 | | * our own queries ourselves (other modules do it for us), so |
679 | | * this is unused. |
680 | | */ |
681 | | void |
682 | | dns64_operate(struct module_qstate* qstate, enum module_ev event, int id, |
683 | | struct outbound_entry* outbound) |
684 | 0 | { |
685 | 0 | struct dns64_qstate* iq; |
686 | 0 | (void)outbound; |
687 | 0 | verbose(VERB_QUERY, "dns64[module %d] operate: extstate:%s event:%s", |
688 | 0 | id, strextstate(qstate->ext_state[id]), |
689 | 0 | strmodulevent(event)); |
690 | 0 | log_query_info(VERB_QUERY, "dns64 operate: query", &qstate->qinfo); |
691 | |
|
692 | 0 | switch(event) { |
693 | 0 | case module_event_new: |
694 | | /* Tag this query as being new and fall through. */ |
695 | 0 | if (!(iq = (struct dns64_qstate*)regional_alloc( |
696 | 0 | qstate->region, sizeof(*iq)))) { |
697 | 0 | log_err("out of memory"); |
698 | 0 | qstate->ext_state[id] = module_error; |
699 | 0 | return; |
700 | 0 | } |
701 | 0 | qstate->minfo[id] = iq; |
702 | 0 | iq->state = DNS64_NEW_QUERY; |
703 | 0 | iq->started_no_cache_store = qstate->no_cache_store; |
704 | 0 | qstate->no_cache_store = 1; |
705 | 0 | ATTR_FALLTHROUGH |
706 | | /* fallthrough */ |
707 | 0 | case module_event_pass: |
708 | 0 | qstate->ext_state[id] = handle_event_pass(qstate, id); |
709 | 0 | break; |
710 | 0 | case module_event_moddone: |
711 | 0 | qstate->ext_state[id] = handle_event_moddone(qstate, id); |
712 | 0 | break; |
713 | 0 | default: |
714 | 0 | qstate->ext_state[id] = module_finished; |
715 | 0 | break; |
716 | 0 | } |
717 | 0 | if(qstate->ext_state[id] == module_finished) { |
718 | 0 | iq = (struct dns64_qstate*)qstate->minfo[id]; |
719 | 0 | if(iq && iq->state != DNS64_INTERNAL_QUERY) |
720 | 0 | qstate->no_cache_store = iq->started_no_cache_store; |
721 | 0 | } |
722 | 0 | } |
723 | | |
724 | | static void |
725 | | dns64_synth_aaaa_data(const struct ub_packed_rrset_key* fk, |
726 | | const struct packed_rrset_data* fd, |
727 | | struct ub_packed_rrset_key *dk, |
728 | | struct packed_rrset_data **dd_out, struct regional *region, |
729 | | struct dns64_env* dns64_env ) |
730 | 0 | { |
731 | 0 | struct packed_rrset_data *dd; |
732 | 0 | size_t i; |
733 | | /* |
734 | | * Create synthesized AAAA RR set data. We need to allocated extra memory |
735 | | * for the RRs themselves. Each RR has a length, TTL, pointer to wireformat |
736 | | * data, 2 bytes of data length, and 16 bytes of IPv6 address. |
737 | | */ |
738 | 0 | if(fd->count > RR_COUNT_MAX) { |
739 | 0 | *dd_out = NULL; |
740 | 0 | return; /* integer overflow protection in alloc */ |
741 | 0 | } |
742 | 0 | if (!(dd = *dd_out = regional_alloc_zero(region, |
743 | 0 | sizeof(struct packed_rrset_data) |
744 | 0 | + fd->count * (sizeof(size_t) + sizeof(time_t) + |
745 | 0 | sizeof(uint8_t*) + 2 + 16)))) { |
746 | 0 | log_err("out of memory"); |
747 | 0 | return; |
748 | 0 | } |
749 | | |
750 | | /* Copy attributes from A RR set. */ |
751 | 0 | dd->ttl = fd->ttl; |
752 | 0 | dd->count = fd->count; |
753 | 0 | dd->rrsig_count = 0; |
754 | 0 | dd->trust = fd->trust; |
755 | 0 | dd->security = fd->security; |
756 | | |
757 | | /* |
758 | | * Synthesize AAAA records. Adjust pointers in structure. |
759 | | */ |
760 | 0 | dd->rr_len = |
761 | 0 | (size_t*)((uint8_t*)dd + sizeof(struct packed_rrset_data)); |
762 | 0 | dd->rr_data = (uint8_t**)&dd->rr_len[dd->count]; |
763 | 0 | dd->rr_ttl = (time_t*)&dd->rr_data[dd->count]; |
764 | 0 | for(i = 0; i < fd->count; ++i) { |
765 | 0 | if (fd->rr_len[i] != 6 || fd->rr_data[i][0] != 0 |
766 | 0 | || fd->rr_data[i][1] != 4) { |
767 | 0 | *dd_out = NULL; |
768 | 0 | return; |
769 | 0 | } |
770 | 0 | dd->rr_len[i] = 18; |
771 | 0 | dd->rr_data[i] = |
772 | 0 | (uint8_t*)&dd->rr_ttl[dd->count] + 18*i; |
773 | 0 | dd->rr_data[i][0] = 0; |
774 | 0 | dd->rr_data[i][1] = 16; |
775 | 0 | synthesize_aaaa( |
776 | 0 | ((struct sockaddr_in6*)&dns64_env->prefix_addr)->sin6_addr.s6_addr, |
777 | 0 | sizeof(((struct sockaddr_in6*)&dns64_env->prefix_addr)->sin6_addr.s6_addr), |
778 | 0 | dns64_env->prefix_net, &fd->rr_data[i][2], |
779 | 0 | fd->rr_len[i]-2, &dd->rr_data[i][2], |
780 | 0 | dd->rr_len[i]-2); |
781 | 0 | dd->rr_ttl[i] = fd->rr_ttl[i]; |
782 | 0 | } |
783 | | |
784 | | /* |
785 | | * Create synthesized AAAA RR set key. This is mostly just bookkeeping, |
786 | | * nothing interesting here. |
787 | | */ |
788 | 0 | if(!dk) { |
789 | 0 | log_err("no key"); |
790 | 0 | *dd_out = NULL; |
791 | 0 | return; |
792 | 0 | } |
793 | | |
794 | 0 | dk->rk.dname = (uint8_t*)regional_alloc_init(region, |
795 | 0 | fk->rk.dname, fk->rk.dname_len); |
796 | |
|
797 | 0 | if(!dk->rk.dname) { |
798 | 0 | log_err("out of memory"); |
799 | 0 | *dd_out = NULL; |
800 | 0 | return; |
801 | 0 | } |
802 | | |
803 | 0 | dk->rk.type = htons(LDNS_RR_TYPE_AAAA); |
804 | 0 | memset(&dk->entry, 0, sizeof(dk->entry)); |
805 | 0 | dk->entry.key = dk; |
806 | 0 | dk->entry.hash = rrset_key_hash(&dk->rk); |
807 | 0 | dk->entry.data = dd; |
808 | |
|
809 | 0 | } |
810 | | |
811 | | /** |
812 | | * Synthesize an AAAA RR set from an A sub-query's answer and add it to the |
813 | | * original empty response. |
814 | | * |
815 | | * \param id This module's instance ID. |
816 | | * \param super Original AAAA query. |
817 | | * \param qstate A query. |
818 | | */ |
819 | | static void |
820 | | dns64_adjust_a(int id, struct module_qstate* super, struct module_qstate* qstate) |
821 | 0 | { |
822 | 0 | struct dns64_env* dns64_env = (struct dns64_env*)super->env->modinfo[id]; |
823 | 0 | struct reply_info *rep, *cp; |
824 | 0 | size_t i, s; |
825 | 0 | struct packed_rrset_data* fd, *dd; |
826 | 0 | struct ub_packed_rrset_key* fk, *dk; |
827 | |
|
828 | 0 | verbose(VERB_ALGO, "converting A answers to AAAA answers"); |
829 | |
|
830 | 0 | log_assert(super->region); |
831 | 0 | log_assert(qstate->return_msg); |
832 | 0 | log_assert(qstate->return_msg->rep); |
833 | | |
834 | | /* If dns64-synthall is enabled, return_msg is not initialized */ |
835 | 0 | if(!super->return_msg) { |
836 | 0 | super->return_msg = (struct dns_msg*)regional_alloc( |
837 | 0 | super->region, sizeof(struct dns_msg)); |
838 | 0 | if(!super->return_msg) |
839 | 0 | return; |
840 | 0 | memset(super->return_msg, 0, sizeof(*super->return_msg)); |
841 | 0 | super->return_msg->qinfo = super->qinfo; |
842 | 0 | } |
843 | | |
844 | 0 | rep = qstate->return_msg->rep; |
845 | | |
846 | | /* |
847 | | * Build the actual reply. |
848 | | */ |
849 | 0 | cp = construct_reply_info_base(super->region, rep->flags, rep->qdcount, |
850 | 0 | rep->ttl, rep->prefetch_ttl, rep->serve_expired_ttl, |
851 | 0 | rep->serve_expired_norec_ttl, |
852 | 0 | rep->an_numrrsets, rep->ns_numrrsets, rep->ar_numrrsets, |
853 | 0 | rep->rrset_count, rep->security, LDNS_EDE_NONE); |
854 | 0 | if(!cp) |
855 | 0 | return; |
856 | | |
857 | | /* allocate ub_key structures special or not */ |
858 | 0 | if(!reply_info_alloc_rrset_keys(cp, NULL, super->region)) { |
859 | 0 | return; |
860 | 0 | } |
861 | | |
862 | | /* copy everything and replace A by AAAA */ |
863 | 0 | for(i=0; i<cp->rrset_count; i++) { |
864 | 0 | fk = rep->rrsets[i]; |
865 | 0 | dk = cp->rrsets[i]; |
866 | 0 | fd = (struct packed_rrset_data*)fk->entry.data; |
867 | 0 | dk->rk = fk->rk; |
868 | 0 | dk->id = fk->id; |
869 | |
|
870 | 0 | if(i<rep->an_numrrsets && fk->rk.type == htons(LDNS_RR_TYPE_A)) { |
871 | | /* also sets dk->entry.hash */ |
872 | 0 | dns64_synth_aaaa_data(fk, fd, dk, &dd, super->region, dns64_env); |
873 | 0 | if(!dd) |
874 | 0 | return; |
875 | | /* Delete negative AAAA record from cache stored by |
876 | | * the iterator module */ |
877 | 0 | rrset_cache_remove(super->env->rrset_cache, dk->rk.dname, |
878 | 0 | dk->rk.dname_len, LDNS_RR_TYPE_AAAA, |
879 | 0 | LDNS_RR_CLASS_IN, 0); |
880 | | /* Delete negative AAAA in msg cache for CNAMEs, |
881 | | * stored by the iterator module */ |
882 | 0 | if(i != 0) /* if not the first RR */ |
883 | 0 | msg_cache_remove(super->env, dk->rk.dname, |
884 | 0 | dk->rk.dname_len, LDNS_RR_TYPE_AAAA, |
885 | 0 | LDNS_RR_CLASS_IN, 0); |
886 | 0 | } else { |
887 | 0 | dk->entry.hash = fk->entry.hash; |
888 | 0 | dk->rk.dname = (uint8_t*)regional_alloc_init(super->region, |
889 | 0 | fk->rk.dname, fk->rk.dname_len); |
890 | |
|
891 | 0 | if(!dk->rk.dname) |
892 | 0 | return; |
893 | | |
894 | 0 | s = packed_rrset_sizeof(fd); |
895 | 0 | dd = (struct packed_rrset_data*)regional_alloc_init( |
896 | 0 | super->region, fd, s); |
897 | |
|
898 | 0 | if(!dd) |
899 | 0 | return; |
900 | 0 | } |
901 | | |
902 | 0 | packed_rrset_ptr_fixup(dd); |
903 | 0 | dk->entry.data = (void*)dd; |
904 | 0 | } |
905 | | |
906 | | /* Commit changes. */ |
907 | 0 | super->return_msg->rep = cp; |
908 | 0 | } |
909 | | |
910 | | /** |
911 | | * Generate a response for the original IPv6 PTR query based on an IPv4 PTR |
912 | | * sub-query's response. |
913 | | * |
914 | | * \param qstate IPv4 PTR sub-query. |
915 | | * \param super Original IPv6 PTR query. |
916 | | */ |
917 | | static void |
918 | | dns64_adjust_ptr(struct module_qstate* qstate, struct module_qstate* super) |
919 | 0 | { |
920 | 0 | struct ub_packed_rrset_key* answer; |
921 | |
|
922 | 0 | verbose(VERB_ALGO, "adjusting PTR reply"); |
923 | | |
924 | | /* Copy the sub-query's reply to the parent. */ |
925 | 0 | if (!(super->return_msg = (struct dns_msg*)regional_alloc(super->region, |
926 | 0 | sizeof(struct dns_msg)))) |
927 | 0 | return; |
928 | 0 | super->return_msg->qinfo = super->qinfo; |
929 | 0 | if (!(super->return_msg->rep = reply_info_copy(qstate->return_msg->rep, |
930 | 0 | NULL, super->region))) |
931 | 0 | return; |
932 | | |
933 | | /* |
934 | | * Adjust the domain name of the answer RR set so that it matches the |
935 | | * initial query's domain name. |
936 | | */ |
937 | 0 | answer = reply_find_answer_rrset(&qstate->qinfo, super->return_msg->rep); |
938 | 0 | if(answer) { |
939 | 0 | answer->rk.dname = super->qinfo.qname; |
940 | 0 | answer->rk.dname_len = super->qinfo.qname_len; |
941 | 0 | } |
942 | 0 | } |
943 | | |
944 | | /** |
945 | | * This function is called when a sub-query finishes to inform the parent query. |
946 | | * |
947 | | * We issue two kinds of sub-queries: PTR and A. |
948 | | * |
949 | | * \param qstate State of the sub-query. |
950 | | * \param id This module's instance ID. |
951 | | * \param super State of the super-query. |
952 | | */ |
953 | | void |
954 | | dns64_inform_super(struct module_qstate* qstate, int id, |
955 | | struct module_qstate* super) |
956 | 0 | { |
957 | 0 | struct dns64_qstate* super_dq = (struct dns64_qstate*)super->minfo[id]; |
958 | 0 | log_query_info(VERB_ALGO, "dns64: inform_super, sub is", |
959 | 0 | &qstate->qinfo); |
960 | 0 | log_query_info(VERB_ALGO, "super is", &super->qinfo); |
961 | | |
962 | | /* |
963 | | * Signal that the sub-query is finished, no matter whether we are |
964 | | * successful or not. This lets the state machine terminate. |
965 | | */ |
966 | 0 | if(!super_dq) { |
967 | 0 | super_dq = (struct dns64_qstate*)regional_alloc(super->region, |
968 | 0 | sizeof(*super_dq)); |
969 | 0 | if(!super_dq) { |
970 | 0 | log_err("out of memory"); |
971 | 0 | super->return_rcode = LDNS_RCODE_SERVFAIL; |
972 | 0 | super->return_msg = NULL; |
973 | 0 | return; |
974 | 0 | } |
975 | 0 | super->minfo[id] = super_dq; |
976 | 0 | memset(super_dq, 0, sizeof(*super_dq)); |
977 | 0 | super_dq->started_no_cache_store = super->no_cache_store; |
978 | 0 | } |
979 | 0 | super_dq->state = DNS64_SUBQUERY_FINISHED; |
980 | | |
981 | | /* If there is no successful answer, we're done. |
982 | | * Guarantee that we have at least a NOERROR reply further on. */ |
983 | 0 | if(qstate->return_rcode != LDNS_RCODE_NOERROR |
984 | 0 | || !qstate->return_msg |
985 | 0 | || !qstate->return_msg->rep) { |
986 | 0 | return; |
987 | 0 | } |
988 | | |
989 | | /* When no A record is found for synthesis fall back to AAAA again. */ |
990 | 0 | if(qstate->qinfo.qtype == LDNS_RR_TYPE_A && |
991 | 0 | !reply_find_answer_rrset(&qstate->qinfo, |
992 | 0 | qstate->return_msg->rep)) { |
993 | 0 | super_dq->state = DNS64_INTERNAL_QUERY; |
994 | 0 | return; |
995 | 0 | } |
996 | | |
997 | | /* Use return code from A query in response to client. */ |
998 | 0 | if (super->return_rcode != LDNS_RCODE_NOERROR) |
999 | 0 | super->return_rcode = qstate->return_rcode; |
1000 | | |
1001 | | /* Generate a response suitable for the original query. */ |
1002 | 0 | if (qstate->qinfo.qtype == LDNS_RR_TYPE_A) { |
1003 | 0 | dns64_adjust_a(id, super, qstate); |
1004 | 0 | } else { |
1005 | 0 | log_assert(qstate->qinfo.qtype == LDNS_RR_TYPE_PTR); |
1006 | 0 | dns64_adjust_ptr(qstate, super); |
1007 | 0 | } |
1008 | | |
1009 | | /* Store the generated response in cache. */ |
1010 | 0 | if ( (!super_dq || !super_dq->started_no_cache_store) && |
1011 | 0 | !dns_cache_store(super->env, &super->qinfo, super->return_msg->rep, |
1012 | 0 | 0, super->prefetch_leeway, 0, NULL, super->query_flags, |
1013 | 0 | qstate->qstarttime, qstate->is_valrec)) |
1014 | 0 | log_err("out of memory"); |
1015 | 0 | } |
1016 | | |
1017 | | /** |
1018 | | * Clear module-specific data from query state. Since we do not allocate memory, |
1019 | | * it's just a matter of setting a pointer to NULL. |
1020 | | * |
1021 | | * \param qstate Query state. |
1022 | | * \param id This module's instance ID. |
1023 | | */ |
1024 | | void |
1025 | | dns64_clear(struct module_qstate* qstate, int id) |
1026 | 0 | { |
1027 | 0 | qstate->minfo[id] = NULL; |
1028 | 0 | } |
1029 | | |
1030 | | /** |
1031 | | * Returns the amount of global memory that this module uses, not including |
1032 | | * per-query data. |
1033 | | * |
1034 | | * \param env Module environment. |
1035 | | * \param id This module's instance ID. |
1036 | | */ |
1037 | | size_t |
1038 | | dns64_get_mem(struct module_env* env, int id) |
1039 | 0 | { |
1040 | 0 | struct dns64_env* dns64_env = (struct dns64_env*)env->modinfo[id]; |
1041 | 0 | if (!dns64_env) |
1042 | 0 | return 0; |
1043 | 0 | return sizeof(*dns64_env); |
1044 | 0 | } |
1045 | | |
1046 | | /** |
1047 | | * The dns64 function block. |
1048 | | */ |
1049 | | static struct module_func_block dns64_block = { |
1050 | | "dns64", |
1051 | | NULL, NULL, &dns64_init, &dns64_deinit, &dns64_operate, |
1052 | | &dns64_inform_super, &dns64_clear, &dns64_get_mem |
1053 | | }; |
1054 | | |
1055 | | /** |
1056 | | * Function for returning the above function block. |
1057 | | */ |
1058 | | struct module_func_block * |
1059 | | dns64_get_funcblock(void) |
1060 | 0 | { |
1061 | 0 | return &dns64_block; |
1062 | 0 | } |