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
29 * - figure out how/where/when/who tunnels DNS over mesh when necessary!
32 #include "gnunet_util_lib.h"
33 #include "gnunet_dns_service.h"
34 #include "gnunet_dnsparser_lib.h"
35 #include "gnunet_vpn_service.h"
36 #include "gnunet_statistics_service.h"
40 * After how long do we time out if we could not get an IP from VPN?
42 #define TIMEOUT GNUNET_TIME_UNIT_MINUTES
46 * Which group of DNS records are we currently processing?
56 * DNS authority records
58 AUTHORITY_RECORDS = 1,
61 * DNS additional records
63 ADDITIONAL_RECORDS = 2,
65 * We're done processing.
72 * Information tracked per DNS request that we are processing.
77 * Handle to submit the final result.
79 struct GNUNET_DNS_RequestHandle *rh;
82 * DNS packet that is being modified.
84 struct GNUNET_DNSPARSER_Packet *dns;
87 * Active redirection request with the VPN.
89 struct GNUNET_VPN_RedirectionRequest *rr;
92 * Record for which we have an active redirection request.
94 struct GNUNET_DNSPARSER_Record *rec;
97 * Offset in the current record group that is being modified.
102 * Group that is being modified
104 enum RequestGroup group;
110 * The handle to the configuration used throughout the process
112 static const struct GNUNET_CONFIGURATION_Handle *cfg;
115 * The handle to the VPN
117 static struct GNUNET_VPN_Handle *vpn_handle;
122 static struct GNUNET_STATISTICS_Handle *stats;
127 static struct GNUNET_DNS_Handle *dns_handle;
130 * Are we doing IPv4-pt?
135 * Are we doing IPv6-pt?
141 * We're done modifying all records in the response. Submit the reply
142 * and free the resources of the rc.
144 * @param rc context to process
147 finish_request (struct RequestContext *rc)
153 GNUNET_DNSPARSER_pack (rc->dns,
158 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
159 _("Failed to pack DNS request. Dropping.\n"));
160 GNUNET_DNS_request_drop (rc->rh);
164 GNUNET_DNS_request_answer (rc->rh,
168 GNUNET_DNSPARSER_free_packet (rc->dns);
174 * Process the next record of the given request context.
175 * When done, submit the reply and free the resources of
178 * @param rc context to process
181 submit_request (struct RequestContext *rc);
185 * Callback invoked from the VPN service once a redirection is
186 * available. Provides the IP address that can now be used to
187 * reach the requested destination. We substitute the active
188 * record and then continue with 'submit_request' to look at
191 * @param cls our 'struct RequestContext'
192 * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
193 * will match 'result_af' from the request
194 * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
195 * that the VPN allocated for the redirection;
196 * traffic to this IP will now be redirected to the
197 * specified target peer; NULL on error
200 vpn_allocation_callback (void *cls,
204 struct RequestContext *rc = cls;
209 GNUNET_DNS_request_drop (rc->rh);
210 GNUNET_DNSPARSER_free_packet (rc->dns);
214 switch (rc->rec->type)
216 case GNUNET_DNSPARSER_TYPE_A:
217 GNUNET_assert (AF_INET == af);
218 memcpy (rc->rec->data.raw.data, address, sizeof (struct in_addr));
220 case GNUNET_DNSPARSER_TYPE_AAAA:
221 GNUNET_assert (AF_INET6 == af);
222 memcpy (rc->rec->data.raw.data, address, sizeof (struct in6_addr));
234 * Modify the given DNS record by asking VPN to create a tunnel
235 * to the given address. When done, continue with submitting
236 * other records from the request context ('submit_request' is
239 * @param rc context to process
240 * @param rec record to modify
243 modify_address (struct RequestContext *rc,
244 struct GNUNET_DNSPARSER_Record *rec)
250 case GNUNET_DNSPARSER_TYPE_A:
252 GNUNET_assert (rec->data.raw.data_len == sizeof (struct in_addr));
254 case GNUNET_DNSPARSER_TYPE_AAAA:
256 GNUNET_assert (rec->data.raw.data_len == sizeof (struct in6_addr));
263 rc->rr = GNUNET_VPN_redirect_to_ip (vpn_handle,
267 GNUNET_TIME_relative_to_absolute (TIMEOUT),
268 &vpn_allocation_callback,
274 * Process the next record of the given request context.
275 * When done, submit the reply and free the resources of
278 * @param rc context to process
281 submit_request (struct RequestContext *rc)
283 struct GNUNET_DNSPARSER_Record *ra;
292 ra = rc->dns->answers;
293 ra_len = rc->dns->num_answers;
295 case AUTHORITY_RECORDS:
296 ra = rc->dns->authority_records;
297 ra_len = rc->dns->num_authority_records;
299 case ADDITIONAL_RECORDS:
300 ra = rc->dns->additional_records;
301 ra_len = rc->dns->num_additional_records;
309 for (i=rc->offset;i<ra_len;i++)
313 case GNUNET_DNSPARSER_TYPE_A:
317 modify_address (rc, &ra[i]);
321 case GNUNET_DNSPARSER_TYPE_AAAA:
325 modify_address (rc, &ra[i]);
337 * Test if any of the given records need protocol-translation work.
339 * @param ra array of records
340 * @param ra_len number of entries in ra
341 * @return GNUNET_YES if any of the given records require protocol-translation
344 work_test (const struct GNUNET_DNSPARSER_Record *ra,
349 for (i=0;i<ra_len;i++)
353 case GNUNET_DNSPARSER_TYPE_A:
357 case GNUNET_DNSPARSER_TYPE_AAAA:
368 * Signature of a function that is called whenever the DNS service
369 * encounters a DNS request and needs to do something with it. The
370 * function has then the chance to generate or modify the response by
371 * calling one of the three "GNUNET_DNS_request_*" continuations.
373 * When a request is intercepted, this function is called first to
374 * give the client a chance to do the complete address resolution;
375 * "rdata" will be NULL for this first call for a DNS request, unless
376 * some other client has already filled in a response.
378 * If multiple clients exist, all of them are called before the global
379 * DNS. The global DNS is only called if all of the clients'
380 * functions call GNUNET_DNS_request_forward. Functions that call
381 * GNUNET_DNS_request_forward will be called again before a final
382 * response is returned to the application. If any of the clients'
383 * functions call GNUNET_DNS_request_drop, the response is dropped.
386 * @param rh request handle to user for reply
387 * @param request_length number of bytes in request
388 * @param request udp payload of the DNS request
391 dns_request_handler (void *cls,
392 struct GNUNET_DNS_RequestHandle *rh,
393 size_t request_length,
396 struct GNUNET_DNSPARSER_Packet *dns;
397 struct RequestContext *rc;
400 dns = GNUNET_DNSPARSER_parse (request, request_length);
403 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
404 _("Failed to parse DNS request. Dropping.\n"));
405 GNUNET_DNS_request_drop (rh);
409 work |= work_test (dns->answers, dns->num_answers);
410 work |= work_test (dns->authority_records, dns->num_authority_records);
411 work |= work_test (dns->additional_records, dns->num_additional_records);
414 GNUNET_DNS_request_forward (rh);
417 rc = GNUNET_malloc (sizeof (struct RequestContext));
427 * Function scheduled as very last function, cleans up after us
430 cleanup (void *cls GNUNET_UNUSED,
431 const struct GNUNET_SCHEDULER_TaskContext *tskctx)
433 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434 "Pt service is shutting down now\n");
435 if (vpn_handle != NULL)
437 GNUNET_VPN_disconnect (vpn_handle);
440 if (dns_handle != NULL)
442 GNUNET_DNS_disconnect (dns_handle);
447 GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
454 * @brief Main function that will be run by the scheduler.
457 * @param args remaining command-line arguments
458 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
459 * @param cfg_ configuration
462 run (void *cls, char *const *args GNUNET_UNUSED,
463 const char *cfgfile GNUNET_UNUSED,
464 const struct GNUNET_CONFIGURATION_Handle *cfg_)
467 stats = GNUNET_STATISTICS_create ("pt", cfg);
468 ipv4_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV4");
469 ipv6_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV6");
470 if (! (ipv4_pt || ipv6_pt))
472 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
473 _("No useful service enabled. Exiting.\n"));
474 GNUNET_SCHEDULER_shutdown ();
477 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls);
479 = GNUNET_DNS_connect (cfg,
480 GNUNET_DNS_FLAG_POST_RESOLUTION,
481 &dns_request_handler, NULL);
482 if (NULL == dns_handle)
484 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
485 _("Failed to connect to %s service. Exiting.\n"),
487 GNUNET_SCHEDULER_shutdown ();
490 vpn_handle = GNUNET_VPN_connect (cfg);
491 if (NULL == vpn_handle)
493 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
494 _("Failed to connect to %s service. Exiting.\n"),
496 GNUNET_SCHEDULER_shutdown ();
505 * @param argc number of arguments from the command line
506 * @param argv command line arguments
507 * @return 0 ok, 1 on error
510 main (int argc, char *const *argv)
512 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
513 GNUNET_GETOPT_OPTION_END
517 GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-pt",
519 ("Daemon to run to perform IP protocol translation to GNUnet"),
520 options, &run, NULL)) ? 0 : 1;
524 /* end of gnunet-daemon-pt.c */