diff options
Diffstat (limited to 'src/service/gns/gnunet-service-gns_resolver.c')
-rw-r--r-- | src/service/gns/gnunet-service-gns_resolver.c | 2992 |
1 files changed, 2992 insertions, 0 deletions
diff --git a/src/service/gns/gnunet-service-gns_resolver.c b/src/service/gns/gnunet-service-gns_resolver.c new file mode 100644 index 000000000..aa0189b15 --- /dev/null +++ b/src/service/gns/gnunet-service-gns_resolver.c | |||
@@ -0,0 +1,2992 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011-2013 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file gns/gnunet-service-gns_resolver.c | ||
23 | * @brief GNU Name System resolver logic | ||
24 | * @author Martin Schanzenbach | ||
25 | * @author Christian Grothoff | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #if HAVE_LIBIDN2 | ||
29 | #if HAVE_IDN2_H | ||
30 | #include <idn2.h> | ||
31 | #elif HAVE_IDN2_IDN2_H | ||
32 | #include <idn2/idn2.h> | ||
33 | #endif | ||
34 | #elif HAVE_LIBIDN | ||
35 | #if HAVE_IDNA_H | ||
36 | #include <idna.h> | ||
37 | #elif HAVE_IDN_IDNA_H | ||
38 | #include <idn/idna.h> | ||
39 | #endif | ||
40 | #endif | ||
41 | #include "gnunet_util_lib.h" | ||
42 | #include "gnunet_dht_service.h" | ||
43 | #include "gnunet_gnsrecord_lib.h" | ||
44 | #include "gnunet_namecache_service.h" | ||
45 | #include "gnunet_dns_service.h" | ||
46 | #include "gnunet_resolver_service.h" | ||
47 | #include "gnunet_revocation_service.h" | ||
48 | #include "gnunet_gns_service.h" | ||
49 | #include "gns.h" | ||
50 | #include "gnunet-service-gns.h" | ||
51 | #include "gnunet-service-gns_resolver.h" | ||
52 | |||
53 | |||
54 | /** | ||
55 | * Default DHT timeout for lookups. | ||
56 | */ | ||
57 | #define DHT_LOOKUP_TIMEOUT GNUNET_TIME_relative_multiply ( \ | ||
58 | GNUNET_TIME_UNIT_SECONDS, 60) | ||
59 | |||
60 | /** | ||
61 | * Default timeout for DNS lookups. | ||
62 | */ | ||
63 | #define DNS_LOOKUP_TIMEOUT GNUNET_TIME_relative_multiply ( \ | ||
64 | GNUNET_TIME_UNIT_SECONDS, 15) | ||
65 | |||
66 | /** | ||
67 | * DHT replication level | ||
68 | */ | ||
69 | #define DHT_GNS_REPLICATION_LEVEL 10 | ||
70 | |||
71 | |||
72 | /** | ||
73 | * DLL to hold the authority chain we had to pass in the resolution | ||
74 | * process. | ||
75 | */ | ||
76 | struct AuthorityChain; | ||
77 | |||
78 | |||
79 | /** | ||
80 | * Element of a resolution process for looking up the | ||
81 | * responsible DNS server hostname in a GNS2DNS recursive | ||
82 | * resolution. | ||
83 | */ | ||
84 | struct Gns2DnsPending | ||
85 | { | ||
86 | /** | ||
87 | * Kept in a DLL. | ||
88 | */ | ||
89 | struct Gns2DnsPending *next; | ||
90 | |||
91 | /** | ||
92 | * Kept in a DLL. | ||
93 | */ | ||
94 | struct Gns2DnsPending *prev; | ||
95 | |||
96 | /** | ||
97 | * Context this activity belongs with. | ||
98 | */ | ||
99 | struct AuthorityChain *ac; | ||
100 | |||
101 | /** | ||
102 | * Handle for the resolution of the IP part of the | ||
103 | * GNS2DNS record. Will return to us the addresses | ||
104 | * of the DNS resolver to use. | ||
105 | */ | ||
106 | struct GNS_ResolverHandle *rh; | ||
107 | |||
108 | /** | ||
109 | * Handle for DNS resolution of the DNS nameserver. | ||
110 | */ | ||
111 | struct GNUNET_RESOLVER_RequestHandle *dns_rh; | ||
112 | |||
113 | /** | ||
114 | * How many results did we get? | ||
115 | */ | ||
116 | unsigned int num_results; | ||
117 | }; | ||
118 | |||
119 | |||
120 | /** | ||
121 | * Handle to a currently pending resolution. On result (positive or | ||
122 | * negative) the #GNS_ResultProcessor is called. | ||
123 | */ | ||
124 | struct GNS_ResolverHandle; | ||
125 | |||
126 | |||
127 | /** | ||
128 | * DLL to hold the authority chain we had to pass in the resolution | ||
129 | * process. | ||
130 | */ | ||
131 | struct AuthorityChain | ||
132 | { | ||
133 | /** | ||
134 | * This is a DLL. | ||
135 | */ | ||
136 | struct AuthorityChain *prev; | ||
137 | |||
138 | /** | ||
139 | * This is a DLL. | ||
140 | */ | ||
141 | struct AuthorityChain *next; | ||
142 | |||
143 | /** | ||
144 | * Resolver handle this entry in the chain belongs to. | ||
145 | */ | ||
146 | struct GNS_ResolverHandle *rh; | ||
147 | |||
148 | /** | ||
149 | * label/name corresponding to the authority | ||
150 | */ | ||
151 | char *label; | ||
152 | |||
153 | /** | ||
154 | * #GNUNET_YES if the authority was a GNS authority, | ||
155 | * #GNUNET_NO if the authority was a DNS authority. | ||
156 | */ | ||
157 | int gns_authority; | ||
158 | |||
159 | /** | ||
160 | * Information about the resolver authority for this label. | ||
161 | */ | ||
162 | union | ||
163 | { | ||
164 | /** | ||
165 | * The zone of the GNS authority | ||
166 | */ | ||
167 | struct GNUNET_CRYPTO_PublicKey gns_authority; | ||
168 | |||
169 | struct | ||
170 | { | ||
171 | /** | ||
172 | * Domain of the DNS resolver that is the authority. | ||
173 | * (appended to construct the DNS name to resolve; | ||
174 | * this is NOT the DNS name of the DNS server!). | ||
175 | */ | ||
176 | char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1]; | ||
177 | |||
178 | /** | ||
179 | * List of resolutions of the 'ip' of the name server that | ||
180 | * are still pending. | ||
181 | */ | ||
182 | struct Gns2DnsPending *gp_head; | ||
183 | |||
184 | /** | ||
185 | * Tail of list of resolutions of the 'ip' of the name server that | ||
186 | * are still pending. | ||
187 | */ | ||
188 | struct Gns2DnsPending *gp_tail; | ||
189 | |||
190 | /** | ||
191 | * Handle to perform DNS lookups with this authority (in GNS2DNS handling). | ||
192 | */ | ||
193 | struct GNUNET_DNSSTUB_Context *dns_handle; | ||
194 | |||
195 | /** | ||
196 | * Did we succeed in getting an IP address for *any* of the DNS servers listed? | ||
197 | * Once we do, we can start with DNS queries. | ||
198 | */ | ||
199 | int found; | ||
200 | |||
201 | /** | ||
202 | * Did we start the recursive resolution via DNS? | ||
203 | */ | ||
204 | int launched; | ||
205 | } dns_authority; | ||
206 | } authority_info; | ||
207 | }; | ||
208 | |||
209 | |||
210 | /** | ||
211 | * A result we got from DNS. | ||
212 | */ | ||
213 | struct DnsResult | ||
214 | { | ||
215 | /** | ||
216 | * Kept in DLL. | ||
217 | */ | ||
218 | struct DnsResult *next; | ||
219 | |||
220 | /** | ||
221 | * Kept in DLL. | ||
222 | */ | ||
223 | struct DnsResult *prev; | ||
224 | |||
225 | /** | ||
226 | * Binary value stored in the DNS record (appended to this struct) | ||
227 | */ | ||
228 | const void *data; | ||
229 | |||
230 | /** | ||
231 | * Expiration time for the DNS record, 0 if we didn't | ||
232 | * get anything useful (i.e. 'gethostbyname()' was used). | ||
233 | */ | ||
234 | uint64_t expiration_time; | ||
235 | |||
236 | /** | ||
237 | * Number of bytes in @e data. | ||
238 | */ | ||
239 | size_t data_size; | ||
240 | |||
241 | /** | ||
242 | * Type of the GNS/DNS record. | ||
243 | */ | ||
244 | uint32_t record_type; | ||
245 | }; | ||
246 | |||
247 | |||
248 | /** | ||
249 | * Handle to a currently pending resolution. On result (positive or | ||
250 | * negative) the #GNS_ResultProcessor is called. | ||
251 | */ | ||
252 | struct GNS_ResolverHandle | ||
253 | { | ||
254 | /** | ||
255 | * DLL | ||
256 | */ | ||
257 | struct GNS_ResolverHandle *next; | ||
258 | |||
259 | /** | ||
260 | * DLL | ||
261 | */ | ||
262 | struct GNS_ResolverHandle *prev; | ||
263 | |||
264 | /** | ||
265 | * The top-level GNS authoritative zone to query | ||
266 | */ | ||
267 | struct GNUNET_CRYPTO_PublicKey authority_zone; | ||
268 | |||
269 | /** | ||
270 | * called when resolution phase finishes | ||
271 | */ | ||
272 | GNS_ResultProcessor proc; | ||
273 | |||
274 | /** | ||
275 | * closure passed to @e proc | ||
276 | */ | ||
277 | void *proc_cls; | ||
278 | |||
279 | /** | ||
280 | * Handle for DHT lookups. should be NULL if no lookups are in progress | ||
281 | */ | ||
282 | struct GNUNET_DHT_GetHandle *get_handle; | ||
283 | |||
284 | |||
285 | /** | ||
286 | * Socket for a DNS request, NULL if none is active. | ||
287 | */ | ||
288 | struct GNUNET_DNSSTUB_RequestSocket *dns_request; | ||
289 | |||
290 | /** | ||
291 | * Handle for standard DNS resolution, NULL if none is active. | ||
292 | */ | ||
293 | struct GNUNET_RESOLVER_RequestHandle *std_resolve; | ||
294 | |||
295 | /** | ||
296 | * Pending Namecache lookup task | ||
297 | */ | ||
298 | struct GNUNET_NAMECACHE_QueueEntry *namecache_qe; | ||
299 | |||
300 | /** | ||
301 | * Pending revocation check. | ||
302 | */ | ||
303 | struct GNUNET_REVOCATION_Query *rev_check; | ||
304 | |||
305 | /** | ||
306 | * Heap node associated with this lookup. Used to limit number of | ||
307 | * concurrent requests. | ||
308 | */ | ||
309 | struct GNUNET_CONTAINER_HeapNode *dht_heap_node; | ||
310 | |||
311 | /** | ||
312 | * DLL to store the authority chain | ||
313 | */ | ||
314 | struct AuthorityChain *ac_head; | ||
315 | |||
316 | /** | ||
317 | * DLL to store the authority chain | ||
318 | */ | ||
319 | struct AuthorityChain *ac_tail; | ||
320 | |||
321 | /** | ||
322 | * ID of a task associated with the resolution process. | ||
323 | */ | ||
324 | struct GNUNET_SCHEDULER_Task *task_id; | ||
325 | |||
326 | /** | ||
327 | * The name to resolve | ||
328 | */ | ||
329 | char *name; | ||
330 | |||
331 | /** | ||
332 | * Legacy Hostname to use if we encountered GNS2DNS record | ||
333 | * and thus can deduct the LEHO from that transition. | ||
334 | */ | ||
335 | char *leho; | ||
336 | |||
337 | /** | ||
338 | * DLL of results we got from DNS. | ||
339 | */ | ||
340 | struct DnsResult *dns_result_head; | ||
341 | |||
342 | /** | ||
343 | * DLL of results we got from DNS. | ||
344 | */ | ||
345 | struct DnsResult *dns_result_tail; | ||
346 | |||
347 | /** | ||
348 | * Current offset in @e name where we are resolving. | ||
349 | */ | ||
350 | size_t name_resolution_pos; | ||
351 | |||
352 | /** | ||
353 | * Use only cache | ||
354 | */ | ||
355 | enum GNUNET_GNS_LocalOptions options; | ||
356 | |||
357 | /** | ||
358 | * For SRV and TLSA records, the number of the | ||
359 | * protocol specified in the name. 0 if no protocol was given. | ||
360 | */ | ||
361 | int protocol; | ||
362 | |||
363 | /** | ||
364 | * For SRV and TLSA records, the number of the | ||
365 | * service specified in the name. 0 if no service was given. | ||
366 | */ | ||
367 | int service; | ||
368 | |||
369 | /** | ||
370 | * Desired type for the resolution. | ||
371 | */ | ||
372 | int record_type; | ||
373 | |||
374 | /** | ||
375 | * We increment the loop limiter for each step in a recursive | ||
376 | * resolution. If it passes our @e loop_threshold (e.g. due to | ||
377 | * self-recursion in the resolution, i.e CNAME fun), we stop. | ||
378 | */ | ||
379 | unsigned int loop_limiter; | ||
380 | |||
381 | /** | ||
382 | * Maximum value of @e loop_limiter allowed by client. | ||
383 | */ | ||
384 | unsigned int loop_threshold; | ||
385 | |||
386 | /** | ||
387 | * 16 bit random ID we used in the @e dns_request. | ||
388 | */ | ||
389 | uint16_t original_dns_id; | ||
390 | }; | ||
391 | |||
392 | |||
393 | /** | ||
394 | * Active namestore caching operations. | ||
395 | */ | ||
396 | struct CacheOps | ||
397 | { | ||
398 | /** | ||
399 | * Organized in a DLL. | ||
400 | */ | ||
401 | struct CacheOps *next; | ||
402 | |||
403 | /** | ||
404 | * Organized in a DLL. | ||
405 | */ | ||
406 | struct CacheOps *prev; | ||
407 | |||
408 | /** | ||
409 | * Pending Namestore caching task. | ||
410 | */ | ||
411 | struct GNUNET_NAMECACHE_QueueEntry *namecache_qe_cache; | ||
412 | }; | ||
413 | |||
414 | |||
415 | /** | ||
416 | * Our handle to the namecache service | ||
417 | */ | ||
418 | static struct GNUNET_NAMECACHE_Handle *namecache_handle; | ||
419 | |||
420 | /** | ||
421 | * Resolver handle to the dht | ||
422 | */ | ||
423 | static struct GNUNET_DHT_Handle *dht_handle; | ||
424 | |||
425 | /** | ||
426 | * Heap for limiting parallel DHT lookups | ||
427 | */ | ||
428 | static struct GNUNET_CONTAINER_Heap *dht_lookup_heap; | ||
429 | |||
430 | /** | ||
431 | * Maximum amount of parallel queries to the DHT | ||
432 | */ | ||
433 | static unsigned long long max_allowed_background_queries; | ||
434 | |||
435 | /** | ||
436 | * Head of resolver lookup list | ||
437 | */ | ||
438 | static struct GNS_ResolverHandle *rlh_head; | ||
439 | |||
440 | /** | ||
441 | * Tail of resolver lookup list | ||
442 | */ | ||
443 | static struct GNS_ResolverHandle *rlh_tail; | ||
444 | |||
445 | /** | ||
446 | * Organized in a DLL. | ||
447 | */ | ||
448 | static struct CacheOps *co_head; | ||
449 | |||
450 | /** | ||
451 | * Organized in a DLL. | ||
452 | */ | ||
453 | static struct CacheOps *co_tail; | ||
454 | |||
455 | /** | ||
456 | * Use namecache | ||
457 | */ | ||
458 | static int disable_cache; | ||
459 | |||
460 | /** | ||
461 | * Global configuration. | ||
462 | */ | ||
463 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
464 | |||
465 | |||
466 | /** | ||
467 | * Determine if this name is canonical (is a legal name in a zone, without delegation); | ||
468 | * note that we do not test that the name does not contain illegal characters, we only | ||
469 | * test for delegation. Note that service records (like _foo._srv) are canonical names | ||
470 | * even though they consist of multiple labels. | ||
471 | * | ||
472 | * Examples: | ||
473 | * a.b.gnu = not canonical | ||
474 | * a = canonical | ||
475 | * _foo._srv = canonical | ||
476 | * _f.bar = not canonical | ||
477 | * | ||
478 | * @param name the name to test | ||
479 | * @return #GNUNET_YES if canonical | ||
480 | */ | ||
481 | /* dead, but keep for now */ int | ||
482 | is_canonical (const char *name) | ||
483 | { | ||
484 | const char *pos; | ||
485 | const char *dot; | ||
486 | |||
487 | if (NULL == strchr (name, | ||
488 | (unsigned char) '.')) | ||
489 | return GNUNET_YES; | ||
490 | if ('_' != name[0]) | ||
491 | return GNUNET_NO; | ||
492 | pos = &name[1]; | ||
493 | while (NULL != (dot = strchr (pos, | ||
494 | (unsigned char) '.'))) | ||
495 | if ('_' != dot[1]) | ||
496 | return GNUNET_NO; | ||
497 | else | ||
498 | pos = dot + 1; | ||
499 | return GNUNET_YES; | ||
500 | } | ||
501 | |||
502 | |||
503 | /* ************************** Resolution **************************** */ | ||
504 | |||
505 | /** | ||
506 | * Expands a name ending in .+ with the zone of origin. | ||
507 | * | ||
508 | * @param rh resolution context | ||
509 | * @param name name to modify (to be free'd or returned) | ||
510 | * @return updated name | ||
511 | */ | ||
512 | static char * | ||
513 | translate_dot_plus (struct GNS_ResolverHandle *rh, | ||
514 | char *name) | ||
515 | { | ||
516 | char *ret; | ||
517 | size_t s_len = strlen (name); | ||
518 | |||
519 | if (0 != strcmp (&name[s_len - 2], | ||
520 | ".+")) | ||
521 | return name; /* did not end in ".+" */ | ||
522 | GNUNET_assert (GNUNET_YES == rh->ac_tail->gns_authority); | ||
523 | GNUNET_asprintf (&ret, | ||
524 | "%.*s.%s", | ||
525 | (int) (s_len - 2), | ||
526 | name, | ||
527 | GNUNET_GNSRECORD_pkey_to_zkey ( | ||
528 | &rh->ac_tail->authority_info.gns_authority)); | ||
529 | GNUNET_free (name); | ||
530 | return ret; | ||
531 | } | ||
532 | |||
533 | |||
534 | /** | ||
535 | * Wrapper around #GNS_resolver_lookup_cancel() as a task. | ||
536 | * Used for delayed cleanup so we can unwind the stack first. | ||
537 | * | ||
538 | * @param cls the `struct GNS_ResolverHandle` | ||
539 | */ | ||
540 | static void | ||
541 | GNS_resolver_lookup_cancel_ (void *cls) | ||
542 | { | ||
543 | struct GNS_ResolverHandle *rh = cls; | ||
544 | |||
545 | rh->task_id = NULL; | ||
546 | GNS_resolver_lookup_cancel (rh); | ||
547 | } | ||
548 | |||
549 | |||
550 | /** | ||
551 | * Function called to asynchronously fail a resolution. | ||
552 | * | ||
553 | * @param rh the resolution to fail | ||
554 | */ | ||
555 | static void | ||
556 | fail_resolution (struct GNS_ResolverHandle *rh) | ||
557 | { | ||
558 | rh->proc (rh->proc_cls, | ||
559 | 0, | ||
560 | NULL); | ||
561 | GNUNET_assert (NULL == rh->task_id); | ||
562 | rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, | ||
563 | rh); | ||
564 | } | ||
565 | |||
566 | |||
567 | /** | ||
568 | * Function called when a resolution times out. | ||
569 | * | ||
570 | * @param cls the `struct GNS_ResolverHandle` | ||
571 | */ | ||
572 | static void | ||
573 | timeout_resolution (void *cls) | ||
574 | { | ||
575 | struct GNS_ResolverHandle *rh = cls; | ||
576 | |||
577 | rh->task_id = NULL; | ||
578 | fail_resolution (rh); | ||
579 | } | ||
580 | |||
581 | |||
582 | /** | ||
583 | * Get the next, rightmost label from the name that we are trying to resolve, | ||
584 | * and update the resolution position accordingly. Labels usually consist | ||
585 | * of up to 63 characters without a period ("."); however, we use a special | ||
586 | * convention to support SRV and TLSA records where the domain name | ||
587 | * includes an encoding for a service and protocol in the name. The | ||
588 | * syntax (see RFC 2782) here is "_Service._Proto.Name" and in this | ||
589 | * special case we include the "_Service._Proto" in the rightmost label. | ||
590 | * Thus, for "_443._tcp.foo.bar" we first return the label "bar" and then | ||
591 | * the label "_443._tcp.foo". The special case is detected by the | ||
592 | * presence of labels beginning with an underscore. Whenever a label | ||
593 | * begins with an underscore, it is combined with the label to its right | ||
594 | * (and the "." is preserved). | ||
595 | * | ||
596 | * @param rh handle to the resolution operation to get the next label from | ||
597 | * @return NULL if there are no more labels | ||
598 | */ | ||
599 | static char * | ||
600 | resolver_lookup_get_next_label (struct GNS_ResolverHandle *rh) | ||
601 | { | ||
602 | const char *rp; | ||
603 | const char *dot; | ||
604 | size_t len; | ||
605 | char *ret; | ||
606 | char *srv_name; | ||
607 | char *proto_name; | ||
608 | struct protoent *pe; | ||
609 | struct servent *se; | ||
610 | |||
611 | if (0 == rh->name_resolution_pos) | ||
612 | return NULL; | ||
613 | dot = memrchr (rh->name, | ||
614 | (int) '.', | ||
615 | rh->name_resolution_pos); | ||
616 | if (NULL == dot) | ||
617 | { | ||
618 | /* done, this was the last one */ | ||
619 | len = rh->name_resolution_pos; | ||
620 | rp = rh->name; | ||
621 | rh->name_resolution_pos = 0; | ||
622 | } | ||
623 | else if (('_' == dot[1]) && | ||
624 | ('_' == rh->name[0]) && | ||
625 | (dot == memchr (rh->name, (int) '.', rh->name_resolution_pos))) | ||
626 | { | ||
627 | /** | ||
628 | * Do not advance a label. This seems to be a name only consisting | ||
629 | * of a BOX indicator (_443,_tcp). | ||
630 | * Which means, it is a BOX under the empty label. | ||
631 | * leaving name_resolution_pos as is and returning empty label. | ||
632 | */ | ||
633 | rp = GNUNET_GNS_EMPTY_LABEL_AT; | ||
634 | len = strlen (GNUNET_GNS_EMPTY_LABEL_AT); | ||
635 | } | ||
636 | else | ||
637 | { | ||
638 | /* advance by one label */ | ||
639 | len = rh->name_resolution_pos - (dot - rh->name) - 1; | ||
640 | rp = dot + 1; | ||
641 | rh->name_resolution_pos = dot - rh->name; | ||
642 | } | ||
643 | rh->protocol = 0; | ||
644 | rh->service = 0; | ||
645 | ret = GNUNET_strndup (rp, len); | ||
646 | /* If we have labels starting with underscore with label on | ||
647 | * the right (SRV/DANE/BOX case), determine port/protocol; | ||
648 | * The format of `rh->name` must be "_PORT._PROTOCOL". | ||
649 | */ | ||
650 | if (('_' == rh->name[0]) && | ||
651 | (NULL != (dot = memrchr (rh->name, | ||
652 | (int) '.', | ||
653 | rh->name_resolution_pos))) && | ||
654 | ('_' == dot[1]) && | ||
655 | (NULL == memrchr (rh->name, | ||
656 | (int) '.', | ||
657 | dot - rh->name))) | ||
658 | { | ||
659 | srv_name = GNUNET_strndup (&rh->name[1], | ||
660 | (dot - rh->name) - 1); | ||
661 | proto_name = GNUNET_strndup (&dot[2], | ||
662 | rh->name_resolution_pos - (dot - rh->name) | ||
663 | - 2); | ||
664 | rh->name_resolution_pos = 0; | ||
665 | pe = getprotobyname (proto_name); | ||
666 | if (NULL == pe) | ||
667 | { | ||
668 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
669 | _ ("Protocol `%s' unknown, skipping labels.\n"), | ||
670 | proto_name); | ||
671 | GNUNET_free (proto_name); | ||
672 | GNUNET_free (srv_name); | ||
673 | return ret; | ||
674 | } | ||
675 | se = getservbyname (srv_name, | ||
676 | proto_name); | ||
677 | if (NULL == se) | ||
678 | { | ||
679 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
680 | _ ( | ||
681 | "Service `%s' unknown for protocol `%s', trying as number.\n"), | ||
682 | srv_name, | ||
683 | proto_name); | ||
684 | if (1 != sscanf (srv_name, "%u", &rh->service)) | ||
685 | { | ||
686 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
687 | _ ("Service `%s' not a port, skipping service labels.\n"), | ||
688 | srv_name); | ||
689 | GNUNET_free (proto_name); | ||
690 | GNUNET_free (srv_name); | ||
691 | return ret; | ||
692 | } | ||
693 | } | ||
694 | else | ||
695 | { | ||
696 | rh->service = ntohs (se->s_port); | ||
697 | } | ||
698 | rh->protocol = pe->p_proto; | ||
699 | GNUNET_free (proto_name); | ||
700 | GNUNET_free (srv_name); | ||
701 | } | ||
702 | return ret; | ||
703 | } | ||
704 | |||
705 | |||
706 | /** | ||
707 | * Gives the cumulative result obtained to the callback and clean up the request. | ||
708 | * | ||
709 | * @param rh resolution process that has culminated in a result | ||
710 | */ | ||
711 | static void | ||
712 | transmit_lookup_dns_result (struct GNS_ResolverHandle *rh) | ||
713 | { | ||
714 | struct DnsResult *pos; | ||
715 | unsigned int n; | ||
716 | unsigned int i; | ||
717 | |||
718 | n = 0; | ||
719 | for (pos = rh->dns_result_head; NULL != pos; pos = pos->next) | ||
720 | n++; | ||
721 | { | ||
722 | struct GNUNET_GNSRECORD_Data rd[n]; | ||
723 | |||
724 | i = 0; | ||
725 | for (pos = rh->dns_result_head; NULL != pos; pos = pos->next) | ||
726 | { | ||
727 | rd[i].data = pos->data; | ||
728 | rd[i].data_size = pos->data_size; | ||
729 | rd[i].record_type = pos->record_type; | ||
730 | rd[i].flags = GNUNET_GNSRECORD_RF_NONE; | ||
731 | /** | ||
732 | * If this is a LEHO, we added this before. It must be a supplemental | ||
733 | * record #LSD0001 | ||
734 | */ | ||
735 | if (GNUNET_GNSRECORD_TYPE_LEHO == rd[i].record_type) | ||
736 | rd[i].flags |= GNUNET_GNSRECORD_RF_SUPPLEMENTAL; | ||
737 | if (0 == pos->expiration_time) | ||
738 | { | ||
739 | rd[i].flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; | ||
740 | rd[i].expiration_time = 0; | ||
741 | } | ||
742 | else | ||
743 | { | ||
744 | rd[i].expiration_time = pos->expiration_time; | ||
745 | } | ||
746 | i++; | ||
747 | } | ||
748 | GNUNET_assert (i == n); | ||
749 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
750 | "Transmitting standard DNS result with %u records\n", | ||
751 | n); | ||
752 | rh->proc (rh->proc_cls, | ||
753 | n, | ||
754 | rd); | ||
755 | } | ||
756 | GNS_resolver_lookup_cancel (rh); | ||
757 | } | ||
758 | |||
759 | |||
760 | /** | ||
761 | * Add a result from DNS to the records to be returned to the application. | ||
762 | * | ||
763 | * @param rh resolution request to extend with a result | ||
764 | * @param expiration_time expiration time for the answer | ||
765 | * @param record_type DNS record type of the answer | ||
766 | * @param data_size number of bytes in @a data | ||
767 | * @param data binary data to return in DNS record | ||
768 | */ | ||
769 | static void | ||
770 | add_dns_result (struct GNS_ResolverHandle *rh, | ||
771 | uint64_t expiration_time, | ||
772 | uint32_t record_type, | ||
773 | size_t data_size, | ||
774 | const void *data) | ||
775 | { | ||
776 | struct DnsResult *res; | ||
777 | |||
778 | res = GNUNET_malloc (sizeof(struct DnsResult) + data_size); | ||
779 | res->expiration_time = expiration_time; | ||
780 | res->data_size = data_size; | ||
781 | res->record_type = record_type; | ||
782 | res->data = &res[1]; | ||
783 | GNUNET_memcpy (&res[1], | ||
784 | data, | ||
785 | data_size); | ||
786 | GNUNET_CONTAINER_DLL_insert (rh->dns_result_head, | ||
787 | rh->dns_result_tail, | ||
788 | res); | ||
789 | } | ||
790 | |||
791 | |||
792 | /** | ||
793 | * We had to do a DNS lookup. Convert the result (if any) and return | ||
794 | * it. | ||
795 | * | ||
796 | * @param cls closure with the `struct GNS_ResolverHandle` | ||
797 | * @param addr one of the addresses of the host, NULL for the last address | ||
798 | * @param addrlen length of the address | ||
799 | */ | ||
800 | static void | ||
801 | handle_dns_result (void *cls, | ||
802 | const struct sockaddr *addr, | ||
803 | socklen_t addrlen) | ||
804 | { | ||
805 | struct GNS_ResolverHandle *rh = cls; | ||
806 | const struct sockaddr_in *sa4; | ||
807 | const struct sockaddr_in6 *sa6; | ||
808 | |||
809 | if (NULL == addr) | ||
810 | { | ||
811 | rh->std_resolve = NULL; | ||
812 | transmit_lookup_dns_result (rh); | ||
813 | return; | ||
814 | } | ||
815 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
816 | "Received %u bytes of DNS IP data\n", | ||
817 | addrlen); | ||
818 | switch (addr->sa_family) | ||
819 | { | ||
820 | case AF_INET: | ||
821 | sa4 = (const struct sockaddr_in *) addr; | ||
822 | add_dns_result (rh, | ||
823 | 0 /* expiration time is unknown */, | ||
824 | GNUNET_DNSPARSER_TYPE_A, | ||
825 | sizeof(struct in_addr), | ||
826 | &sa4->sin_addr); | ||
827 | break; | ||
828 | |||
829 | case AF_INET6: | ||
830 | sa6 = (const struct sockaddr_in6 *) addr; | ||
831 | add_dns_result (rh, | ||
832 | 0 /* expiration time is unknown */, | ||
833 | GNUNET_DNSPARSER_TYPE_AAAA, | ||
834 | sizeof(struct in6_addr), | ||
835 | &sa6->sin6_addr); | ||
836 | break; | ||
837 | |||
838 | default: | ||
839 | GNUNET_break (0); | ||
840 | break; | ||
841 | } | ||
842 | } | ||
843 | |||
844 | |||
845 | /** | ||
846 | * Task scheduled to continue with the resolution process. | ||
847 | * | ||
848 | * @param cls the 'struct GNS_ResolverHandle' of the resolution | ||
849 | */ | ||
850 | static void | ||
851 | recursive_resolution (void *cls); | ||
852 | |||
853 | |||
854 | /** | ||
855 | * Begin the resolution process from 'name', starting with | ||
856 | * the identification of the zone specified by 'name'. | ||
857 | * | ||
858 | * @param cls closure with `struct GNS_ResolverHandle *rh` | ||
859 | */ | ||
860 | static void | ||
861 | start_resolver_lookup (void *cls); | ||
862 | |||
863 | |||
864 | /** | ||
865 | * Function called with the result of a DNS resolution. | ||
866 | * | ||
867 | * @param cls the request handle of the resolution that | ||
868 | * we were attempting to make | ||
869 | * @param dns dns response, never NULL | ||
870 | * @param dns_len number of bytes in @a dns | ||
871 | */ | ||
872 | static void | ||
873 | dns_result_parser (void *cls, | ||
874 | const struct GNUNET_TUN_DnsHeader *dns, | ||
875 | size_t dns_len) | ||
876 | { | ||
877 | struct GNS_ResolverHandle *rh = cls; | ||
878 | struct GNUNET_DNSPARSER_Packet *p; | ||
879 | const struct GNUNET_DNSPARSER_Record *rec; | ||
880 | unsigned int rd_count; | ||
881 | |||
882 | if (NULL == dns) | ||
883 | { | ||
884 | rh->dns_request = NULL; | ||
885 | GNUNET_SCHEDULER_cancel (rh->task_id); | ||
886 | rh->task_id = NULL; | ||
887 | fail_resolution (rh); | ||
888 | return; | ||
889 | } | ||
890 | if (rh->original_dns_id != dns->id) | ||
891 | { | ||
892 | /* DNS answer, but for another query */ | ||
893 | return; | ||
894 | } | ||
895 | p = GNUNET_DNSPARSER_parse ((const char *) dns, | ||
896 | dns_len); | ||
897 | if (NULL == p) | ||
898 | { | ||
899 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
900 | _ ("Failed to parse DNS response\n")); | ||
901 | return; | ||
902 | } | ||
903 | |||
904 | /* We got a result from DNS */ | ||
905 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
906 | "Received DNS response for `%s' with %u answers\n", | ||
907 | rh->ac_tail->label, | ||
908 | (unsigned int) p->num_answers); | ||
909 | if ((p->num_answers > 0) && | ||
910 | (GNUNET_DNSPARSER_TYPE_CNAME == p->answers[0].type) && | ||
911 | (GNUNET_DNSPARSER_TYPE_CNAME != rh->record_type)) | ||
912 | { | ||
913 | int af; | ||
914 | |||
915 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
916 | "Got CNAME `%s' from DNS for `%s'\n", | ||
917 | p->answers[0].data.hostname, | ||
918 | rh->name); | ||
919 | if (NULL != rh->std_resolve) | ||
920 | { | ||
921 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
922 | "Multiple CNAME results from DNS resolving `%s'! Not really allowed...\n", | ||
923 | rh->name); | ||
924 | GNUNET_RESOLVER_request_cancel (rh->std_resolve); | ||
925 | } | ||
926 | GNUNET_free (rh->name); | ||
927 | rh->name = GNUNET_strdup (p->answers[0].data.hostname); | ||
928 | rh->name_resolution_pos = strlen (rh->name); | ||
929 | switch (rh->record_type) | ||
930 | { | ||
931 | case GNUNET_DNSPARSER_TYPE_A: | ||
932 | af = AF_INET; | ||
933 | break; | ||
934 | |||
935 | case GNUNET_DNSPARSER_TYPE_AAAA: | ||
936 | af = AF_INET6; | ||
937 | break; | ||
938 | |||
939 | default: | ||
940 | af = AF_UNSPEC; | ||
941 | break; | ||
942 | } | ||
943 | if (NULL != rh->leho) | ||
944 | add_dns_result (rh, | ||
945 | GNUNET_TIME_UNIT_HOURS.rel_value_us, | ||
946 | GNUNET_GNSRECORD_TYPE_LEHO, | ||
947 | strlen (rh->leho), | ||
948 | rh->leho); | ||
949 | rh->std_resolve = GNUNET_RESOLVER_ip_get (rh->name, | ||
950 | af, | ||
951 | DNS_LOOKUP_TIMEOUT, | ||
952 | &handle_dns_result, | ||
953 | rh); | ||
954 | GNUNET_DNSPARSER_free_packet (p); | ||
955 | GNUNET_DNSSTUB_resolve_cancel (rh->dns_request); | ||
956 | rh->dns_request = NULL; | ||
957 | return; | ||
958 | } | ||
959 | |||
960 | /* convert from (parsed) DNS to (binary) GNS format! */ | ||
961 | rd_count = p->num_answers + p->num_authority_records | ||
962 | + p->num_additional_records; | ||
963 | { | ||
964 | struct GNUNET_GNSRECORD_Data rd[rd_count + 1]; /* +1 for LEHO */ | ||
965 | int skip; | ||
966 | char buf[UINT16_MAX]; | ||
967 | size_t buf_off; | ||
968 | size_t buf_start; | ||
969 | |||
970 | buf_off = 0; | ||
971 | skip = 0; | ||
972 | memset (rd, | ||
973 | 0, | ||
974 | sizeof(rd)); | ||
975 | for (unsigned int i = 0; i < rd_count; i++) | ||
976 | { | ||
977 | if (i < p->num_answers) | ||
978 | rec = &p->answers[i]; | ||
979 | else if (i < p->num_answers + p->num_authority_records) | ||
980 | rec = &p->authority_records[i - p->num_answers]; | ||
981 | else | ||
982 | rec = &p->additional_records[i - p->num_answers | ||
983 | - p->num_authority_records]; | ||
984 | /* As we copied the full DNS name to 'rh->ac_tail->label', this | ||
985 | should be the correct check to see if this record is actually | ||
986 | a record for our label... */ | ||
987 | if (0 != strcmp (rec->name, | ||
988 | rh->ac_tail->label)) | ||
989 | { | ||
990 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
991 | "Dropping record `%s', does not match desired name `%s'\n", | ||
992 | rec->name, | ||
993 | rh->ac_tail->label); | ||
994 | skip++; | ||
995 | continue; | ||
996 | } | ||
997 | rd[i - skip].record_type = rec->type; | ||
998 | rd[i - skip].expiration_time = rec->expiration_time.abs_value_us; | ||
999 | switch (rec->type) | ||
1000 | { | ||
1001 | case GNUNET_DNSPARSER_TYPE_A: | ||
1002 | if (rec->data.raw.data_len != sizeof(struct in_addr)) | ||
1003 | { | ||
1004 | GNUNET_break_op (0); | ||
1005 | skip++; | ||
1006 | continue; | ||
1007 | } | ||
1008 | rd[i - skip].data_size = rec->data.raw.data_len; | ||
1009 | rd[i - skip].data = rec->data.raw.data; | ||
1010 | break; | ||
1011 | |||
1012 | case GNUNET_DNSPARSER_TYPE_AAAA: | ||
1013 | if (rec->data.raw.data_len != sizeof(struct in6_addr)) | ||
1014 | { | ||
1015 | GNUNET_break_op (0); | ||
1016 | skip++; | ||
1017 | continue; | ||
1018 | } | ||
1019 | rd[i - skip].data_size = rec->data.raw.data_len; | ||
1020 | rd[i - skip].data = rec->data.raw.data; | ||
1021 | break; | ||
1022 | |||
1023 | case GNUNET_DNSPARSER_TYPE_CNAME: | ||
1024 | case GNUNET_DNSPARSER_TYPE_PTR: | ||
1025 | case GNUNET_DNSPARSER_TYPE_NS: | ||
1026 | buf_start = buf_off; | ||
1027 | if (GNUNET_OK != | ||
1028 | GNUNET_DNSPARSER_builder_add_name (buf, | ||
1029 | sizeof(buf), | ||
1030 | &buf_off, | ||
1031 | rec->data.hostname)) | ||
1032 | { | ||
1033 | GNUNET_break (0); | ||
1034 | skip++; | ||
1035 | continue; | ||
1036 | } | ||
1037 | rd[i - skip].data_size = buf_off - buf_start; | ||
1038 | rd[i - skip].data = &buf[buf_start]; | ||
1039 | break; | ||
1040 | |||
1041 | case GNUNET_DNSPARSER_TYPE_SOA: | ||
1042 | buf_start = buf_off; | ||
1043 | if (GNUNET_OK != | ||
1044 | GNUNET_DNSPARSER_builder_add_soa (buf, | ||
1045 | sizeof(buf), | ||
1046 | &buf_off, | ||
1047 | rec->data.soa)) | ||
1048 | { | ||
1049 | GNUNET_break (0); | ||
1050 | skip++; | ||
1051 | continue; | ||
1052 | } | ||
1053 | rd[i - skip].data_size = buf_off - buf_start; | ||
1054 | rd[i - skip].data = &buf[buf_start]; | ||
1055 | break; | ||
1056 | |||
1057 | case GNUNET_DNSPARSER_TYPE_MX: | ||
1058 | buf_start = buf_off; | ||
1059 | if (GNUNET_OK != | ||
1060 | GNUNET_DNSPARSER_builder_add_mx (buf, | ||
1061 | sizeof(buf), | ||
1062 | &buf_off, | ||
1063 | rec->data.mx)) | ||
1064 | { | ||
1065 | GNUNET_break (0); | ||
1066 | skip++; | ||
1067 | continue; | ||
1068 | } | ||
1069 | rd[i - skip].data_size = buf_off - buf_start; | ||
1070 | rd[i - skip].data = &buf[buf_start]; | ||
1071 | break; | ||
1072 | |||
1073 | case GNUNET_DNSPARSER_TYPE_SRV: | ||
1074 | buf_start = buf_off; | ||
1075 | if (GNUNET_OK != | ||
1076 | GNUNET_DNSPARSER_builder_add_srv (buf, | ||
1077 | sizeof(buf), | ||
1078 | &buf_off, | ||
1079 | rec->data.srv)) | ||
1080 | { | ||
1081 | GNUNET_break (0); | ||
1082 | skip++; | ||
1083 | continue; | ||
1084 | } | ||
1085 | rd[i - skip].data_size = buf_off - buf_start; | ||
1086 | rd[i - skip].data = &buf[buf_start]; | ||
1087 | break; | ||
1088 | |||
1089 | default: | ||
1090 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1091 | _ ("Skipping record of unsupported type %d\n"), | ||
1092 | rec->type); | ||
1093 | skip++; | ||
1094 | continue; | ||
1095 | } | ||
1096 | } /* end of for all records in answer */ | ||
1097 | if (NULL != rh->leho) | ||
1098 | { | ||
1099 | rd[rd_count - skip].record_type = GNUNET_GNSRECORD_TYPE_LEHO; | ||
1100 | rd[rd_count - skip].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; | ||
1101 | rd[rd_count - skip].flags |= GNUNET_GNSRECORD_RF_SUPPLEMENTAL; | ||
1102 | rd[rd_count - skip].expiration_time = GNUNET_TIME_UNIT_HOURS.rel_value_us; | ||
1103 | rd[rd_count - skip].data = rh->leho; | ||
1104 | rd[rd_count - skip].data_size = strlen (rh->leho); | ||
1105 | skip--; /* skip one LESS */ | ||
1106 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1107 | "Adding LEHO %s\n", | ||
1108 | rh->leho); | ||
1109 | } | ||
1110 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1111 | "Returning DNS response for `%s' with %u answers\n", | ||
1112 | rh->ac_tail->label, | ||
1113 | (unsigned int) (rd_count - skip)); | ||
1114 | rh->proc (rh->proc_cls, | ||
1115 | rd_count - skip, | ||
1116 | rd); | ||
1117 | GNUNET_DNSSTUB_resolve_cancel (rh->dns_request); | ||
1118 | rh->dns_request = NULL; | ||
1119 | } | ||
1120 | GNUNET_DNSPARSER_free_packet (p); | ||
1121 | if (NULL != rh->task_id) | ||
1122 | GNUNET_SCHEDULER_cancel (rh->task_id); /* should be timeout task */ | ||
1123 | rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, | ||
1124 | rh); | ||
1125 | } | ||
1126 | |||
1127 | |||
1128 | /** | ||
1129 | * Perform recursive DNS resolution. Asks the given DNS resolver to | ||
1130 | * resolve "rh->dns_name", possibly recursively proceeding following | ||
1131 | * NS delegations, CNAMES, etc., until 'rh->loop_limiter' bounds us or | ||
1132 | * we find the answer. | ||
1133 | * | ||
1134 | * @param rh resolution information | ||
1135 | */ | ||
1136 | static void | ||
1137 | recursive_dns_resolution (struct GNS_ResolverHandle *rh) | ||
1138 | { | ||
1139 | struct AuthorityChain *ac; | ||
1140 | struct GNUNET_DNSPARSER_Query *query; | ||
1141 | struct GNUNET_DNSPARSER_Packet *p; | ||
1142 | char *dns_request; | ||
1143 | size_t dns_request_length; | ||
1144 | int ret; | ||
1145 | |||
1146 | ac = rh->ac_tail; | ||
1147 | GNUNET_assert (NULL != ac); | ||
1148 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1149 | "Starting DNS lookup for `%s'\n", | ||
1150 | ac->label); | ||
1151 | GNUNET_assert (GNUNET_NO == ac->gns_authority); | ||
1152 | query = GNUNET_new (struct GNUNET_DNSPARSER_Query); | ||
1153 | query->name = GNUNET_strdup (ac->label); | ||
1154 | query->type = rh->record_type; | ||
1155 | query->dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET; | ||
1156 | p = GNUNET_new (struct GNUNET_DNSPARSER_Packet); | ||
1157 | p->queries = query; | ||
1158 | p->num_queries = 1; | ||
1159 | p->id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, | ||
1160 | UINT16_MAX); | ||
1161 | p->flags.opcode = GNUNET_TUN_DNS_OPCODE_QUERY; | ||
1162 | p->flags.recursion_desired = 1; | ||
1163 | ret = GNUNET_DNSPARSER_pack (p, | ||
1164 | 1024, | ||
1165 | &dns_request, | ||
1166 | &dns_request_length); | ||
1167 | if (GNUNET_OK != ret) | ||
1168 | { | ||
1169 | GNUNET_break (0); | ||
1170 | rh->proc (rh->proc_cls, | ||
1171 | 0, | ||
1172 | NULL); | ||
1173 | GNUNET_assert (NULL == rh->task_id); | ||
1174 | rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, | ||
1175 | rh); | ||
1176 | } | ||
1177 | else | ||
1178 | { | ||
1179 | rh->original_dns_id = p->id; | ||
1180 | GNUNET_assert (NULL != ac->authority_info.dns_authority.dns_handle); | ||
1181 | GNUNET_assert (NULL == rh->dns_request); | ||
1182 | rh->leho = GNUNET_strdup (ac->label); | ||
1183 | rh->dns_request = GNUNET_DNSSTUB_resolve ( | ||
1184 | ac->authority_info.dns_authority.dns_handle, | ||
1185 | dns_request, | ||
1186 | dns_request_length, | ||
1187 | &dns_result_parser, | ||
1188 | rh); | ||
1189 | rh->task_id = GNUNET_SCHEDULER_add_delayed (DNS_LOOKUP_TIMEOUT, | ||
1190 | &timeout_resolution, | ||
1191 | rh); | ||
1192 | } | ||
1193 | if (GNUNET_SYSERR != ret) | ||
1194 | GNUNET_free (dns_request); | ||
1195 | GNUNET_DNSPARSER_free_packet (p); | ||
1196 | } | ||
1197 | |||
1198 | |||
1199 | /** | ||
1200 | * We encountered a REDIRECT record during our resolution. | ||
1201 | * Merge it into our chain. | ||
1202 | * | ||
1203 | * @param rh resolution we are performing | ||
1204 | * @param rname value of the redirect record we got for the current | ||
1205 | * authority chain tail | ||
1206 | */ | ||
1207 | static void | ||
1208 | handle_gns_redirect_result (struct GNS_ResolverHandle *rh, | ||
1209 | const char *rname) | ||
1210 | { | ||
1211 | size_t nlen; | ||
1212 | char *res; | ||
1213 | const char *tld; | ||
1214 | struct AuthorityChain *ac; | ||
1215 | int af; | ||
1216 | struct GNUNET_CRYPTO_PublicKey zone; | ||
1217 | |||
1218 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1219 | "Handling GNS REDIRECT result `%s'\n", | ||
1220 | rname); | ||
1221 | nlen = strlen (rname); | ||
1222 | tld = GNS_get_tld (rname); | ||
1223 | if (0 == strcmp ("+", tld)) | ||
1224 | { | ||
1225 | /* REDIRECT resolution continues relative to current domain */ | ||
1226 | if (0 == rh->name_resolution_pos) | ||
1227 | { | ||
1228 | res = GNUNET_strndup (rname, nlen - 2); | ||
1229 | rh->name_resolution_pos = nlen - 2; | ||
1230 | } | ||
1231 | else | ||
1232 | { | ||
1233 | GNUNET_asprintf (&res, | ||
1234 | "%.*s.%.*s", | ||
1235 | (int) rh->name_resolution_pos, | ||
1236 | rh->name, | ||
1237 | (int) (nlen - 2), | ||
1238 | rname); | ||
1239 | rh->name_resolution_pos = strlen (res); | ||
1240 | } | ||
1241 | GNUNET_free (rh->name); | ||
1242 | rh->name = res; | ||
1243 | ac = GNUNET_new (struct AuthorityChain); | ||
1244 | ac->rh = rh; | ||
1245 | ac->gns_authority = GNUNET_YES; | ||
1246 | ac->authority_info.gns_authority = | ||
1247 | rh->ac_tail->authority_info.gns_authority; | ||
1248 | ac->label = resolver_lookup_get_next_label (rh); | ||
1249 | /* add AC to tail */ | ||
1250 | GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, | ||
1251 | rh->ac_tail, | ||
1252 | ac); | ||
1253 | rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, | ||
1254 | rh); | ||
1255 | return; | ||
1256 | } | ||
1257 | if (GNUNET_OK == GNUNET_GNSRECORD_zkey_to_pkey (tld, &zone)) | ||
1258 | { | ||
1259 | /* REDIRECT resolution continues relative to current domain */ | ||
1260 | if (0 == rh->name_resolution_pos) | ||
1261 | { | ||
1262 | GNUNET_asprintf (&res, | ||
1263 | "%.*s", | ||
1264 | (int) (strlen (rname) - (strlen (tld) + 1)), | ||
1265 | rname); | ||
1266 | } | ||
1267 | else | ||
1268 | { | ||
1269 | GNUNET_asprintf (&res, | ||
1270 | "%.*s.%.*s", | ||
1271 | (int) rh->name_resolution_pos, | ||
1272 | rh->name, | ||
1273 | (int) (strlen (rname) - (strlen (tld) + 1)), | ||
1274 | rname); | ||
1275 | } | ||
1276 | rh->name_resolution_pos = strlen (res); | ||
1277 | GNUNET_free (rh->name); | ||
1278 | rh->name = res; | ||
1279 | ac = GNUNET_new (struct AuthorityChain); | ||
1280 | ac->rh = rh; | ||
1281 | ac->gns_authority = GNUNET_YES; | ||
1282 | ac->authority_info.gns_authority = zone; | ||
1283 | ac->label = resolver_lookup_get_next_label (rh); | ||
1284 | /* add AC to tail */ | ||
1285 | GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, | ||
1286 | rh->ac_tail, | ||
1287 | ac); | ||
1288 | rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, | ||
1289 | rh); | ||
1290 | return; | ||
1291 | } | ||
1292 | |||
1293 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1294 | "Got REDIRECT `%s' from GNS for `%s'\n", | ||
1295 | rname, | ||
1296 | rh->name); | ||
1297 | if (NULL != rh->std_resolve) | ||
1298 | { | ||
1299 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1300 | "Multiple REDIRECT results from GNS resolving `%s'! Not really allowed...\n", | ||
1301 | rh->name); | ||
1302 | GNUNET_RESOLVER_request_cancel (rh->std_resolve); | ||
1303 | } | ||
1304 | /* name is absolute, go to DNS */ | ||
1305 | GNUNET_free (rh->name); | ||
1306 | rh->name = GNUNET_strdup (rname); | ||
1307 | rh->name_resolution_pos = strlen (rh->name); | ||
1308 | switch (rh->record_type) | ||
1309 | { | ||
1310 | case GNUNET_DNSPARSER_TYPE_A: | ||
1311 | af = AF_INET; | ||
1312 | break; | ||
1313 | |||
1314 | case GNUNET_DNSPARSER_TYPE_AAAA: | ||
1315 | af = AF_INET6; | ||
1316 | break; | ||
1317 | |||
1318 | default: | ||
1319 | af = AF_UNSPEC; | ||
1320 | break; | ||
1321 | } | ||
1322 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1323 | "Doing standard DNS lookup for `%s'\n", | ||
1324 | rh->name); | ||
1325 | |||
1326 | rh->std_resolve = GNUNET_RESOLVER_ip_get (rh->name, | ||
1327 | af, | ||
1328 | DNS_LOOKUP_TIMEOUT, | ||
1329 | &handle_dns_result, | ||
1330 | rh); | ||
1331 | } | ||
1332 | |||
1333 | |||
1334 | /** | ||
1335 | * We encountered a CNAME record during our resolution. | ||
1336 | * Merge it into our chain. | ||
1337 | * | ||
1338 | * @param rh resolution we are performing | ||
1339 | * @param cname value of the cname record we got for the current | ||
1340 | * authority chain tail | ||
1341 | */ | ||
1342 | static void | ||
1343 | handle_gns_cname_result (struct GNS_ResolverHandle *rh, | ||
1344 | const char *cname) | ||
1345 | { | ||
1346 | int af; | ||
1347 | |||
1348 | GNUNET_free (rh->name); | ||
1349 | rh->name = GNUNET_strdup (cname); | ||
1350 | rh->name_resolution_pos = strlen (rh->name); | ||
1351 | switch (rh->record_type) | ||
1352 | { | ||
1353 | case GNUNET_DNSPARSER_TYPE_A: | ||
1354 | af = AF_INET; | ||
1355 | break; | ||
1356 | |||
1357 | case GNUNET_DNSPARSER_TYPE_AAAA: | ||
1358 | af = AF_INET6; | ||
1359 | break; | ||
1360 | |||
1361 | default: | ||
1362 | af = AF_UNSPEC; | ||
1363 | break; | ||
1364 | } | ||
1365 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1366 | "Doing standard DNS lookup for `%s'\n", | ||
1367 | rh->name); | ||
1368 | |||
1369 | rh->std_resolve = GNUNET_RESOLVER_ip_get (rh->name, | ||
1370 | af, | ||
1371 | DNS_LOOKUP_TIMEOUT, | ||
1372 | &handle_dns_result, | ||
1373 | rh); | ||
1374 | } | ||
1375 | |||
1376 | |||
1377 | /** | ||
1378 | * Process records that were decrypted from a block. | ||
1379 | * | ||
1380 | * @param cls closure with the 'struct GNS_ResolverHandle' | ||
1381 | * @param rd_count number of entries in @a rd array | ||
1382 | * @param rd array of records with data to store | ||
1383 | */ | ||
1384 | static void | ||
1385 | handle_gns_resolution_result (void *cls, | ||
1386 | unsigned int rd_count, | ||
1387 | const struct GNUNET_GNSRECORD_Data *rd); | ||
1388 | |||
1389 | |||
1390 | /** | ||
1391 | * We have resolved one or more of the nameservers for a | ||
1392 | * GNS2DNS lookup. Once we have some of them, begin using | ||
1393 | * the DNSSTUB resolver. | ||
1394 | * | ||
1395 | * @param ac context for GNS2DNS resolution | ||
1396 | */ | ||
1397 | static void | ||
1398 | continue_with_gns2dns (struct AuthorityChain *ac) | ||
1399 | { | ||
1400 | struct GNS_ResolverHandle *rh = ac->rh; | ||
1401 | |||
1402 | if ((NULL != ac->authority_info.dns_authority.gp_head) && | ||
1403 | (GNUNET_NO == ac->authority_info.dns_authority.found)) | ||
1404 | return; /* more pending and none found yet */ | ||
1405 | if (GNUNET_NO == ac->authority_info.dns_authority.found) | ||
1406 | { | ||
1407 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1408 | "Failed to resolve DNS server for `%s' in GNS2DNS resolution\n", | ||
1409 | ac->authority_info.dns_authority.name); | ||
1410 | fail_resolution (rh); | ||
1411 | return; | ||
1412 | } | ||
1413 | if (GNUNET_NO != ac->authority_info.dns_authority.launched) | ||
1414 | return; /* already running, do not launch again! */ | ||
1415 | /* recurse */ | ||
1416 | ac->authority_info.dns_authority.launched = GNUNET_YES; | ||
1417 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1418 | "Will continue resolution using DNS to resolve `%s'\n", | ||
1419 | ac->label); | ||
1420 | GNUNET_assert (NULL == rh->task_id); | ||
1421 | rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, | ||
1422 | rh); | ||
1423 | } | ||
1424 | |||
1425 | |||
1426 | /** | ||
1427 | * We've resolved the IP address for the DNS resolver to use | ||
1428 | * after encountering a GNS2DNS record. | ||
1429 | * | ||
1430 | * @param cls the `struct Gns2DnsPending` used for this request | ||
1431 | * @param rd_count number of records in @a rd | ||
1432 | * @param rd addresses for the DNS resolver (presumably) | ||
1433 | */ | ||
1434 | static void | ||
1435 | handle_gns2dns_result (void *cls, | ||
1436 | unsigned int rd_count, | ||
1437 | const struct GNUNET_GNSRECORD_Data *rd) | ||
1438 | { | ||
1439 | struct Gns2DnsPending *gp = cls; | ||
1440 | struct AuthorityChain *ac = gp->ac; | ||
1441 | |||
1442 | GNUNET_CONTAINER_DLL_remove (ac->authority_info.dns_authority.gp_head, | ||
1443 | ac->authority_info.dns_authority.gp_tail, | ||
1444 | gp); | ||
1445 | /* enable cleanup of 'rh' handle that automatically comes after we return, | ||
1446 | and which expects 'rh' to be in the #rlh_head DLL. */ | ||
1447 | if (NULL != gp->rh) | ||
1448 | { | ||
1449 | GNUNET_CONTAINER_DLL_insert (rlh_head, | ||
1450 | rlh_tail, | ||
1451 | gp->rh); | ||
1452 | gp->rh = NULL; | ||
1453 | } | ||
1454 | GNUNET_free (gp); | ||
1455 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1456 | "Received %u results for IP address of DNS server for GNS2DNS transition\n", | ||
1457 | rd_count); | ||
1458 | /* find suitable A/AAAA record */ | ||
1459 | for (unsigned int j = 0; j < rd_count; j++) | ||
1460 | { | ||
1461 | switch (rd[j].record_type) | ||
1462 | { | ||
1463 | case GNUNET_DNSPARSER_TYPE_A: | ||
1464 | { | ||
1465 | struct sockaddr_in v4; | ||
1466 | |||
1467 | if (sizeof(struct in_addr) != rd[j].data_size) | ||
1468 | { | ||
1469 | GNUNET_break_op (0); | ||
1470 | continue; | ||
1471 | } | ||
1472 | memset (&v4, | ||
1473 | 0, | ||
1474 | sizeof(v4)); | ||
1475 | v4.sin_family = AF_INET; | ||
1476 | v4.sin_port = htons (53); | ||
1477 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
1478 | v4.sin_len = (u_char) sizeof(v4); | ||
1479 | #endif | ||
1480 | GNUNET_memcpy (&v4.sin_addr, | ||
1481 | rd[j].data, | ||
1482 | sizeof(struct in_addr)); | ||
1483 | if (GNUNET_OK == | ||
1484 | GNUNET_DNSSTUB_add_dns_sa ( | ||
1485 | ac->authority_info.dns_authority.dns_handle, | ||
1486 | (const struct sockaddr *) &v4)) | ||
1487 | ac->authority_info.dns_authority.found = GNUNET_YES; | ||
1488 | break; | ||
1489 | } | ||
1490 | |||
1491 | case GNUNET_DNSPARSER_TYPE_AAAA: | ||
1492 | { | ||
1493 | struct sockaddr_in6 v6; | ||
1494 | |||
1495 | if (sizeof(struct in6_addr) != rd[j].data_size) | ||
1496 | { | ||
1497 | GNUNET_break_op (0); | ||
1498 | continue; | ||
1499 | } | ||
1500 | /* FIXME: might want to check if we support IPv6 here, | ||
1501 | and otherwise skip this one and hope we find another */ | ||
1502 | memset (&v6, | ||
1503 | 0, | ||
1504 | sizeof(v6)); | ||
1505 | v6.sin6_family = AF_INET6; | ||
1506 | v6.sin6_port = htons (53); | ||
1507 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
1508 | v6.sin6_len = (u_char) sizeof(v6); | ||
1509 | #endif | ||
1510 | GNUNET_memcpy (&v6.sin6_addr, | ||
1511 | rd[j].data, | ||
1512 | sizeof(struct in6_addr)); | ||
1513 | if (GNUNET_OK == | ||
1514 | GNUNET_DNSSTUB_add_dns_sa ( | ||
1515 | ac->authority_info.dns_authority.dns_handle, | ||
1516 | (const struct sockaddr *) &v6)) | ||
1517 | ac->authority_info.dns_authority.found = GNUNET_YES; | ||
1518 | break; | ||
1519 | } | ||
1520 | |||
1521 | default: | ||
1522 | break; | ||
1523 | } | ||
1524 | } | ||
1525 | continue_with_gns2dns (ac); | ||
1526 | } | ||
1527 | |||
1528 | |||
1529 | /** | ||
1530 | * Function called by the resolver for each address obtained from DNS. | ||
1531 | * | ||
1532 | * @param cls closure, a `struct Gns2DnsPending *` | ||
1533 | * @param addr one of the addresses of the host, NULL for the last address | ||
1534 | * @param addrlen length of @a addr | ||
1535 | */ | ||
1536 | static void | ||
1537 | handle_gns2dns_ip (void *cls, | ||
1538 | const struct sockaddr *addr, | ||
1539 | socklen_t addrlen) | ||
1540 | { | ||
1541 | struct Gns2DnsPending *gp = cls; | ||
1542 | struct AuthorityChain *ac = gp->ac; | ||
1543 | struct sockaddr_storage ss; | ||
1544 | struct sockaddr_in *v4; | ||
1545 | struct sockaddr_in6 *v6; | ||
1546 | |||
1547 | if (NULL == addr) | ||
1548 | { | ||
1549 | /* DNS resolution finished */ | ||
1550 | if (0 == gp->num_results) | ||
1551 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1552 | "Failed to use DNS to resolve name of DNS resolver\n"); | ||
1553 | GNUNET_CONTAINER_DLL_remove (ac->authority_info.dns_authority.gp_head, | ||
1554 | ac->authority_info.dns_authority.gp_tail, | ||
1555 | gp); | ||
1556 | GNUNET_free (gp); | ||
1557 | continue_with_gns2dns (ac); | ||
1558 | return; | ||
1559 | } | ||
1560 | GNUNET_memcpy (&ss, | ||
1561 | addr, | ||
1562 | addrlen); | ||
1563 | switch (ss.ss_family) | ||
1564 | { | ||
1565 | case AF_INET: | ||
1566 | v4 = (struct sockaddr_in *) &ss; | ||
1567 | v4->sin_port = htons (53); | ||
1568 | gp->num_results++; | ||
1569 | break; | ||
1570 | |||
1571 | case AF_INET6: | ||
1572 | v6 = (struct sockaddr_in6 *) &ss; | ||
1573 | v6->sin6_port = htons (53); | ||
1574 | gp->num_results++; | ||
1575 | break; | ||
1576 | |||
1577 | default: | ||
1578 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1579 | "Unsupported AF %d\n", | ||
1580 | ss.ss_family); | ||
1581 | return; | ||
1582 | } | ||
1583 | if (GNUNET_OK == | ||
1584 | GNUNET_DNSSTUB_add_dns_sa (ac->authority_info.dns_authority.dns_handle, | ||
1585 | (struct sockaddr *) &ss)) | ||
1586 | ac->authority_info.dns_authority.found = GNUNET_YES; | ||
1587 | } | ||
1588 | |||
1589 | |||
1590 | /** | ||
1591 | * We found a REDIRECT record, perform recursive resolution on it. | ||
1592 | * | ||
1593 | * @param rh resolution handle | ||
1594 | * @param rd record with CNAME to resolve recursively | ||
1595 | */ | ||
1596 | static void | ||
1597 | recursive_redirect_resolution (struct GNS_ResolverHandle *rh, | ||
1598 | const struct GNUNET_GNSRECORD_Data *rd) | ||
1599 | { | ||
1600 | handle_gns_redirect_result (rh, | ||
1601 | rd->data); | ||
1602 | } | ||
1603 | |||
1604 | |||
1605 | /** | ||
1606 | * We found a CNAME record, perform recursive resolution on it. | ||
1607 | * | ||
1608 | * @param rh resolution handle | ||
1609 | * @param rd record with CNAME to resolve recursively | ||
1610 | */ | ||
1611 | static void | ||
1612 | recursive_cname_resolution (struct GNS_ResolverHandle *rh, | ||
1613 | const struct GNUNET_GNSRECORD_Data *rd) | ||
1614 | { | ||
1615 | char *cname; | ||
1616 | size_t off; | ||
1617 | |||
1618 | off = 0; | ||
1619 | cname = GNUNET_DNSPARSER_parse_name (rd->data, | ||
1620 | rd->data_size, | ||
1621 | &off); | ||
1622 | if ((NULL == cname) || | ||
1623 | (off != rd->data_size)) | ||
1624 | { | ||
1625 | GNUNET_break_op (0); /* record not well-formed */ | ||
1626 | GNUNET_free (cname); | ||
1627 | fail_resolution (rh); | ||
1628 | return; | ||
1629 | } | ||
1630 | handle_gns_cname_result (rh, | ||
1631 | cname); | ||
1632 | GNUNET_free (cname); | ||
1633 | } | ||
1634 | |||
1635 | |||
1636 | /** | ||
1637 | * We found a PKEY record, perform recursive resolution on it. | ||
1638 | * | ||
1639 | * @param rh resolution handle | ||
1640 | * @param rd record with PKEY to resolve recursively | ||
1641 | */ | ||
1642 | static void | ||
1643 | recursive_pkey_resolution (struct GNS_ResolverHandle *rh, | ||
1644 | const struct GNUNET_GNSRECORD_Data *rd) | ||
1645 | { | ||
1646 | struct AuthorityChain *ac; | ||
1647 | struct GNUNET_CRYPTO_PublicKey auth; | ||
1648 | |||
1649 | /* delegation to another zone */ | ||
1650 | if (GNUNET_OK != GNUNET_GNSRECORD_identity_from_data (rd->data, | ||
1651 | rd->data_size, | ||
1652 | rd->record_type, | ||
1653 | &auth)) | ||
1654 | { | ||
1655 | GNUNET_break_op (0); | ||
1656 | fail_resolution (rh); | ||
1657 | return; | ||
1658 | } | ||
1659 | /* expand authority chain */ | ||
1660 | ac = GNUNET_new (struct AuthorityChain); | ||
1661 | ac->rh = rh; | ||
1662 | ac->gns_authority = GNUNET_YES; | ||
1663 | ac->authority_info.gns_authority = auth; | ||
1664 | ac->label = resolver_lookup_get_next_label (rh); | ||
1665 | /* add AC to tail */ | ||
1666 | GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, | ||
1667 | rh->ac_tail, | ||
1668 | ac); | ||
1669 | /* recurse */ | ||
1670 | rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, | ||
1671 | rh); | ||
1672 | } | ||
1673 | |||
1674 | |||
1675 | /** | ||
1676 | * We found one or more GNS2DNS records, perform recursive resolution on it. | ||
1677 | * (to be precise, one or more records in @a rd is GNS2DNS, there may be others, | ||
1678 | * so this function still needs to check which ones are GNS2DNS). | ||
1679 | * | ||
1680 | * @param rh resolution handle | ||
1681 | * @param rd_count length of the @a rd array | ||
1682 | * @param rd record with PKEY to resolve recursively | ||
1683 | * @return #GNUNET_OK if this worked, #GNUNET_SYSERR if no GNS2DNS records were in @a rd | ||
1684 | */ | ||
1685 | static int | ||
1686 | recursive_gns2dns_resolution (struct GNS_ResolverHandle *rh, | ||
1687 | unsigned int rd_count, | ||
1688 | const struct GNUNET_GNSRECORD_Data *rd) | ||
1689 | { | ||
1690 | struct AuthorityChain *ac; | ||
1691 | const char *tld; | ||
1692 | char *ns; | ||
1693 | |||
1694 | ns = NULL; | ||
1695 | /* expand authority chain */ | ||
1696 | ac = GNUNET_new (struct AuthorityChain); | ||
1697 | ac->rh = rh; | ||
1698 | ac->authority_info.dns_authority.dns_handle = GNUNET_DNSSTUB_start (4); | ||
1699 | |||
1700 | for (unsigned int i = 0; i < rd_count; i++) | ||
1701 | { | ||
1702 | char *ip; | ||
1703 | char *n; | ||
1704 | size_t off; | ||
1705 | struct Gns2DnsPending *gp; | ||
1706 | struct GNUNET_CRYPTO_PublicKey zone; | ||
1707 | struct sockaddr_in v4; | ||
1708 | struct sockaddr_in6 v6; | ||
1709 | |||
1710 | if (GNUNET_GNSRECORD_TYPE_GNS2DNS != rd[i].record_type) | ||
1711 | { | ||
1712 | /** | ||
1713 | * Records other than GNS2DNS not allowed | ||
1714 | */ | ||
1715 | GNUNET_free (ns); | ||
1716 | GNUNET_free (ac); | ||
1717 | return GNUNET_SYSERR; | ||
1718 | } | ||
1719 | off = 0; | ||
1720 | n = GNUNET_DNSPARSER_parse_name (rd[i].data, | ||
1721 | rd[i].data_size, | ||
1722 | &off); | ||
1723 | ip = GNUNET_strdup (&((const char *) rd[i].data)[off]); | ||
1724 | if ((NULL == n) || | ||
1725 | (NULL == ip)) | ||
1726 | { | ||
1727 | GNUNET_break_op (0); | ||
1728 | GNUNET_free (n); | ||
1729 | GNUNET_free (ip); | ||
1730 | continue; | ||
1731 | } | ||
1732 | |||
1733 | off += strlen (ip) + 1; | ||
1734 | |||
1735 | if (off != rd[i].data_size) | ||
1736 | { | ||
1737 | GNUNET_break_op (0); | ||
1738 | GNUNET_free (n); | ||
1739 | GNUNET_free (ip); | ||
1740 | continue; | ||
1741 | } | ||
1742 | /* resolve 'ip' to determine the IP(s) of the DNS | ||
1743 | resolver to use for lookup of 'ns' */ | ||
1744 | if (NULL != ns) | ||
1745 | { | ||
1746 | if (0 != strcasecmp (ns, | ||
1747 | n)) | ||
1748 | { | ||
1749 | /* NS values must all be the same for all GNS2DNS records, | ||
1750 | anything else leads to insanity */ | ||
1751 | GNUNET_break_op (0); | ||
1752 | GNUNET_free (n); | ||
1753 | GNUNET_free (ip); | ||
1754 | continue; | ||
1755 | } | ||
1756 | GNUNET_free (n); | ||
1757 | } | ||
1758 | else | ||
1759 | { | ||
1760 | ns = n; | ||
1761 | } | ||
1762 | |||
1763 | /* check if 'ip' is already an IPv4/IPv6 address */ | ||
1764 | if ((1 == inet_pton (AF_INET, | ||
1765 | ip, | ||
1766 | &v4)) || | ||
1767 | (1 == inet_pton (AF_INET6, | ||
1768 | ip, | ||
1769 | &v6))) | ||
1770 | { | ||
1771 | GNUNET_break (GNUNET_OK == | ||
1772 | GNUNET_DNSSTUB_add_dns_ip ( | ||
1773 | ac->authority_info.dns_authority.dns_handle, | ||
1774 | ip)); | ||
1775 | ac->authority_info.dns_authority.found = GNUNET_YES; | ||
1776 | GNUNET_free (ip); | ||
1777 | continue; | ||
1778 | } | ||
1779 | tld = GNS_get_tld (ip); | ||
1780 | if ((0 != strcmp (tld, "+")) && | ||
1781 | (GNUNET_OK != GNUNET_GNSRECORD_zkey_to_pkey (tld, &zone))) | ||
1782 | { | ||
1783 | /* 'ip' is a DNS name */ | ||
1784 | gp = GNUNET_new (struct Gns2DnsPending); | ||
1785 | gp->ac = ac; | ||
1786 | GNUNET_CONTAINER_DLL_insert (ac->authority_info.dns_authority.gp_head, | ||
1787 | ac->authority_info.dns_authority.gp_tail, | ||
1788 | gp); | ||
1789 | gp->dns_rh = GNUNET_RESOLVER_ip_get (ip, | ||
1790 | AF_UNSPEC, | ||
1791 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
1792 | &handle_gns2dns_ip, | ||
1793 | gp); | ||
1794 | GNUNET_free (ip); | ||
1795 | continue; | ||
1796 | } | ||
1797 | /* 'ip' should be a GNS name */ | ||
1798 | gp = GNUNET_new (struct Gns2DnsPending); | ||
1799 | gp->ac = ac; | ||
1800 | GNUNET_CONTAINER_DLL_insert (ac->authority_info.dns_authority.gp_head, | ||
1801 | ac->authority_info.dns_authority.gp_tail, | ||
1802 | gp); | ||
1803 | gp->rh = GNUNET_new (struct GNS_ResolverHandle); | ||
1804 | if (0 == strcmp (tld, "+")) | ||
1805 | { | ||
1806 | ip = translate_dot_plus (rh, | ||
1807 | ip); | ||
1808 | tld = GNS_get_tld (ip); | ||
1809 | if (GNUNET_OK != | ||
1810 | GNUNET_GNSRECORD_zkey_to_pkey (tld, | ||
1811 | &zone)) | ||
1812 | { | ||
1813 | GNUNET_break_op (0); | ||
1814 | GNUNET_free (ip); | ||
1815 | continue; | ||
1816 | } | ||
1817 | } | ||
1818 | gp->rh->authority_zone = zone; | ||
1819 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1820 | "Resolving `%s' to determine IP address of DNS server for GNS2DNS transition for `%s'\n", | ||
1821 | ip, | ||
1822 | ns); | ||
1823 | gp->rh->name = ip; | ||
1824 | gp->rh->name_resolution_pos = strlen (ip) - strlen (tld) - 1; | ||
1825 | gp->rh->proc = &handle_gns2dns_result; | ||
1826 | gp->rh->proc_cls = gp; | ||
1827 | gp->rh->record_type = GNUNET_GNSRECORD_TYPE_ANY; | ||
1828 | gp->rh->options = GNUNET_GNS_LO_DEFAULT; | ||
1829 | gp->rh->loop_limiter = rh->loop_limiter + 1; | ||
1830 | gp->rh->loop_threshold = rh->loop_threshold; | ||
1831 | gp->rh->task_id | ||
1832 | = GNUNET_SCHEDULER_add_now (&start_resolver_lookup, | ||
1833 | gp->rh); | ||
1834 | } /* end 'for all records' */ | ||
1835 | |||
1836 | if (NULL == ns) | ||
1837 | { | ||
1838 | /* not a single GNS2DNS record found */ | ||
1839 | GNUNET_free (ac); | ||
1840 | return GNUNET_SYSERR; | ||
1841 | } | ||
1842 | GNUNET_assert (strlen (ns) <= GNUNET_DNSPARSER_MAX_NAME_LENGTH); | ||
1843 | strcpy (ac->authority_info.dns_authority.name, | ||
1844 | ns); | ||
1845 | /* for DNS recursion, the label is the full DNS name, | ||
1846 | created from the remainder of the GNS name and the | ||
1847 | name in the NS record */ | ||
1848 | GNUNET_asprintf (&ac->label, | ||
1849 | "%.*s%s%s", | ||
1850 | (int) rh->name_resolution_pos, | ||
1851 | rh->name, | ||
1852 | (0 != rh->name_resolution_pos) ? "." : "", | ||
1853 | ns); | ||
1854 | GNUNET_free (ns); | ||
1855 | |||
1856 | { | ||
1857 | /* the GNS name is UTF-8 and may include multibyte chars. | ||
1858 | * We have to convert the combined name to a DNS-compatible IDNA. | ||
1859 | */ | ||
1860 | char *tmp = ac->label; | ||
1861 | |||
1862 | if (IDNA_SUCCESS != idna_to_ascii_8z (tmp, | ||
1863 | &ac->label, | ||
1864 | IDNA_ALLOW_UNASSIGNED)) | ||
1865 | { | ||
1866 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1867 | _ ("Name `%s' cannot be converted to IDNA."), | ||
1868 | tmp); | ||
1869 | GNUNET_free (tmp); | ||
1870 | GNUNET_free (ac); | ||
1871 | return GNUNET_SYSERR; | ||
1872 | } | ||
1873 | GNUNET_free (tmp); | ||
1874 | } | ||
1875 | |||
1876 | GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, | ||
1877 | rh->ac_tail, | ||
1878 | ac); | ||
1879 | if (strlen (ac->label) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) | ||
1880 | { | ||
1881 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1882 | _ ("GNS lookup resulted in DNS name that is too long (`%s')\n"), | ||
1883 | ac->label); | ||
1884 | GNUNET_free (ac->label); | ||
1885 | GNUNET_free (ac); | ||
1886 | return GNUNET_SYSERR; | ||
1887 | } | ||
1888 | continue_with_gns2dns (ac); | ||
1889 | return GNUNET_OK; | ||
1890 | } | ||
1891 | |||
1892 | |||
1893 | static void | ||
1894 | handle_gns_resolution_result (void *cls, | ||
1895 | unsigned int rd_count, | ||
1896 | const struct GNUNET_GNSRECORD_Data *rd) | ||
1897 | { | ||
1898 | struct GNS_ResolverHandle *rh = cls; | ||
1899 | char *cname; | ||
1900 | char scratch[UINT16_MAX]; | ||
1901 | size_t scratch_off; | ||
1902 | size_t scratch_start; | ||
1903 | size_t off; | ||
1904 | struct GNUNET_GNSRECORD_Data rd_new[rd_count]; | ||
1905 | unsigned int rd_off; | ||
1906 | |||
1907 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1908 | "Resolution succeeded for `%s' in zone %s, got %u records\n", | ||
1909 | rh->ac_tail->label, | ||
1910 | GNUNET_GNSRECORD_z2s (&rh->ac_tail->authority_info.gns_authority), | ||
1911 | rd_count); | ||
1912 | if (0 == rd_count) | ||
1913 | { | ||
1914 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1915 | _ ("GNS lookup failed (zero records found for `%s')\n"), | ||
1916 | rh->name); | ||
1917 | fail_resolution (rh); | ||
1918 | return; | ||
1919 | } | ||
1920 | |||
1921 | if (0 == rh->name_resolution_pos) | ||
1922 | { | ||
1923 | /* top-level match, are we done yet? */ | ||
1924 | if ((rd_count > 0) && | ||
1925 | (GNUNET_DNSPARSER_TYPE_CNAME == rd[0].record_type) && | ||
1926 | (GNUNET_DNSPARSER_TYPE_CNAME != rh->record_type)) | ||
1927 | { | ||
1928 | off = 0; | ||
1929 | cname = GNUNET_DNSPARSER_parse_name (rd[0].data, | ||
1930 | rd[0].data_size, | ||
1931 | &off); | ||
1932 | if ((NULL == cname) || | ||
1933 | (off != rd[0].data_size)) | ||
1934 | { | ||
1935 | GNUNET_break_op (0); | ||
1936 | GNUNET_free (cname); | ||
1937 | fail_resolution (rh); | ||
1938 | return; | ||
1939 | } | ||
1940 | handle_gns_cname_result (rh, | ||
1941 | cname); | ||
1942 | GNUNET_free (cname); | ||
1943 | return; | ||
1944 | } | ||
1945 | if ((rd_count > 0) && | ||
1946 | (GNUNET_GNSRECORD_TYPE_REDIRECT == rd[0].record_type) && | ||
1947 | (GNUNET_GNSRECORD_TYPE_REDIRECT != rh->record_type)) | ||
1948 | { | ||
1949 | handle_gns_redirect_result (rh, | ||
1950 | rd[0].data); | ||
1951 | return; | ||
1952 | } | ||
1953 | |||
1954 | |||
1955 | /* If A/AAAA was requested, | ||
1956 | * but we got a GNS2DNS record */ | ||
1957 | if ((GNUNET_DNSPARSER_TYPE_A == rh->record_type) || | ||
1958 | (GNUNET_DNSPARSER_TYPE_AAAA == rh->record_type)) | ||
1959 | { | ||
1960 | for (unsigned int i = 0; i < rd_count; i++) | ||
1961 | { | ||
1962 | switch (rd[i].record_type) | ||
1963 | { | ||
1964 | case GNUNET_GNSRECORD_TYPE_GNS2DNS: | ||
1965 | { | ||
1966 | /* delegation to DNS */ | ||
1967 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1968 | "Found GNS2DNS record, delegating to DNS!\n"); | ||
1969 | if (GNUNET_OK == | ||
1970 | recursive_gns2dns_resolution (rh, | ||
1971 | rd_count, | ||
1972 | rd)) | ||
1973 | return; | ||
1974 | else | ||
1975 | goto fail; | ||
1976 | } | ||
1977 | |||
1978 | default: | ||
1979 | break; | ||
1980 | } /* end: switch */ | ||
1981 | } /* end: for rd */ | ||
1982 | } /* end: name_resolution_pos */ | ||
1983 | /* convert relative names in record values to absolute names, | ||
1984 | using 'scratch' array for memory allocations */ | ||
1985 | scratch_off = 0; | ||
1986 | rd_off = 0; | ||
1987 | for (unsigned int i = 0; i < rd_count; i++) | ||
1988 | { | ||
1989 | GNUNET_assert (rd_off <= i); | ||
1990 | if ((0 != rh->protocol) && | ||
1991 | (0 != rh->service) && | ||
1992 | (GNUNET_GNSRECORD_TYPE_BOX != rd[i].record_type)) | ||
1993 | continue; /* we _only_ care about boxed records */ | ||
1994 | |||
1995 | GNUNET_assert (rd_off < rd_count); | ||
1996 | rd_new[rd_off] = rd[i]; | ||
1997 | /* Check if the embedded name(s) end in "+", and if so, | ||
1998 | replace the "+" with the zone at "ac_tail", changing the name | ||
1999 | to a ".ZONEKEY". The name is allocated on the 'scratch' array, | ||
2000 | so we can free it afterwards. */ | ||
2001 | switch (rd[i].record_type) | ||
2002 | { | ||
2003 | case GNUNET_GNSRECORD_TYPE_REDIRECT: | ||
2004 | { | ||
2005 | char *rname; | ||
2006 | rname = GNUNET_strndup (rd[i].data, rd[i].data_size); | ||
2007 | rname = translate_dot_plus (rh, rname); | ||
2008 | GNUNET_break (NULL != rname); | ||
2009 | scratch_start = scratch_off; | ||
2010 | memcpy (&scratch[scratch_start], rname, strlen (rname) + 1); | ||
2011 | scratch_off += strlen (rname) + 1; | ||
2012 | GNUNET_assert (rd_off < rd_count); | ||
2013 | rd_new[rd_off].data = &scratch[scratch_start]; | ||
2014 | rd_new[rd_off].data_size = scratch_off - scratch_start; | ||
2015 | rd_off++; | ||
2016 | GNUNET_free (rname); | ||
2017 | } | ||
2018 | break; | ||
2019 | |||
2020 | case GNUNET_DNSPARSER_TYPE_CNAME: | ||
2021 | { | ||
2022 | char *cname; | ||
2023 | |||
2024 | off = 0; | ||
2025 | cname = GNUNET_DNSPARSER_parse_name (rd[i].data, | ||
2026 | rd[i].data_size, | ||
2027 | &off); | ||
2028 | if ((NULL == cname) || | ||
2029 | (off != rd[i].data_size)) | ||
2030 | { | ||
2031 | GNUNET_break_op (0); /* record not well-formed */ | ||
2032 | } | ||
2033 | else | ||
2034 | { | ||
2035 | cname = translate_dot_plus (rh, cname); | ||
2036 | GNUNET_break (NULL != cname); | ||
2037 | scratch_start = scratch_off; | ||
2038 | if (GNUNET_OK != | ||
2039 | GNUNET_DNSPARSER_builder_add_name (scratch, | ||
2040 | sizeof(scratch), | ||
2041 | &scratch_off, | ||
2042 | cname)) | ||
2043 | { | ||
2044 | GNUNET_break (0); | ||
2045 | } | ||
2046 | else | ||
2047 | { | ||
2048 | GNUNET_assert (rd_off < rd_count); | ||
2049 | rd_new[rd_off].data = &scratch[scratch_start]; | ||
2050 | rd_new[rd_off].data_size = scratch_off - scratch_start; | ||
2051 | rd_off++; | ||
2052 | } | ||
2053 | } | ||
2054 | GNUNET_free (cname); | ||
2055 | } | ||
2056 | break; | ||
2057 | |||
2058 | case GNUNET_DNSPARSER_TYPE_SOA: | ||
2059 | { | ||
2060 | struct GNUNET_DNSPARSER_SoaRecord *soa; | ||
2061 | |||
2062 | off = 0; | ||
2063 | soa = GNUNET_DNSPARSER_parse_soa (rd[i].data, | ||
2064 | rd[i].data_size, | ||
2065 | &off); | ||
2066 | if ((NULL == soa) || | ||
2067 | (off != rd[i].data_size)) | ||
2068 | { | ||
2069 | GNUNET_break_op (0); /* record not well-formed */ | ||
2070 | } | ||
2071 | else | ||
2072 | { | ||
2073 | soa->mname = translate_dot_plus (rh, soa->mname); | ||
2074 | soa->rname = translate_dot_plus (rh, soa->rname); | ||
2075 | scratch_start = scratch_off; | ||
2076 | if (GNUNET_OK != | ||
2077 | GNUNET_DNSPARSER_builder_add_soa (scratch, | ||
2078 | sizeof(scratch), | ||
2079 | &scratch_off, | ||
2080 | soa)) | ||
2081 | { | ||
2082 | GNUNET_break (0); | ||
2083 | } | ||
2084 | else | ||
2085 | { | ||
2086 | GNUNET_assert (rd_off < rd_count); | ||
2087 | rd_new[rd_off].data = &scratch[scratch_start]; | ||
2088 | rd_new[rd_off].data_size = scratch_off - scratch_start; | ||
2089 | rd_off++; | ||
2090 | } | ||
2091 | } | ||
2092 | if (NULL != soa) | ||
2093 | GNUNET_DNSPARSER_free_soa (soa); | ||
2094 | } | ||
2095 | break; | ||
2096 | |||
2097 | case GNUNET_DNSPARSER_TYPE_MX: | ||
2098 | { | ||
2099 | struct GNUNET_DNSPARSER_MxRecord *mx; | ||
2100 | |||
2101 | off = 0; | ||
2102 | mx = GNUNET_DNSPARSER_parse_mx (rd[i].data, | ||
2103 | rd[i].data_size, | ||
2104 | &off); | ||
2105 | if ((NULL == mx) || | ||
2106 | (off != rd[i].data_size)) | ||
2107 | { | ||
2108 | GNUNET_break_op (0); /* record not well-formed */ | ||
2109 | } | ||
2110 | else | ||
2111 | { | ||
2112 | mx->mxhost = translate_dot_plus (rh, mx->mxhost); | ||
2113 | scratch_start = scratch_off; | ||
2114 | if (GNUNET_OK != | ||
2115 | GNUNET_DNSPARSER_builder_add_mx (scratch, | ||
2116 | sizeof(scratch), | ||
2117 | &scratch_off, | ||
2118 | mx)) | ||
2119 | { | ||
2120 | GNUNET_break (0); | ||
2121 | } | ||
2122 | else | ||
2123 | { | ||
2124 | GNUNET_assert (rd_off < rd_count); | ||
2125 | rd_new[rd_off].data = &scratch[scratch_start]; | ||
2126 | rd_new[rd_off].data_size = scratch_off - scratch_start; | ||
2127 | rd_off++; | ||
2128 | } | ||
2129 | } | ||
2130 | if (NULL != mx) | ||
2131 | GNUNET_DNSPARSER_free_mx (mx); | ||
2132 | } | ||
2133 | break; | ||
2134 | |||
2135 | case GNUNET_DNSPARSER_TYPE_SRV: | ||
2136 | { | ||
2137 | struct GNUNET_DNSPARSER_SrvRecord *srv; | ||
2138 | |||
2139 | off = 0; | ||
2140 | srv = GNUNET_DNSPARSER_parse_srv (rd[i].data, | ||
2141 | rd[i].data_size, | ||
2142 | &off); | ||
2143 | if ((NULL == srv) || | ||
2144 | (off != rd[i].data_size)) | ||
2145 | { | ||
2146 | GNUNET_break_op (0); /* record not well-formed */ | ||
2147 | } | ||
2148 | else | ||
2149 | { | ||
2150 | srv->target = translate_dot_plus (rh, srv->target); | ||
2151 | scratch_start = scratch_off; | ||
2152 | if (GNUNET_OK != | ||
2153 | GNUNET_DNSPARSER_builder_add_srv (scratch, | ||
2154 | sizeof(scratch), | ||
2155 | &scratch_off, | ||
2156 | srv)) | ||
2157 | { | ||
2158 | GNUNET_break (0); | ||
2159 | } | ||
2160 | else | ||
2161 | { | ||
2162 | GNUNET_assert (rd_off < rd_count); | ||
2163 | rd_new[rd_off].data = &scratch[scratch_start]; | ||
2164 | rd_new[rd_off].data_size = scratch_off - scratch_start; | ||
2165 | rd_off++; | ||
2166 | } | ||
2167 | } | ||
2168 | if (NULL != srv) | ||
2169 | GNUNET_DNSPARSER_free_srv (srv); | ||
2170 | } | ||
2171 | break; | ||
2172 | |||
2173 | case GNUNET_GNSRECORD_TYPE_PKEY: | ||
2174 | case GNUNET_GNSRECORD_TYPE_EDKEY: | ||
2175 | { | ||
2176 | struct GNUNET_CRYPTO_PublicKey pubkey; | ||
2177 | if (rd[i].data_size < sizeof(uint32_t)) | ||
2178 | { | ||
2179 | GNUNET_break_op (0); | ||
2180 | break; | ||
2181 | } | ||
2182 | if (GNUNET_OK != | ||
2183 | GNUNET_GNSRECORD_identity_from_data (rd[i].data, | ||
2184 | rd[i].data_size, | ||
2185 | rd[i].record_type, | ||
2186 | &pubkey)) | ||
2187 | { | ||
2188 | GNUNET_break_op (0); | ||
2189 | break; | ||
2190 | } | ||
2191 | rd_off++; | ||
2192 | if (rd[i].record_type != rh->record_type) | ||
2193 | { | ||
2194 | /* try to resolve "@" */ | ||
2195 | struct AuthorityChain *ac; | ||
2196 | |||
2197 | ac = GNUNET_new (struct AuthorityChain); | ||
2198 | ac->rh = rh; | ||
2199 | ac->gns_authority = GNUNET_YES; | ||
2200 | ac->authority_info.gns_authority = pubkey; | ||
2201 | ac->label = GNUNET_strdup (GNUNET_GNS_EMPTY_LABEL_AT); | ||
2202 | GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, | ||
2203 | rh->ac_tail, | ||
2204 | ac); | ||
2205 | rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, | ||
2206 | rh); | ||
2207 | return; | ||
2208 | } | ||
2209 | } | ||
2210 | break; | ||
2211 | |||
2212 | case GNUNET_GNSRECORD_TYPE_GNS2DNS: | ||
2213 | { | ||
2214 | /* delegation to DNS */ | ||
2215 | if (GNUNET_GNSRECORD_TYPE_GNS2DNS == rh->record_type) | ||
2216 | { | ||
2217 | rd_off++; | ||
2218 | break; /* do not follow to DNS, we wanted the GNS2DNS record! */ | ||
2219 | } | ||
2220 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2221 | "Found GNS2DNS record, delegating to DNS!\n"); | ||
2222 | if (GNUNET_OK == | ||
2223 | recursive_gns2dns_resolution (rh, | ||
2224 | rd_count, | ||
2225 | rd)) | ||
2226 | return; | ||
2227 | else | ||
2228 | goto fail; | ||
2229 | } | ||
2230 | |||
2231 | case GNUNET_GNSRECORD_TYPE_BOX: | ||
2232 | { | ||
2233 | /* unbox SRV/TLSA records if a specific one was requested */ | ||
2234 | if ((0 != rh->protocol) && | ||
2235 | (0 != rh->service) && | ||
2236 | (rd[i].data_size >= sizeof(struct GNUNET_GNSRECORD_BoxRecord))) | ||
2237 | { | ||
2238 | const struct GNUNET_GNSRECORD_BoxRecord *box; | ||
2239 | |||
2240 | box = rd[i].data; | ||
2241 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2242 | "Got BOX record, checking if parameters match... %u/%u vs %u/%u\n", | ||
2243 | ntohs (box->protocol), ntohs (box->service), | ||
2244 | rh->protocol, rh->service); | ||
2245 | if ((ntohs (box->protocol) == rh->protocol) && | ||
2246 | (ntohs (box->service) == rh->service)) | ||
2247 | { | ||
2248 | /* Box matches, unbox! */ | ||
2249 | GNUNET_assert (rd_off < rd_count); | ||
2250 | rd_new[rd_off].record_type = ntohl (box->record_type); | ||
2251 | rd_new[rd_off].data_size -= sizeof(struct | ||
2252 | GNUNET_GNSRECORD_BoxRecord); | ||
2253 | rd_new[rd_off].data = &box[1]; | ||
2254 | rd_off++; | ||
2255 | } | ||
2256 | } | ||
2257 | else | ||
2258 | { | ||
2259 | /* no specific protocol/service specified, preserve all BOX | ||
2260 | records (for modern, GNS-enabled applications) */ | ||
2261 | rd_off++; | ||
2262 | } | ||
2263 | break; | ||
2264 | } | ||
2265 | |||
2266 | default: | ||
2267 | rd_off++; | ||
2268 | break; | ||
2269 | } /* end: switch */ | ||
2270 | } /* end: for rd_count */ | ||
2271 | |||
2272 | /* yes, we are done, return result */ | ||
2273 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2274 | "Returning GNS response for `%s' with %u answers\n", | ||
2275 | rh->ac_tail->label, | ||
2276 | rd_off); | ||
2277 | rh->proc (rh->proc_cls, | ||
2278 | rd_off, | ||
2279 | rd_new); | ||
2280 | rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, | ||
2281 | rh); | ||
2282 | return; | ||
2283 | } | ||
2284 | |||
2285 | switch (rd[0].record_type) | ||
2286 | { | ||
2287 | case GNUNET_GNSRECORD_TYPE_REDIRECT: | ||
2288 | GNUNET_break_op (1 == rd_count); /* REDIRECT should be unique */ | ||
2289 | recursive_redirect_resolution (rh, | ||
2290 | &rd[0]); | ||
2291 | return; | ||
2292 | |||
2293 | case GNUNET_DNSPARSER_TYPE_CNAME: | ||
2294 | GNUNET_break_op (1 == rd_count); /* CNAME should be unique */ | ||
2295 | recursive_cname_resolution (rh, | ||
2296 | &rd[0]); | ||
2297 | return; | ||
2298 | |||
2299 | case GNUNET_GNSRECORD_TYPE_PKEY: | ||
2300 | case GNUNET_GNSRECORD_TYPE_EDKEY: | ||
2301 | GNUNET_break_op (1 == rd_count); /* PKEY should be unique */ | ||
2302 | recursive_pkey_resolution (rh, | ||
2303 | &rd[0]); | ||
2304 | return; | ||
2305 | |||
2306 | case GNUNET_GNSRECORD_TYPE_GNS2DNS: | ||
2307 | if (GNUNET_OK == | ||
2308 | recursive_gns2dns_resolution (rh, | ||
2309 | rd_count, | ||
2310 | rd)) | ||
2311 | return; | ||
2312 | break; | ||
2313 | default: | ||
2314 | if (GNUNET_YES != GNUNET_GNSRECORD_is_critical (rd[0].record_type)) | ||
2315 | return; | ||
2316 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2317 | _ ("Unable to process critical delegation record\n")); | ||
2318 | break; | ||
2319 | } | ||
2320 | fail: | ||
2321 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2322 | _ ("GNS lookup recursion failed (no delegation record found)\n")); | ||
2323 | fail_resolution (rh); | ||
2324 | } | ||
2325 | |||
2326 | |||
2327 | /** | ||
2328 | * Function called once the namestore has completed the request for | ||
2329 | * caching a block. | ||
2330 | * | ||
2331 | * @param cls closure with the `struct CacheOps` | ||
2332 | * @param success #GNUNET_OK on success | ||
2333 | * @param emsg error message | ||
2334 | */ | ||
2335 | static void | ||
2336 | namecache_cache_continuation (void *cls, | ||
2337 | int32_t success, | ||
2338 | const char *emsg) | ||
2339 | { | ||
2340 | struct CacheOps *co = cls; | ||
2341 | |||
2342 | co->namecache_qe_cache = NULL; | ||
2343 | if (GNUNET_OK != success) | ||
2344 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2345 | _ ("Failed to cache GNS resolution: %s\n"), | ||
2346 | emsg); | ||
2347 | GNUNET_CONTAINER_DLL_remove (co_head, | ||
2348 | co_tail, | ||
2349 | co); | ||
2350 | GNUNET_free (co); | ||
2351 | } | ||
2352 | |||
2353 | |||
2354 | /** | ||
2355 | * Iterator called on each result obtained for a DHT | ||
2356 | * operation that expects a reply | ||
2357 | * | ||
2358 | * @param cls closure with the `struct GNS_ResolverHandle` | ||
2359 | * @param exp when will this value expire | ||
2360 | * @param key key of the result | ||
2361 | * @param trunc_peer truncated peer, NULL if not truncated | ||
2362 | * @param get_path peers on reply path (or NULL if not recorded) | ||
2363 | * [0] = datastore's first neighbor, [length - 1] = local peer | ||
2364 | * @param get_path_length number of entries in @a get_path | ||
2365 | * @param put_path peers on the PUT path (or NULL if not recorded) | ||
2366 | * [0] = origin, [length - 1] = datastore | ||
2367 | * @param put_path_length number of entries in @a put_path | ||
2368 | * @param type type of the result | ||
2369 | * @param size number of bytes in data | ||
2370 | * @param data pointer to the result data | ||
2371 | */ | ||
2372 | static void | ||
2373 | handle_dht_response (void *cls, | ||
2374 | struct GNUNET_TIME_Absolute exp, | ||
2375 | const struct GNUNET_HashCode *key, | ||
2376 | const struct GNUNET_PeerIdentity *trunc_peer, | ||
2377 | const struct GNUNET_DHT_PathElement *get_path, | ||
2378 | unsigned int get_path_length, | ||
2379 | const struct GNUNET_DHT_PathElement *put_path, | ||
2380 | unsigned int put_path_length, | ||
2381 | enum GNUNET_BLOCK_Type type, | ||
2382 | size_t size, | ||
2383 | const void *data) | ||
2384 | { | ||
2385 | struct GNS_ResolverHandle *rh = cls; | ||
2386 | struct AuthorityChain *ac = rh->ac_tail; | ||
2387 | const struct GNUNET_GNSRECORD_Block *block; | ||
2388 | struct CacheOps *co; | ||
2389 | |||
2390 | (void) exp; | ||
2391 | (void) key; | ||
2392 | (void) get_path; | ||
2393 | (void) get_path_length; | ||
2394 | (void) put_path; | ||
2395 | (void) put_path_length; | ||
2396 | (void) type; | ||
2397 | GNUNET_DHT_get_stop (rh->get_handle); | ||
2398 | rh->get_handle = NULL; | ||
2399 | GNUNET_CONTAINER_heap_remove_node (rh->dht_heap_node); | ||
2400 | rh->dht_heap_node = NULL; | ||
2401 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2402 | "Handling response from the DHT\n"); | ||
2403 | if (size < sizeof(struct GNUNET_GNSRECORD_Block)) | ||
2404 | { | ||
2405 | /* how did this pass DHT block validation!? */ | ||
2406 | GNUNET_break (0); | ||
2407 | fail_resolution (rh); | ||
2408 | return; | ||
2409 | } | ||
2410 | block = data; | ||
2411 | if (size != GNUNET_GNSRECORD_block_get_size (block)) | ||
2412 | { | ||
2413 | /* how did this pass DHT block validation!? */ | ||
2414 | GNUNET_break (0); | ||
2415 | fail_resolution (rh); | ||
2416 | return; | ||
2417 | } | ||
2418 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2419 | "Decrypting DHT block of size %lu for `%s', expires %s\n", | ||
2420 | GNUNET_GNSRECORD_block_get_size (block), | ||
2421 | rh->name, | ||
2422 | GNUNET_STRINGS_absolute_time_to_string (exp)); | ||
2423 | if (GNUNET_OK != | ||
2424 | GNUNET_GNSRECORD_block_decrypt (block, | ||
2425 | &ac->authority_info.gns_authority, | ||
2426 | ac->label, | ||
2427 | &handle_gns_resolution_result, | ||
2428 | rh)) | ||
2429 | { | ||
2430 | GNUNET_break_op (0); /* block was ill-formed */ | ||
2431 | fail_resolution (rh); | ||
2432 | return; | ||
2433 | } | ||
2434 | if (0 == GNUNET_TIME_absolute_get_remaining ( | ||
2435 | GNUNET_GNSRECORD_block_get_expiration (block)). | ||
2436 | rel_value_us) | ||
2437 | { | ||
2438 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2439 | "Received expired block from the DHT, will not cache it.\n"); | ||
2440 | return; | ||
2441 | } | ||
2442 | if (GNUNET_YES == disable_cache) | ||
2443 | return; | ||
2444 | /* Cache well-formed blocks */ | ||
2445 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2446 | "Caching response from the DHT in namecache\n"); | ||
2447 | co = GNUNET_new (struct CacheOps); | ||
2448 | co->namecache_qe_cache = GNUNET_NAMECACHE_block_cache (namecache_handle, | ||
2449 | block, | ||
2450 | & | ||
2451 | namecache_cache_continuation, | ||
2452 | co); | ||
2453 | GNUNET_CONTAINER_DLL_insert (co_head, | ||
2454 | co_tail, | ||
2455 | co); | ||
2456 | } | ||
2457 | |||
2458 | |||
2459 | /** | ||
2460 | * Initiate a DHT query for a set of GNS records. | ||
2461 | * | ||
2462 | * @param rh resolution handle | ||
2463 | * @param query key to use in the DHT lookup | ||
2464 | */ | ||
2465 | static void | ||
2466 | start_dht_request (struct GNS_ResolverHandle *rh, | ||
2467 | const struct GNUNET_HashCode *query) | ||
2468 | { | ||
2469 | struct GNS_ResolverHandle *rx; | ||
2470 | |||
2471 | GNUNET_assert (NULL == rh->get_handle); | ||
2472 | rh->get_handle = GNUNET_DHT_get_start (dht_handle, | ||
2473 | GNUNET_BLOCK_TYPE_GNS_NAMERECORD, | ||
2474 | query, | ||
2475 | DHT_GNS_REPLICATION_LEVEL, | ||
2476 | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, | ||
2477 | NULL, 0, | ||
2478 | &handle_dht_response, rh); | ||
2479 | rh->dht_heap_node = GNUNET_CONTAINER_heap_insert (dht_lookup_heap, | ||
2480 | rh, | ||
2481 | GNUNET_TIME_absolute_get (). | ||
2482 | abs_value_us); | ||
2483 | if (GNUNET_CONTAINER_heap_get_size (dht_lookup_heap) > | ||
2484 | max_allowed_background_queries) | ||
2485 | { | ||
2486 | /* fail longest-standing DHT request */ | ||
2487 | rx = GNUNET_CONTAINER_heap_remove_root (dht_lookup_heap); | ||
2488 | rx->dht_heap_node = NULL; | ||
2489 | GNUNET_assert (NULL != rx); | ||
2490 | fail_resolution (rx); | ||
2491 | } | ||
2492 | } | ||
2493 | |||
2494 | |||
2495 | /** | ||
2496 | * Process a records that were decrypted from a block that we got from | ||
2497 | * the namecache. Simply calls #handle_gns_resolution_result(). | ||
2498 | * | ||
2499 | * @param cls closure with the `struct GNS_ResolverHandle` | ||
2500 | * @param rd_count number of entries in @a rd array | ||
2501 | * @param rd array of records with data to store | ||
2502 | */ | ||
2503 | static void | ||
2504 | handle_gns_namecache_resolution_result (void *cls, | ||
2505 | unsigned int rd_count, | ||
2506 | const struct GNUNET_GNSRECORD_Data *rd) | ||
2507 | { | ||
2508 | struct GNS_ResolverHandle *rh = cls; | ||
2509 | |||
2510 | if (0 == rd_count) | ||
2511 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2512 | _ ("GNS namecache returned empty result for `%s'\n"), | ||
2513 | rh->name); | ||
2514 | handle_gns_resolution_result (rh, | ||
2515 | rd_count, | ||
2516 | rd); | ||
2517 | } | ||
2518 | |||
2519 | |||
2520 | /** | ||
2521 | * Process a record that was stored in the namecache. | ||
2522 | * | ||
2523 | * @param cls closure with the `struct GNS_ResolverHandle` | ||
2524 | * @param block block that was stored in the namecache | ||
2525 | */ | ||
2526 | static void | ||
2527 | handle_namecache_block_response (void *cls, | ||
2528 | const struct GNUNET_GNSRECORD_Block *block) | ||
2529 | { | ||
2530 | struct GNS_ResolverHandle *rh = cls; | ||
2531 | struct AuthorityChain *ac = rh->ac_tail; | ||
2532 | const char *label = ac->label; | ||
2533 | const struct GNUNET_CRYPTO_PublicKey *auth = | ||
2534 | &ac->authority_info.gns_authority; | ||
2535 | struct GNUNET_HashCode query; | ||
2536 | |||
2537 | GNUNET_assert (NULL != rh->namecache_qe); | ||
2538 | rh->namecache_qe = NULL; | ||
2539 | if (NULL == block) | ||
2540 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2541 | "No block found\n"); | ||
2542 | else | ||
2543 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2544 | "Got block with expiration %s\n", | ||
2545 | GNUNET_STRINGS_absolute_time_to_string ( | ||
2546 | GNUNET_GNSRECORD_block_get_expiration (block))); | ||
2547 | if (((GNUNET_GNS_LO_DEFAULT == rh->options) || | ||
2548 | ((GNUNET_GNS_LO_LOCAL_MASTER == rh->options) && | ||
2549 | (ac != rh->ac_head))) && | ||
2550 | ((NULL == block) || | ||
2551 | (0 == GNUNET_TIME_absolute_get_remaining ( | ||
2552 | GNUNET_GNSRECORD_block_get_expiration (block)). | ||
2553 | rel_value_us))) | ||
2554 | { | ||
2555 | /* namecache knows nothing; try DHT lookup */ | ||
2556 | GNUNET_GNSRECORD_query_from_public_key (auth, | ||
2557 | label, | ||
2558 | &query); | ||
2559 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2560 | "Starting DHT lookup for `%s' in zone `%s' under key `%s'\n", | ||
2561 | ac->label, | ||
2562 | GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority), | ||
2563 | GNUNET_h2s (&query)); | ||
2564 | start_dht_request (rh, &query); | ||
2565 | return; | ||
2566 | } | ||
2567 | |||
2568 | if ((NULL == block) || | ||
2569 | (0 == GNUNET_TIME_absolute_get_remaining ( | ||
2570 | GNUNET_GNSRECORD_block_get_expiration (block)). | ||
2571 | rel_value_us)) | ||
2572 | { | ||
2573 | /* DHT not permitted and no local result, fail */ | ||
2574 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2575 | "Resolution failed for `%s' in zone %s (DHT lookup not permitted by configuration)\n", | ||
2576 | ac->label, | ||
2577 | GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority)); | ||
2578 | fail_resolution (rh); | ||
2579 | return; | ||
2580 | } | ||
2581 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2582 | "Received result from namecache for label `%s'\n", | ||
2583 | ac->label); | ||
2584 | |||
2585 | if (GNUNET_OK != | ||
2586 | GNUNET_GNSRECORD_block_decrypt (block, | ||
2587 | auth, | ||
2588 | label, | ||
2589 | &handle_gns_namecache_resolution_result, | ||
2590 | rh)) | ||
2591 | { | ||
2592 | GNUNET_break_op (0); /* block was ill-formed */ | ||
2593 | /* try DHT instead */ | ||
2594 | GNUNET_GNSRECORD_query_from_public_key (auth, | ||
2595 | label, | ||
2596 | &query); | ||
2597 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2598 | "Starting DHT lookup for `%s' in zone `%s' under key `%s'\n", | ||
2599 | ac->label, | ||
2600 | GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority), | ||
2601 | GNUNET_h2s (&query)); | ||
2602 | start_dht_request (rh, &query); | ||
2603 | return; | ||
2604 | } | ||
2605 | } | ||
2606 | |||
2607 | |||
2608 | /** | ||
2609 | * Lookup tail of our authority chain in the namecache. | ||
2610 | * | ||
2611 | * @param rh query we are processing | ||
2612 | */ | ||
2613 | static void | ||
2614 | recursive_gns_resolution_namecache (struct GNS_ResolverHandle *rh) | ||
2615 | { | ||
2616 | struct AuthorityChain *ac = rh->ac_tail; | ||
2617 | struct GNUNET_HashCode query; | ||
2618 | |||
2619 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2620 | "Starting GNS resolution for `%s' in zone %s\n", | ||
2621 | ac->label, | ||
2622 | GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority)); | ||
2623 | GNUNET_GNSRECORD_query_from_public_key (&ac->authority_info.gns_authority, | ||
2624 | ac->label, | ||
2625 | &query); | ||
2626 | if (GNUNET_YES != disable_cache) | ||
2627 | { | ||
2628 | rh->namecache_qe | ||
2629 | = GNUNET_NAMECACHE_lookup_block (namecache_handle, | ||
2630 | &query, | ||
2631 | &handle_namecache_block_response, | ||
2632 | rh); | ||
2633 | GNUNET_assert (NULL != rh->namecache_qe); | ||
2634 | } | ||
2635 | else | ||
2636 | { | ||
2637 | start_dht_request (rh, | ||
2638 | &query); | ||
2639 | } | ||
2640 | } | ||
2641 | |||
2642 | |||
2643 | /** | ||
2644 | * Function called with the result from a revocation check. | ||
2645 | * | ||
2646 | * @param cls the `struct GNS_ResovlerHandle` | ||
2647 | * @param is_valid #GNUNET_YES if the zone was not yet revoked | ||
2648 | */ | ||
2649 | static void | ||
2650 | handle_revocation_result (void *cls, | ||
2651 | int is_valid) | ||
2652 | { | ||
2653 | struct GNS_ResolverHandle *rh = cls; | ||
2654 | struct AuthorityChain *ac = rh->ac_tail; | ||
2655 | |||
2656 | rh->rev_check = NULL; | ||
2657 | if (GNUNET_YES != is_valid) | ||
2658 | { | ||
2659 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2660 | _ ("Zone %s was revoked, resolution fails\n"), | ||
2661 | GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority)); | ||
2662 | fail_resolution (rh); | ||
2663 | return; | ||
2664 | } | ||
2665 | recursive_gns_resolution_namecache (rh); | ||
2666 | } | ||
2667 | |||
2668 | |||
2669 | /** | ||
2670 | * Perform revocation check on tail of our authority chain. | ||
2671 | * | ||
2672 | * @param rh query we are processing | ||
2673 | */ | ||
2674 | static void | ||
2675 | recursive_gns_resolution_revocation (struct GNS_ResolverHandle *rh) | ||
2676 | { | ||
2677 | struct AuthorityChain *ac = rh->ac_tail; | ||
2678 | |||
2679 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2680 | "Starting revocation check for zone %s\n", | ||
2681 | GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority)); | ||
2682 | rh->rev_check = GNUNET_REVOCATION_query (cfg, | ||
2683 | &ac->authority_info.gns_authority, | ||
2684 | &handle_revocation_result, | ||
2685 | rh); | ||
2686 | GNUNET_assert (NULL != rh->rev_check); | ||
2687 | } | ||
2688 | |||
2689 | |||
2690 | static void | ||
2691 | recursive_resolution (void *cls) | ||
2692 | { | ||
2693 | struct GNS_ResolverHandle *rh = cls; | ||
2694 | |||
2695 | rh->task_id = NULL; | ||
2696 | if (rh->loop_threshold < rh->loop_limiter++) | ||
2697 | { | ||
2698 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2699 | "Encountered unbounded recursion resolving `%s'\n", | ||
2700 | rh->name); | ||
2701 | fail_resolution (rh); | ||
2702 | return; | ||
2703 | } | ||
2704 | if (GNUNET_YES == rh->ac_tail->gns_authority) | ||
2705 | recursive_gns_resolution_revocation (rh); | ||
2706 | else | ||
2707 | recursive_dns_resolution (rh); | ||
2708 | } | ||
2709 | |||
2710 | |||
2711 | static void | ||
2712 | start_resolver_lookup (void *cls) | ||
2713 | { | ||
2714 | struct GNS_ResolverHandle *rh = cls; | ||
2715 | struct AuthorityChain *ac; | ||
2716 | struct in_addr v4; | ||
2717 | struct in6_addr v6; | ||
2718 | |||
2719 | rh->task_id = NULL; | ||
2720 | if (1 == inet_pton (AF_INET, | ||
2721 | rh->name, | ||
2722 | &v4)) | ||
2723 | { | ||
2724 | /* name is IPv4 address, pretend it's an A record */ | ||
2725 | struct GNUNET_GNSRECORD_Data rd; | ||
2726 | |||
2727 | rd.data = &v4; | ||
2728 | rd.data_size = sizeof(v4); | ||
2729 | rd.expiration_time = UINT64_MAX; | ||
2730 | rd.record_type = GNUNET_DNSPARSER_TYPE_A; | ||
2731 | rd.flags = 0; | ||
2732 | rh->proc (rh->proc_cls, | ||
2733 | 1, | ||
2734 | &rd); | ||
2735 | GNUNET_assert (NULL == rh->task_id); | ||
2736 | rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, | ||
2737 | rh); | ||
2738 | return; | ||
2739 | } | ||
2740 | if (1 == inet_pton (AF_INET6, | ||
2741 | rh->name, | ||
2742 | &v6)) | ||
2743 | { | ||
2744 | /* name is IPv6 address, pretend it's an AAAA record */ | ||
2745 | struct GNUNET_GNSRECORD_Data rd; | ||
2746 | |||
2747 | rd.data = &v6; | ||
2748 | rd.data_size = sizeof(v6); | ||
2749 | rd.expiration_time = UINT64_MAX; | ||
2750 | rd.record_type = GNUNET_DNSPARSER_TYPE_AAAA; | ||
2751 | rd.flags = 0; | ||
2752 | rh->proc (rh->proc_cls, | ||
2753 | 1, | ||
2754 | &rd); | ||
2755 | GNUNET_assert (NULL == rh->task_id); | ||
2756 | rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, | ||
2757 | rh); | ||
2758 | return; | ||
2759 | } | ||
2760 | |||
2761 | ac = GNUNET_new (struct AuthorityChain); | ||
2762 | ac->rh = rh; | ||
2763 | ac->label = resolver_lookup_get_next_label (rh); | ||
2764 | if (NULL == ac->label) | ||
2765 | /* name was just the "TLD", so we default to label | ||
2766 | #GNUNET_GNS_EMPTY_LABEL_AT */ | ||
2767 | ac->label = GNUNET_strdup (GNUNET_GNS_EMPTY_LABEL_AT); | ||
2768 | ac->gns_authority = GNUNET_YES; | ||
2769 | ac->authority_info.gns_authority = rh->authority_zone; | ||
2770 | GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, | ||
2771 | rh->ac_tail, | ||
2772 | ac); | ||
2773 | rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, | ||
2774 | rh); | ||
2775 | } | ||
2776 | |||
2777 | |||
2778 | /** | ||
2779 | * Lookup of a record in a specific zone calls lookup result processor | ||
2780 | * on result. | ||
2781 | * | ||
2782 | * @param zone the zone to perform the lookup in | ||
2783 | * @param record_type the record type to look up | ||
2784 | * @param name the name to look up | ||
2785 | * @param options local options to control local lookup | ||
2786 | * @param recursion_depth_limit how many zones to traverse | ||
2787 | * at most | ||
2788 | * @param proc the processor to call on result | ||
2789 | * @param proc_cls the closure to pass to @a proc | ||
2790 | * @return handle to cancel operation | ||
2791 | */ | ||
2792 | struct GNS_ResolverHandle * | ||
2793 | GNS_resolver_lookup (const struct GNUNET_CRYPTO_PublicKey *zone, | ||
2794 | uint32_t record_type, | ||
2795 | const char *name, | ||
2796 | enum GNUNET_GNS_LocalOptions options, | ||
2797 | uint16_t recursion_depth_limit, | ||
2798 | GNS_ResultProcessor proc, | ||
2799 | void *proc_cls) | ||
2800 | { | ||
2801 | struct GNS_ResolverHandle *rh; | ||
2802 | |||
2803 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2804 | "Starting lookup for `%s'\n", | ||
2805 | name); | ||
2806 | rh = GNUNET_new (struct GNS_ResolverHandle); | ||
2807 | GNUNET_CONTAINER_DLL_insert (rlh_head, | ||
2808 | rlh_tail, | ||
2809 | rh); | ||
2810 | rh->authority_zone = *zone; | ||
2811 | rh->proc = proc; | ||
2812 | rh->proc_cls = proc_cls; | ||
2813 | rh->options = options; | ||
2814 | rh->record_type = record_type; | ||
2815 | rh->name = GNUNET_strdup (name); | ||
2816 | rh->name_resolution_pos = strlen (name); | ||
2817 | rh->loop_threshold = recursion_depth_limit; | ||
2818 | rh->task_id = GNUNET_SCHEDULER_add_now (&start_resolver_lookup, | ||
2819 | rh); | ||
2820 | return rh; | ||
2821 | } | ||
2822 | |||
2823 | |||
2824 | /** | ||
2825 | * Cancel active resolution (i.e. client disconnected). | ||
2826 | * | ||
2827 | * @param rh resolution to abort | ||
2828 | */ | ||
2829 | void | ||
2830 | GNS_resolver_lookup_cancel (struct GNS_ResolverHandle *rh) | ||
2831 | { | ||
2832 | struct DnsResult *dr; | ||
2833 | struct AuthorityChain *ac; | ||
2834 | |||
2835 | GNUNET_CONTAINER_DLL_remove (rlh_head, | ||
2836 | rlh_tail, | ||
2837 | rh); | ||
2838 | if (NULL != rh->dns_request) | ||
2839 | { | ||
2840 | GNUNET_DNSSTUB_resolve_cancel (rh->dns_request); | ||
2841 | rh->dns_request = NULL; | ||
2842 | } | ||
2843 | while (NULL != (ac = rh->ac_head)) | ||
2844 | { | ||
2845 | GNUNET_CONTAINER_DLL_remove (rh->ac_head, | ||
2846 | rh->ac_tail, | ||
2847 | ac); | ||
2848 | if (GNUNET_NO == ac->gns_authority) | ||
2849 | { | ||
2850 | struct Gns2DnsPending *gp; | ||
2851 | |||
2852 | while (NULL != (gp = ac->authority_info.dns_authority.gp_head)) | ||
2853 | { | ||
2854 | GNUNET_CONTAINER_DLL_remove (ac->authority_info.dns_authority.gp_head, | ||
2855 | ac->authority_info.dns_authority.gp_tail, | ||
2856 | gp); | ||
2857 | if (NULL != gp->rh) | ||
2858 | { | ||
2859 | /* rh->g2dc->rh is NOT in the DLL yet, so to enable us | ||
2860 | using GNS_resolver_lookup_cancel here, we need to | ||
2861 | add it first... */ | ||
2862 | GNUNET_CONTAINER_DLL_insert (rlh_head, | ||
2863 | rlh_tail, | ||
2864 | gp->rh); | ||
2865 | GNUNET_assert (NULL == gp->rh->task_id); | ||
2866 | gp->rh->task_id = GNUNET_SCHEDULER_add_now ( | ||
2867 | &GNS_resolver_lookup_cancel_, | ||
2868 | gp->rh); | ||
2869 | gp->rh = NULL; | ||
2870 | } | ||
2871 | if (NULL != gp->dns_rh) | ||
2872 | { | ||
2873 | GNUNET_RESOLVER_request_cancel (gp->dns_rh); | ||
2874 | gp->dns_rh = NULL; | ||
2875 | } | ||
2876 | GNUNET_free (gp); | ||
2877 | } | ||
2878 | GNUNET_DNSSTUB_stop (ac->authority_info.dns_authority.dns_handle); | ||
2879 | } | ||
2880 | GNUNET_free (ac->label); | ||
2881 | GNUNET_free (ac); | ||
2882 | } | ||
2883 | if (NULL != rh->task_id) | ||
2884 | { | ||
2885 | GNUNET_SCHEDULER_cancel (rh->task_id); | ||
2886 | rh->task_id = NULL; | ||
2887 | } | ||
2888 | if (NULL != rh->get_handle) | ||
2889 | { | ||
2890 | GNUNET_DHT_get_stop (rh->get_handle); | ||
2891 | rh->get_handle = NULL; | ||
2892 | } | ||
2893 | if (NULL != rh->dht_heap_node) | ||
2894 | { | ||
2895 | GNUNET_CONTAINER_heap_remove_node (rh->dht_heap_node); | ||
2896 | rh->dht_heap_node = NULL; | ||
2897 | } | ||
2898 | if (NULL != rh->namecache_qe) | ||
2899 | { | ||
2900 | GNUNET_NAMECACHE_cancel (rh->namecache_qe); | ||
2901 | rh->namecache_qe = NULL; | ||
2902 | } | ||
2903 | if (NULL != rh->rev_check) | ||
2904 | { | ||
2905 | GNUNET_REVOCATION_query_cancel (rh->rev_check); | ||
2906 | rh->rev_check = NULL; | ||
2907 | } | ||
2908 | if (NULL != rh->std_resolve) | ||
2909 | { | ||
2910 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2911 | "Canceling standard DNS resolution\n"); | ||
2912 | GNUNET_RESOLVER_request_cancel (rh->std_resolve); | ||
2913 | rh->std_resolve = NULL; | ||
2914 | } | ||
2915 | while (NULL != (dr = rh->dns_result_head)) | ||
2916 | { | ||
2917 | GNUNET_CONTAINER_DLL_remove (rh->dns_result_head, | ||
2918 | rh->dns_result_tail, | ||
2919 | dr); | ||
2920 | GNUNET_free (dr); | ||
2921 | } | ||
2922 | GNUNET_free (rh->leho); | ||
2923 | GNUNET_free (rh->name); | ||
2924 | GNUNET_free (rh); | ||
2925 | } | ||
2926 | |||
2927 | |||
2928 | /* ***************** Resolver initialization ********************* */ | ||
2929 | |||
2930 | |||
2931 | /** | ||
2932 | * Initialize the resolver | ||
2933 | * | ||
2934 | * @param nc the namecache handle | ||
2935 | * @param dht the dht handle | ||
2936 | * @param c configuration handle | ||
2937 | * @param max_bg_queries maximum number of parallel background queries in dht | ||
2938 | */ | ||
2939 | void | ||
2940 | GNS_resolver_init (struct GNUNET_NAMECACHE_Handle *nc, | ||
2941 | struct GNUNET_DHT_Handle *dht, | ||
2942 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
2943 | unsigned long long max_bg_queries) | ||
2944 | { | ||
2945 | cfg = c; | ||
2946 | namecache_handle = nc; | ||
2947 | dht_handle = dht; | ||
2948 | dht_lookup_heap = | ||
2949 | GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); | ||
2950 | max_allowed_background_queries = max_bg_queries; | ||
2951 | disable_cache = GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
2952 | "namecache", | ||
2953 | "DISABLE"); | ||
2954 | if (GNUNET_YES == disable_cache) | ||
2955 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2956 | "Namecache disabled\n"); | ||
2957 | } | ||
2958 | |||
2959 | |||
2960 | /** | ||
2961 | * Shutdown resolver | ||
2962 | */ | ||
2963 | void | ||
2964 | GNS_resolver_done () | ||
2965 | { | ||
2966 | struct GNS_ResolverHandle *rh; | ||
2967 | struct CacheOps *co; | ||
2968 | |||
2969 | /* abort active resolutions */ | ||
2970 | while (NULL != (rh = rlh_head)) | ||
2971 | { | ||
2972 | rh->proc (rh->proc_cls, | ||
2973 | 0, | ||
2974 | NULL); | ||
2975 | GNS_resolver_lookup_cancel (rh); | ||
2976 | } | ||
2977 | while (NULL != (co = co_head)) | ||
2978 | { | ||
2979 | GNUNET_CONTAINER_DLL_remove (co_head, | ||
2980 | co_tail, | ||
2981 | co); | ||
2982 | GNUNET_NAMECACHE_cancel (co->namecache_qe_cache); | ||
2983 | GNUNET_free (co); | ||
2984 | } | ||
2985 | GNUNET_CONTAINER_heap_destroy (dht_lookup_heap); | ||
2986 | dht_lookup_heap = NULL; | ||
2987 | dht_handle = NULL; | ||
2988 | namecache_handle = NULL; | ||
2989 | } | ||
2990 | |||
2991 | |||
2992 | /* end of gnunet-service-gns_resolver.c */ | ||