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_mesh_service.h"
31 #include "gnunet_tun_lib.h"
32 #include "gnunet_dht_service.h"
33 #include "gnunet_vpn_service.h"
34 #include "gnunet_statistics_service.h"
35 #include "gnunet_applications.h"
36 #include "block_dns.h"
40 * After how long do we time out if we could not get an IP from VPN or MESH?
42 #define TIMEOUT GNUNET_TIME_UNIT_MINUTES
45 * How many bytes of payload do we allow at most for a DNS reply?
46 * Given that this is pretty much limited to loopback, we can be
47 * pretty high (Linux loopback defaults to 16k, most local UDP packets
48 * should survive up to 9k (NFS), so 8k should be pretty safe in
51 #define MAX_DNS_SIZE (8 * 1024)
54 * How many tunnels do we open at most at the same time?
56 #define MAX_OPEN_TUNNELS 4
60 * Which group of DNS records are we currently processing?
70 * DNS authority records
72 AUTHORITY_RECORDS = 1,
75 * DNS additional records
77 ADDITIONAL_RECORDS = 2,
80 * We're done processing.
87 * Information tracked per DNS reply that we are processing.
92 * Handle to submit the final result.
94 struct GNUNET_DNS_RequestHandle *rh;
97 * DNS packet that is being modified.
99 struct GNUNET_DNSPARSER_Packet *dns;
102 * Active redirection request with the VPN.
104 struct GNUNET_VPN_RedirectionRequest *rr;
107 * Record for which we have an active redirection request.
109 struct GNUNET_DNSPARSER_Record *rec;
112 * Offset in the current record group that is being modified.
117 * Group that is being modified
119 enum RequestGroup group;
125 * Handle to a peer that advertised that it is willing to serve
126 * as a DNS exit. We try to keep a few tunnels open and a few
135 struct MeshExit *next;
140 struct MeshExit *prev;
143 * Tunnel we use for DNS requests over MESH, NULL if we did
144 * not initialze a tunnel to this peer yet.
146 struct GNUNET_MESH_Tunnel *mesh_tunnel;
149 * At what time did the peer's advertisement expire?
151 struct GNUNET_TIME_Absolute expiration;
154 * Head of DLL of requests waiting for a response.
156 struct RequestContext *receive_queue_head;
159 * Tail of DLL of requests waiting for a response.
161 struct RequestContext *receive_queue_tail;
164 * Head of DLL of requests to be transmitted to a mesh_tunnel.
166 struct RequestContext *transmit_queue_head;
169 * Tail of DLL of requests to be transmitted to a mesh_tunnel.
171 struct RequestContext *transmit_queue_tail;
174 * Active transmission request for this tunnel (or NULL).
176 struct GNUNET_MESH_TransmitHandle *mesh_th;
179 * Identity of the peer that is providing the exit for us.
181 struct GNUNET_PeerIdentity peer;
184 * How many DNS requests did we transmit via this tunnel?
186 unsigned int num_transmitted;
189 * How many DNS requests were answered via this tunnel?
191 unsigned int num_answered;
198 * State we keep for a request that is going out via MESH.
200 struct RequestContext
203 * We keep these in a DLL.
205 struct RequestContext *next;
208 * We keep these in a DLL.
210 struct RequestContext *prev;
213 * Exit that was chosen for this request.
215 struct MeshExit *exit;
218 * Handle for interaction with DNS service.
220 struct GNUNET_DNS_RequestHandle *rh;
223 * Message we're sending out via MESH, allocated at the
224 * end of this struct.
226 const struct GNUNET_MessageHeader *mesh_message;
229 * Task used to abort this operation with timeout.
231 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
234 * Length of the request message that follows this struct.
239 * ID of the original DNS request (used to match the reply).
244 * #GNUNET_NO if this request is still in the transmit_queue,
245 * #GNUNET_YES if we are in the receive_queue.
247 int16_t was_transmitted;
253 * Head of DLL of mesh exits. Mesh exits with an open tunnel are
254 * always at the beginning (so we do not have to traverse the entire
255 * list to find them).
257 static struct MeshExit *exit_head;
260 * Tail of DLL of mesh exits.
262 static struct MeshExit *exit_tail;
265 * The handle to the configuration used throughout the process
267 static const struct GNUNET_CONFIGURATION_Handle *cfg;
270 * The handle to the VPN
272 static struct GNUNET_VPN_Handle *vpn_handle;
275 * The handle to the MESH service
277 static struct GNUNET_MESH_Handle *mesh_handle;
282 static struct GNUNET_STATISTICS_Handle *stats;
285 * The handle to DNS post-resolution modifications.
287 static struct GNUNET_DNS_Handle *dns_post_handle;
290 * The handle to DNS pre-resolution modifications.
292 static struct GNUNET_DNS_Handle *dns_pre_handle;
295 * Handle to access the DHT.
297 static struct GNUNET_DHT_Handle *dht;
300 * Our DHT GET operation to find DNS exits.
302 static struct GNUNET_DHT_GetHandle *dht_get;
305 * Are we doing IPv4-pt?
310 * Are we doing IPv6-pt?
315 * Are we tunneling DNS queries?
317 static int dns_tunnel;
320 * Number of DNS exit peers we currently have in the mesh tunnel.
321 * Used to see if using the mesh tunnel makes any sense right now,
322 * as well as to decide if we should open new tunnels.
324 static unsigned int dns_exit_available;
328 * We are short on mesh exits, try to open another one.
333 struct MeshExit *pos;
334 uint32_t candidate_count;
335 uint32_t candidate_selected;
338 for (pos = exit_head; NULL != pos; pos = pos->next)
339 if (NULL == pos->mesh_tunnel)
341 candidate_selected = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
344 for (pos = exit_head; NULL != pos; pos = pos->next)
345 if (NULL == pos->mesh_tunnel)
348 if (candidate_selected < candidate_count)
350 /* move to the head of the DLL */
351 pos->mesh_tunnel = GNUNET_MESH_tunnel_create (mesh_handle,
354 GNUNET_APPLICATION_TYPE_INTERNET_RESOLVER,
355 GNUNET_YES /* no buffer */,
356 GNUNET_NO /* reliable */);
357 if (NULL == pos->mesh_tunnel)
362 GNUNET_CONTAINER_DLL_remove (exit_head,
365 GNUNET_CONTAINER_DLL_insert (exit_head,
368 dns_exit_available++;
372 GNUNET_assert (NULL == exit_head);
377 * Compute the weight of the given exit. The higher the weight,
378 * the more likely it will be that the tunnel will be chosen.
379 * A weigt of zero means that we should close the tunnel as it
380 * is so bad, that we should not use it.
382 * @param exit exit to calculate the weight for
383 * @return weight of the tunnel
386 get_tunnel_weight (struct MeshExit *exit)
389 uint32_t drop_percent;
390 uint32_t good_percent;
392 GNUNET_assert (exit->num_transmitted >= exit->num_answered);
393 dropped = exit->num_transmitted - exit->num_answered;
394 if (exit->num_transmitted > 0)
395 drop_percent = (uint32_t) ((100LL * dropped) / exit->num_transmitted);
397 drop_percent = 50; /* no data */
398 if ( (exit->num_transmitted > 20) &&
399 (drop_percent > 25) )
400 return 0; /* statistically significant, and > 25% loss, die */
401 good_percent = 100 - drop_percent;
402 GNUNET_assert (0 != good_percent);
403 if ( UINT32_MAX / good_percent / good_percent < exit->num_transmitted)
404 return UINT32_MAX; /* formula below would overflow */
405 return 1 + good_percent * good_percent * exit->num_transmitted;
410 * Choose a mesh exit for a DNS request. We try to use a tunnel
411 * that is reliable and currently available. All existing
412 * tunnels are given a base weight of 1, plus a score relating
413 * to the total number of queries answered in relation to the
414 * total number of queries we sent to that tunnel. That
415 * score is doubled if the tunnel is currently idle.
417 * @return NULL if no exit is known, otherwise the
418 * exit that we should use to queue a message with
420 static struct MeshExit *
423 struct MeshExit *pos;
424 uint64_t total_transmitted;
425 uint64_t selected_offset;
426 uint32_t tunnel_weight;
428 total_transmitted = 0;
429 for (pos = exit_head; NULL != pos; pos = pos->next)
431 if (NULL == pos->mesh_tunnel)
433 tunnel_weight = get_tunnel_weight (pos);
434 total_transmitted += tunnel_weight;
435 /* double weight for idle tunnels */
436 if (NULL == pos->mesh_th)
437 total_transmitted += tunnel_weight;
439 if (0 == total_transmitted)
441 /* no tunnels available, or only a very bad one... */
444 selected_offset = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
446 total_transmitted = 0;
447 for (pos = exit_head; NULL != pos; pos = pos->next)
449 if (NULL == pos->mesh_tunnel)
451 tunnel_weight = get_tunnel_weight (pos);
452 total_transmitted += tunnel_weight;
453 /* double weight for idle tunnels */
454 if (NULL == pos->mesh_th)
455 total_transmitted += tunnel_weight;
456 if (total_transmitted > selected_offset)
465 * We're done modifying all records in the response. Submit the reply
466 * and free the resources of the rc.
468 * @param rc context to process
471 finish_request (struct ReplyContext *rc)
477 GNUNET_DNSPARSER_pack (rc->dns,
482 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
483 _("Failed to pack DNS request. Dropping.\n"));
484 GNUNET_DNS_request_drop (rc->rh);
488 GNUNET_STATISTICS_update (stats,
489 gettext_noop ("# DNS requests mapped to VPN"),
491 GNUNET_DNS_request_answer (rc->rh,
495 GNUNET_DNSPARSER_free_packet (rc->dns);
501 * Process the next record of the given request context.
502 * When done, submit the reply and free the resources of
505 * @param rc context to process
508 submit_request (struct ReplyContext *rc);
512 * Callback invoked from the VPN service once a redirection is
513 * available. Provides the IP address that can now be used to
514 * reach the requested destination. We substitute the active
515 * record and then continue with 'submit_request' to look at
518 * @param cls our `struct ReplyContext`
519 * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
520 * will match 'result_af' from the request
521 * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
522 * that the VPN allocated for the redirection;
523 * traffic to this IP will now be redirected to the
524 * specified target peer; NULL on error
527 vpn_allocation_callback (void *cls,
531 struct ReplyContext *rc = cls;
536 GNUNET_DNS_request_drop (rc->rh);
537 GNUNET_DNSPARSER_free_packet (rc->dns);
541 GNUNET_STATISTICS_update (stats,
542 gettext_noop ("# DNS records modified"),
544 switch (rc->rec->type)
546 case GNUNET_DNSPARSER_TYPE_A:
547 GNUNET_assert (AF_INET == af);
548 memcpy (rc->rec->data.raw.data, address, sizeof (struct in_addr));
550 case GNUNET_DNSPARSER_TYPE_AAAA:
551 GNUNET_assert (AF_INET6 == af);
552 memcpy (rc->rec->data.raw.data, address, sizeof (struct in6_addr));
564 * Modify the given DNS record by asking VPN to create a tunnel
565 * to the given address. When done, continue with submitting
566 * other records from the request context ('submit_request' is
569 * @param rc context to process
570 * @param rec record to modify
573 modify_address (struct ReplyContext *rc,
574 struct GNUNET_DNSPARSER_Record *rec)
580 case GNUNET_DNSPARSER_TYPE_A:
582 GNUNET_assert (rec->data.raw.data_len == sizeof (struct in_addr));
584 case GNUNET_DNSPARSER_TYPE_AAAA:
586 GNUNET_assert (rec->data.raw.data_len == sizeof (struct in6_addr));
593 rc->rr = GNUNET_VPN_redirect_to_ip (vpn_handle,
596 GNUNET_TIME_relative_to_absolute (TIMEOUT),
597 &vpn_allocation_callback,
603 * Process the next record of the given request context.
604 * When done, submit the reply and free the resources of
607 * @param rc context to process
610 submit_request (struct ReplyContext *rc)
612 struct GNUNET_DNSPARSER_Record *ra;
621 ra = rc->dns->answers;
622 ra_len = rc->dns->num_answers;
624 case AUTHORITY_RECORDS:
625 ra = rc->dns->authority_records;
626 ra_len = rc->dns->num_authority_records;
628 case ADDITIONAL_RECORDS:
629 ra = rc->dns->additional_records;
630 ra_len = rc->dns->num_additional_records;
638 for (i=rc->offset;i<ra_len;i++)
642 case GNUNET_DNSPARSER_TYPE_A:
646 modify_address (rc, &ra[i]);
650 case GNUNET_DNSPARSER_TYPE_AAAA:
654 modify_address (rc, &ra[i]);
666 * Test if any of the given records need protocol-translation work.
668 * @param ra array of records
669 * @param ra_len number of entries in @a ra
670 * @return #GNUNET_YES if any of the given records require protocol-translation
673 work_test (const struct GNUNET_DNSPARSER_Record *ra,
678 for (i=0;i<ra_len;i++)
682 case GNUNET_DNSPARSER_TYPE_A:
686 case GNUNET_DNSPARSER_TYPE_AAAA:
697 * This function is called AFTER we got an IP address for a
698 * DNS request. Now, the PT daemon has the chance to substitute
699 * the IP address with one from the VPN range to tunnel requests
700 * destined for this IP address via VPN and MESH.
703 * @param rh request handle to user for reply
704 * @param request_length number of bytes in request
705 * @param request udp payload of the DNS request
708 dns_post_request_handler (void *cls,
709 struct GNUNET_DNS_RequestHandle *rh,
710 size_t request_length,
713 struct GNUNET_DNSPARSER_Packet *dns;
714 struct ReplyContext *rc;
717 GNUNET_STATISTICS_update (stats,
718 gettext_noop ("# DNS replies intercepted"),
720 dns = GNUNET_DNSPARSER_parse (request, request_length);
723 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
724 _("Failed to parse DNS request. Dropping.\n"));
725 GNUNET_DNS_request_drop (rh);
729 work |= work_test (dns->answers, dns->num_answers);
730 work |= work_test (dns->authority_records, dns->num_authority_records);
731 work |= work_test (dns->additional_records, dns->num_additional_records);
734 GNUNET_DNS_request_forward (rh);
735 GNUNET_DNSPARSER_free_packet (dns);
738 rc = GNUNET_new (struct ReplyContext);
748 * Transmit a DNS request via MESH and move the request
749 * handle to the receive queue.
751 * @param cls the `struct MeshExit`
752 * @param size number of bytes available in buf
753 * @param buf where to copy the message
754 * @return number of bytes written to buf
757 transmit_dns_request_to_mesh (void *cls,
761 struct MeshExit *exit = cls;
762 struct RequestContext *rc;
765 exit->mesh_th = NULL;
766 if (NULL == (rc = exit->transmit_queue_head))
771 exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_tunnel,
775 &transmit_dns_request_to_mesh,
779 GNUNET_assert (GNUNET_NO == rc->was_transmitted);
780 memcpy (buf, rc->mesh_message, mlen);
781 GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
782 exit->transmit_queue_tail,
784 rc->was_transmitted = GNUNET_YES;
785 GNUNET_CONTAINER_DLL_insert (exit->receive_queue_head,
786 exit->receive_queue_tail,
788 rc = exit->transmit_queue_head;
790 exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_tunnel,
794 &transmit_dns_request_to_mesh,
801 * Task run if the time to answer a DNS request via MESH is over.
803 * @param cls the `struct RequestContext` to abort
804 * @param tc scheduler context
807 timeout_request (void *cls,
808 const struct GNUNET_SCHEDULER_TaskContext *tc)
810 struct RequestContext *rc = cls;
811 struct MeshExit *exit = rc->exit;
813 if (rc->was_transmitted)
815 exit->num_transmitted++;
816 GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
817 exit->receive_queue_tail,
822 GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
823 exit->transmit_queue_tail,
826 GNUNET_STATISTICS_update (stats,
827 gettext_noop ("# DNS requests dropped (timeout)"),
829 GNUNET_DNS_request_drop (rc->rh);
831 if ( (0 == get_tunnel_weight (exit)) &&
832 (NULL == exit->receive_queue_head) &&
833 (NULL == exit->transmit_queue_head) )
835 /* this straw broke the camel's back: this tunnel now has
836 such a low score that it will not be used; close it! */
837 GNUNET_assert (NULL == exit->mesh_th);
838 GNUNET_MESH_tunnel_destroy (exit->mesh_tunnel);
839 exit->mesh_tunnel = NULL;
840 GNUNET_CONTAINER_DLL_remove (exit_head,
843 GNUNET_CONTAINER_DLL_insert_tail (exit_head,
846 /* go back to semi-innocent: mark as not great, but
847 avoid a prohibitively negative score (see
848 #get_tunnel_weight, which checks for a certain
849 minimum number of transmissions before making
851 exit->num_transmitted = 5;
852 exit->num_answered = 0;
853 dns_exit_available--;
854 /* now try to open an alternative exit */
861 * This function is called *before* the DNS request has been
862 * given to a "local" DNS resolver. Tunneling for DNS requests
863 * was enabled, so we now need to send the request via some MESH
864 * tunnel to a DNS EXIT for resolution.
867 * @param rh request handle to user for reply
868 * @param request_length number of bytes in request
869 * @param request udp payload of the DNS request
872 dns_pre_request_handler (void *cls,
873 struct GNUNET_DNS_RequestHandle *rh,
874 size_t request_length,
877 struct RequestContext *rc;
879 struct GNUNET_MessageHeader hdr;
880 struct GNUNET_TUN_DnsHeader dns;
881 struct MeshExit *exit;
883 GNUNET_STATISTICS_update (stats,
884 gettext_noop ("# DNS requests intercepted"),
886 if (0 == dns_exit_available)
888 GNUNET_STATISTICS_update (stats,
889 gettext_noop ("# DNS requests dropped (DNS mesh tunnel down)"),
891 GNUNET_DNS_request_drop (rh);
894 if (request_length < sizeof (dns))
896 GNUNET_STATISTICS_update (stats,
897 gettext_noop ("# DNS requests dropped (malformed)"),
899 GNUNET_DNS_request_drop (rh);
902 memcpy (&dns, request, sizeof (dns));
903 mlen = sizeof (struct GNUNET_MessageHeader) + request_length;
904 exit = choose_exit ();
905 GNUNET_assert (NULL != exit);
906 GNUNET_assert (NULL != exit->mesh_tunnel);
907 rc = GNUNET_malloc (sizeof (struct RequestContext) + mlen);
910 rc->mesh_message = (const struct GNUNET_MessageHeader*) &rc[1];
911 rc->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
916 hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_TO_INTERNET);
917 hdr.size = htons (mlen);
918 memcpy (&rc[1], &hdr, sizeof (struct GNUNET_MessageHeader));
919 memcpy (&(((char*)&rc[1])[sizeof (struct GNUNET_MessageHeader)]),
922 GNUNET_CONTAINER_DLL_insert_tail (exit->transmit_queue_head,
923 exit->transmit_queue_tail,
925 if (NULL == exit->mesh_th)
926 exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_tunnel,
930 &transmit_dns_request_to_mesh,
936 * Process a request via mesh to perform a DNS query.
939 * @param tunnel connection to the other end
940 * @param tunnel_ctx pointer to our `struct MeshExit`
941 * @param message the actual message
942 * @return #GNUNET_OK to keep the connection open,
943 * #GNUNET_SYSERR to close it (signal serious error)
946 receive_dns_response (void *cls,
947 struct GNUNET_MESH_Tunnel *tunnel,
949 const struct GNUNET_MessageHeader *message)
951 struct MeshExit *exit = *tunnel_ctx;
952 struct GNUNET_TUN_DnsHeader dns;
954 struct RequestContext *rc;
956 mlen = ntohs (message->size);
957 mlen -= sizeof (struct GNUNET_MessageHeader);
958 if (mlen < sizeof (struct GNUNET_TUN_DnsHeader))
961 return GNUNET_SYSERR;
963 memcpy (&dns, &message[1], sizeof (dns));
964 for (rc = exit->receive_queue_head; NULL != rc; rc = rc->next)
966 GNUNET_assert (GNUNET_YES == rc->was_transmitted);
967 if (dns.id == rc->dns_id)
969 GNUNET_STATISTICS_update (stats,
970 gettext_noop ("# DNS replies received"),
972 GNUNET_DNS_request_answer (rc->rh,
974 (const void*) &message[1]);
975 GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
976 exit->receive_queue_tail,
978 GNUNET_SCHEDULER_cancel (rc->timeout_task);
980 exit->num_answered++;
981 exit->num_transmitted++;
985 GNUNET_STATISTICS_update (stats,
986 gettext_noop ("# DNS replies dropped (too late?)"),
993 * Abort all pending DNS requests with the given mesh exit.
995 * @param exit mesh exit to abort requests for
998 abort_all_requests (struct MeshExit *exit)
1000 struct RequestContext *rc;
1002 while (NULL != (rc = exit->receive_queue_head))
1004 GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
1005 exit->receive_queue_tail,
1007 GNUNET_DNS_request_drop (rc->rh);
1008 GNUNET_SCHEDULER_cancel (rc->timeout_task);
1011 while (NULL != (rc = exit->transmit_queue_head))
1013 GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
1014 exit->transmit_queue_tail,
1016 GNUNET_DNS_request_drop (rc->rh);
1017 GNUNET_SCHEDULER_cancel (rc->timeout_task);
1024 * Function scheduled as very last function, cleans up after us
1026 * @param cls closure, NULL
1027 * @param tskctx scheduler context, unused
1031 const struct GNUNET_SCHEDULER_TaskContext *tskctx)
1033 struct MeshExit *exit;
1035 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1036 "Protocol translation daemon is shutting down now\n");
1037 if (NULL != vpn_handle)
1039 GNUNET_VPN_disconnect (vpn_handle);
1042 while (NULL != (exit = exit_head))
1044 GNUNET_CONTAINER_DLL_remove (exit_head,
1047 if (NULL != exit->mesh_th)
1049 GNUNET_MESH_notify_transmit_ready_cancel (exit->mesh_th);
1050 exit->mesh_th = NULL;
1052 if (NULL != exit->mesh_tunnel)
1054 GNUNET_MESH_tunnel_destroy (exit->mesh_tunnel);
1055 exit->mesh_tunnel = NULL;
1057 abort_all_requests (exit);
1060 if (NULL != mesh_handle)
1062 GNUNET_MESH_disconnect (mesh_handle);
1065 if (NULL != dns_post_handle)
1067 GNUNET_DNS_disconnect (dns_post_handle);
1068 dns_post_handle = NULL;
1070 if (NULL != dns_pre_handle)
1072 GNUNET_DNS_disconnect (dns_pre_handle);
1073 dns_pre_handle = NULL;
1077 GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
1080 if (NULL != dht_get)
1082 GNUNET_DHT_get_stop (dht_get);
1087 GNUNET_DHT_disconnect (dht);
1094 * Function called whenever a tunnel is destroyed. Should clean up
1095 * the associated state and attempt to build a new one.
1097 * It must NOT call #GNUNET_MESH_tunnel_destroy on the tunnel.
1099 * @param cls closure (the `struct MeshExit` set from #GNUNET_MESH_connect)
1100 * @param tunnel connection to the other end (henceforth invalid)
1101 * @param tunnel_ctx place where local state associated
1102 * with the tunnel is stored
1105 mesh_tunnel_end_cb (void *cls,
1106 const struct GNUNET_MESH_Tunnel *tunnel,
1109 struct MeshExit *exit = tunnel_ctx;
1110 struct MeshExit *alt;
1111 struct RequestContext *rc;
1113 if (NULL != exit->mesh_th)
1115 GNUNET_MESH_notify_transmit_ready_cancel (exit->mesh_th);
1116 exit->mesh_th = NULL;
1118 exit->mesh_tunnel = NULL;
1119 dns_exit_available--;
1120 /* open alternative tunnels */
1122 if (NULL == exit->mesh_tunnel)
1124 /* our tunnel is now closed, move our requests to an alternative
1126 alt = choose_exit ();
1127 while (NULL != (rc = exit->transmit_queue_head))
1129 GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
1130 exit->transmit_queue_tail,
1133 GNUNET_CONTAINER_DLL_insert (alt->transmit_queue_head,
1134 alt->transmit_queue_tail,
1137 while (NULL != (rc = exit->receive_queue_head))
1139 GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
1140 exit->receive_queue_tail,
1142 rc->was_transmitted = GNUNET_NO;
1144 GNUNET_CONTAINER_DLL_insert (alt->transmit_queue_head,
1145 alt->transmit_queue_tail,
1151 /* the same peer was chosen, just make sure the queue processing is restarted */
1154 if ( (NULL == alt->mesh_th) &&
1155 (NULL != (rc = alt->transmit_queue_head)) )
1156 alt->mesh_th = GNUNET_MESH_notify_transmit_ready (alt->mesh_tunnel,
1160 &transmit_dns_request_to_mesh,
1166 * Function called whenever we find an advertisement for a
1167 * DNS exit in the DHT. If we don't have a mesh tunnel,
1168 * we should build one; otherwise, we should save the
1169 * advertisement for later use.
1171 * @param cls closure
1172 * @param exp when will this value expire
1173 * @param key key of the result
1174 * @param get_path peers on reply path (or NULL if not recorded)
1175 * [0] = datastore's first neighbor, [length - 1] = local peer
1176 * @param get_path_length number of entries in @a get_path
1177 * @param put_path peers on the PUT path (or NULL if not recorded)
1178 * [0] = origin, [length - 1] = datastore
1179 * @param put_path_length number of entries in @a put_path
1180 * @param type type of the result
1181 * @param size number of bytes in @a data
1182 * @param data pointer to the result data
1185 handle_dht_result (void *cls,
1186 struct GNUNET_TIME_Absolute exp,
1187 const struct GNUNET_HashCode *key,
1188 const struct GNUNET_PeerIdentity *get_path,
1189 unsigned int get_path_length,
1190 const struct GNUNET_PeerIdentity *put_path,
1191 unsigned int put_path_length,
1192 enum GNUNET_BLOCK_Type type,
1193 size_t size, const void *data)
1195 const struct GNUNET_DNS_Advertisement *ad;
1196 struct MeshExit *exit;
1198 if (sizeof (struct GNUNET_DNS_Advertisement) != size)
1204 for (exit = exit_head; NULL != exit; exit = exit->next)
1205 if (0 == memcmp (&ad->peer,
1207 sizeof (struct GNUNET_PeerIdentity)))
1211 exit = GNUNET_new (struct MeshExit);
1212 exit->peer = ad->peer;
1213 /* tunnel is closed, so insert at the end */
1214 GNUNET_CONTAINER_DLL_insert_tail (exit_head,
1218 exit->expiration = GNUNET_TIME_absolute_max (exit->expiration,
1219 GNUNET_TIME_absolute_ntoh (ad->expiration_time));
1220 if (dns_exit_available < MAX_OPEN_TUNNELS)
1226 * @brief Main function that will be run by the scheduler.
1228 * @param cls closure
1229 * @param args remaining command-line arguments
1230 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1231 * @param cfg_ configuration
1234 run (void *cls, char *const *args GNUNET_UNUSED,
1235 const char *cfgfile GNUNET_UNUSED,
1236 const struct GNUNET_CONFIGURATION_Handle *cfg_)
1238 struct GNUNET_HashCode dns_key;
1241 stats = GNUNET_STATISTICS_create ("pt", cfg);
1242 ipv4_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV4");
1243 ipv6_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV6");
1244 dns_tunnel = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_DNS");
1245 if (! (ipv4_pt || ipv6_pt || dns_tunnel))
1247 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1248 _("No useful service enabled. Exiting.\n"));
1249 GNUNET_SCHEDULER_shutdown ();
1252 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls);
1253 if (ipv4_pt || ipv6_pt)
1256 = GNUNET_DNS_connect (cfg,
1257 GNUNET_DNS_FLAG_POST_RESOLUTION,
1258 &dns_post_request_handler, NULL);
1259 if (NULL == dns_post_handle)
1261 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1262 _("Failed to connect to %s service. Exiting.\n"),
1264 GNUNET_SCHEDULER_shutdown ();
1267 vpn_handle = GNUNET_VPN_connect (cfg);
1268 if (NULL == vpn_handle)
1270 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1271 _("Failed to connect to %s service. Exiting.\n"),
1273 GNUNET_SCHEDULER_shutdown ();
1279 static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1280 {&receive_dns_response, GNUNET_MESSAGE_TYPE_VPN_DNS_FROM_INTERNET, 0},
1285 = GNUNET_DNS_connect (cfg,
1286 GNUNET_DNS_FLAG_PRE_RESOLUTION,
1287 &dns_pre_request_handler, NULL);
1288 if (NULL == dns_pre_handle)
1290 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1291 _("Failed to connect to %s service. Exiting.\n"),
1293 GNUNET_SCHEDULER_shutdown ();
1296 mesh_handle = GNUNET_MESH_connect (cfg, NULL, NULL,
1297 &mesh_tunnel_end_cb,
1298 mesh_handlers, NULL);
1299 if (NULL == mesh_handle)
1301 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1302 _("Failed to connect to %s service. Exiting.\n"),
1304 GNUNET_SCHEDULER_shutdown ();
1307 dht = GNUNET_DHT_connect (cfg, 1);
1310 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1311 _("Failed to connect to %s service. Exiting.\n"),
1313 GNUNET_SCHEDULER_shutdown ();
1316 GNUNET_CRYPTO_hash ("dns", strlen ("dns"), &dns_key);
1317 dht_get = GNUNET_DHT_get_start (dht,
1318 GNUNET_BLOCK_TYPE_DNS,
1321 GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
1323 &handle_dht_result, NULL);
1331 * @param argc number of arguments from the command line
1332 * @param argv command line arguments
1333 * @return 0 ok, 1 on error
1336 main (int argc, char *const *argv)
1338 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1339 GNUNET_GETOPT_OPTION_END
1343 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1346 GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-pt",
1348 ("Daemon to run to perform IP protocol translation to GNUnet"),
1349 options, &run, NULL)) ? 0 : 1;
1350 GNUNET_free ((void*) argv);
1355 /* end of gnunet-daemon-pt.c */