2 This file is part of GNUnet.
3 (C) 2010, 2012 Christian Grothoff
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.
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.
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.
22 * @file pt/gnunet-daemon-pt.c
23 * @brief tool to manipulate DNS and VPN services to perform protocol translation (IPvX over GNUnet)
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet_dns_service.h"
29 #include "gnunet_dnsparser_lib.h"
30 #include "gnunet_vpn_service.h"
31 #include "gnunet_statistics_service.h"
35 * After how long do we time out if we could not get an IP from VPN?
37 #define TIMEOUT GNUNET_TIME_UNIT_MINUTES
41 * How many bytes of payload do we allow at most for a DNS reply?
42 * Given that this is pretty much limited to loopback, we can be
43 * pretty high (Linux loopback defaults to 16k, most local UDP packets
44 * should survive up to 9k (NFS), so 8k should be pretty safe in
47 #define MAX_DNS_SIZE (8 * 1024)
51 * Which group of DNS records are we currently processing?
61 * DNS authority records
63 AUTHORITY_RECORDS = 1,
66 * DNS additional records
68 ADDITIONAL_RECORDS = 2,
71 * We're done processing.
78 * Information tracked per DNS request that we are processing.
83 * Handle to submit the final result.
85 struct GNUNET_DNS_RequestHandle *rh;
88 * DNS packet that is being modified.
90 struct GNUNET_DNSPARSER_Packet *dns;
93 * Active redirection request with the VPN.
95 struct GNUNET_VPN_RedirectionRequest *rr;
98 * Record for which we have an active redirection request.
100 struct GNUNET_DNSPARSER_Record *rec;
103 * Offset in the current record group that is being modified.
108 * Group that is being modified
110 enum RequestGroup group;
116 * The handle to the configuration used throughout the process
118 static const struct GNUNET_CONFIGURATION_Handle *cfg;
121 * The handle to the VPN
123 static struct GNUNET_VPN_Handle *vpn_handle;
128 static struct GNUNET_STATISTICS_Handle *stats;
133 static struct GNUNET_DNS_Handle *dns_handle;
136 * Are we doing IPv4-pt?
141 * Are we doing IPv6-pt?
147 * We're done modifying all records in the response. Submit the reply
148 * and free the resources of the rc.
150 * @param rc context to process
153 finish_request (struct RequestContext *rc)
159 GNUNET_DNSPARSER_pack (rc->dns,
164 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
165 _("Failed to pack DNS request. Dropping.\n"));
166 GNUNET_DNS_request_drop (rc->rh);
170 GNUNET_STATISTICS_update (stats,
171 gettext_noop ("# DNS requests mapped to VPN"),
173 GNUNET_DNS_request_answer (rc->rh,
177 GNUNET_DNSPARSER_free_packet (rc->dns);
183 * Process the next record of the given request context.
184 * When done, submit the reply and free the resources of
187 * @param rc context to process
190 submit_request (struct RequestContext *rc);
194 * Callback invoked from the VPN service once a redirection is
195 * available. Provides the IP address that can now be used to
196 * reach the requested destination. We substitute the active
197 * record and then continue with 'submit_request' to look at
200 * @param cls our 'struct RequestContext'
201 * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
202 * will match 'result_af' from the request
203 * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
204 * that the VPN allocated for the redirection;
205 * traffic to this IP will now be redirected to the
206 * specified target peer; NULL on error
209 vpn_allocation_callback (void *cls,
213 struct RequestContext *rc = cls;
218 GNUNET_DNS_request_drop (rc->rh);
219 GNUNET_DNSPARSER_free_packet (rc->dns);
223 GNUNET_STATISTICS_update (stats,
224 gettext_noop ("# DNS records modified"),
226 switch (rc->rec->type)
228 case GNUNET_DNSPARSER_TYPE_A:
229 GNUNET_assert (AF_INET == af);
230 memcpy (rc->rec->data.raw.data, address, sizeof (struct in_addr));
232 case GNUNET_DNSPARSER_TYPE_AAAA:
233 GNUNET_assert (AF_INET6 == af);
234 memcpy (rc->rec->data.raw.data, address, sizeof (struct in6_addr));
246 * Modify the given DNS record by asking VPN to create a tunnel
247 * to the given address. When done, continue with submitting
248 * other records from the request context ('submit_request' is
251 * @param rc context to process
252 * @param rec record to modify
255 modify_address (struct RequestContext *rc,
256 struct GNUNET_DNSPARSER_Record *rec)
262 case GNUNET_DNSPARSER_TYPE_A:
264 GNUNET_assert (rec->data.raw.data_len == sizeof (struct in_addr));
266 case GNUNET_DNSPARSER_TYPE_AAAA:
268 GNUNET_assert (rec->data.raw.data_len == sizeof (struct in6_addr));
275 rc->rr = GNUNET_VPN_redirect_to_ip (vpn_handle,
279 GNUNET_TIME_relative_to_absolute (TIMEOUT),
280 &vpn_allocation_callback,
286 * Process the next record of the given request context.
287 * When done, submit the reply and free the resources of
290 * @param rc context to process
293 submit_request (struct RequestContext *rc)
295 struct GNUNET_DNSPARSER_Record *ra;
304 ra = rc->dns->answers;
305 ra_len = rc->dns->num_answers;
307 case AUTHORITY_RECORDS:
308 ra = rc->dns->authority_records;
309 ra_len = rc->dns->num_authority_records;
311 case ADDITIONAL_RECORDS:
312 ra = rc->dns->additional_records;
313 ra_len = rc->dns->num_additional_records;
321 for (i=rc->offset;i<ra_len;i++)
325 case GNUNET_DNSPARSER_TYPE_A:
329 modify_address (rc, &ra[i]);
333 case GNUNET_DNSPARSER_TYPE_AAAA:
337 modify_address (rc, &ra[i]);
349 * Test if any of the given records need protocol-translation work.
351 * @param ra array of records
352 * @param ra_len number of entries in ra
353 * @return GNUNET_YES if any of the given records require protocol-translation
356 work_test (const struct GNUNET_DNSPARSER_Record *ra,
361 for (i=0;i<ra_len;i++)
365 case GNUNET_DNSPARSER_TYPE_A:
369 case GNUNET_DNSPARSER_TYPE_AAAA:
380 * Signature of a function that is called whenever the DNS service
381 * encounters a DNS request and needs to do something with it. The
382 * function has then the chance to generate or modify the response by
383 * calling one of the three "GNUNET_DNS_request_*" continuations.
385 * When a request is intercepted, this function is called first to
386 * give the client a chance to do the complete address resolution;
387 * "rdata" will be NULL for this first call for a DNS request, unless
388 * some other client has already filled in a response.
390 * If multiple clients exist, all of them are called before the global
391 * DNS. The global DNS is only called if all of the clients'
392 * functions call GNUNET_DNS_request_forward. Functions that call
393 * GNUNET_DNS_request_forward will be called again before a final
394 * response is returned to the application. If any of the clients'
395 * functions call GNUNET_DNS_request_drop, the response is dropped.
398 * @param rh request handle to user for reply
399 * @param request_length number of bytes in request
400 * @param request udp payload of the DNS request
403 dns_request_handler (void *cls,
404 struct GNUNET_DNS_RequestHandle *rh,
405 size_t request_length,
408 struct GNUNET_DNSPARSER_Packet *dns;
409 struct RequestContext *rc;
412 GNUNET_STATISTICS_update (stats,
413 gettext_noop ("# DNS requests intercepted"),
415 dns = GNUNET_DNSPARSER_parse (request, request_length);
418 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
419 _("Failed to parse DNS request. Dropping.\n"));
420 GNUNET_DNS_request_drop (rh);
424 work |= work_test (dns->answers, dns->num_answers);
425 work |= work_test (dns->authority_records, dns->num_authority_records);
426 work |= work_test (dns->additional_records, dns->num_additional_records);
429 GNUNET_DNS_request_forward (rh);
432 rc = GNUNET_malloc (sizeof (struct RequestContext));
442 * Function scheduled as very last function, cleans up after us
445 cleanup (void *cls GNUNET_UNUSED,
446 const struct GNUNET_SCHEDULER_TaskContext *tskctx)
448 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
449 "Protocol translation daemon is shutting down now\n");
450 if (vpn_handle != NULL)
452 GNUNET_VPN_disconnect (vpn_handle);
455 if (dns_handle != NULL)
457 GNUNET_DNS_disconnect (dns_handle);
462 GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
469 * @brief Main function that will be run by the scheduler.
472 * @param args remaining command-line arguments
473 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
474 * @param cfg_ configuration
477 run (void *cls, char *const *args GNUNET_UNUSED,
478 const char *cfgfile GNUNET_UNUSED,
479 const struct GNUNET_CONFIGURATION_Handle *cfg_)
482 stats = GNUNET_STATISTICS_create ("pt", cfg);
483 ipv4_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV4");
484 ipv6_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV6");
485 if (! (ipv4_pt || ipv6_pt))
487 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
488 _("No useful service enabled. Exiting.\n"));
489 GNUNET_SCHEDULER_shutdown ();
492 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls);
494 = GNUNET_DNS_connect (cfg,
495 GNUNET_DNS_FLAG_POST_RESOLUTION,
496 &dns_request_handler, NULL);
497 if (NULL == dns_handle)
499 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
500 _("Failed to connect to %s service. Exiting.\n"),
502 GNUNET_SCHEDULER_shutdown ();
505 vpn_handle = GNUNET_VPN_connect (cfg);
506 if (NULL == vpn_handle)
508 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
509 _("Failed to connect to %s service. Exiting.\n"),
511 GNUNET_SCHEDULER_shutdown ();
520 * @param argc number of arguments from the command line
521 * @param argv command line arguments
522 * @return 0 ok, 1 on error
525 main (int argc, char *const *argv)
527 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
528 GNUNET_GETOPT_OPTION_END
532 GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-pt",
534 ("Daemon to run to perform IP protocol translation to GNUnet"),
535 options, &run, NULL)) ? 0 : 1;
539 /* end of gnunet-daemon-pt.c */