diff options
author | Christian Grothoff <christian@grothoff.org> | 2012-01-02 15:24:49 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2012-01-02 15:24:49 +0000 |
commit | 9bb646da61d4306cd7628c08b495b0ac5cf9435d (patch) | |
tree | 2c6fe58de6bf324c481f8c9b9d533014b35535ad /src/dns | |
parent | 62bc5704a09329f12b6a93ba2e051b87ad8083e6 (diff) | |
download | gnunet-9bb646da61d4306cd7628c08b495b0ac5cf9435d.tar.gz gnunet-9bb646da61d4306cd7628c08b495b0ac5cf9435d.zip |
-starting with new DNS service code
Diffstat (limited to 'src/dns')
-rw-r--r-- | src/dns/gnunet-service-dns_new.c | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/src/dns/gnunet-service-dns_new.c b/src/dns/gnunet-service-dns_new.c new file mode 100644 index 000000000..65db9373b --- /dev/null +++ b/src/dns/gnunet-service-dns_new.c | |||
@@ -0,0 +1,551 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2012 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | 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 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file dns/gnunet-service-dns_new.c | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_util_lib.h" | ||
27 | #include "gnunet_constants.h" | ||
28 | #include "gnunet_protocols.h" | ||
29 | #include "gnunet_dnsparser_lib.h" | ||
30 | #include "gnunet_signatures.h" | ||
31 | #include "dns_new.h" | ||
32 | |||
33 | |||
34 | /** | ||
35 | * Entry we keep for each active request. | ||
36 | */ | ||
37 | struct RequestRecord | ||
38 | { | ||
39 | |||
40 | |||
41 | /** | ||
42 | * Entry of this request record in the heap (for fast removal). | ||
43 | */ | ||
44 | struct GNUNET_CONTAINER_HeapNode *hentry; | ||
45 | |||
46 | /** | ||
47 | * Array of clients and their answers. | ||
48 | */ | ||
49 | struct GNUNET_SERVER_Client *client; | ||
50 | |||
51 | }; | ||
52 | |||
53 | |||
54 | /** | ||
55 | * Entry we keep for each client. | ||
56 | */ | ||
57 | struct ClientRecord | ||
58 | { | ||
59 | /** | ||
60 | * Kept in doubly-linked list. | ||
61 | */ | ||
62 | struct ClientRecord *next; | ||
63 | |||
64 | /** | ||
65 | * Kept in doubly-linked list. | ||
66 | */ | ||
67 | struct ClientRecord *prev; | ||
68 | |||
69 | /** | ||
70 | * Handle to the client. | ||
71 | */ | ||
72 | struct GNUNET_SERVER_Client *client; | ||
73 | |||
74 | }; | ||
75 | |||
76 | |||
77 | /** | ||
78 | * The IPv4 UDP-Socket through which DNS-Resolves will be sent if they are not to be | ||
79 | * sent through gnunet. The port of this socket will not be hijacked. | ||
80 | */ | ||
81 | static struct GNUNET_NETWORK_Handle *dnsout4; | ||
82 | |||
83 | /** | ||
84 | * The IPv6 UDP-Socket through which DNS-Resolves will be sent if they are not to be | ||
85 | * sent through gnunet. The port of this socket will not be hijacked. | ||
86 | */ | ||
87 | static struct GNUNET_NETWORK_Handle *dnsout6; | ||
88 | |||
89 | /** | ||
90 | * Task for reading from dnsout4. | ||
91 | */ | ||
92 | static GNUNET_SCHEDULER_TaskIdentifier read4_task; | ||
93 | |||
94 | /** | ||
95 | * Task for reading from dnsout6. | ||
96 | */ | ||
97 | static GNUNET_SCHEDULER_TaskIdentifier read6_task; | ||
98 | |||
99 | /** | ||
100 | * The port bound to the socket dnsout (and/or dnsout6). We always (try) to bind | ||
101 | * both sockets to the same port. | ||
102 | */ | ||
103 | static uint16_t dnsoutport; | ||
104 | |||
105 | /** | ||
106 | * The configuration to use | ||
107 | */ | ||
108 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
109 | |||
110 | /** | ||
111 | * Handle to DNS hijacker helper process ("gnunet-helper-dns"). | ||
112 | */ | ||
113 | static struct GNUNET_HELPER_Handle *hijacker; | ||
114 | |||
115 | /** | ||
116 | * Command-line arguments we are giving to the hijacker process. | ||
117 | */ | ||
118 | static char *helper_argv[8]; | ||
119 | |||
120 | /** | ||
121 | * Head of DLL of clients we consult. | ||
122 | */ | ||
123 | static struct ClientRecord *clients_head; | ||
124 | |||
125 | /** | ||
126 | * Tail of DLL of clients we consult. | ||
127 | */ | ||
128 | static struct ClientRecord *clients_tail; | ||
129 | |||
130 | /** | ||
131 | * Hash map of open requests. | ||
132 | */ | ||
133 | static struct GNUNET_CONTAINER_MultiHashMap *request_map; | ||
134 | |||
135 | /** | ||
136 | * Heap with open requests (sorted by time received, oldest on top). | ||
137 | */ | ||
138 | static struct GNUNET_CONTAINER_Heap *request_heap; | ||
139 | |||
140 | |||
141 | |||
142 | /** | ||
143 | * Task run during shutdown. | ||
144 | * | ||
145 | * @param cls unused | ||
146 | * @param tc unused | ||
147 | */ | ||
148 | static void | ||
149 | cleanup_task (void *cls GNUNET_UNUSED, | ||
150 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
151 | { | ||
152 | unsigned int i; | ||
153 | struct RequestRecord *rr; | ||
154 | |||
155 | GNUNET_HELPER_stop (hijacker); | ||
156 | hijacker = NULL; | ||
157 | for (i=0;i<8;i++) | ||
158 | GNUNET_free_non_null (helper_argv[i]); | ||
159 | if (NULL != dnsout4) | ||
160 | { | ||
161 | GNUNET_NETWORK_socket_destroy (dnsout4); | ||
162 | dnsout4 = NULL; | ||
163 | } | ||
164 | if (GNUNET_SCHEDULER_NO_TASK != read4_task) | ||
165 | { | ||
166 | GNUNET_SCHEDULER_cancel (read4_task); | ||
167 | read4_task = GNUNET_SCHEDULER_NO_TASK; | ||
168 | } | ||
169 | if (NULL != dnsout6) | ||
170 | { | ||
171 | GNUNET_NETWORK_socket_destroy (dnsout6); | ||
172 | dnsout6 = NULL; | ||
173 | } | ||
174 | if (GNUNET_SCHEDULER_NO_TASK != read6_task) | ||
175 | { | ||
176 | GNUNET_SCHEDULER_cancel (read6_task); | ||
177 | read6_task = GNUNET_SCHEDULER_NO_TASK; | ||
178 | } | ||
179 | if (NULL != request_heap) | ||
180 | { | ||
181 | while (NULL != (rr = GNUNET_CONTAINER_heap_remove_root (request_heap))) | ||
182 | { | ||
183 | // FIXME: free rest of 'rr' | ||
184 | GNUNET_free (rr); | ||
185 | } | ||
186 | GNUNET_CONTAINER_heap_destroy (request_heap); | ||
187 | request_heap = NULL; | ||
188 | } | ||
189 | if (NULL != request_map) | ||
190 | { | ||
191 | GNUNET_CONTAINER_multihashmap_destroy (request_map); | ||
192 | request_map = NULL; | ||
193 | } | ||
194 | } | ||
195 | |||
196 | |||
197 | /** | ||
198 | * A client disconnected, clean up after it. | ||
199 | * | ||
200 | * @param cls unused | ||
201 | * @param client handle of client that disconnected | ||
202 | */ | ||
203 | static void | ||
204 | client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) | ||
205 | { | ||
206 | struct ClientRecord *cr; | ||
207 | |||
208 | /* FIXME: clean up after client */ | ||
209 | for (cr = clients_head; NULL != cr; cr = cr->next) | ||
210 | { | ||
211 | if (cr->client == client) | ||
212 | { | ||
213 | GNUNET_SERVER_client_drop (client); | ||
214 | GNUNET_CONTAINER_DLL_remove (clients_head, | ||
215 | clients_tail, | ||
216 | cr); | ||
217 | GNUNET_free (cr); | ||
218 | return; | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | |||
223 | |||
224 | /** | ||
225 | * Read a DNS response from the (unhindered) UDP-Socket | ||
226 | * | ||
227 | * @param cls socket to read from | ||
228 | * @param tc scheduler context (must be shutdown or read ready) | ||
229 | */ | ||
230 | static void | ||
231 | read_response (void *cls, | ||
232 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
233 | { | ||
234 | struct GNUNET_NETWORK_Handle *dnsout = cls; | ||
235 | struct sockaddr_in addr4; | ||
236 | struct sockaddr_in6 addr6; | ||
237 | struct sockaddr *addr; | ||
238 | socklen_t addrlen; | ||
239 | ssize_t r; | ||
240 | int len; | ||
241 | |||
242 | if (dnsout == dnsout4) | ||
243 | { | ||
244 | addrlen = sizeof (struct sockaddr_in); | ||
245 | addr = (struct sockaddr* ) &addr4; | ||
246 | read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
247 | dnsout, | ||
248 | &read_response, | ||
249 | dnsout); | ||
250 | } | ||
251 | else | ||
252 | { | ||
253 | addrlen = sizeof (struct sockaddr_in6); | ||
254 | addr = (struct sockaddr* ) &addr6; | ||
255 | read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
256 | dnsout, | ||
257 | &read_response, | ||
258 | dnsout); | ||
259 | } | ||
260 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | ||
261 | return; | ||
262 | |||
263 | #ifndef MINGW | ||
264 | if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len)) | ||
265 | { | ||
266 | /* conservative choice: */ | ||
267 | len = 65536; | ||
268 | } | ||
269 | #else | ||
270 | /* port the code above? */ | ||
271 | len = 65536; | ||
272 | #endif | ||
273 | |||
274 | { | ||
275 | unsigned char buf[len]; | ||
276 | |||
277 | memset (addr, 0, addrlen); | ||
278 | r = GNUNET_NETWORK_socket_recvfrom (dnsout, | ||
279 | buf, sizeof (buf), | ||
280 | addr, &addrlen); | ||
281 | if (-1 == r) | ||
282 | { | ||
283 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom"); | ||
284 | return; | ||
285 | } | ||
286 | // NOTE: struct dns_pkt *dns = (struct dns_pkt *) buf; | ||
287 | // FIXME: handle_response (buf, r, addr, addrlen); | ||
288 | } | ||
289 | } | ||
290 | |||
291 | |||
292 | /** | ||
293 | * Open source port for sending DNS request on IPv4. | ||
294 | * | ||
295 | * @return GNUNET_OK on success | ||
296 | */ | ||
297 | static int | ||
298 | open_port4 () | ||
299 | { | ||
300 | struct sockaddr_in addr; | ||
301 | socklen_t addrlen; | ||
302 | |||
303 | dnsout4 = GNUNET_NETWORK_socket_create (AF_INET, SOCK_DGRAM, 0); | ||
304 | if (dnsout4 == NULL) | ||
305 | return GNUNET_SYSERR; | ||
306 | |||
307 | memset (&addr, 0, sizeof (struct sockaddr_in)); | ||
308 | addr.sin_family = AF_INET; | ||
309 | int err = GNUNET_NETWORK_socket_bind (dnsout4, | ||
310 | (struct sockaddr *) &addr, | ||
311 | sizeof (struct sockaddr_in)); | ||
312 | |||
313 | if (err != GNUNET_OK) | ||
314 | { | ||
315 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
316 | _("Could not bind to any port: %s\n"), | ||
317 | STRERROR (errno)); | ||
318 | GNUNET_NETWORK_socket_destroy (dnsout4); | ||
319 | dnsout4 = NULL; | ||
320 | return GNUNET_SYSERR; | ||
321 | } | ||
322 | |||
323 | /* Read the port we bound to */ | ||
324 | addrlen = sizeof (struct sockaddr_in); | ||
325 | if (0 != getsockname (GNUNET_NETWORK_get_fd (dnsout4), | ||
326 | (struct sockaddr *) &addr, | ||
327 | &addrlen)) | ||
328 | { | ||
329 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
330 | _("Could not determine port I got: %s\n"), | ||
331 | STRERROR (errno)); | ||
332 | GNUNET_NETWORK_socket_destroy (dnsout4); | ||
333 | dnsout4 = NULL; | ||
334 | return GNUNET_SYSERR; | ||
335 | } | ||
336 | dnsoutport = htons (addr.sin_port); | ||
337 | |||
338 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
339 | _("GNUnet DNS will exit on source port %u\n"), | ||
340 | (unsigned int) dnsoutport); | ||
341 | read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
342 | dnsout, | ||
343 | &read_response, dnsout4); | ||
344 | return GNUNET_OK; | ||
345 | } | ||
346 | |||
347 | |||
348 | /** | ||
349 | * Open source port for sending DNS request on IPv6. Should be | ||
350 | * called AFTER open_port4. | ||
351 | * | ||
352 | * @return GNUNET_OK on success | ||
353 | */ | ||
354 | static int | ||
355 | open_port6 () | ||
356 | { | ||
357 | struct sockaddr_in6 addr; | ||
358 | |||
359 | dnsout6 = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_DGRAM, 0); | ||
360 | if (dnsout6 == NULL) | ||
361 | { | ||
362 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
363 | _("Could not create IPv6 socket: %s\n"), | ||
364 | STRERROR (errno)); | ||
365 | return GNUNET_SYSERR; | ||
366 | } | ||
367 | memset (&addr, 0, sizeof (struct sockaddr_in6)); | ||
368 | addr.sin6_family = AF_INET6; | ||
369 | addr.sin6_port = htons (dnsoutport); | ||
370 | int err = GNUNET_NETWORK_socket_bind (dnsout6, | ||
371 | (struct sockaddr *) &addr, | ||
372 | sizeof (struct sockaddr_in6)); | ||
373 | |||
374 | if (err != GNUNET_OK) | ||
375 | { | ||
376 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
377 | _("Could not bind to port %u: %s\n"), | ||
378 | (unsigned int) dnsoutport, | ||
379 | STRERROR (errno)); | ||
380 | return GNUNET_SYSERR; | ||
381 | } | ||
382 | if (0 == dnsoutport) | ||
383 | { | ||
384 | addrlen = sizeof (struct sockaddr_in6); | ||
385 | if (0 != getsockname (GNUNET_NETWORK_get_fd (dnsout6), | ||
386 | (struct sockaddr *) &addr, | ||
387 | &addrlen)) | ||
388 | { | ||
389 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
390 | _("Could not determine port I got: %s\n"), | ||
391 | STRERROR (errno)); | ||
392 | GNUNET_NETWORK_socket_destroy (dnsout6); | ||
393 | dnsout6 = NULL; | ||
394 | return GNUNET_SYSERR; | ||
395 | } | ||
396 | } | ||
397 | dnsoutport = htons (addr.sin6_port); | ||
398 | read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
399 | dnsout6, | ||
400 | &read_response, dnsout6); | ||
401 | return GNUNET_YES; | ||
402 | } | ||
403 | |||
404 | |||
405 | /** | ||
406 | * We got a new client. Make sure all new DNS requests pass by its desk. | ||
407 | * | ||
408 | * @param cls unused | ||
409 | * @param client the new client | ||
410 | * @param message the init message (unused) | ||
411 | */ | ||
412 | static void | ||
413 | handle_client_init (void *cls GNUNET_UNUSED, | ||
414 | struct GNUNET_SERVER_Client *client, | ||
415 | const struct GNUNET_MessageHeader *message GNUNET_UNUSED) | ||
416 | { | ||
417 | struct ClientRecord *cr; | ||
418 | |||
419 | cr = GNUNET_malloc (sizeof (struct ClientRecord)); | ||
420 | cr->client = client; | ||
421 | GNUNET_SERVER_client_keep (client); | ||
422 | GNUNET_CONTAINER_DLL_insert (clients_head, | ||
423 | clients_tail, | ||
424 | cr); | ||
425 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
426 | } | ||
427 | |||
428 | |||
429 | /** | ||
430 | * We got a response from a client. | ||
431 | * | ||
432 | * @param cls unused | ||
433 | * @param client the client | ||
434 | * @param message the response | ||
435 | */ | ||
436 | static void | ||
437 | handle_client_response (void *cls GNUNET_UNUSED, | ||
438 | struct GNUNET_SERVER_Client *client, | ||
439 | const struct GNUNET_MessageHeader *message GNUNET_UNUSED) | ||
440 | { | ||
441 | // FIXME: validate and parse response, process response | ||
442 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
443 | } | ||
444 | |||
445 | |||
446 | /** | ||
447 | * Functions with this signature are called whenever a complete | ||
448 | * message is received by the tokenizer from the DNS hijack process. | ||
449 | * | ||
450 | * @param cls closure | ||
451 | * @param client identification of the client | ||
452 | * @param message the actual message, a DNS request we should handle | ||
453 | */ | ||
454 | static void | ||
455 | process_helper_messages (void *cls, void *client, | ||
456 | const struct GNUNET_MessageHeader *message) | ||
457 | { | ||
458 | /* FIXME: parse message, create record, start processing! */ | ||
459 | /* FIXME: put request into queue for clients / system DNS */ | ||
460 | } | ||
461 | |||
462 | |||
463 | /** | ||
464 | * @param cls closure | ||
465 | * @param server the initialized server | ||
466 | * @param cfg_ configuration to use | ||
467 | */ | ||
468 | static void | ||
469 | run (void *cls, struct GNUNET_SERVER_Handle *server, | ||
470 | const struct GNUNET_CONFIGURATION_Handle *cfg_) | ||
471 | { | ||
472 | static const struct GNUNET_SERVER_MessageHandler handlers[] = { | ||
473 | /* callback, cls, type, size */ | ||
474 | {&handle_client_init, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT, sizeof (struct GNUNET_MessageHeader)}, | ||
475 | {&handle_client_response, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE, 0}, | ||
476 | {NULL, NULL, 0, 0} | ||
477 | }; | ||
478 | char port_s[6]; | ||
479 | char *virt_dns; | ||
480 | struct GNUNET_OS_Process *proc; | ||
481 | char *ifc_name; | ||
482 | char *ipv4_addr; | ||
483 | char *ipv4_mask; | ||
484 | char *ipv6_addr; | ||
485 | char *ipv6_mask; | ||
486 | |||
487 | cfg = cfg_; | ||
488 | if (GNUNET_YES == | ||
489 | GNUNET_CONFIGURATION_get_value_yesno (cfg_, "dns", "PROVIDE_EXIT")) | ||
490 | { | ||
491 | if ( (GNUNET_OK != open_port4 ()) && | ||
492 | (GNUNET_OK != open_port6 ()) ) | ||
493 | { | ||
494 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
495 | _("Failed to open any port to provide DNS exit\n")); | ||
496 | GNUNET_SCHEDULER_shutdown (); | ||
497 | return; | ||
498 | } | ||
499 | } | ||
500 | |||
501 | if (GNUNET_SYSERR == | ||
502 | GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "VIRTIFC", &ifc_name)) | ||
503 | { | ||
504 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
505 | "No entry 'VIRTDNS' in configuration!\n"); | ||
506 | GNUNET_SCHEDULER_shutdown (); | ||
507 | return; | ||
508 | } | ||
509 | /* FIXME: get all config options we need here! */ | ||
510 | request_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); | ||
511 | request_map = GNUNET_CONTAINER_multihashmap_create (1024 * 16); | ||
512 | GNUNET_snprintf (port_s, | ||
513 | sizeof (port_s), | ||
514 | "%u", | ||
515 | (unsigned int) dnsoutport); | ||
516 | helper_argv[0] = GNUNET_strdup ("gnunet-dns"); | ||
517 | helper_argv[1] = ifc_name; | ||
518 | helper_argv[2] = ipv6_addr; | ||
519 | helper_argv[3] = ipv6_mask; | ||
520 | helper_argv[4] = ipv4_addr; | ||
521 | helper_argv[5] = ipv4_mask; | ||
522 | helper_argv[6] = GNUNET_strdup (port_s); | ||
523 | helper_argv[7] = NULL; | ||
524 | hijacker = GNUNET_HELPER_start ("gnunet-helper-dns", | ||
525 | helper_argv, | ||
526 | &process_helper_messages, | ||
527 | NULL); | ||
528 | GNUNET_SERVER_add_handlers (server, handlers); | ||
529 | GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL); | ||
530 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task, | ||
531 | cls); | ||
532 | } | ||
533 | |||
534 | |||
535 | /** | ||
536 | * The main function for the dns service. | ||
537 | * | ||
538 | * @param argc number of arguments from the command line | ||
539 | * @param argv command line arguments | ||
540 | * @return 0 ok, 1 on error | ||
541 | */ | ||
542 | int | ||
543 | main (int argc, char *const *argv) | ||
544 | { | ||
545 | return (GNUNET_OK == | ||
546 | GNUNET_SERVICE_run (argc, argv, "dns", GNUNET_SERVICE_OPTION_NONE, | ||
547 | &run, NULL)) ? 0 : 1; | ||
548 | } | ||
549 | |||
550 | |||
551 | /* end of gnunet-service-dns_new.c */ | ||