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 channels 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 channels open and a few
135 struct MeshExit *next;
140 struct MeshExit *prev;
143 * Channel we use for DNS requests over MESH, NULL if we did
144 * not initialze a channel to this peer yet.
146 struct GNUNET_MESH_Channel *mesh_channel;
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_channel.
166 struct RequestContext *transmit_queue_head;
169 * Tail of DLL of requests to be transmitted to a mesh_channel.
171 struct RequestContext *transmit_queue_tail;
174 * Active transmission request for this channel (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 channel?
186 unsigned int num_transmitted;
189 * How many DNS requests were answered via this channel?
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 channel 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 channeling DNS queries?
317 static int dns_channel;
320 * Number of DNS exit peers we currently have in the mesh channel.
321 * Used to see if using the mesh channel makes any sense right now,
322 * as well as to decide if we should open new channels.
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_channel)
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_channel)
348 if (candidate_selected < candidate_count)
350 /* move to the head of the DLL */
351 pos->mesh_channel = GNUNET_MESH_channel_create (mesh_handle,
354 GNUNET_APPLICATION_TYPE_INTERNET_RESOLVER,
355 GNUNET_MESH_OPTION_DEFAULT);
356 if (NULL == pos->mesh_channel)
361 GNUNET_CONTAINER_DLL_remove (exit_head,
364 GNUNET_CONTAINER_DLL_insert (exit_head,
367 dns_exit_available++;
371 GNUNET_assert (NULL == exit_head);
376 * Compute the weight of the given exit. The higher the weight,
377 * the more likely it will be that the channel will be chosen.
378 * A weigt of zero means that we should close the channel as it
379 * is so bad, that we should not use it.
381 * @param exit exit to calculate the weight for
382 * @return weight of the channel
385 get_channel_weight (struct MeshExit *exit)
388 uint32_t drop_percent;
389 uint32_t good_percent;
391 GNUNET_assert (exit->num_transmitted >= exit->num_answered);
392 dropped = exit->num_transmitted - exit->num_answered;
393 if (exit->num_transmitted > 0)
394 drop_percent = (uint32_t) ((100LL * dropped) / exit->num_transmitted);
396 drop_percent = 50; /* no data */
397 if ( (exit->num_transmitted > 20) &&
398 (drop_percent > 25) )
399 return 0; /* statistically significant, and > 25% loss, die */
400 good_percent = 100 - drop_percent;
401 GNUNET_assert (0 != good_percent);
402 if ( UINT32_MAX / good_percent / good_percent < exit->num_transmitted)
403 return UINT32_MAX; /* formula below would overflow */
404 return 1 + good_percent * good_percent * exit->num_transmitted;
409 * Choose a mesh exit for a DNS request. We try to use a channel
410 * that is reliable and currently available. All existing
411 * channels are given a base weight of 1, plus a score relating
412 * to the total number of queries answered in relation to the
413 * total number of queries we sent to that channel. That
414 * score is doubled if the channel is currently idle.
416 * @return NULL if no exit is known, otherwise the
417 * exit that we should use to queue a message with
419 static struct MeshExit *
422 struct MeshExit *pos;
423 uint64_t total_transmitted;
424 uint64_t selected_offset;
425 uint32_t channel_weight;
427 total_transmitted = 0;
428 for (pos = exit_head; NULL != pos; pos = pos->next)
430 if (NULL == pos->mesh_channel)
432 channel_weight = get_channel_weight (pos);
433 total_transmitted += channel_weight;
434 /* double weight for idle channels */
435 if (NULL == pos->mesh_th)
436 total_transmitted += channel_weight;
438 if (0 == total_transmitted)
440 /* no channels available, or only a very bad one... */
443 selected_offset = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
445 total_transmitted = 0;
446 for (pos = exit_head; NULL != pos; pos = pos->next)
448 if (NULL == pos->mesh_channel)
450 channel_weight = get_channel_weight (pos);
451 total_transmitted += channel_weight;
452 /* double weight for idle channels */
453 if (NULL == pos->mesh_th)
454 total_transmitted += channel_weight;
455 if (total_transmitted > selected_offset)
464 * We're done modifying all records in the response. Submit the reply
465 * and free the resources of the rc.
467 * @param rc context to process
470 finish_request (struct ReplyContext *rc)
476 GNUNET_DNSPARSER_pack (rc->dns,
481 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
482 _("Failed to pack DNS request. Dropping.\n"));
483 GNUNET_DNS_request_drop (rc->rh);
487 GNUNET_STATISTICS_update (stats,
488 gettext_noop ("# DNS requests mapped to VPN"),
490 GNUNET_DNS_request_answer (rc->rh,
494 GNUNET_DNSPARSER_free_packet (rc->dns);
500 * Process the next record of the given request context.
501 * When done, submit the reply and free the resources of
504 * @param rc context to process
507 submit_request (struct ReplyContext *rc);
511 * Callback invoked from the VPN service once a redirection is
512 * available. Provides the IP address that can now be used to
513 * reach the requested destination. We substitute the active
514 * record and then continue with 'submit_request' to look at
517 * @param cls our `struct ReplyContext`
518 * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
519 * will match 'result_af' from the request
520 * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
521 * that the VPN allocated for the redirection;
522 * traffic to this IP will now be redirected to the
523 * specified target peer; NULL on error
526 vpn_allocation_callback (void *cls,
530 struct ReplyContext *rc = cls;
535 GNUNET_DNS_request_drop (rc->rh);
536 GNUNET_DNSPARSER_free_packet (rc->dns);
540 GNUNET_STATISTICS_update (stats,
541 gettext_noop ("# DNS records modified"),
543 switch (rc->rec->type)
545 case GNUNET_DNSPARSER_TYPE_A:
546 GNUNET_assert (AF_INET == af);
547 memcpy (rc->rec->data.raw.data, address, sizeof (struct in_addr));
549 case GNUNET_DNSPARSER_TYPE_AAAA:
550 GNUNET_assert (AF_INET6 == af);
551 memcpy (rc->rec->data.raw.data, address, sizeof (struct in6_addr));
563 * Modify the given DNS record by asking VPN to create a channel
564 * to the given address. When done, continue with submitting
565 * other records from the request context ('submit_request' is
568 * @param rc context to process
569 * @param rec record to modify
572 modify_address (struct ReplyContext *rc,
573 struct GNUNET_DNSPARSER_Record *rec)
579 case GNUNET_DNSPARSER_TYPE_A:
581 GNUNET_assert (rec->data.raw.data_len == sizeof (struct in_addr));
583 case GNUNET_DNSPARSER_TYPE_AAAA:
585 GNUNET_assert (rec->data.raw.data_len == sizeof (struct in6_addr));
592 rc->rr = GNUNET_VPN_redirect_to_ip (vpn_handle,
595 GNUNET_TIME_relative_to_absolute (TIMEOUT),
596 &vpn_allocation_callback,
602 * Process the next record of the given request context.
603 * When done, submit the reply and free the resources of
606 * @param rc context to process
609 submit_request (struct ReplyContext *rc)
611 struct GNUNET_DNSPARSER_Record *ra;
620 ra = rc->dns->answers;
621 ra_len = rc->dns->num_answers;
623 case AUTHORITY_RECORDS:
624 ra = rc->dns->authority_records;
625 ra_len = rc->dns->num_authority_records;
627 case ADDITIONAL_RECORDS:
628 ra = rc->dns->additional_records;
629 ra_len = rc->dns->num_additional_records;
637 for (i=rc->offset;i<ra_len;i++)
641 case GNUNET_DNSPARSER_TYPE_A:
645 modify_address (rc, &ra[i]);
649 case GNUNET_DNSPARSER_TYPE_AAAA:
653 modify_address (rc, &ra[i]);
665 * Test if any of the given records need protocol-translation work.
667 * @param ra array of records
668 * @param ra_len number of entries in @a ra
669 * @return #GNUNET_YES if any of the given records require protocol-translation
672 work_test (const struct GNUNET_DNSPARSER_Record *ra,
677 for (i=0;i<ra_len;i++)
681 case GNUNET_DNSPARSER_TYPE_A:
685 case GNUNET_DNSPARSER_TYPE_AAAA:
696 * This function is called AFTER we got an IP address for a
697 * DNS request. Now, the PT daemon has the chance to substitute
698 * the IP address with one from the VPN range to channel requests
699 * destined for this IP address via VPN and MESH.
702 * @param rh request handle to user for reply
703 * @param request_length number of bytes in request
704 * @param request udp payload of the DNS request
707 dns_post_request_handler (void *cls,
708 struct GNUNET_DNS_RequestHandle *rh,
709 size_t request_length,
712 struct GNUNET_DNSPARSER_Packet *dns;
713 struct ReplyContext *rc;
716 GNUNET_STATISTICS_update (stats,
717 gettext_noop ("# DNS replies intercepted"),
719 dns = GNUNET_DNSPARSER_parse (request, request_length);
722 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
723 _("Failed to parse DNS request. Dropping.\n"));
724 GNUNET_DNS_request_drop (rh);
728 work |= work_test (dns->answers, dns->num_answers);
729 work |= work_test (dns->authority_records, dns->num_authority_records);
730 work |= work_test (dns->additional_records, dns->num_additional_records);
733 GNUNET_DNS_request_forward (rh);
734 GNUNET_DNSPARSER_free_packet (dns);
737 rc = GNUNET_new (struct ReplyContext);
747 * Transmit a DNS request via MESH and move the request
748 * handle to the receive queue.
750 * @param cls the `struct MeshExit`
751 * @param size number of bytes available in buf
752 * @param buf where to copy the message
753 * @return number of bytes written to buf
756 transmit_dns_request_to_mesh (void *cls,
760 struct MeshExit *exit = cls;
761 struct RequestContext *rc;
764 exit->mesh_th = NULL;
765 if (NULL == (rc = exit->transmit_queue_head))
770 exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_channel,
774 &transmit_dns_request_to_mesh,
778 GNUNET_assert (GNUNET_NO == rc->was_transmitted);
779 memcpy (buf, rc->mesh_message, mlen);
780 GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
781 exit->transmit_queue_tail,
783 rc->was_transmitted = GNUNET_YES;
784 GNUNET_CONTAINER_DLL_insert (exit->receive_queue_head,
785 exit->receive_queue_tail,
787 rc = exit->transmit_queue_head;
789 exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_channel,
793 &transmit_dns_request_to_mesh,
800 * Task run if the time to answer a DNS request via MESH is over.
802 * @param cls the `struct RequestContext` to abort
803 * @param tc scheduler context
806 timeout_request (void *cls,
807 const struct GNUNET_SCHEDULER_TaskContext *tc)
809 struct RequestContext *rc = cls;
810 struct MeshExit *exit = rc->exit;
812 if (rc->was_transmitted)
814 exit->num_transmitted++;
815 GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
816 exit->receive_queue_tail,
821 GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
822 exit->transmit_queue_tail,
825 GNUNET_STATISTICS_update (stats,
826 gettext_noop ("# DNS requests dropped (timeout)"),
828 GNUNET_DNS_request_drop (rc->rh);
830 if ( (0 == get_channel_weight (exit)) &&
831 (NULL == exit->receive_queue_head) &&
832 (NULL == exit->transmit_queue_head) )
834 /* this straw broke the camel's back: this channel now has
835 such a low score that it will not be used; close it! */
836 GNUNET_assert (NULL == exit->mesh_th);
837 GNUNET_MESH_channel_destroy (exit->mesh_channel);
838 exit->mesh_channel = NULL;
839 GNUNET_CONTAINER_DLL_remove (exit_head,
842 GNUNET_CONTAINER_DLL_insert_tail (exit_head,
845 /* go back to semi-innocent: mark as not great, but
846 avoid a prohibitively negative score (see
847 #get_channel_weight, which checks for a certain
848 minimum number of transmissions before making
850 exit->num_transmitted = 5;
851 exit->num_answered = 0;
852 dns_exit_available--;
853 /* now try to open an alternative exit */
860 * This function is called *before* the DNS request has been
861 * given to a "local" DNS resolver. Channeling for DNS requests
862 * was enabled, so we now need to send the request via some MESH
863 * channel to a DNS EXIT for resolution.
866 * @param rh request handle to user for reply
867 * @param request_length number of bytes in request
868 * @param request udp payload of the DNS request
871 dns_pre_request_handler (void *cls,
872 struct GNUNET_DNS_RequestHandle *rh,
873 size_t request_length,
876 struct RequestContext *rc;
878 struct GNUNET_MessageHeader hdr;
879 struct GNUNET_TUN_DnsHeader dns;
880 struct MeshExit *exit;
882 GNUNET_STATISTICS_update (stats,
883 gettext_noop ("# DNS requests intercepted"),
885 if (0 == dns_exit_available)
887 GNUNET_STATISTICS_update (stats,
888 gettext_noop ("# DNS requests dropped (DNS mesh channel down)"),
890 GNUNET_DNS_request_drop (rh);
893 if (request_length < sizeof (dns))
895 GNUNET_STATISTICS_update (stats,
896 gettext_noop ("# DNS requests dropped (malformed)"),
898 GNUNET_DNS_request_drop (rh);
901 memcpy (&dns, request, sizeof (dns));
902 mlen = sizeof (struct GNUNET_MessageHeader) + request_length;
903 exit = choose_exit ();
904 GNUNET_assert (NULL != exit);
905 GNUNET_assert (NULL != exit->mesh_channel);
906 rc = GNUNET_malloc (sizeof (struct RequestContext) + mlen);
909 rc->mesh_message = (const struct GNUNET_MessageHeader*) &rc[1];
910 rc->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
915 hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_TO_INTERNET);
916 hdr.size = htons (mlen);
917 memcpy (&rc[1], &hdr, sizeof (struct GNUNET_MessageHeader));
918 memcpy (&(((char*)&rc[1])[sizeof (struct GNUNET_MessageHeader)]),
921 GNUNET_CONTAINER_DLL_insert_tail (exit->transmit_queue_head,
922 exit->transmit_queue_tail,
924 if (NULL == exit->mesh_th)
925 exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_channel,
929 &transmit_dns_request_to_mesh,
935 * Process a request via mesh to perform a DNS query.
938 * @param channel connection to the other end
939 * @param channel_ctx pointer to our `struct MeshExit`
940 * @param message the actual message
941 * @return #GNUNET_OK to keep the connection open,
942 * #GNUNET_SYSERR to close it (signal serious error)
945 receive_dns_response (void *cls,
946 struct GNUNET_MESH_Channel *channel,
948 const struct GNUNET_MessageHeader *message)
950 struct MeshExit *exit = *channel_ctx;
951 struct GNUNET_TUN_DnsHeader dns;
953 struct RequestContext *rc;
955 mlen = ntohs (message->size);
956 mlen -= sizeof (struct GNUNET_MessageHeader);
957 if (mlen < sizeof (struct GNUNET_TUN_DnsHeader))
960 return GNUNET_SYSERR;
962 memcpy (&dns, &message[1], sizeof (dns));
963 for (rc = exit->receive_queue_head; NULL != rc; rc = rc->next)
965 GNUNET_assert (GNUNET_YES == rc->was_transmitted);
966 if (dns.id == rc->dns_id)
968 GNUNET_STATISTICS_update (stats,
969 gettext_noop ("# DNS replies received"),
971 GNUNET_DNS_request_answer (rc->rh,
973 (const void*) &message[1]);
974 GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
975 exit->receive_queue_tail,
977 GNUNET_SCHEDULER_cancel (rc->timeout_task);
979 exit->num_answered++;
980 exit->num_transmitted++;
984 GNUNET_STATISTICS_update (stats,
985 gettext_noop ("# DNS replies dropped (too late?)"),
992 * Abort all pending DNS requests with the given mesh exit.
994 * @param exit mesh exit to abort requests for
997 abort_all_requests (struct MeshExit *exit)
999 struct RequestContext *rc;
1001 while (NULL != (rc = exit->receive_queue_head))
1003 GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
1004 exit->receive_queue_tail,
1006 GNUNET_DNS_request_drop (rc->rh);
1007 GNUNET_SCHEDULER_cancel (rc->timeout_task);
1010 while (NULL != (rc = exit->transmit_queue_head))
1012 GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
1013 exit->transmit_queue_tail,
1015 GNUNET_DNS_request_drop (rc->rh);
1016 GNUNET_SCHEDULER_cancel (rc->timeout_task);
1023 * Function scheduled as very last function, cleans up after us
1025 * @param cls closure, NULL
1026 * @param tskctx scheduler context, unused
1030 const struct GNUNET_SCHEDULER_TaskContext *tskctx)
1032 struct MeshExit *exit;
1034 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1035 "Protocol translation daemon is shutting down now\n");
1036 if (NULL != vpn_handle)
1038 GNUNET_VPN_disconnect (vpn_handle);
1041 while (NULL != (exit = exit_head))
1043 GNUNET_CONTAINER_DLL_remove (exit_head,
1046 if (NULL != exit->mesh_th)
1048 GNUNET_MESH_notify_transmit_ready_cancel (exit->mesh_th);
1049 exit->mesh_th = NULL;
1051 if (NULL != exit->mesh_channel)
1053 GNUNET_MESH_channel_destroy (exit->mesh_channel);
1054 exit->mesh_channel = NULL;
1056 abort_all_requests (exit);
1059 if (NULL != mesh_handle)
1061 GNUNET_MESH_disconnect (mesh_handle);
1064 if (NULL != dns_post_handle)
1066 GNUNET_DNS_disconnect (dns_post_handle);
1067 dns_post_handle = NULL;
1069 if (NULL != dns_pre_handle)
1071 GNUNET_DNS_disconnect (dns_pre_handle);
1072 dns_pre_handle = NULL;
1076 GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
1079 if (NULL != dht_get)
1081 GNUNET_DHT_get_stop (dht_get);
1086 GNUNET_DHT_disconnect (dht);
1093 * Function called whenever a channel is destroyed. Should clean up
1094 * the associated state and attempt to build a new one.
1096 * It must NOT call #GNUNET_MESH_channel_destroy on the channel.
1098 * @param cls closure (the `struct MeshExit` set from #GNUNET_MESH_connect)
1099 * @param channel connection to the other end (henceforth invalid)
1100 * @param channel_ctx place where local state associated
1101 * with the channel is stored
1104 mesh_channel_end_cb (void *cls,
1105 const struct GNUNET_MESH_Channel *channel,
1108 struct MeshExit *exit = channel_ctx;
1109 struct MeshExit *alt;
1110 struct RequestContext *rc;
1112 if (NULL != exit->mesh_th)
1114 GNUNET_MESH_notify_transmit_ready_cancel (exit->mesh_th);
1115 exit->mesh_th = NULL;
1117 exit->mesh_channel = NULL;
1118 dns_exit_available--;
1119 /* open alternative channels */
1121 if (NULL == exit->mesh_channel)
1123 /* our channel is now closed, move our requests to an alternative
1125 alt = choose_exit ();
1126 while (NULL != (rc = exit->transmit_queue_head))
1128 GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
1129 exit->transmit_queue_tail,
1132 GNUNET_CONTAINER_DLL_insert (alt->transmit_queue_head,
1133 alt->transmit_queue_tail,
1136 while (NULL != (rc = exit->receive_queue_head))
1138 GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
1139 exit->receive_queue_tail,
1141 rc->was_transmitted = GNUNET_NO;
1143 GNUNET_CONTAINER_DLL_insert (alt->transmit_queue_head,
1144 alt->transmit_queue_tail,
1150 /* the same peer was chosen, just make sure the queue processing is restarted */
1153 if ( (NULL == alt->mesh_th) &&
1154 (NULL != (rc = alt->transmit_queue_head)) )
1155 alt->mesh_th = GNUNET_MESH_notify_transmit_ready (alt->mesh_channel,
1159 &transmit_dns_request_to_mesh,
1165 * Function called whenever we find an advertisement for a
1166 * DNS exit in the DHT. If we don't have a mesh channel,
1167 * we should build one; otherwise, we should save the
1168 * advertisement for later use.
1170 * @param cls closure
1171 * @param exp when will this value expire
1172 * @param key key of the result
1173 * @param get_path peers on reply path (or NULL if not recorded)
1174 * [0] = datastore's first neighbor, [length - 1] = local peer
1175 * @param get_path_length number of entries in @a get_path
1176 * @param put_path peers on the PUT path (or NULL if not recorded)
1177 * [0] = origin, [length - 1] = datastore
1178 * @param put_path_length number of entries in @a put_path
1179 * @param type type of the result
1180 * @param size number of bytes in @a data
1181 * @param data pointer to the result data
1184 handle_dht_result (void *cls,
1185 struct GNUNET_TIME_Absolute exp,
1186 const struct GNUNET_HashCode *key,
1187 const struct GNUNET_PeerIdentity *get_path,
1188 unsigned int get_path_length,
1189 const struct GNUNET_PeerIdentity *put_path,
1190 unsigned int put_path_length,
1191 enum GNUNET_BLOCK_Type type,
1192 size_t size, const void *data)
1194 const struct GNUNET_DNS_Advertisement *ad;
1195 struct MeshExit *exit;
1197 if (sizeof (struct GNUNET_DNS_Advertisement) != size)
1203 for (exit = exit_head; NULL != exit; exit = exit->next)
1204 if (0 == memcmp (&ad->peer,
1206 sizeof (struct GNUNET_PeerIdentity)))
1210 exit = GNUNET_new (struct MeshExit);
1211 exit->peer = ad->peer;
1212 /* channel is closed, so insert at the end */
1213 GNUNET_CONTAINER_DLL_insert_tail (exit_head,
1217 exit->expiration = GNUNET_TIME_absolute_max (exit->expiration,
1218 GNUNET_TIME_absolute_ntoh (ad->expiration_time));
1219 if (dns_exit_available < MAX_OPEN_TUNNELS)
1225 * @brief Main function that will be run by the scheduler.
1227 * @param cls closure
1228 * @param args remaining command-line arguments
1229 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1230 * @param cfg_ configuration
1233 run (void *cls, char *const *args GNUNET_UNUSED,
1234 const char *cfgfile GNUNET_UNUSED,
1235 const struct GNUNET_CONFIGURATION_Handle *cfg_)
1237 struct GNUNET_HashCode dns_key;
1240 stats = GNUNET_STATISTICS_create ("pt", cfg);
1241 ipv4_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV4");
1242 ipv6_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV6");
1243 dns_channel = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_DNS");
1244 if (! (ipv4_pt || ipv6_pt || dns_channel))
1246 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1247 _("No useful service enabled. Exiting.\n"));
1248 GNUNET_SCHEDULER_shutdown ();
1251 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls);
1252 if (ipv4_pt || ipv6_pt)
1255 = GNUNET_DNS_connect (cfg,
1256 GNUNET_DNS_FLAG_POST_RESOLUTION,
1257 &dns_post_request_handler, NULL);
1258 if (NULL == dns_post_handle)
1260 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1261 _("Failed to connect to %s service. Exiting.\n"),
1263 GNUNET_SCHEDULER_shutdown ();
1266 vpn_handle = GNUNET_VPN_connect (cfg);
1267 if (NULL == vpn_handle)
1269 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1270 _("Failed to connect to %s service. Exiting.\n"),
1272 GNUNET_SCHEDULER_shutdown ();
1278 static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1279 {&receive_dns_response, GNUNET_MESSAGE_TYPE_VPN_DNS_FROM_INTERNET, 0},
1284 = GNUNET_DNS_connect (cfg,
1285 GNUNET_DNS_FLAG_PRE_RESOLUTION,
1286 &dns_pre_request_handler, NULL);
1287 if (NULL == dns_pre_handle)
1289 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1290 _("Failed to connect to %s service. Exiting.\n"),
1292 GNUNET_SCHEDULER_shutdown ();
1295 mesh_handle = GNUNET_MESH_connect (cfg, NULL, NULL,
1296 &mesh_channel_end_cb,
1297 mesh_handlers, NULL);
1298 if (NULL == mesh_handle)
1300 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1301 _("Failed to connect to %s service. Exiting.\n"),
1303 GNUNET_SCHEDULER_shutdown ();
1306 dht = GNUNET_DHT_connect (cfg, 1);
1309 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1310 _("Failed to connect to %s service. Exiting.\n"),
1312 GNUNET_SCHEDULER_shutdown ();
1315 GNUNET_CRYPTO_hash ("dns", strlen ("dns"), &dns_key);
1316 dht_get = GNUNET_DHT_get_start (dht,
1317 GNUNET_BLOCK_TYPE_DNS,
1320 GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
1322 &handle_dht_result, NULL);
1330 * @param argc number of arguments from the command line
1331 * @param argv command line arguments
1332 * @return 0 ok, 1 on error
1335 main (int argc, char *const *argv)
1337 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1338 GNUNET_GETOPT_OPTION_END
1342 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1345 GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-pt",
1347 ("Daemon to run to perform IP protocol translation to GNUnet"),
1348 options, &run, NULL)) ? 0 : 1;
1349 GNUNET_free ((void*) argv);
1354 /* end of gnunet-daemon-pt.c */