aboutsummaryrefslogtreecommitdiff
path: root/src/pt
diff options
context:
space:
mode:
Diffstat (limited to 'src/pt')
-rw-r--r--src/pt/.gitignore6
-rw-r--r--src/pt/Makefile.am110
-rw-r--r--src/pt/gnunet-daemon-pt.c1327
-rw-r--r--src/pt/pt.conf12
-rw-r--r--src/pt/test_gns_vpn.c865
-rw-r--r--src/pt/test_gns_vpn.conf74
-rw-r--r--src/pt/test_gnunet_vpn.c519
-rw-r--r--src/pt/test_gnunet_vpn.conf48
8 files changed, 0 insertions, 2961 deletions
diff --git a/src/pt/.gitignore b/src/pt/.gitignore
deleted file mode 100644
index ea678ffe5..000000000
--- a/src/pt/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
1gnunet-daemon-pt
2test_gns_vpn
3test_gnunet_vpn-4_over
4test_gnunet_vpn-4_to_6
5test_gnunet_vpn-6_over
6test_gnunet_vpn-6_to_4
diff --git a/src/pt/Makefile.am b/src/pt/Makefile.am
deleted file mode 100644
index fc2cb592c..000000000
--- a/src/pt/Makefile.am
+++ /dev/null
@@ -1,110 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6endif
7
8pkgcfgdir= $(pkgdatadir)/config.d/
9
10libexecdir= $(pkglibdir)/libexec/
11
12plugindir = $(libdir)/gnunet
13
14dist_pkgcfg_DATA = \
15 pt.conf
16
17libexec_PROGRAMS = \
18 gnunet-daemon-pt
19
20gnunet_daemon_pt_SOURCES = \
21 gnunet-daemon-pt.c
22gnunet_daemon_pt_LDADD = \
23 $(top_builddir)/src/vpn/libgnunetvpn.la \
24 $(top_builddir)/src/cadet/libgnunetcadet.la \
25 $(top_builddir)/src/dht/libgnunetdht.la \
26 $(top_builddir)/src/dns/libgnunetdns.la \
27 $(top_builddir)/src/statistics/libgnunetstatistics.la \
28 $(top_builddir)/src/util/libgnunetutil.la \
29 $(GN_LIBINTL)
30
31if HAVE_LIBGNURL
32LIB_GNURL=@LIBGNURL@
33CPP_GNURL=@LIBGNURL_CPPFLAGS@
34if LINUX
35 VPN_TEST = \
36 test_gnunet_vpn-4_to_6 \
37 test_gnunet_vpn-6_to_4 \
38 test_gnunet_vpn-6_over \
39 test_gnunet_vpn-4_over \
40 test_gns_vpn
41endif
42else
43if HAVE_LIBCURL
44LIB_GNURL=@LIBCURL@
45CPP_GNURL=@LIBCURL_CPPFLAGS@
46if LINUX
47 VPN_TEST = \
48 test_gnunet_vpn-4_to_6 \
49 test_gnunet_vpn-6_to_4 \
50 test_gnunet_vpn-6_over \
51 test_gnunet_vpn-4_over \
52 test_gns_vpn
53endif
54endif
55endif
56
57check_PROGRAMS = $(VPN_TEST)
58
59if ENABLE_TEST_RUN
60AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
61TESTS = $(check_PROGRAMS)
62endif
63
64EXTRA_DIST = \
65 test_gnunet_vpn.conf \
66 test_gns_vpn.conf
67
68
69
70test_gns_vpn_SOURCES = \
71 test_gns_vpn.c
72test_gns_vpn_LDADD = $(MHD_LIBS) $(LIB_GNURL) \
73 $(top_builddir)/src/namestore/libgnunetnamestore.la \
74 $(top_builddir)/src/identity/libgnunetidentity.la \
75 $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
76 $(top_builddir)/src/testing/libgnunettesting.la \
77 $(top_builddir)/src/util/libgnunetutil.la
78test_gns_vpn_CFLAGS = $(MHD_CFLAGS) $(CPP_GNURL) $(AM_CFLAGS)
79
80test_gnunet_vpn_4_over_SOURCES = \
81 test_gnunet_vpn.c
82test_gnunet_vpn_4_over_LDADD = $(MHD_LIBS) $(LIB_GNURL) \
83 $(top_builddir)/src/vpn/libgnunetvpn.la \
84 $(top_builddir)/src/testing/libgnunettesting.la \
85 $(top_builddir)/src/util/libgnunetutil.la
86test_gnunet_vpn_4_over_CFLAGS = $(MHD_CFLAGS) $(CPP_GNURL) $(AM_CFLAGS)
87
88test_gnunet_vpn_6_over_SOURCES = \
89 test_gnunet_vpn.c
90test_gnunet_vpn_6_over_LDADD = $(MHD_LIBS) $(LIB_GNURL) \
91 $(top_builddir)/src/vpn/libgnunetvpn.la \
92 $(top_builddir)/src/testing/libgnunettesting.la \
93 $(top_builddir)/src/util/libgnunetutil.la
94test_gnunet_vpn_6_over_CFLAGS = $(MHD_CFLAGS) $(CPP_GNURL) $(AM_CFLAGS)
95
96test_gnunet_vpn_4_to_6_SOURCES = \
97 test_gnunet_vpn.c
98test_gnunet_vpn_4_to_6_LDADD = $(MHD_LIBS) $(LIB_GNURL) \
99 $(top_builddir)/src/vpn/libgnunetvpn.la \
100 $(top_builddir)/src/testing/libgnunettesting.la \
101 $(top_builddir)/src/util/libgnunetutil.la
102test_gnunet_vpn_4_to_6_CFLAGS = $(MHD_CFLAGS) $(CPP_GNURL) $(AM_CFLAGS)
103
104test_gnunet_vpn_6_to_4_SOURCES = \
105 test_gnunet_vpn.c
106test_gnunet_vpn_6_to_4_LDADD = $(MHD_LIBS) $(LIB_GNURL) \
107 $(top_builddir)/src/vpn/libgnunetvpn.la \
108 $(top_builddir)/src/testing/libgnunettesting.la \
109 $(top_builddir)/src/util/libgnunetutil.la
110test_gnunet_vpn_6_to_4_CFLAGS = $(MHD_CFLAGS) $(CPP_GNURL) $(AM_CFLAGS)
diff --git a/src/pt/gnunet-daemon-pt.c b/src/pt/gnunet-daemon-pt.c
deleted file mode 100644
index 67227b97f..000000000
--- a/src/pt/gnunet-daemon-pt.c
+++ /dev/null
@@ -1,1327 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2012, 2017 Christian Grothoff
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file pt/gnunet-daemon-pt.c
22 * @brief tool to manipulate DNS and VPN services to perform protocol translation (IPvX over GNUnet)
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_dns_service.h"
28#include "gnunet_dnsparser_lib.h"
29#include "gnunet_cadet_service.h"
30#include "gnunet_tun_lib.h"
31#include "gnunet_dht_service.h"
32#include "gnunet_vpn_service.h"
33#include "gnunet_statistics_service.h"
34#include "gnunet_applications.h"
35#include "block_dns.h"
36
37
38/**
39 * After how long do we time out if we could not get an IP from VPN or CADET?
40 */
41#define TIMEOUT GNUNET_TIME_UNIT_MINUTES
42
43/**
44 * How many bytes of payload do we allow at most for a DNS reply?
45 * Given that this is pretty much limited to loopback, we can be
46 * pretty high (Linux loopback defaults to 16k, most local UDP packets
47 * should survive up to 9k (NFS), so 8k should be pretty safe in
48 * general).
49 */
50#define MAX_DNS_SIZE (8 * 1024)
51
52/**
53 * How many channels do we open at most at the same time?
54 */
55#define MAX_OPEN_TUNNELS 4
56
57
58/**
59 * Which group of DNS records are we currently processing?
60 */
61enum RequestGroup
62{
63 /**
64 * DNS answers
65 */
66 ANSWERS = 0,
67
68 /**
69 * DNS authority records
70 */
71 AUTHORITY_RECORDS = 1,
72
73 /**
74 * DNS additional records
75 */
76 ADDITIONAL_RECORDS = 2,
77
78 /**
79 * We're done processing.
80 */
81 END = 3
82};
83
84
85/**
86 * Information tracked per DNS reply that we are processing.
87 */
88struct ReplyContext
89{
90 /**
91 * Handle to submit the final result.
92 */
93 struct GNUNET_DNS_RequestHandle *rh;
94
95 /**
96 * DNS packet that is being modified.
97 */
98 struct GNUNET_DNSPARSER_Packet *dns;
99
100 /**
101 * Active redirection request with the VPN.
102 */
103 struct GNUNET_VPN_RedirectionRequest *rr;
104
105 /**
106 * Record for which we have an active redirection request.
107 */
108 struct GNUNET_DNSPARSER_Record *rec;
109
110 /**
111 * Offset in the current record group that is being modified.
112 */
113 unsigned int offset;
114
115 /**
116 * Group that is being modified
117 */
118 enum RequestGroup group;
119};
120
121
122/**
123 * Handle to a peer that advertised that it is willing to serve
124 * as a DNS exit. We try to keep a few channels open and a few
125 * peers in reserve.
126 */
127struct CadetExit
128{
129 /**
130 * Kept in a DLL.
131 */
132 struct CadetExit *next;
133
134 /**
135 * Kept in a DLL.
136 */
137 struct CadetExit *prev;
138
139 /**
140 * Channel we use for DNS requests over CADET, NULL if we did
141 * not initialize a channel to this peer yet.
142 */
143 struct GNUNET_CADET_Channel *cadet_channel;
144
145 /**
146 * At what time did the peer's advertisement expire?
147 */
148 struct GNUNET_TIME_Absolute expiration;
149
150 /**
151 * Head of DLL of requests waiting for a response.
152 */
153 struct RequestContext *receive_queue_head;
154
155 /**
156 * Tail of DLL of requests waiting for a response.
157 */
158 struct RequestContext *receive_queue_tail;
159
160 /**
161 * Identity of the peer that is providing the exit for us.
162 */
163 struct GNUNET_PeerIdentity peer;
164
165 /**
166 * How many DNS requests did we transmit via this channel?
167 */
168 unsigned int num_transmitted;
169
170 /**
171 * How many DNS requests were answered via this channel?
172 */
173 unsigned int num_answered;
174
175 /**
176 * Size of the window, 0 if we are busy.
177 */
178 /* unsigned */ int idle;
179};
180
181
182/**
183 * State we keep for a request that is going out via CADET.
184 */
185struct RequestContext
186{
187 /**
188 * We keep these in a DLL.
189 */
190 struct RequestContext *next;
191
192 /**
193 * We keep these in a DLL.
194 */
195 struct RequestContext *prev;
196
197 /**
198 * Exit that was chosen for this request.
199 */
200 struct CadetExit *exit;
201
202 /**
203 * Handle for interaction with DNS service.
204 */
205 struct GNUNET_DNS_RequestHandle *rh;
206
207 /**
208 * Envelope with the request we are transmitting.
209 */
210 struct GNUNET_MQ_Envelope *env;
211
212 /**
213 * Task used to abort this operation with timeout.
214 */
215 struct GNUNET_SCHEDULER_Task *timeout_task;
216
217 /**
218 * Length of the request message that follows this struct.
219 */
220 uint16_t mlen;
221
222 /**
223 * ID of the original DNS request (used to match the reply).
224 */
225 uint16_t dns_id;
226};
227
228
229/**
230 * Head of DLL of cadet exits. Cadet exits with an open channel are
231 * always at the beginning (so we do not have to traverse the entire
232 * list to find them).
233 */
234static struct CadetExit *exit_head;
235
236/**
237 * Tail of DLL of cadet exits.
238 */
239static struct CadetExit *exit_tail;
240
241/**
242 * The handle to the configuration used throughout the process
243 */
244static const struct GNUNET_CONFIGURATION_Handle *cfg;
245
246/**
247 * The handle to the VPN
248 */
249static struct GNUNET_VPN_Handle *vpn_handle;
250
251/**
252 * The handle to the CADET service
253 */
254static struct GNUNET_CADET_Handle *cadet_handle;
255
256/**
257 * Statistics.
258 */
259static struct GNUNET_STATISTICS_Handle *stats;
260
261/**
262 * The handle to DNS post-resolution modifications.
263 */
264static struct GNUNET_DNS_Handle *dns_post_handle;
265
266/**
267 * The handle to DNS pre-resolution modifications.
268 */
269static struct GNUNET_DNS_Handle *dns_pre_handle;
270
271/**
272 * Handle to access the DHT.
273 */
274static struct GNUNET_DHT_Handle *dht;
275
276/**
277 * Our DHT GET operation to find DNS exits.
278 */
279static struct GNUNET_DHT_GetHandle *dht_get;
280
281/**
282 * Are we doing IPv4-pt?
283 */
284static int ipv4_pt;
285
286/**
287 * Are we doing IPv6-pt?
288 */
289static int ipv6_pt;
290
291/**
292 * Are we channeling DNS queries?
293 */
294static int dns_channel;
295
296/**
297 * Number of DNS exit peers we currently have in the cadet channel.
298 * Used to see if using the cadet channel makes any sense right now,
299 * as well as to decide if we should open new channels.
300 */
301static unsigned int dns_exit_available;
302
303
304/**
305 * We are short on cadet exits, try to open another one.
306 */
307static void
308try_open_exit (void);
309
310
311/**
312 * Compute the weight of the given exit. The higher the weight,
313 * the more likely it will be that the channel will be chosen.
314 * A weigt of zero means that we should close the channel as it
315 * is so bad, that we should not use it.
316 *
317 * @param exit exit to calculate the weight for
318 * @return weight of the channel
319 */
320static uint32_t
321get_channel_weight (struct CadetExit *exit)
322{
323 uint32_t dropped;
324 uint32_t drop_percent;
325 uint32_t good_percent;
326
327 GNUNET_assert (exit->num_transmitted >= exit->num_answered);
328 dropped = exit->num_transmitted - exit->num_answered;
329 if (exit->num_transmitted > 0)
330 drop_percent = (uint32_t) ((100LL * dropped) / exit->num_transmitted);
331 else
332 drop_percent = 50; /* no data */
333 if ((exit->num_transmitted > 20) &&
334 (drop_percent > 25))
335 return 0; /* statistically significant, and > 25% loss, die */
336 good_percent = 100 - drop_percent;
337 GNUNET_assert (0 != good_percent);
338 if (UINT32_MAX / good_percent / good_percent < exit->num_transmitted)
339 return UINT32_MAX; /* formula below would overflow */
340 return 1 + good_percent * good_percent * exit->num_transmitted;
341}
342
343
344/**
345 * Choose a cadet exit for a DNS request. We try to use a channel
346 * that is reliable and currently available. All existing
347 * channels are given a base weight of 1, plus a score relating
348 * to the total number of queries answered in relation to the
349 * total number of queries we sent to that channel. That
350 * score is doubled if the channel is currently idle.
351 *
352 * @return NULL if no exit is known, otherwise the
353 * exit that we should use to queue a message with
354 */
355static struct CadetExit *
356choose_exit ()
357{
358 struct CadetExit *pos;
359 uint64_t total_transmitted;
360 uint64_t selected_offset;
361 uint32_t channel_weight;
362
363 total_transmitted = 0;
364 for (pos = exit_head; NULL != pos; pos = pos->next)
365 {
366 if (NULL == pos->cadet_channel)
367 break;
368 channel_weight = get_channel_weight (pos);
369 total_transmitted += channel_weight;
370 /* double weight for idle channels */
371 if (0 != pos->idle)
372 total_transmitted += channel_weight;
373 }
374 if (0 == total_transmitted)
375 {
376 /* no channels available, or only a very bad one... */
377 return exit_head;
378 }
379 selected_offset = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
380 total_transmitted);
381 total_transmitted = 0;
382 for (pos = exit_head; NULL != pos; pos = pos->next)
383 {
384 if (NULL == pos->cadet_channel)
385 break;
386 channel_weight = get_channel_weight (pos);
387 total_transmitted += channel_weight;
388 /* double weight for idle channels */
389 if (0 != pos->idle)
390 total_transmitted += channel_weight;
391 if (total_transmitted > selected_offset)
392 return pos;
393 }
394 GNUNET_break (0);
395 return NULL;
396}
397
398
399/**
400 * We're done modifying all records in the response. Submit the reply
401 * and free the resources of the rc.
402 *
403 * @param rc context to process
404 */
405static void
406finish_request (struct ReplyContext *rc)
407{
408 char *buf;
409 size_t buf_len;
410
411 if (GNUNET_SYSERR ==
412 GNUNET_DNSPARSER_pack (rc->dns,
413 MAX_DNS_SIZE,
414 &buf,
415 &buf_len))
416 {
417 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
418 _ ("Failed to pack DNS request. Dropping.\n"));
419 GNUNET_DNS_request_drop (rc->rh);
420 }
421 else
422 {
423 GNUNET_STATISTICS_update (stats,
424 gettext_noop ("# DNS requests mapped to VPN"),
425 1, GNUNET_NO);
426 GNUNET_DNS_request_answer (rc->rh,
427 buf_len,
428 buf);
429 GNUNET_free (buf);
430 }
431 GNUNET_DNSPARSER_free_packet (rc->dns);
432 GNUNET_free (rc);
433}
434
435
436/**
437 * Process the next record of the given request context.
438 * When done, submit the reply and free the resources of
439 * the rc.
440 *
441 * @param rc context to process
442 */
443static void
444submit_request (struct ReplyContext *rc);
445
446
447/**
448 * Callback invoked from the VPN service once a redirection is
449 * available. Provides the IP address that can now be used to
450 * reach the requested destination. We substitute the active
451 * record and then continue with 'submit_request' to look at
452 * the other records.
453 *
454 * @param cls our `struct ReplyContext`
455 * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
456 * will match 'result_af' from the request
457 * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
458 * that the VPN allocated for the redirection;
459 * traffic to this IP will now be redirected to the
460 * specified target peer; NULL on error
461 */
462static void
463vpn_allocation_callback (void *cls,
464 int af,
465 const void *address)
466{
467 struct ReplyContext *rc = cls;
468
469 rc->rr = NULL;
470 if (af == AF_UNSPEC)
471 {
472 GNUNET_DNS_request_drop (rc->rh);
473 GNUNET_DNSPARSER_free_packet (rc->dns);
474 GNUNET_free (rc);
475 return;
476 }
477 GNUNET_STATISTICS_update (stats,
478 gettext_noop ("# DNS records modified"),
479 1,
480 GNUNET_NO);
481 switch (rc->rec->type)
482 {
483 case GNUNET_DNSPARSER_TYPE_A:
484 GNUNET_assert (AF_INET == af);
485 GNUNET_memcpy (rc->rec->data.raw.data,
486 address,
487 sizeof(struct in_addr));
488 break;
489
490 case GNUNET_DNSPARSER_TYPE_AAAA:
491 GNUNET_assert (AF_INET6 == af);
492 GNUNET_memcpy (rc->rec->data.raw.data,
493 address,
494 sizeof(struct in6_addr));
495 break;
496
497 default:
498 GNUNET_assert (0);
499 return;
500 }
501 rc->rec = NULL;
502 submit_request (rc);
503}
504
505
506/**
507 * Modify the given DNS record by asking VPN to create a channel
508 * to the given address. When done, continue with submitting
509 * other records from the request context ('submit_request' is
510 * our continuation).
511 *
512 * @param rc context to process
513 * @param rec record to modify
514 */
515static void
516modify_address (struct ReplyContext *rc,
517 struct GNUNET_DNSPARSER_Record *rec)
518{
519 int af;
520
521 switch (rec->type)
522 {
523 case GNUNET_DNSPARSER_TYPE_A:
524 af = AF_INET;
525 GNUNET_assert (rec->data.raw.data_len == sizeof(struct in_addr));
526 break;
527
528 case GNUNET_DNSPARSER_TYPE_AAAA:
529 af = AF_INET6;
530 GNUNET_assert (rec->data.raw.data_len == sizeof(struct in6_addr));
531 break;
532
533 default:
534 GNUNET_assert (0);
535 return;
536 }
537 rc->rec = rec;
538 rc->rr = GNUNET_VPN_redirect_to_ip (vpn_handle,
539 af,
540 af,
541 rec->data.raw.data,
542 GNUNET_TIME_relative_to_absolute (
543 TIMEOUT),
544 &vpn_allocation_callback,
545 rc);
546}
547
548
549/**
550 * Process the next record of the given request context.
551 * When done, submit the reply and free the resources of
552 * the rc.
553 *
554 * @param rc context to process
555 */
556static void
557submit_request (struct ReplyContext *rc)
558{
559 struct GNUNET_DNSPARSER_Record *ra;
560 unsigned int ra_len;
561 unsigned int i;
562
563 while (1)
564 {
565 switch (rc->group)
566 {
567 case ANSWERS:
568 ra = rc->dns->answers;
569 ra_len = rc->dns->num_answers;
570 break;
571
572 case AUTHORITY_RECORDS:
573 ra = rc->dns->authority_records;
574 ra_len = rc->dns->num_authority_records;
575 break;
576
577 case ADDITIONAL_RECORDS:
578 ra = rc->dns->additional_records;
579 ra_len = rc->dns->num_additional_records;
580 break;
581
582 case END:
583 finish_request (rc);
584 return;
585
586 default:
587 GNUNET_assert (0);
588 }
589 for (i = rc->offset; i < ra_len; i++)
590 {
591 switch (ra[i].type)
592 {
593 case GNUNET_DNSPARSER_TYPE_A:
594 if (ipv4_pt)
595 {
596 rc->offset = i + 1;
597 modify_address (rc,
598 &ra[i]);
599 return;
600 }
601 break;
602
603 case GNUNET_DNSPARSER_TYPE_AAAA:
604 if (ipv6_pt)
605 {
606 rc->offset = i + 1;
607 modify_address (rc,
608 &ra[i]);
609 return;
610 }
611 break;
612 }
613 }
614 rc->group++;
615 }
616}
617
618
619/**
620 * Test if any of the given records need protocol-translation work.
621 *
622 * @param ra array of records
623 * @param ra_len number of entries in @a ra
624 * @return #GNUNET_YES if any of the given records require protocol-translation
625 */
626static int
627work_test (const struct GNUNET_DNSPARSER_Record *ra,
628 unsigned int ra_len)
629{
630 unsigned int i;
631
632 for (i = 0; i < ra_len; i++)
633 {
634 switch (ra[i].type)
635 {
636 case GNUNET_DNSPARSER_TYPE_A:
637 if (ipv4_pt)
638 return GNUNET_YES;
639 break;
640
641 case GNUNET_DNSPARSER_TYPE_AAAA:
642 if (ipv6_pt)
643 return GNUNET_YES;
644 break;
645 }
646 }
647 return GNUNET_NO;
648}
649
650
651/**
652 * This function is called AFTER we got an IP address for a
653 * DNS request. Now, the PT daemon has the chance to substitute
654 * the IP address with one from the VPN range to channel requests
655 * destined for this IP address via VPN and CADET.
656 *
657 * @param cls closure
658 * @param rh request handle to user for reply
659 * @param request_length number of bytes in request
660 * @param request udp payload of the DNS request
661 */
662static void
663dns_post_request_handler (void *cls,
664 struct GNUNET_DNS_RequestHandle *rh,
665 size_t request_length,
666 const char *request)
667{
668 struct GNUNET_DNSPARSER_Packet *dns;
669 struct ReplyContext *rc;
670 int work;
671
672 GNUNET_STATISTICS_update (stats,
673 gettext_noop ("# DNS replies intercepted"),
674 1, GNUNET_NO);
675 dns = GNUNET_DNSPARSER_parse (request,
676 request_length);
677 if (NULL == dns)
678 {
679 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
680 _ ("Failed to parse DNS request. Dropping.\n"));
681 GNUNET_DNS_request_drop (rh);
682 return;
683 }
684 work = GNUNET_NO;
685 work |= work_test (dns->answers,
686 dns->num_answers);
687 work |= work_test (dns->authority_records,
688 dns->num_authority_records);
689 work |= work_test (dns->additional_records,
690 dns->num_additional_records);
691 if (! work)
692 {
693 GNUNET_DNS_request_forward (rh);
694 GNUNET_DNSPARSER_free_packet (dns);
695 return;
696 }
697 rc = GNUNET_new (struct ReplyContext);
698 rc->rh = rh;
699 rc->dns = dns;
700 rc->offset = 0;
701 rc->group = ANSWERS;
702 submit_request (rc);
703}
704
705
706/**
707 * Task run if the time to answer a DNS request via CADET is over.
708 *
709 * @param cls the `struct RequestContext` to abort
710 */
711static void
712timeout_request (void *cls)
713{
714 struct RequestContext *rc = cls;
715 struct CadetExit *exit = rc->exit;
716
717 GNUNET_STATISTICS_update (stats,
718 gettext_noop ("# DNS requests dropped (timeout)"),
719 1,
720 GNUNET_NO);
721 GNUNET_DNS_request_drop (rc->rh);
722 GNUNET_free (rc);
723 if ((0 == get_channel_weight (exit)) &&
724 (NULL == exit->receive_queue_head))
725 {
726 /* this straw broke the camel's back: this channel now has
727 such a low score that it will not be used; close it! */
728 GNUNET_CADET_channel_destroy (exit->cadet_channel);
729 exit->cadet_channel = NULL;
730 GNUNET_CONTAINER_DLL_remove (exit_head,
731 exit_tail,
732 exit);
733 GNUNET_CONTAINER_DLL_insert_tail (exit_head,
734 exit_tail,
735 exit);
736 /* go back to semi-innocent: mark as not great, but
737 avoid a prohibitively negative score (see
738 #get_channel_weight(), which checks for a certain
739 minimum number of transmissions before making
740 up an opinion) */exit->num_transmitted = 5;
741 exit->num_answered = 0;
742 dns_exit_available--;
743 /* now try to open an alternative exit */
744 try_open_exit ();
745 }
746}
747
748
749/**
750 * This function is called *before* the DNS request has been
751 * given to a "local" DNS resolver. Channeling for DNS requests
752 * was enabled, so we now need to send the request via some CADET
753 * channel to a DNS EXIT for resolution.
754 *
755 * @param cls closure
756 * @param rh request handle to user for reply
757 * @param request_length number of bytes in request
758 * @param request udp payload of the DNS request
759 */
760static void
761dns_pre_request_handler (void *cls,
762 struct GNUNET_DNS_RequestHandle *rh,
763 size_t request_length,
764 const char *request)
765{
766 struct RequestContext *rc;
767 struct GNUNET_MQ_Envelope *env;
768 struct GNUNET_MessageHeader *hdr;
769 struct GNUNET_TUN_DnsHeader dns;
770 struct CadetExit *exit;
771
772 GNUNET_STATISTICS_update (stats,
773 gettext_noop ("# DNS requests intercepted"),
774 1, GNUNET_NO);
775 if (0 == dns_exit_available)
776 {
777 GNUNET_STATISTICS_update (stats,
778 gettext_noop (
779 "# DNS requests dropped (DNS cadet channel down)"),
780 1, GNUNET_NO);
781 GNUNET_DNS_request_drop (rh);
782 return;
783 }
784 if (request_length < sizeof(dns))
785 {
786 GNUNET_STATISTICS_update (stats,
787 gettext_noop (
788 "# DNS requests dropped (malformed)"),
789 1, GNUNET_NO);
790 GNUNET_DNS_request_drop (rh);
791 return;
792 }
793 exit = choose_exit ();
794 GNUNET_assert (NULL != exit);
795 GNUNET_assert (NULL != exit->cadet_channel);
796
797 env = GNUNET_MQ_msg_extra (hdr,
798 request_length,
799 GNUNET_MESSAGE_TYPE_VPN_DNS_TO_INTERNET);
800 GNUNET_memcpy (&hdr[1],
801 request,
802 request_length);
803 rc = GNUNET_new (struct RequestContext);
804 rc->exit = exit;
805 rc->rh = rh;
806 rc->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
807 &timeout_request,
808 rc);
809 GNUNET_memcpy (&dns,
810 request,
811 sizeof(dns));
812 rc->dns_id = dns.id;
813 rc->env = env;
814 GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
815 exit->receive_queue_tail,
816 rc);
817 if (0 < exit->idle)
818 exit->idle--;
819 exit->num_transmitted++;
820 GNUNET_MQ_send (GNUNET_CADET_get_mq (exit->cadet_channel),
821 GNUNET_MQ_env_copy (env));
822}
823
824
825GNUNET_NETWORK_STRUCT_BEGIN
826
827/**
828 * Message with a DNS response.
829 */
830struct DnsResponseMessage
831{
832 /**
833 * GNUnet header, of type #GNUNET_MESSAGE_TYPE_VPN_DNS_FROM_INTERNET
834 */
835 struct GNUNET_MessageHeader header;
836
837 /**
838 * DNS header.
839 */
840 struct GNUNET_TUN_DnsHeader dns;
841
842 /* Followed by more DNS payload */
843};
844
845GNUNET_NETWORK_STRUCT_END
846
847/**
848 * Process a request via cadet to perform a DNS query.
849 *
850 * @param cls the `struct CadetExit` which got the message
851 * @param msg the actual message
852 * @return #GNUNET_OK to keep the connection open,
853 * #GNUNET_SYSERR to close it (signal serious error)
854 */
855static int
856check_dns_response (void *cls,
857 const struct DnsResponseMessage *msg)
858{
859 return GNUNET_OK; /* all OK */
860}
861
862
863/**
864 * Process a request via cadet to perform a DNS query.
865 *
866 * @param cls the `struct CadetExit` which got the message
867 * @param msg the actual message
868 */
869static void
870handle_dns_response (void *cls,
871 const struct DnsResponseMessage *msg)
872{
873 struct CadetExit *exit = cls;
874 size_t mlen;
875 struct RequestContext *rc;
876
877 mlen = ntohs (msg->header.size) - sizeof(*msg);
878 for (rc = exit->receive_queue_head; NULL != rc; rc = rc->next)
879 {
880 if (msg->dns.id == rc->dns_id)
881 {
882 GNUNET_STATISTICS_update (stats,
883 gettext_noop ("# DNS replies received"),
884 1,
885 GNUNET_NO);
886 GNUNET_DNS_request_answer (rc->rh,
887 mlen + sizeof(struct GNUNET_TUN_DnsHeader),
888 (const void *) &msg->dns);
889 GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
890 exit->receive_queue_tail,
891 rc);
892 GNUNET_SCHEDULER_cancel (rc->timeout_task);
893 GNUNET_MQ_discard (rc->env);
894 GNUNET_free (rc);
895 exit->num_answered++;
896 return;
897 }
898 }
899 GNUNET_STATISTICS_update (stats,
900 gettext_noop ("# DNS replies dropped (too late?)"),
901 1, GNUNET_NO);
902}
903
904
905/**
906 * Abort all pending DNS requests with the given cadet exit.
907 *
908 * @param exit cadet exit to abort requests for
909 */
910static void
911abort_all_requests (struct CadetExit *exit)
912{
913 struct RequestContext *rc;
914
915 while (NULL != (rc = exit->receive_queue_head))
916 {
917 GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
918 exit->receive_queue_tail,
919 rc);
920 GNUNET_DNS_request_drop (rc->rh);
921 GNUNET_SCHEDULER_cancel (rc->timeout_task);
922 GNUNET_MQ_discard (rc->env);
923 GNUNET_free (rc);
924 }
925}
926
927
928/**
929 * Function scheduled as very last function, cleans up after us
930 *
931 * @param cls closure, NULL
932 */
933static void
934cleanup (void *cls)
935{
936 struct CadetExit *exit;
937
938 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
939 "Protocol translation daemon is shutting down now\n");
940 if (NULL != vpn_handle)
941 {
942 GNUNET_VPN_disconnect (vpn_handle);
943 vpn_handle = NULL;
944 }
945 while (NULL != (exit = exit_head))
946 {
947 GNUNET_CONTAINER_DLL_remove (exit_head,
948 exit_tail,
949 exit);
950 if (NULL != exit->cadet_channel)
951 {
952 GNUNET_CADET_channel_destroy (exit->cadet_channel);
953 exit->cadet_channel = NULL;
954 }
955 abort_all_requests (exit);
956 GNUNET_free (exit);
957 }
958 if (NULL != cadet_handle)
959 {
960 GNUNET_CADET_disconnect (cadet_handle);
961 cadet_handle = NULL;
962 }
963 if (NULL != dns_post_handle)
964 {
965 GNUNET_DNS_disconnect (dns_post_handle);
966 dns_post_handle = NULL;
967 }
968 if (NULL != dns_pre_handle)
969 {
970 GNUNET_DNS_disconnect (dns_pre_handle);
971 dns_pre_handle = NULL;
972 }
973 if (NULL != stats)
974 {
975 GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
976 stats = NULL;
977 }
978 if (NULL != dht_get)
979 {
980 GNUNET_DHT_get_stop (dht_get);
981 dht_get = NULL;
982 }
983 if (NULL != dht)
984 {
985 GNUNET_DHT_disconnect (dht);
986 dht = NULL;
987 }
988}
989
990
991/**
992 * Function called whenever a channel is destroyed. Should clean up
993 * the associated state and attempt to build a new one.
994 *
995 * It must NOT call #GNUNET_CADET_channel_destroy on the channel.
996 *
997 * @param cls closure (the `struct CadetExit` set from #GNUNET_CADET_connect)
998 * @param channel connection to the other end (henceforth invalid)
999 * @param channel_ctx place where local state associated
1000 * with the channel is stored
1001 */
1002static void
1003cadet_channel_end_cb (void *cls,
1004 const struct GNUNET_CADET_Channel *channel)
1005{
1006 struct CadetExit *exit = cls;
1007 struct CadetExit *alt;
1008 struct RequestContext *rc;
1009
1010 exit->cadet_channel = NULL;
1011 dns_exit_available--;
1012 /* open alternative channels */
1013 /* our channel is now closed, move our requests to an alternative
1014 channel */
1015 alt = choose_exit ();
1016 while (NULL != (rc = exit->receive_queue_head))
1017 {
1018 GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
1019 exit->receive_queue_tail,
1020 rc);
1021 rc->exit = alt;
1022 GNUNET_CONTAINER_DLL_insert (alt->receive_queue_head,
1023 alt->receive_queue_tail,
1024 rc);
1025 GNUNET_MQ_send (GNUNET_CADET_get_mq (alt->cadet_channel),
1026 GNUNET_MQ_env_copy (rc->env));
1027 }
1028 try_open_exit ();
1029}
1030
1031
1032/**
1033 * Function called whenever a channel has excess capacity.
1034 *
1035 * @param cls the `struct CadetExit`
1036 * @param channel connection to the other end
1037 * @param window_size how much capacity do we have
1038 */
1039static void
1040channel_idle_notify_cb (void *cls,
1041 const struct GNUNET_CADET_Channel *channel,
1042 int window_size)
1043{
1044 struct CadetExit *pos = cls;
1045
1046 pos->idle = window_size;
1047}
1048
1049
1050/**
1051 * We are short on cadet exits, try to open another one.
1052 */
1053static void
1054try_open_exit ()
1055{
1056 struct CadetExit *pos;
1057 uint32_t candidate_count;
1058 uint32_t candidate_selected;
1059 struct GNUNET_HashCode port;
1060
1061 GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_INTERNET_RESOLVER,
1062 strlen (GNUNET_APPLICATION_PORT_INTERNET_RESOLVER),
1063 &port);
1064 candidate_count = 0;
1065 for (pos = exit_head; NULL != pos; pos = pos->next)
1066 if (NULL == pos->cadet_channel)
1067 candidate_count++;
1068 if (0 == candidate_count)
1069 {
1070 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1071 "No DNS exits available yet.\n");
1072 return;
1073 }
1074 candidate_selected = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1075 candidate_count);
1076 candidate_count = 0;
1077 for (pos = exit_head; NULL != pos; pos = pos->next)
1078 if (NULL == pos->cadet_channel)
1079 {
1080 candidate_count++;
1081 if (candidate_selected < candidate_count)
1082 {
1083 struct GNUNET_MQ_MessageHandler cadet_handlers[] = {
1084 GNUNET_MQ_hd_var_size (dns_response,
1085 GNUNET_MESSAGE_TYPE_VPN_DNS_FROM_INTERNET,
1086 struct DnsResponseMessage,
1087 pos),
1088 GNUNET_MQ_handler_end ()
1089 };
1090
1091
1092 /* move to the head of the DLL */
1093 pos->cadet_channel
1094 = GNUNET_CADET_channel_create (cadet_handle,
1095 pos,
1096 &pos->peer,
1097 &port,
1098 &channel_idle_notify_cb,
1099 &cadet_channel_end_cb,
1100 cadet_handlers);
1101 if (NULL == pos->cadet_channel)
1102 {
1103 GNUNET_break (0);
1104 continue;
1105 }
1106 GNUNET_CONTAINER_DLL_remove (exit_head,
1107 exit_tail,
1108 pos);
1109 GNUNET_CONTAINER_DLL_insert (exit_head,
1110 exit_tail,
1111 pos);
1112 dns_exit_available++;
1113 return;
1114 }
1115 }
1116 GNUNET_assert (NULL == exit_head);
1117}
1118
1119
1120/**
1121 * Function called whenever we find an advertisement for a
1122 * DNS exit in the DHT. If we don't have a cadet channel,
1123 * we should build one; otherwise, we should save the
1124 * advertisement for later use.
1125 *
1126 * @param cls closure
1127 * @param exp when will this value expire
1128 * @param key key of the result
1129 * @param trunc_peer peer that was truncated (or NULL if not truncated)
1130 * @param get_path peers on reply path (or NULL if not recorded)
1131 * [0] = datastore's first neighbor, [length - 1] = local peer
1132 * @param get_path_length number of entries in @a get_path
1133 * @param put_path peers on the PUT path (or NULL if not recorded)
1134 * [0] = origin, [length - 1] = datastore
1135 * @param put_path_length number of entries in @a put_path
1136 * @param type type of the result
1137 * @param size number of bytes in @a data
1138 * @param data pointer to the result data
1139 */
1140static void
1141handle_dht_result (void *cls,
1142 struct GNUNET_TIME_Absolute exp,
1143 const struct GNUNET_HashCode *key,
1144 const struct GNUNET_PeerIdentity *trunc_peer,
1145 const struct GNUNET_DHT_PathElement *get_path,
1146 unsigned int get_path_length,
1147 const struct GNUNET_DHT_PathElement *put_path,
1148 unsigned int put_path_length,
1149 enum GNUNET_BLOCK_Type type,
1150 size_t size, const void *data)
1151{
1152 const struct GNUNET_DNS_Advertisement *ad;
1153 struct CadetExit *exit;
1154
1155 if (sizeof(struct GNUNET_DNS_Advertisement) != size)
1156 {
1157 GNUNET_break (0);
1158 return;
1159 }
1160 ad = data;
1161 for (exit = exit_head; NULL != exit; exit = exit->next)
1162 if (0 == GNUNET_memcmp (&ad->peer,
1163 &exit->peer))
1164 break;
1165 if (NULL == exit)
1166 {
1167 exit = GNUNET_new (struct CadetExit);
1168 exit->peer = ad->peer;
1169 /* channel is closed, so insert at the end */
1170 GNUNET_CONTAINER_DLL_insert_tail (exit_head,
1171 exit_tail,
1172 exit);
1173 }
1174 exit->expiration = GNUNET_TIME_absolute_max (exit->expiration,
1175 GNUNET_TIME_absolute_ntoh (
1176 ad->expiration_time));
1177 if (dns_exit_available < MAX_OPEN_TUNNELS)
1178 try_open_exit ();
1179}
1180
1181
1182/**
1183 * @brief Main function that will be run by the scheduler.
1184 *
1185 * @param cls closure
1186 * @param args remaining command-line arguments
1187 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1188 * @param cfg_ configuration
1189 */
1190static void
1191run (void *cls, char *const *args GNUNET_UNUSED,
1192 const char *cfgfile GNUNET_UNUSED,
1193 const struct GNUNET_CONFIGURATION_Handle *cfg_)
1194{
1195 struct GNUNET_HashCode dns_key;
1196
1197 cfg = cfg_;
1198 stats = GNUNET_STATISTICS_create ("pt",
1199 cfg);
1200 ipv4_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1201 "pt",
1202 "TUNNEL_IPV4");
1203 ipv6_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1204 "pt",
1205 "TUNNEL_IPV6");
1206 dns_channel = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1207 "pt",
1208 "TUNNEL_DNS");
1209 if (! (ipv4_pt || ipv6_pt || dns_channel))
1210 {
1211 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1212 _ ("No useful service enabled. Exiting.\n"));
1213 GNUNET_SCHEDULER_shutdown ();
1214 return;
1215 }
1216 GNUNET_SCHEDULER_add_shutdown (&cleanup, cls);
1217 if (ipv4_pt || ipv6_pt)
1218 {
1219 dns_post_handle
1220 = GNUNET_DNS_connect (cfg,
1221 GNUNET_DNS_FLAG_POST_RESOLUTION,
1222 &dns_post_request_handler,
1223 NULL);
1224 if (NULL == dns_post_handle)
1225 {
1226 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1227 _ ("Failed to connect to %s service. Exiting.\n"),
1228 "DNS");
1229 GNUNET_SCHEDULER_shutdown ();
1230 return;
1231 }
1232 vpn_handle = GNUNET_VPN_connect (cfg);
1233 if (NULL == vpn_handle)
1234 {
1235 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1236 _ ("Failed to connect to %s service. Exiting.\n"),
1237 "VPN");
1238 GNUNET_SCHEDULER_shutdown ();
1239 return;
1240 }
1241 }
1242 if (dns_channel)
1243 {
1244 dns_pre_handle
1245 = GNUNET_DNS_connect (cfg,
1246 GNUNET_DNS_FLAG_PRE_RESOLUTION,
1247 &dns_pre_request_handler,
1248 NULL);
1249 if (NULL == dns_pre_handle)
1250 {
1251 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1252 _ ("Failed to connect to %s service. Exiting.\n"),
1253 "DNS");
1254 GNUNET_SCHEDULER_shutdown ();
1255 return;
1256 }
1257 cadet_handle = GNUNET_CADET_connect (cfg);
1258 if (NULL == cadet_handle)
1259 {
1260 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1261 _ ("Failed to connect to %s service. Exiting.\n"),
1262 "CADET");
1263 GNUNET_SCHEDULER_shutdown ();
1264 return;
1265 }
1266 dht = GNUNET_DHT_connect (cfg, 1);
1267 if (NULL == dht)
1268 {
1269 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1270 _ ("Failed to connect to %s service. Exiting.\n"),
1271 "DHT");
1272 GNUNET_SCHEDULER_shutdown ();
1273 return;
1274 }
1275 GNUNET_CRYPTO_hash ("dns",
1276 strlen ("dns"),
1277 &dns_key);
1278 dht_get = GNUNET_DHT_get_start (dht,
1279 GNUNET_BLOCK_TYPE_DNS,
1280 &dns_key,
1281 1,
1282 GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
1283 NULL, 0,
1284 &handle_dht_result,
1285 NULL);
1286 }
1287}
1288
1289
1290/**
1291 * The main function
1292 *
1293 * @param argc number of arguments from the command line
1294 * @param argv command line arguments
1295 * @return 0 ok, 1 on error
1296 */
1297int
1298main (int argc,
1299 char *const *argv)
1300{
1301 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1302 GNUNET_GETOPT_OPTION_END
1303 };
1304 int ret;
1305
1306 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc,
1307 argv,
1308 &argc,
1309 &argv))
1310 return 2;
1311 ret = (GNUNET_OK ==
1312 GNUNET_PROGRAM_run (argc,
1313 argv,
1314 "gnunet-daemon-pt",
1315 gettext_noop (
1316 "Daemon to run to perform IP protocol translation to GNUnet"),
1317 options,
1318 &run,
1319 NULL))
1320 ? 0
1321 : 1;
1322 GNUNET_free_nz ((void *) argv);
1323 return ret;
1324}
1325
1326
1327/* end of gnunet-daemon-pt.c */
diff --git a/src/pt/pt.conf b/src/pt/pt.conf
deleted file mode 100644
index 2c8c93c81..000000000
--- a/src/pt/pt.conf
+++ /dev/null
@@ -1,12 +0,0 @@
1[pt]
2BINARY = gnunet-daemon-pt
3NOARMBIND = YES
4
5# Set this to YES to tunnel IPv4 traffic over GNUnet
6TUNNEL_IPV4 = NO
7
8# Set this to YES to tunnel IPv6 traffic over GNUnet
9TUNNEL_IPV6 = NO
10
11# Set this to YES to tunnel DNS traffic over GNUnet
12TUNNEL_DNS = NO
diff --git a/src/pt/test_gns_vpn.c b/src/pt/test_gns_vpn.c
deleted file mode 100644
index d2d28d7c7..000000000
--- a/src/pt/test_gns_vpn.c
+++ /dev/null
@@ -1,865 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2007, 2009, 2011, 2012, 2015, 2017 Christian Grothoff
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file test_gns_vpn.c
23 * @brief testcase for accessing VPN services via GNS
24 * @author Martin Schanzenbach
25 * @author Christian Grothoff
26 *
27 * This test requires libcurl/libgnurl *with* support for C-ARES.
28 * This is NOT the default on most platforms, which means the test
29 * will be skipped in many cases. Compile libcurl/libgnurl with
30 * "--enable-ares" to get this test to pass.
31 *
32 * Furthermore, the test relies on gnunet-dns2gns being able to bind
33 * to port 53. This means that 'setcap' has to have worked during
34 * 'make install'. If this failed, but everything else is OK, the
35 * test may FAIL hard even though it is just an installation issue (we
36 * cannot conveniently test for the setcap to have worked). However,
37 * you should get a warning that gnunet-dns2gns failed to 'bind'.
38 */
39#include "platform.h"
40/* Just included for the right curl.h */
41#include "gnunet_curl_lib.h"
42#include <microhttpd.h>
43#include "gnunet_identity_service.h"
44#include "gnunet_namestore_service.h"
45#include "gnunet_gnsrecord_lib.h"
46#include "gnunet_gns_service.h"
47#include "gnunet_testing_lib.h"
48#include "gnunet_mhd_compat.h"
49
50#define PORT 8080
51#define TEST_DOMAIN "www.gnu"
52
53#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
54
55/**
56 * Return value for #main().
57 */
58static int global_ret;
59
60static struct GNUNET_NAMESTORE_Handle *namestore;
61
62static struct MHD_Daemon *mhd;
63
64static struct GNUNET_SCHEDULER_Task *mhd_task_id;
65
66static struct GNUNET_SCHEDULER_Task *curl_task_id;
67
68static struct GNUNET_SCHEDULER_Task *timeout_task;
69
70static struct GNUNET_IDENTITY_Handle *identity;
71
72static struct GNUNET_NAMESTORE_QueueEntry *qe;
73
74static CURL *curl;
75
76static CURLM *multi;
77
78static char *url;
79
80static struct GNUNET_PeerIdentity id;
81
82/**
83 * IP address of the ultimate destination.
84 */
85static const char *dest_ip;
86
87/**
88 * Address family of the dest_ip.
89 */
90static int dest_af;
91
92/**
93 * Address family to use by the curl client.
94 */
95static int src_af;
96
97static int use_v6;
98
99
100struct CBC
101{
102 char buf[1024];
103 size_t pos;
104};
105
106static struct CBC cbc;
107
108
109static size_t
110copy_buffer (void *ptr,
111 size_t size,
112 size_t nmemb,
113 void *ctx)
114{
115 struct CBC *cbc = ctx;
116
117 if (cbc->pos + size * nmemb > sizeof(cbc->buf))
118 return 0; /* overflow */
119 GNUNET_memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
120 cbc->pos += size * nmemb;
121 return size * nmemb;
122}
123
124
125static MHD_RESULT
126mhd_ahc (void *cls,
127 struct MHD_Connection *connection,
128 const char *url,
129 const char *method,
130 const char *version,
131 const char *upload_data, size_t *upload_data_size,
132 void **unused)
133{
134 static int ptr;
135 struct MHD_Response *response;
136 int ret;
137
138 if (0 != strcmp ("GET", method))
139 return MHD_NO; /* unexpected method */
140 if (&ptr != *unused)
141 {
142 *unused = &ptr;
143 return MHD_YES;
144 }
145 *unused = NULL;
146 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
147 "MHD sends response for request to URL `%s'\n", url);
148 response = MHD_create_response_from_buffer (strlen (url),
149 (void *) url,
150 MHD_RESPMEM_MUST_COPY);
151 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
152 MHD_destroy_response (response);
153 if (ret == MHD_NO)
154 abort ();
155 return ret;
156}
157
158
159static void
160do_shutdown (void *cls)
161{
162 if (NULL != mhd_task_id)
163 {
164 GNUNET_SCHEDULER_cancel (mhd_task_id);
165 mhd_task_id = NULL;
166 }
167 if (NULL != curl_task_id)
168 {
169 GNUNET_SCHEDULER_cancel (curl_task_id);
170 curl_task_id = NULL;
171 }
172 if (NULL != timeout_task)
173 {
174 GNUNET_SCHEDULER_cancel (timeout_task);
175 timeout_task = NULL;
176 }
177 if (NULL != mhd)
178 {
179 MHD_stop_daemon (mhd);
180 mhd = NULL;
181 }
182 if (NULL != identity)
183 {
184 GNUNET_IDENTITY_disconnect (identity);
185 identity = NULL;
186 }
187 if (NULL != qe)
188 {
189 GNUNET_NAMESTORE_cancel (qe);
190 qe = NULL;
191 }
192 if (NULL != namestore)
193 {
194 GNUNET_NAMESTORE_disconnect (namestore);
195 namestore = NULL;
196 }
197 GNUNET_free (url);
198 url = NULL;
199}
200
201
202static void
203do_timeout (void *cls)
204{
205 timeout_task = NULL;
206 GNUNET_SCHEDULER_shutdown ();
207}
208
209
210/**
211 * Function to run the HTTP client.
212 */
213static void
214curl_main (void);
215
216
217static void
218curl_task (void *cls)
219{
220 curl_task_id = NULL;
221 curl_main ();
222}
223
224
225static void
226curl_main ()
227{
228 fd_set rs;
229 fd_set ws;
230 fd_set es;
231 int max;
232 struct GNUNET_NETWORK_FDSet nrs;
233 struct GNUNET_NETWORK_FDSet nws;
234 struct GNUNET_TIME_Relative delay;
235 long timeout;
236 int running;
237 struct CURLMsg *msg;
238
239 max = 0;
240 FD_ZERO (&rs);
241 FD_ZERO (&ws);
242 FD_ZERO (&es);
243 curl_multi_perform (multi, &running);
244 if (running == 0)
245 {
246 GNUNET_assert (NULL != (msg = curl_multi_info_read (multi, &running)));
247 if (msg->msg == CURLMSG_DONE)
248 {
249 if (msg->data.result != CURLE_OK)
250 {
251 fprintf (stderr,
252 "%s failed at %s:%d: `%s'\n",
253 "curl_multi_perform",
254 __FILE__,
255 __LINE__, curl_easy_strerror (msg->data.result));
256 global_ret = 1;
257 }
258 }
259 curl_multi_remove_handle (multi, curl);
260 curl_multi_cleanup (multi);
261 curl_easy_cleanup (curl);
262 curl = NULL;
263 multi = NULL;
264 if (cbc.pos != strlen ("/hello_world"))
265 {
266 GNUNET_break (0);
267 global_ret = 2;
268 }
269 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
270 {
271 GNUNET_break (0);
272 global_ret = 3;
273 }
274 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
275 "Download complete, shutting down!\n");
276 GNUNET_SCHEDULER_shutdown ();
277 return;
278 }
279 GNUNET_assert (CURLM_OK == curl_multi_fdset (multi, &rs, &ws, &es, &max));
280 if ((CURLM_OK != curl_multi_timeout (multi, &timeout)) ||
281 (-1 == timeout))
282 delay = GNUNET_TIME_UNIT_SECONDS;
283 else
284 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
285 (unsigned int) timeout);
286 GNUNET_NETWORK_fdset_copy_native (&nrs,
287 &rs,
288 max + 1);
289 GNUNET_NETWORK_fdset_copy_native (&nws,
290 &ws,
291 max + 1);
292 curl_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
293 delay,
294 &nrs,
295 &nws,
296 &curl_task,
297 NULL);
298}
299
300
301static void
302start_curl (void *cls)
303{
304 CURLcode ec;
305
306 curl_task_id = NULL;
307 GNUNET_asprintf (&url,
308 "http://%s/hello_world",
309 TEST_DOMAIN);
310 curl = curl_easy_init ();
311 curl_easy_setopt (curl, CURLOPT_URL, url);
312 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, &copy_buffer);
313 curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbc);
314 curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
315 curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L);
316 curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 150L);
317 curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
318 if (CURLE_OK !=
319 (ec = curl_easy_setopt (curl,
320 CURLOPT_DNS_SERVERS,
321 "127.0.0.1:53")))
322 {
323 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
324 "curl build without support for CURLOPT_DNS_SERVERS (%s), cannot run test\n",
325 curl_easy_strerror (ec));
326 global_ret = 77;
327 GNUNET_SCHEDULER_shutdown ();
328 return;
329 }
330 multi = curl_multi_init ();
331 GNUNET_assert (multi != NULL);
332 GNUNET_assert (CURLM_OK == curl_multi_add_handle (multi, curl));
333 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
334 "Beginning HTTP download from `%s'\n",
335 url);
336 curl_main ();
337}
338
339
340/**
341 * Callback invoked from the namestore service once record is
342 * created.
343 *
344 * @param cls closure
345 * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
346 * will match 'result_af' from the request
347 * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
348 * that the VPN allocated for the redirection;
349 * traffic to this IP will now be redirected to the
350 * specified target peer; NULL on error
351 */
352static void
353commence_testing (void *cls,
354 int32_t success,
355 const char *emsg)
356{
357 qe = NULL;
358 if ((NULL != emsg) &&
359 (GNUNET_YES != success))
360 {
361 fprintf (stderr,
362 "NS failed to create record %s\n",
363 emsg);
364 GNUNET_SCHEDULER_shutdown ();
365 return;
366 }
367
368 /* wait a little bit before downloading, as we just created the record */
369 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
370 "Launching cURL request\n");
371 curl_task_id
372 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
373 &start_curl,
374 NULL);
375}
376
377
378/**
379 * Function to keep the HTTP server running.
380 */
381static void
382mhd_main (void);
383
384
385static void
386mhd_task (void *cls)
387{
388 mhd_task_id = NULL;
389 MHD_run (mhd);
390 mhd_main ();
391}
392
393
394static void
395mhd_main ()
396{
397 struct GNUNET_NETWORK_FDSet nrs;
398 struct GNUNET_NETWORK_FDSet nws;
399 fd_set rs;
400 fd_set ws;
401 fd_set es;
402 int max_fd;
403 unsigned MHD_LONG_LONG timeout;
404 struct GNUNET_TIME_Relative delay;
405
406 GNUNET_assert (NULL == mhd_task_id);
407 FD_ZERO (&rs);
408 FD_ZERO (&ws);
409 FD_ZERO (&es);
410 max_fd = -1;
411 GNUNET_assert (MHD_YES ==
412 MHD_get_fdset (mhd, &rs, &ws, &es, &max_fd));
413 if (MHD_YES == MHD_get_timeout (mhd, &timeout))
414 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
415 (unsigned int) timeout);
416 else
417 delay = GNUNET_TIME_UNIT_FOREVER_REL;
418 GNUNET_NETWORK_fdset_copy_native (&nrs,
419 &rs,
420 max_fd + 1);
421 GNUNET_NETWORK_fdset_copy_native (&nws,
422 &ws,
423 max_fd + 1);
424 mhd_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
425 delay,
426 &nrs,
427 &nws,
428 &mhd_task,
429 NULL);
430}
431
432
433/**
434 * Open '/dev/null' and make the result the given
435 * file descriptor.
436 *
437 * @param target_fd desired FD to point to /dev/null
438 * @param flags open flags (O_RDONLY, O_WRONLY)
439 */
440static void
441open_dev_null (int target_fd,
442 int flags)
443{
444 int fd;
445
446 fd = open ("/dev/null", flags);
447 if (-1 == fd)
448 abort ();
449 if (fd == target_fd)
450 return;
451 if (-1 == dup2 (fd, target_fd))
452 {
453 (void) close (fd);
454 abort ();
455 }
456 (void) close (fd);
457}
458
459
460/**
461 * Run the given command and wait for it to complete.
462 *
463 * @param file name of the binary to run
464 * @param cmd command line arguments (as given to 'execv')
465 * @return 0 on success, 1 on any error
466 */
467static int
468fork_and_exec (const char *file,
469 char *const cmd[])
470{
471 int status;
472 pid_t pid;
473 pid_t ret;
474
475 pid = fork ();
476 if (-1 == pid)
477 {
478 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
479 "fork");
480 return 1;
481 }
482 if (0 == pid)
483 {
484 /* we are the child process */
485 /* close stdin/stdout to not cause interference
486 with the helper's main protocol! */
487 (void) close (0);
488 open_dev_null (0, O_RDONLY);
489 (void) close (1);
490 open_dev_null (1, O_WRONLY);
491 (void) execv (file, cmd);
492 /* can only get here on error */
493 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
494 "exec",
495 file);
496 _exit (1);
497 }
498 /* keep running waitpid as long as the only error we get is 'EINTR' */
499 while ((-1 == (ret = waitpid (pid, &status, 0))) &&
500 (errno == EINTR))
501 ;
502 if (-1 == ret)
503 {
504 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
505 "waitpid");
506 return 1;
507 }
508 if (! (WIFEXITED (status) &&
509 (0 == WEXITSTATUS (status))))
510 {
511 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
512 "Process `%s` returned status code %d/%d.\n",
513 file,
514 WIFEXITED (status),
515 WEXITSTATUS (status));
516 return 1;
517 }
518 /* child process completed and returned success, we're happy */
519 return 0;
520}
521
522
523/**
524 * Method called to inform about the egos of this peer.
525 *
526 * When used with #GNUNET_IDENTITY_connect, this function is
527 * initially called for all egos and then again whenever a
528 * ego's name changes or if it is deleted. At the end of
529 * the initial pass over all egos, the function is once called
530 * with 'NULL' for @a ego. That does NOT mean that the callback won't
531 * be invoked in the future or that there was an error.
532 *
533 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get, this
534 * function is only called ONCE, and 'NULL' being passed in @a ego does
535 * indicate an error (for example because name is taken or no default value is
536 * known). If @a ego is non-NULL and if '*ctx' is set in those callbacks, the
537 * value WILL be passed to a subsequent call to the identity callback of
538 * #GNUNET_IDENTITY_connect (if that one was not NULL).
539 *
540 * When an identity is renamed, this function is called with the
541 * (known) @a ego but the NEW @a name.
542 *
543 * When an identity is deleted, this function is called with the
544 * (known) ego and "NULL" for the @a name. In this case,
545 * the @a ego is henceforth invalid (and the @a ctx should also be
546 * cleaned up).
547 *
548 * @param cls closure
549 * @param ego ego handle
550 * @param ctx context for application to store data for this ego
551 * (during the lifetime of this process, initially NULL)
552 * @param name name assigned by the user for this ego,
553 * NULL if the user just deleted the ego and it
554 * must thus no longer be used
555 */
556static void
557identity_cb (void *cls,
558 struct GNUNET_IDENTITY_Ego *ego,
559 void **ctx,
560 const char *name)
561{
562 const struct GNUNET_IDENTITY_PrivateKey *zone_key;
563 struct GNUNET_GNSRECORD_Data rd;
564 char *rd_string;
565 char *peername;
566
567 if (NULL == name)
568 return;
569 if (NULL == ego)
570 {
571 if (NULL == qe)
572 {
573 fprintf (stderr,
574 "Failed to find master-zone ego\n");
575 GNUNET_SCHEDULER_shutdown ();
576 return;
577 }
578 GNUNET_IDENTITY_disconnect (identity);
579 identity = NULL;
580 return;
581 }
582 GNUNET_assert (NULL != name);
583 if (0 != strcmp (name,
584 "master-zone"))
585 {
586 fprintf (stderr,
587 "Unexpected name %s\n",
588 name);
589 return;
590 }
591 zone_key = GNUNET_IDENTITY_ego_get_private_key (ego);
592 rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
593 peername = GNUNET_strdup (GNUNET_i2s_full (&id));
594 GNUNET_asprintf (&rd_string,
595 "6 %s %s",
596 peername,
597 "www");
598 GNUNET_free (peername);
599 GNUNET_assert (GNUNET_OK ==
600 GNUNET_GNSRECORD_string_to_value (GNUNET_GNSRECORD_TYPE_VPN,
601 rd_string,
602 (void **) &rd.data,
603 &rd.data_size));
604 rd.record_type = GNUNET_GNSRECORD_TYPE_VPN;
605
606 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
607 "Creating `www` record\n");
608 qe = GNUNET_NAMESTORE_records_store (namestore,
609 zone_key,
610 "www",
611 1, &rd,
612 &commence_testing,
613 NULL);
614 GNUNET_free_nz ((void **) rd.data);
615 GNUNET_free (rd_string);
616}
617
618
619static void
620run (void *cls,
621 const struct GNUNET_CONFIGURATION_Handle *cfg,
622 struct GNUNET_TESTING_Peer *peer)
623{
624 enum MHD_FLAG flags;
625
626 char *bin;
627 char *bin_identity;
628 char *bin_gns;
629 char *bin_arm;
630 char *config;
631
632 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
633 "Test logic starting...\n");
634 if (GNUNET_OK !=
635 GNUNET_CONFIGURATION_get_value_string (cfg,
636 "arm",
637 "CONFIG",
638 &config))
639 {
640 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
641 "Failed to locate configuration file. Skipping test.\n");
642 GNUNET_SCHEDULER_shutdown ();
643 return;
644 }
645
646 char *const identity_args[] = {
647 "gnunet-identity",
648 "-C", "master-zone",
649 "-c", config,
650 NULL
651 };
652 char *const identity2_args[] = {
653 "gnunet-identity",
654 "-e", "master-zone",
655 "-s", "gns-master",
656 "-c", config,
657 NULL
658 };
659 char *const identity3_args[] = {
660 "gnunet-identity",
661 "-e", "master-zone",
662 "-s", "dns2gns",
663 "-c", config,
664 NULL
665 };
666 char *const arm_args[] = {
667 "gnunet-arm",
668 "-i", "dns2gns",
669 "-c", config,
670 NULL
671 };
672 char *const gns_args[] = {
673 "gnunet-gns",
674 "-u", "www.gnu",
675 "-c", config,
676 NULL
677 };
678
679 GNUNET_TESTING_peer_get_identity (peer,
680 &id);
681 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
682 NULL);
683 timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
684 &do_timeout,
685 NULL);
686 bin = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
687 GNUNET_asprintf (&bin_identity,
688 "%s/%s",
689 bin,
690 "gnunet-identity");
691 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
692 "Creating `master-zone` ego\n");
693 if (0 != fork_and_exec (bin_identity, identity_args))
694 {
695 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
696 "Failed to run `gnunet-identity -C`. Skipping test.\n");
697 GNUNET_SCHEDULER_shutdown ();
698 GNUNET_free (bin_identity);
699 GNUNET_free (config);
700 GNUNET_free (bin);
701 return;
702 }
703 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
704 "Setting `master-zone` ego as default for `gns-master` and `dns2gns`\n");
705 if (0 != fork_and_exec (bin_identity, identity2_args))
706 {
707 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
708 "Failed to run `gnunet-identity -e`. Skipping test.\n");
709 GNUNET_SCHEDULER_shutdown ();
710 GNUNET_free (bin_identity);
711 GNUNET_free (config);
712 GNUNET_free (bin);
713 return;
714 }
715 if (0 != fork_and_exec (bin_identity, identity3_args))
716 {
717 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
718 "Failed to run `gnunet-identity -e`. Skipping test.\n");
719 GNUNET_SCHEDULER_shutdown ();
720 GNUNET_free (bin_identity);
721 GNUNET_free (config);
722 GNUNET_free (bin);
723 return;
724 }
725 GNUNET_free (bin_identity);
726
727 /* do lookup just to launch GNS service */
728 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
729 "Resolving `www.gnu` zone entry to launch GNS (will yield no answer yet)\n");
730 GNUNET_asprintf (&bin_gns,
731 "%s/%s",
732 bin,
733 "gnunet-gns");
734 if (0 != fork_and_exec (bin_gns,
735 gns_args))
736 {
737 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
738 "Failed to run `gnunet-gns -u. Skipping test.\n");
739 GNUNET_SCHEDULER_shutdown ();
740 GNUNET_free (bin_gns);
741 GNUNET_free (config);
742 GNUNET_free (bin);
743 return;
744 }
745 GNUNET_free (bin_gns);
746
747 GNUNET_asprintf (&bin_arm,
748 "%s/%s",
749 bin,
750 "gnunet-arm");
751 if (0 != fork_and_exec (bin_arm,
752 arm_args))
753 {
754 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
755 "Failed to run `gnunet-arm -i dns2gns. Skipping test.\n");
756 GNUNET_SCHEDULER_shutdown ();
757 GNUNET_free (bin_arm);
758 GNUNET_free (config);
759 GNUNET_free (bin);
760 return;
761 }
762 GNUNET_free (bin_arm);
763
764 GNUNET_free (config);
765 GNUNET_free (bin);
766 sleep (1); /* give dns2gns chance to really run */
767
768 namestore = GNUNET_NAMESTORE_connect (cfg);
769 GNUNET_assert (NULL != namestore);
770 flags = MHD_USE_DEBUG;
771 if (GNUNET_YES == use_v6)
772 flags |= MHD_USE_DUAL_STACK;
773 mhd = MHD_start_daemon (flags,
774 PORT,
775 NULL, NULL,
776 &mhd_ahc, NULL,
777 MHD_OPTION_END);
778 GNUNET_assert (NULL != mhd);
779 mhd_main ();
780
781 identity = GNUNET_IDENTITY_connect (cfg,
782 &identity_cb,
783 NULL);
784}
785
786
787int
788main (int argc,
789 char *const *argv)
790{
791 char *bin_vpn;
792 char *bin_exit;
793
794 GNUNET_log_setup ("test-gns-vpn",
795 "WARNING",
796 NULL);
797 if (0 != access ("/dev/net/tun", R_OK))
798 {
799 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
800 "access",
801 "/dev/net/tun");
802 fprintf (stderr,
803 "WARNING: System unable to run test, skipping.\n");
804 return 77;
805 }
806
807 bin_vpn = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-vpn");
808 bin_exit = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-exit");
809 if ((0 != geteuid ()) &&
810 ((GNUNET_YES !=
811 GNUNET_OS_check_helper_binary (bin_vpn,
812 GNUNET_YES,
813 "-d gnunet-vpn - - 169.1.3.3.7 255.255.255.0"))
814 || // ipv4 only please!
815 (GNUNET_YES !=
816 GNUNET_OS_check_helper_binary (bin_exit,
817 GNUNET_YES,
818 "-d gnunet-vpn - - - 169.1.3.3.7 255.255.255.0")))) // no nat, ipv4 only
819 {
820 fprintf (stderr,
821 "WARNING: gnunet-helper-{exit,vpn} binaries in $PATH are not SUID, refusing to run test (as it would have to fail).\n");
822 fprintf (stderr,
823 "Change $PATH ('.' in $PATH before $GNUNET_PREFIX/bin is problematic) or permissions (run 'make install' as root) to fix this!\n");
824 GNUNET_free (bin_vpn);
825 GNUNET_free (bin_exit);
826 return 77;
827 }
828 GNUNET_free (bin_vpn);
829 GNUNET_free (bin_exit);
830
831 dest_ip = "169.254.86.1";
832 dest_af = AF_INET;
833 src_af = AF_INET;
834
835 if (GNUNET_OK == GNUNET_NETWORK_test_pf (PF_INET6))
836 use_v6 = GNUNET_YES;
837 else
838 use_v6 = GNUNET_NO;
839
840 if ((GNUNET_OK != GNUNET_NETWORK_test_pf (src_af)) ||
841 (GNUNET_OK != GNUNET_NETWORK_test_pf (dest_af)))
842 {
843 fprintf (stderr,
844 "Required address families not supported by this system, skipping test.\n");
845 return 77;
846 }
847 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
848 {
849 fprintf (stderr, "failed to initialize curl\n");
850 return 2;
851 }
852
853
854 if (0 !=
855 GNUNET_TESTING_peer_run ("test_gns_vpn",
856 "test_gns_vpn.conf",
857 &run,
858 NULL))
859 return 1;
860 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-vpn");
861 return global_ret;
862}
863
864
865/* end of test_gns_vpn.c */
diff --git a/src/pt/test_gns_vpn.conf b/src/pt/test_gns_vpn.conf
deleted file mode 100644
index 9277aa3c3..000000000
--- a/src/pt/test_gns_vpn.conf
+++ /dev/null
@@ -1,74 +0,0 @@
1[PATHS]
2GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-gns-vpn/
3
4[transport]
5PLUGINS = tcp
6
7[arm]
8PORT = 0
9ALLOW_SHUTDOWN = YES
10
11[exit]
12IMMEDIATE_START = YES
13IPV6ADDR = FC5A:04E1:C2BA::1
14IPV6PREFIX = 96
15IPV4ADDR = 169.254.86.1
16IPV4MASK = 255.255.255.0
17
18EXIT_IPV4 = YES
19EXIT_IPV6 = YES
20
21# FIXME: can we use 'lo'?
22EXIT_IFNAME = eth1
23
24[dns]
25DNS_EXIT = 8.8.8.8
26IMMEDIATE_START = YES
27
28[identity]
29START_ON_DEMAND = YES
30IMMEDIATE_START = YES
31
32[hostlist]
33START_ON_DEMAND = NO
34IMMEDIATE_START = NO
35
36[zonemaster]
37START_ON_DEMAND = YES
38IMMEDIATE_START = YES
39
40#[vpn]
41#PREFIX = valgrind
42
43[nse]
44WORKBITS = 1
45
46# repeating some values from the default configurations
47# here as the respective network addresses are also
48# hard-wired in the tests and the MUST match (!)
49[vpn]
50IPV6ADDR = FC2D:FDAA:6A26::1
51IPV6PREFIX = 64
52IPV4ADDR = 169.254.20.1
53IPV4MASK = 255.255.255.0
54#PREFIX = valgrind
55
56
57[www.gnunet.]
58TCP_REDIRECTS = 80:localhost4:8080
59TTL = 3600000
60
61[gns]
62START_ON_DEMAND = YES
63ZONEKEY = $GNUNET_TEST_HOME/.zonekey
64
65# Delays starting of GNS, as we need to first
66# setup the identity subsystem properly.
67IMMEDIATE_START = NO
68
69[namestore]
70START_ON_DEMAND = YES
71
72[nat]
73USE_LOCALADDR = YES
74RETURN_LOCAL_ADDRESSES = YES
diff --git a/src/pt/test_gnunet_vpn.c b/src/pt/test_gnunet_vpn.c
deleted file mode 100644
index ab197b60d..000000000
--- a/src/pt/test_gnunet_vpn.c
+++ /dev/null
@@ -1,519 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2007, 2009, 2011, 2012 Christian Grothoff
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file test_gnunet_vpn.c
23 * @brief testcase for tunneling HTTP over the GNUnet VPN
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27/* Just included for the right curl.h */
28#include "gnunet_curl_lib.h"
29#include <microhttpd.h>
30#include "gnunet_vpn_service.h"
31#include "gnunet_testing_lib.h"
32#include "gnunet_mhd_compat.h"
33
34#define PORT 48080
35
36#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
37
38
39/**
40 * Return value for 'main'.
41 */
42static int global_ret;
43
44static struct GNUNET_VPN_Handle *vpn;
45
46static struct MHD_Daemon *mhd;
47
48static struct GNUNET_SCHEDULER_Task *mhd_task_id;
49
50static struct GNUNET_SCHEDULER_Task *curl_task_id;
51
52static struct GNUNET_SCHEDULER_Task *timeout_task_id;
53
54static struct GNUNET_VPN_RedirectionRequest *rr;
55
56static CURL *curl;
57
58static CURLM *multi;
59
60static char *url;
61
62/**
63 * IP address of the ultimate destination.
64 */
65static const char *dest_ip;
66
67/**
68 * Address family of the dest_ip.
69 */
70static int dest_af;
71
72/**
73 * Address family to use by the curl client.
74 */
75static int src_af;
76
77
78struct CBC
79{
80 char buf[1024];
81 size_t pos;
82};
83
84static struct CBC cbc;
85
86
87static size_t
88copy_buffer (void *ptr, size_t size, size_t nmemb, void *ctx)
89{
90 struct CBC *cbc = ctx;
91
92 if (cbc->pos + size * nmemb > sizeof(cbc->buf))
93 return 0; /* overflow */
94 GNUNET_memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
95 cbc->pos += size * nmemb;
96 return size * nmemb;
97}
98
99
100static MHD_RESULT
101mhd_ahc (void *cls,
102 struct MHD_Connection *connection,
103 const char *url,
104 const char *method,
105 const char *version,
106 const char *upload_data,
107 size_t *upload_data_size,
108 void **unused)
109{
110 static int ptr;
111 struct MHD_Response *response;
112 int ret;
113
114 if (0 != strcmp ("GET", method))
115 return MHD_NO; /* unexpected method */
116 if (&ptr != *unused)
117 {
118 *unused = &ptr;
119 return MHD_YES;
120 }
121 *unused = NULL;
122 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
123 "MHD sends response for request to URL `%s'\n", url);
124 response =
125 MHD_create_response_from_buffer (strlen (url), (void *) url,
126 MHD_RESPMEM_MUST_COPY);
127 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
128 MHD_destroy_response (response);
129 if (ret == MHD_NO)
130 abort ();
131 return ret;
132}
133
134
135static void
136do_shutdown (void *cls)
137{
138 if (NULL != mhd_task_id)
139 {
140 GNUNET_SCHEDULER_cancel (mhd_task_id);
141 mhd_task_id = NULL;
142 }
143 if (NULL != curl_task_id)
144 {
145 GNUNET_SCHEDULER_cancel (curl_task_id);
146 curl_task_id = NULL;
147 }
148 if (NULL != timeout_task_id)
149 {
150 GNUNET_SCHEDULER_cancel (timeout_task_id);
151 timeout_task_id = NULL;
152 }
153 if (NULL != mhd)
154 {
155 MHD_stop_daemon (mhd);
156 mhd = NULL;
157 }
158 if (NULL != rr)
159 {
160 GNUNET_VPN_cancel_request (rr);
161 rr = NULL;
162 }
163 if (NULL != vpn)
164 {
165 GNUNET_VPN_disconnect (vpn);
166 vpn = NULL;
167 }
168 GNUNET_free (url);
169 url = NULL;
170}
171
172
173/**
174 * Function to run the HTTP client.
175 */
176static void
177curl_main (void *cls)
178{
179 fd_set rs;
180 fd_set ws;
181 fd_set es;
182 int max;
183 struct GNUNET_NETWORK_FDSet nrs;
184 struct GNUNET_NETWORK_FDSet nws;
185 struct GNUNET_TIME_Relative delay;
186 long timeout;
187 int running;
188 struct CURLMsg *msg;
189
190 curl_task_id = NULL;
191 max = 0;
192 FD_ZERO (&rs);
193 FD_ZERO (&ws);
194 FD_ZERO (&es);
195 curl_multi_perform (multi, &running);
196 if (running == 0)
197 {
198 GNUNET_assert (NULL != (msg = curl_multi_info_read (multi, &running)));
199 if (msg->msg == CURLMSG_DONE)
200 {
201 if (msg->data.result != CURLE_OK)
202 {
203 fprintf (stderr, "%s failed at %s:%d: `%s'\n", "curl_multi_perform",
204 __FILE__, __LINE__, curl_easy_strerror (msg->data.result));
205 global_ret = 1;
206 }
207 }
208 curl_multi_remove_handle (multi, curl);
209 curl_multi_cleanup (multi);
210 curl_easy_cleanup (curl);
211 curl = NULL;
212 multi = NULL;
213 if (cbc.pos != strlen ("/hello_world"))
214 {
215 GNUNET_break (0);
216 global_ret = 2;
217 }
218 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
219 {
220 GNUNET_break (0);
221 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
222 "You might want to check if your host-based firewall is blocking the connections.\n");
223 global_ret = 3;
224 }
225 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download complete, shutting down!\n");
226 GNUNET_SCHEDULER_shutdown ();
227 return;
228 }
229 GNUNET_assert (CURLM_OK == curl_multi_fdset (multi, &rs, &ws, &es, &max));
230 if ((CURLM_OK != curl_multi_timeout (multi, &timeout)) || (-1 == timeout))
231 delay = GNUNET_TIME_UNIT_SECONDS;
232 else
233 delay =
234 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
235 (unsigned int) timeout);
236 GNUNET_NETWORK_fdset_copy_native (&nrs, &rs, max + 1);
237 GNUNET_NETWORK_fdset_copy_native (&nws, &ws, max + 1);
238 curl_task_id =
239 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, delay,
240 &nrs, &nws, &curl_main, NULL);
241}
242
243
244/**
245 * Callback invoked from the VPN service once a redirection is
246 * available. Provides the IP address that can now be used to
247 * reach the requested destination (in our case, the MHD server)
248 *
249 * @param cls closure
250 * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
251 * will match 'result_af' from the request
252 * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
253 * that the VPN allocated for the redirection;
254 * traffic to this IP will now be redirected to the
255 * specified target peer; NULL on error
256 */
257static void
258allocation_cb (void *cls, int af, const void *address)
259{
260 char ips[INET6_ADDRSTRLEN];
261
262 rr = NULL;
263 if (src_af != af)
264 {
265 fprintf (stderr,
266 "VPN failed to allocate appropriate address\n");
267 GNUNET_SCHEDULER_shutdown ();
268 return;
269 }
270 if (AF_INET6 == af)
271 GNUNET_asprintf (&url,
272 "http://[%s]:%u/hello_world",
273 inet_ntop (af,
274 address,
275 ips,
276 sizeof(ips)),
277 (unsigned int) PORT);
278 else
279 GNUNET_asprintf (&url,
280 "http://%s:%u/hello_world",
281 inet_ntop (af,
282 address,
283 ips,
284 sizeof(ips)),
285 (unsigned int) PORT);
286 curl = curl_easy_init ();
287 curl_easy_setopt (curl, CURLOPT_URL, url);
288 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, &copy_buffer);
289 curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbc);
290 curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
291 curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L);
292 curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 15L);
293 curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
294 curl_easy_setopt (curl, CURLOPT_VERBOSE, 0);
295
296 multi = curl_multi_init ();
297 GNUNET_assert (multi != NULL);
298 GNUNET_assert (CURLM_OK == curl_multi_add_handle (multi, curl));
299 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
300 "Beginning HTTP download from `%s'\n",
301 url);
302 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
303 &curl_main,
304 NULL);
305}
306
307
308/**
309 * Function to keep the HTTP server running.
310 */
311static void
312mhd_main (void);
313
314
315static void
316mhd_task (void *cls)
317{
318 mhd_task_id = NULL;
319 MHD_run (mhd);
320 mhd_main ();
321}
322
323
324static void
325do_timeout (void *cls)
326{
327 timeout_task_id = NULL;
328 GNUNET_SCHEDULER_shutdown ();
329 GNUNET_break (0);
330 global_ret = 1;
331}
332
333
334static void
335mhd_main ()
336{
337 struct GNUNET_NETWORK_FDSet nrs;
338 struct GNUNET_NETWORK_FDSet nws;
339 fd_set rs;
340 fd_set ws;
341 fd_set es;
342 int max_fd;
343 unsigned MHD_LONG_LONG timeout;
344 struct GNUNET_TIME_Relative delay;
345
346 GNUNET_assert (NULL == mhd_task_id);
347 FD_ZERO (&rs);
348 FD_ZERO (&ws);
349 FD_ZERO (&es);
350 max_fd = -1;
351 GNUNET_assert (MHD_YES == MHD_get_fdset (mhd, &rs, &ws, &es, &max_fd));
352 if (MHD_YES == MHD_get_timeout (mhd, &timeout))
353 delay =
354 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
355 (unsigned int) timeout);
356 else
357 delay = GNUNET_TIME_UNIT_FOREVER_REL;
358 GNUNET_NETWORK_fdset_copy_native (&nrs, &rs, max_fd + 1);
359 GNUNET_NETWORK_fdset_copy_native (&nws, &ws, max_fd + 1);
360 mhd_task_id =
361 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, delay,
362 &nrs, &nws, &mhd_task, NULL);
363}
364
365
366static void
367run (void *cls,
368 const struct GNUNET_CONFIGURATION_Handle *cfg,
369 struct GNUNET_TESTING_Peer *peer)
370{
371 struct in_addr v4;
372 struct in6_addr v6;
373 void *addr;
374 enum MHD_FLAG flags;
375
376 vpn = GNUNET_VPN_connect (cfg);
377 GNUNET_assert (NULL != vpn);
378 flags = MHD_USE_DEBUG;
379 if (AF_INET6 == dest_af)
380 flags |= MHD_USE_IPv6;
381 mhd =
382 MHD_start_daemon (flags, PORT, NULL, NULL, &mhd_ahc, NULL,
383 MHD_OPTION_END);
384
385
386 GNUNET_assert (NULL != mhd);
387 mhd_main ();
388 addr = NULL;
389 switch (dest_af)
390 {
391 case AF_INET:
392 GNUNET_assert (1 == inet_pton (dest_af, dest_ip, &v4));
393 addr = &v4;
394 break;
395
396 case AF_INET6:
397 GNUNET_assert (1 == inet_pton (dest_af, dest_ip, &v6));
398 addr = &v6;
399 break;
400
401 default:
402 GNUNET_assert (0);
403 }
404 rr = GNUNET_VPN_redirect_to_ip (vpn, src_af, dest_af, addr,
405 GNUNET_TIME_UNIT_FOREVER_ABS, &allocation_cb,
406 NULL);
407 timeout_task_id =
408 GNUNET_SCHEDULER_add_delayed (TIMEOUT,
409 &do_timeout,
410 NULL);
411 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
412 NULL);
413}
414
415
416int
417main (int argc, char *const *argv)
418{
419 const char *type;
420 const char *bin;
421 char *vpn_binary;
422 char *exit_binary;
423 int ret = 0;
424
425 if (0 != access ("/dev/net/tun", R_OK))
426 {
427 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
428 "access",
429 "/dev/net/tun");
430 fprintf (stderr,
431 "WARNING: System unable to run test, skipping.\n");
432 return 77;
433 }
434
435 vpn_binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-vpn");
436 exit_binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-exit");
437 if ((GNUNET_YES != (ret = GNUNET_OS_check_helper_binary (vpn_binary,
438 GNUNET_YES,
439 "-d gnunet-vpn - - 169.1.3.3.7 255.255.255.0")))
440 || // ipv4 only please!
441 (GNUNET_YES != (ret = GNUNET_OS_check_helper_binary (exit_binary,
442 GNUNET_YES,
443 "-d gnunet-vpn - - - 169.1.3.3.7 255.255.255.0")))) // no nat, ipv4 only
444 {
445 GNUNET_free (vpn_binary);
446 GNUNET_free (exit_binary);
447 fprintf (stderr,
448 "WARNING: gnunet-helper-{exit,vpn} binaries are not SUID, refusing to run test (as it would have to fail). %d\n",
449 ret);
450 return 77;
451 }
452
453 GNUNET_free (vpn_binary);
454 GNUNET_free (exit_binary);
455 bin = argv[0];
456 if (NULL != strstr (bin, "lt-"))
457 bin = strstr (bin, "lt-") + 4;
458 type = strstr (bin, "-");
459 if (NULL == type)
460 {
461 fprintf (stderr,
462 "invalid binary name\n");
463 return 1;
464 }
465 type++;
466 /* on Windows, .exe is suffixed to these binaries,
467 * thus cease comparison after the 6th char.
468 */
469 if (0 == strncmp (type, "4_to_6", 6))
470 {
471 dest_ip = "FC5A:04E1:C2BA::1";
472 dest_af = AF_INET6;
473 src_af = AF_INET;
474 }
475 else if (0 == strncmp (type, "6_to_4", 6))
476 {
477 dest_ip = "169.254.86.1";
478 dest_af = AF_INET;
479 src_af = AF_INET6;
480 }
481 else if (0 == strncmp (type, "4_over", 6))
482 {
483 dest_ip = "169.254.86.1";
484 dest_af = AF_INET;
485 src_af = AF_INET;
486 }
487 else if (0 == strncmp (type, "6_over", 6))
488 {
489 dest_ip = "FC5A:04E1:C2BA::1";
490 dest_af = AF_INET6;
491 src_af = AF_INET6;
492 }
493 else
494 {
495 fprintf (stderr, "invalid binary suffix `%s'\n", type);
496 return 1;
497 }
498 if ((GNUNET_OK != GNUNET_NETWORK_test_pf (src_af)) ||
499 (GNUNET_OK != GNUNET_NETWORK_test_pf (dest_af)))
500 {
501 fprintf (stderr,
502 "Required address families not supported by this system, skipping test.\n");
503 return 0;
504 }
505 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
506 {
507 fprintf (stderr, "failed to initialize curl\n");
508 return 2;
509 }
510 if (0 !=
511 GNUNET_TESTING_peer_run ("test-gnunet-vpn", "test_gnunet_vpn.conf", &run,
512 NULL))
513 return 1;
514 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-vpn");
515 return global_ret;
516}
517
518
519/* end of test_gnunet_vpn.c */
diff --git a/src/pt/test_gnunet_vpn.conf b/src/pt/test_gnunet_vpn.conf
deleted file mode 100644
index 9bee93e02..000000000
--- a/src/pt/test_gnunet_vpn.conf
+++ /dev/null
@@ -1,48 +0,0 @@
1[PATHS]
2GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-vpn/
3
4[transport]
5PLUGINS = tcp
6
7[arm]
8PORT = 0
9ALLOW_SHUTDOWN = YES
10
11[exit]
12IMMEDIATE_START = YES
13EXIT_IPV4 = YES
14EXIT_IPV6 = YES
15IPV6ADDR = FC5A:04E1:C2BA::1
16IPV6PREFIX = 96
17IPV4ADDR = 169.254.86.1
18IPV4MASK = 255.255.255.0
19
20# FIXME: can we use 'lo'?
21EXIT_IFNAME = eth1
22
23[hostlist]
24START_ON_DEMAND = NO
25IMMEDIATE_START = NO
26
27[nse]
28WORKBITS = 1
29
30# repeating some values from the default configurations
31# here as the respective network addresses are also
32# hard-wired in the tests and the MUST match (!)
33[vpn]
34IPV6ADDR = FC2D:FDAA:6A26::1
35IPV6PREFIX = 64
36IPV4ADDR = 169.254.20.1
37IPV4MASK = 255.255.255.0
38
39
40[gns]
41START_ON_DEMAND = NO
42
43[nat]
44USE_LOCALADDR = YES
45RETURN_LOCAL_ADDRESSES = YES
46
47[consensus]
48START_ON_DEMAND = NO