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