aboutsummaryrefslogtreecommitdiff
path: root/src/dns
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2012-01-02 15:24:49 +0000
committerChristian Grothoff <christian@grothoff.org>2012-01-02 15:24:49 +0000
commit9bb646da61d4306cd7628c08b495b0ac5cf9435d (patch)
tree2c6fe58de6bf324c481f8c9b9d533014b35535ad /src/dns
parent62bc5704a09329f12b6a93ba2e051b87ad8083e6 (diff)
downloadgnunet-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.c551
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 */
37struct 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 */
57struct 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 */
81static 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 */
87static struct GNUNET_NETWORK_Handle *dnsout6;
88
89/**
90 * Task for reading from dnsout4.
91 */
92static GNUNET_SCHEDULER_TaskIdentifier read4_task;
93
94/**
95 * Task for reading from dnsout6.
96 */
97static 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 */
103static uint16_t dnsoutport;
104
105/**
106 * The configuration to use
107 */
108static const struct GNUNET_CONFIGURATION_Handle *cfg;
109
110/**
111 * Handle to DNS hijacker helper process ("gnunet-helper-dns").
112 */
113static struct GNUNET_HELPER_Handle *hijacker;
114
115/**
116 * Command-line arguments we are giving to the hijacker process.
117 */
118static char *helper_argv[8];
119
120/**
121 * Head of DLL of clients we consult.
122 */
123static struct ClientRecord *clients_head;
124
125/**
126 * Tail of DLL of clients we consult.
127 */
128static struct ClientRecord *clients_tail;
129
130/**
131 * Hash map of open requests.
132 */
133static struct GNUNET_CONTAINER_MultiHashMap *request_map;
134
135/**
136 * Heap with open requests (sorted by time received, oldest on top).
137 */
138static 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 */
148static void
149cleanup_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 */
203static void
204client_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 */
230static void
231read_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 */
297static int
298open_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 */
354static int
355open_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 */
412static void
413handle_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 */
436static void
437handle_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 */
454static void
455process_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 */
468static void
469run (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 */
542int
543main (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 */