aboutsummaryrefslogtreecommitdiff
path: root/src/service/gns
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/gns')
-rw-r--r--src/service/gns/.gitignore12
-rw-r--r--src/service/gns/Makefile.am180
-rw-r--r--src/service/gns/gns.conf.in59
-rw-r--r--src/service/gns/gns.h104
-rw-r--r--src/service/gns/gns_api.c440
-rw-r--r--src/service/gns/gns_api.h74
-rw-r--r--src/service/gns/gns_tld_api.c352
-rw-r--r--src/service/gns/gnunet-bcd.c677
-rw-r--r--src/service/gns/gnunet-dns2gns.c1024
-rw-r--r--src/service/gns/gnunet-gns-benchmark.c618
-rw-r--r--src/service/gns/gnunet-gns-import.c498
-rw-r--r--src/service/gns/gnunet-gns-proxy.c3912
-rw-r--r--src/service/gns/gnunet-service-gns.c618
-rw-r--r--src/service/gns/gnunet-service-gns.h54
-rw-r--r--src/service/gns/gnunet-service-gns_interceptor.c430
-rw-r--r--src/service/gns/gnunet-service-gns_interceptor.h46
-rw-r--r--src/service/gns/gnunet-service-gns_resolver.c3173
-rw-r--r--src/service/gns/gnunet-service-gns_resolver.h106
-rw-r--r--src/service/gns/gnunet_w32nsp_lib.h10
-rw-r--r--src/service/gns/meson.build113
-rw-r--r--src/service/gns/nss/Makefile.am43
-rw-r--r--src/service/gns/nss/map-file14
-rw-r--r--src/service/gns/nss/meson.build34
-rw-r--r--src/service/gns/nss/nss_gns.c252
-rw-r--r--src/service/gns/nss/nss_gns_query.c164
-rw-r--r--src/service/gns/nss/nss_gns_query.h73
-rw-r--r--src/service/gns/test_gns_proxy.c576
-rw-r--r--src/service/gns/test_gns_proxy.conf27
-rwxr-xr-xsrc/service/gns/test_proxy.sh71
-rw-r--r--src/service/gns/tlds.conf15
-rw-r--r--src/service/gns/w32resolver.h71
31 files changed, 13840 insertions, 0 deletions
diff --git a/src/service/gns/.gitignore b/src/service/gns/.gitignore
new file mode 100644
index 000000000..3bbb2eb3d
--- /dev/null
+++ b/src/service/gns/.gitignore
@@ -0,0 +1,12 @@
1gnunet-service-gns
2gnunet-bcd
3gnunet-dns2gns
4gnunet-gns
5gnunet-gns-proxy
6gnunet-gns-benchmark
7test_gns_proxy
8local.crt
9local.der
10local.key
11server.csr
12gnunet-gns-proxy-setup-ca
diff --git a/src/service/gns/Makefile.am b/src/service/gns/Makefile.am
new file mode 100644
index 000000000..a169b43a6
--- /dev/null
+++ b/src/service/gns/Makefile.am
@@ -0,0 +1,180 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if HAVE_GLIBCNSS
5NSS_SUBDIR = nss
6endif
7
8SUBDIRS = . $(NSS_SUBDIR)
9
10if HAVE_LIBIDN
11 LIBIDN= -lidn
12else
13 LIBIDN=
14endif
15
16if HAVE_LIBIDN2
17 LIBIDN2= -lidn2
18else
19 LIBIDN2=
20endif
21
22USE_VPN = $(top_builddir)/src/service/vpn/libgnunetvpn.la
23
24if USE_COVERAGE
25 AM_CFLAGS = --coverage -O0
26endif
27
28pkgcfgdir = $(pkgdatadir)/config.d/
29
30libexecdir= $(pkglibdir)/libexec/
31
32plugindir = $(libdir)/gnunet
33
34pkgcfg_DATA = \
35 gns.conf
36dist_pkgcfg_DATA = \
37 tlds.conf
38
39lib_LTLIBRARIES = \
40 libgnunetgns.la
41
42libexec_PROGRAMS = \
43 gnunet-service-gns \
44 gnunet-dns2gns
45
46noinst_PROGRAMS = \
47 gnunet-gns-benchmark
48
49if HAVE_PDFLATEX
50libexec_PROGRAMS += gnunet-bcd
51endif
52
53if HAVE_GNUTLS
54libexec_PROGRAMS += gnunet-gns-proxy
55endif
56
57gnunet_gns_benchmark_SOURCES = \
58 gnunet-gns-benchmark.c
59gnunet_gns_benchmark_LDADD = \
60 libgnunetgns.la \
61 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
62 $(top_builddir)/src/service/identity/libgnunetidentity.la \
63 $(top_builddir)/src/lib/util/libgnunetutil.la \
64 $(GN_LIBINTL)
65
66
67gnunet_bcd_SOURCES = \
68 gnunet-bcd.c
69gnunet_bcd_LDADD = \
70 $(top_builddir)/src/lib/util/libgnunetutil.la \
71 $(top_builddir)/src/service/identity/libgnunetidentity.la \
72 $(GN_LIBINTL) $(MHD_LIBS)
73gnunet_bcd_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
74
75
76gnunet_dns2gns_SOURCES = \
77 gnunet-dns2gns.c
78gnunet_dns2gns_LDADD = \
79 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
80 libgnunetgns.la \
81 $(top_builddir)/src/lib/util/libgnunetutil.la \
82 $(USE_VPN) \
83 $(top_builddir)/src/service/identity/libgnunetidentity.la \
84 $(GN_LIBINTL)
85
86if HAVE_SUDO
87SUDO_OR_DOAS_BINARY= $(SUDO_BINARY) -n
88else
89if HAVE_DOAS_BINARY
90SUDO_OR_DOAS_BINARY= $(DOAS_BINARY)
91endif
92endif
93
94if LINUX
95HIJACKBIN = gnunet-dns2gns
96install-exec-hook:
97 $(SUDO_OR_DOAS_BINARY) setcap 'cap_net_bind_service=+ep' $(DESTDIR)$(libexecdir)/gnunet-dns2gns || true
98else
99install-exec-hook:
100endif
101
102gnunet_gns_proxy_SOURCES = \
103 gnunet-gns-proxy.c
104gnunet_gns_proxy_LDADD = $(MHD_LIBS) @LIBCURL@ -lgnutls \
105 libgnunetgns.la \
106 $(top_builddir)/src/service/identity/libgnunetidentity.la \
107 $(top_builddir)/src/lib/util/libgnunetutil.la \
108 $(GN_LIBINTL)
109if HAVE_GNUTLS_DANE
110gnunet_gns_proxy_LDADD += -lgnutls-dane
111endif
112gnunet_gns_proxy_CFLAGS = $(MHD_CFLAGS) @LIBCURL_CPPFLAGS@ $(AM_CFLAGS)
113
114test_gns_proxy_SOURCES = \
115 test_gns_proxy.c
116test_gns_proxy_LDADD = $(MHD_LIBS) @LIBCURL@ -lgnutls \
117 $(top_builddir)/src/lib/util/libgnunetutil.la \
118 $(GN_LIBINTL)
119test_gns_proxy_CFLAGS = $(MHD_CFLAGS) @LIBCURL_CPPFLAGS@ $(AM_CFLAGS)
120
121#gnunet_gns_import_SOURCES = \
122# gnunet-gns-import.c
123#gnunet_gns_import_LDADD = \
124# $(top_builddir)/src/service/identity/libgnunetidentity.la \
125# $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
126# $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
127# $(top_builddir)/src/lib/util/libgnunetutil.la \
128# $(GN_LIBINTL)
129
130
131gnunet_service_gns_SOURCES = \
132 gnunet-service-gns.c gnunet-service-gns.h \
133 gnunet-service-gns_resolver.c gnunet-service-gns_resolver.h \
134 gnunet-service-gns_interceptor.c gnunet-service-gns_interceptor.h
135gnunet_service_gns_LDADD = \
136 -lm \
137 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
138 $(top_builddir)/src/service/identity/libgnunetidentity.la \
139 $(top_builddir)/src/service/revocation/libgnunetrevocation.la \
140 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
141 $(top_builddir)/src/lib/util/libgnunetutil.la \
142 $(top_builddir)/src/service/dns/libgnunetdns.la \
143 $(top_builddir)/src/service/dht/libgnunetdht.la \
144 $(top_builddir)/src/service/namecache/libgnunetnamecache.la \
145 $(LIBIDN) $(LIBIDN2) \
146 $(GN_LIBINTL)
147
148
149libgnunetgns_la_SOURCES = \
150 gns_api.c gns_api.h \
151 gns_tld_api.c gns.h
152libgnunetgns_la_LIBADD = \
153 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIB) \
154 $(top_builddir)/src/service/identity/libgnunetidentity.la \
155 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la
156libgnunetgns_la_LDFLAGS = \
157 $(GN_LIBINTL) \
158 $(GN_LIB_LDFLAGS)
159
160
161if HAVE_GNUTLS
162check_PROGRAMS = \
163 test_gns_proxy
164endif
165
166if HAVE_GNUTLS_CURL
167check_SCRIPTS = \
168 test_proxy.sh
169endif
170
171EXTRA_DIST = \
172 test_gns_proxy.conf \
173 test_proxy.sh
174
175if ENABLE_TEST_RUN
176if HAVE_SQLITE
177 AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
178 TESTS = $(check_SCRIPTS)
179endif
180endif
diff --git a/src/service/gns/gns.conf.in b/src/service/gns/gns.conf.in
new file mode 100644
index 000000000..d8a27ec22
--- /dev/null
+++ b/src/service/gns/gns.conf.in
@@ -0,0 +1,59 @@
1[gns]
2START_ON_DEMAND = @START_ON_DEMAND@
3IMMEDIATE_START = YES
4HOSTNAME = localhost
5BINARY = gnunet-service-gns
6UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-gns.sock
7@JAVAPORT@PORT = 2102
8
9# Do we require users that want to access GNS to run this process
10# (usually not a good idea)
11UNIX_MATCH_UID = NO
12
13# Do we require users that want to access GNS to be in the 'gnunet' group?
14UNIX_MATCH_GID = YES
15
16# How many queries is GNS allowed to perform in the background at the same time?
17MAX_PARALLEL_BACKGROUND_QUERIES = 1000
18
19# Should we use the DNS interception mechanism? If set to YES
20# we will ask gnunet-service-dns to pass DNS queries to us. Otherwise,
21# we only answer GNS queries via the API (which itself may be
22# called via NSS or other mechanisms).
23INTERCEPT_DNS = NO
24
25# PREFIX = valgrind --leak-check=full --track-origins=yes
26
27[gns-proxy]
28BINARY = gnunet-gns-proxy
29START_ON_DEMAND = NO
30RUN_PER_USER = YES
31BIND_TO=127.0.0.1
32BIND_TO6=::1
33
34# Where is the certificate for the GNS proxy stored?
35PROXY_CACERT = $GNUNET_DATA_HOME/gns/gns_ca_cert.pem
36PROXY_UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-gns-proxy.sock
37
38[bcd]
39BINARY = gnunet-bcd
40OPTIONS = -p 8888
41START_ON_DEMAND = NO
42RUN_PER_USER = YES
43
44[dns2gns]
45BINARY = gnunet-dns2gns
46START_ON_DEMAND = NO
47RUN_PER_USER = NO
48BIND_TO=127.0.0.1
49BIND_TO6=::1
50
51# -d: DNS resolver to use
52OPTIONS = -d 9.9.9.9
53PORT = 15353
54
55# This setting is useful in combination with systemd-resolve and
56# NetworkManager-dispatcher. It allows the interfaces to automatically
57# configure the dns2gns server for interfaces going up
58# See also: contrib/packages/fedora/10-dns2gns.sh
59ENABLE_RESOLVECTL_NMDISPATCHER = NO
diff --git a/src/service/gns/gns.h b/src/service/gns/gns.h
new file mode 100644
index 000000000..d882278f5
--- /dev/null
+++ b/src/service/gns/gns.h
@@ -0,0 +1,104 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012-2020 GNUnet e.V.
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 gns/gns.h
22 * @brief IPC messages between GNS API and GNS service
23 * @author Martin Schanzenbach
24 */
25#ifndef GNS_H
26#define GNS_H
27
28#include "gnunet_gns_service.h"
29
30
31GNUNET_NETWORK_STRUCT_BEGIN
32
33/**
34 * Message from client to GNS service to lookup records.
35 */
36struct LookupMessage
37{
38 /**
39 * Header of type #GNUNET_MESSAGE_TYPE_GNS_LOOKUP
40 */
41 struct GNUNET_MessageHeader header;
42
43 /**
44 * Unique identifier for this request (for key collisions).
45 */
46 uint32_t id GNUNET_PACKED;
47
48 /**
49 * Local options for where to look for results
50 * (an `enum GNUNET_GNS_LocalOptions` in NBO).
51 */
52 int16_t options GNUNET_PACKED;
53
54 /**
55 * Recursion depth limit, i.e. how many more
56 * GNS zones may be traversed during the resolution
57 * of this name.
58 */
59 uint16_t recursion_depth_limit GNUNET_PACKED;
60
61 /**
62 * the type of record to look up
63 */
64 int32_t type GNUNET_PACKED;
65
66 /**
67 * Length of the zone key
68 */
69 uint32_t key_len GNUNET_PACKED;
70 /**
71 * Followed by the zone that is to be used for lookup
72 * Followed by the zero-terminated name to look up */
73};
74
75
76/**
77 * Message from GNS service to client: new results.
78 */
79struct LookupResultMessage
80{
81 /**
82 * Header of type #GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT
83 */
84 struct GNUNET_MessageHeader header;
85
86 /**
87 * Unique identifier for this request (for key collisions).
88 */
89 uint32_t id GNUNET_PACKED;
90
91 /**
92 * The number of records contained in response. Zero for
93 * NXDOMAIN (as GNS always returns all records, there is
94 * no "NO DATA" case).
95 */
96 uint32_t rd_count GNUNET_PACKED;
97
98 /* followed by rd_count GNUNET_GNSRECORD_Data structs*/
99};
100
101
102GNUNET_NETWORK_STRUCT_END
103
104#endif
diff --git a/src/service/gns/gns_api.c b/src/service/gns/gns_api.c
new file mode 100644
index 000000000..0dc7580f9
--- /dev/null
+++ b/src/service/gns/gns_api.c
@@ -0,0 +1,440 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2020 GNUnet e.V.
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 gns/gns_api.c
22 * @brief library to access the GNS service
23 * @author Martin Schanzenbach
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_constants.h"
29#include "gnunet_arm_service.h"
30#include "gnunet_protocols.h"
31#include "gnunet_dht_service.h"
32#include "gns.h"
33#include "gns_api.h"
34
35
36#define LOG(kind, ...) GNUNET_log_from (kind, "gns-api", __VA_ARGS__)
37
38/**
39 * Default recursion depth limit to apply if
40 * the application does not specify any.
41 */
42#define DEFAULT_LIMIT 128
43
44/**
45 * Handle to a lookup request
46 */
47struct GNUNET_GNS_LookupRequest
48{
49 /**
50 * DLL
51 */
52 struct GNUNET_GNS_LookupRequest *next;
53
54 /**
55 * DLL
56 */
57 struct GNUNET_GNS_LookupRequest *prev;
58
59 /**
60 * handle to gns
61 */
62 struct GNUNET_GNS_Handle *gns_handle;
63
64 /**
65 * processor to call on lookup result
66 */
67 GNUNET_GNS_LookupResultProcessor lookup_proc;
68
69 /**
70 * @e lookup_proc closure
71 */
72 void *proc_cls;
73
74 /**
75 * Envelope with the message for this queue entry.
76 */
77 struct GNUNET_MQ_Envelope *env;
78
79 /**
80 * request id
81 */
82 uint32_t r_id;
83};
84
85
86/**
87 * Reconnect to GNS service.
88 *
89 * @param handle the handle to the GNS service
90 */
91static void
92reconnect (struct GNUNET_GNS_Handle *handle);
93
94
95/**
96 * Reconnect to GNS
97 *
98 * @param cls the handle
99 */
100static void
101reconnect_task (void *cls)
102{
103 struct GNUNET_GNS_Handle *handle = cls;
104
105 handle->reconnect_task = NULL;
106 reconnect (handle);
107}
108
109
110/**
111 * Disconnect from service and then reconnect.
112 *
113 * @param handle our handle
114 */
115static void
116force_reconnect (struct GNUNET_GNS_Handle *handle)
117{
118 GNUNET_MQ_destroy (handle->mq);
119 handle->mq = NULL;
120 handle->reconnect_backoff
121 = GNUNET_TIME_STD_BACKOFF (handle->reconnect_backoff);
122 handle->reconnect_task
123 = GNUNET_SCHEDULER_add_delayed (handle->reconnect_backoff,
124 &reconnect_task,
125 handle);
126}
127
128
129/**
130 * Generic error handler, called with the appropriate error code and
131 * the same closure specified at the creation of the message queue.
132 * Not every message queue implementation supports an error handler.
133 *
134 * @param cls closure with the `struct GNUNET_GNS_Handle *`
135 * @param error error code
136 */
137static void
138mq_error_handler (void *cls,
139 enum GNUNET_MQ_Error error)
140{
141 struct GNUNET_GNS_Handle *handle = cls;
142
143 LOG (GNUNET_ERROR_TYPE_WARNING,
144 "Problem with message queue. error: %i\n",
145 error);
146 force_reconnect (handle);
147}
148
149
150/**
151 * Check validity of message received from the GNS service
152 *
153 * @param cls the `struct GNUNET_GNS_Handle *`
154 * @param loookup_msg the incoming message
155 */
156static int
157check_result (void *cls,
158 const struct LookupResultMessage *lookup_msg)
159{
160 size_t mlen = ntohs (lookup_msg->header.size) - sizeof(*lookup_msg);
161 uint32_t rd_count = ntohl (lookup_msg->rd_count);
162 struct GNUNET_GNSRECORD_Data rd[rd_count];
163
164 (void) cls;
165 if (GNUNET_SYSERR ==
166 GNUNET_GNSRECORD_records_deserialize (mlen,
167 (const char *) &lookup_msg[1],
168 rd_count,
169 rd))
170 {
171 GNUNET_break (0);
172 return GNUNET_SYSERR;
173 }
174 return GNUNET_OK;
175}
176
177
178/**
179 * Handler for messages received from the GNS service
180 *
181 * @param cls the `struct GNUNET_GNS_Handle *`
182 * @param loookup_msg the incoming message
183 */
184static void
185handle_result (void *cls,
186 const struct LookupResultMessage *lookup_msg)
187{
188 struct GNUNET_GNS_Handle *handle = cls;
189 size_t mlen = ntohs (lookup_msg->header.size) - sizeof(*lookup_msg);
190 uint32_t rd_count = ntohl (lookup_msg->rd_count);
191 struct GNUNET_GNSRECORD_Data rd[rd_count];
192 uint32_t r_id = ntohl (lookup_msg->id);
193 struct GNUNET_GNS_LookupRequest *lr;
194 GNUNET_GNS_LookupResultProcessor proc;
195 void *proc_cls;
196
197 LOG (GNUNET_ERROR_TYPE_DEBUG,
198 "Received lookup reply from GNS service (%u records)\n",
199 (unsigned int) rd_count);
200 for (lr = handle->lookup_head; NULL != lr; lr = lr->next)
201 if (lr->r_id == r_id)
202 break;
203 if (NULL == lr)
204 return;
205 proc = lr->lookup_proc;
206 proc_cls = lr->proc_cls;
207
208 GNUNET_assert (GNUNET_OK ==
209 GNUNET_GNSRECORD_records_deserialize (mlen,
210 (const
211 char *) &lookup_msg[1],
212 rd_count,
213 rd));
214 proc (proc_cls,
215 rd_count,
216 rd);
217 GNUNET_CONTAINER_DLL_remove (handle->lookup_head,
218 handle->lookup_tail,
219 lr);
220 if (NULL != lr->env)
221 GNUNET_MQ_discard (lr->env);
222 GNUNET_free (lr);
223}
224
225
226/**
227 * Reconnect to GNS service.
228 *
229 * @param handle the handle to the GNS service
230 */
231static void
232reconnect (struct GNUNET_GNS_Handle *handle)
233{
234 struct GNUNET_MQ_MessageHandler handlers[] = {
235 GNUNET_MQ_hd_var_size (result,
236 GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT,
237 struct LookupResultMessage,
238 handle),
239 GNUNET_MQ_handler_end ()
240 };
241
242 GNUNET_assert (NULL == handle->mq);
243 LOG (GNUNET_ERROR_TYPE_DEBUG,
244 "Trying to connect to GNS\n");
245 handle->mq = GNUNET_CLIENT_connect (handle->cfg,
246 "gns",
247 handlers,
248 &mq_error_handler,
249 handle);
250 if (NULL == handle->mq)
251 return;
252 for (struct GNUNET_GNS_LookupRequest *lh = handle->lookup_head;
253 NULL != lh;
254 lh = lh->next)
255 GNUNET_MQ_send_copy (handle->mq,
256 lh->env);
257}
258
259
260/**
261 * Initialize the connection with the GNS service.
262 *
263 * @param cfg configuration to use
264 * @return handle to the GNS service, or NULL on error
265 */
266struct GNUNET_GNS_Handle *
267GNUNET_GNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
268{
269 struct GNUNET_GNS_Handle *handle;
270
271 handle = GNUNET_new (struct GNUNET_GNS_Handle);
272 handle->cfg = cfg;
273 reconnect (handle);
274 if (NULL == handle->mq)
275 {
276 GNUNET_free (handle);
277 return NULL;
278 }
279 return handle;
280}
281
282
283/**
284 * Shutdown connection with the GNS service.
285 *
286 * @param handle handle of the GNS connection to stop
287 */
288void
289GNUNET_GNS_disconnect (struct GNUNET_GNS_Handle *handle)
290{
291 if (NULL != handle->mq)
292 {
293 GNUNET_MQ_destroy (handle->mq);
294 handle->mq = NULL;
295 }
296 if (NULL != handle->reconnect_task)
297 {
298 GNUNET_SCHEDULER_cancel (handle->reconnect_task);
299 handle->reconnect_task = NULL;
300 }
301 GNUNET_assert (NULL == handle->lookup_head);
302 GNUNET_free (handle);
303}
304
305
306/**
307 * Cancel pending lookup request
308 *
309 * @param lr the lookup request to cancel
310 * @return closure from the lookup result processor
311 */
312void *
313GNUNET_GNS_lookup_cancel (struct GNUNET_GNS_LookupRequest *lr)
314{
315 struct GNUNET_GNS_Handle *handle = lr->gns_handle;
316 void *ret;
317
318 GNUNET_CONTAINER_DLL_remove (handle->lookup_head,
319 handle->lookup_tail,
320 lr);
321 GNUNET_MQ_discard (lr->env);
322 ret = lr->proc_cls;
323 GNUNET_free (lr);
324 return ret;
325}
326
327
328/**
329 * Perform an asynchronous lookup operation on the GNS.
330 *
331 * @param handle handle to the GNS service
332 * @param name the name to look up (in UTF-8 encoding)
333 * @param zone zone to look in
334 * @param type the GNS record type to look for
335 * @param options local options for the lookup
336 * @param recursion_depth_limit maximum number of zones
337 * that the lookup may (still) traverse
338 * @param proc function to call on result
339 * @param proc_cls closure for @a proc
340 * @return handle to the queued request
341 */
342struct GNUNET_GNS_LookupRequest *
343GNUNET_GNS_lookup_limited (struct GNUNET_GNS_Handle *handle,
344 const char *name,
345 const struct GNUNET_CRYPTO_PublicKey *zone,
346 uint32_t type,
347 enum GNUNET_GNS_LocalOptions options,
348 uint16_t recursion_depth_limit,
349 GNUNET_GNS_LookupResultProcessor proc,
350 void *proc_cls)
351{
352 /* IPC to shorten gns names, return shorten_handle */
353 struct LookupMessage *lookup_msg;
354 struct GNUNET_GNS_LookupRequest *lr;
355 size_t nlen;
356 size_t key_len;
357 ssize_t written;
358 char *buf;
359
360 if (NULL == name)
361 {
362 GNUNET_break (0);
363 return NULL;
364 }
365 LOG (GNUNET_ERROR_TYPE_DEBUG,
366 "Trying to lookup `%s' in GNS\n",
367 name);
368 nlen = strlen (name) + 1;
369 if (nlen >= GNUNET_MAX_MESSAGE_SIZE - sizeof(*lr))
370 {
371 GNUNET_break (0);
372 return NULL;
373 }
374 lr = GNUNET_new (struct GNUNET_GNS_LookupRequest);
375 lr->gns_handle = handle;
376 lr->lookup_proc = proc;
377 lr->proc_cls = proc_cls;
378 lr->r_id = handle->r_id_gen++;
379 key_len = GNUNET_CRYPTO_public_key_get_length (zone);
380 lr->env = GNUNET_MQ_msg_extra (lookup_msg,
381 nlen + key_len,
382 GNUNET_MESSAGE_TYPE_GNS_LOOKUP);
383 buf = (char *) &lookup_msg[1];
384 lookup_msg->id = htonl (lr->r_id);
385 lookup_msg->options = htons ((uint16_t) options);
386 lookup_msg->recursion_depth_limit
387 = htons (recursion_depth_limit);
388 lookup_msg->key_len = htonl (key_len);
389 written = GNUNET_CRYPTO_write_public_key_to_buffer (zone,
390 buf,
391 key_len);
392 GNUNET_assert (0 <= written);
393 buf += written;
394 lookup_msg->type = htonl (type);
395 GNUNET_memcpy (buf,
396 name,
397 nlen);
398 GNUNET_CONTAINER_DLL_insert (handle->lookup_head,
399 handle->lookup_tail,
400 lr);
401 if (NULL != handle->mq)
402 GNUNET_MQ_send_copy (handle->mq,
403 lr->env);
404 return lr;
405}
406
407
408/**
409 * Perform an asynchronous lookup operation on the GNS.
410 *
411 * @param handle handle to the GNS service
412 * @param name the name to look up (in UTF-8 encoding)
413 * @param zone the zone to start the resolution in
414 * @param type the record type to look up
415 * @param options local options for the lookup
416 * @param proc processor to call on result
417 * @param proc_cls closure for @a proc
418 * @return handle to the get request
419 */
420struct GNUNET_GNS_LookupRequest*
421GNUNET_GNS_lookup (struct GNUNET_GNS_Handle *handle,
422 const char *name,
423 const struct GNUNET_CRYPTO_PublicKey *zone,
424 uint32_t type,
425 enum GNUNET_GNS_LocalOptions options,
426 GNUNET_GNS_LookupResultProcessor proc,
427 void *proc_cls)
428{
429 return GNUNET_GNS_lookup_limited (handle,
430 name,
431 zone,
432 type,
433 options,
434 DEFAULT_LIMIT,
435 proc,
436 proc_cls);
437}
438
439
440/* end of gns_api.c */
diff --git a/src/service/gns/gns_api.h b/src/service/gns/gns_api.h
new file mode 100644
index 000000000..003955bdd
--- /dev/null
+++ b/src/service/gns/gns_api.h
@@ -0,0 +1,74 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013, 2016, 2018 GNUnet e.V.
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 gns/gns_api.h
22 * @brief shared data structures of libgnunetgns
23 * @author Martin Schanzenbach
24 * @author Christian Grothoff
25 */
26#ifndef GNS_API_H
27#define GNS_API_H
28
29#include "gnunet_gns_service.h"
30
31
32/**
33 * Connection to the GNS service.
34 */
35struct GNUNET_GNS_Handle
36{
37 /**
38 * Configuration to use.
39 */
40 const struct GNUNET_CONFIGURATION_Handle *cfg;
41
42 /**
43 * Connection to service (if available).
44 */
45 struct GNUNET_MQ_Handle *mq;
46
47 /**
48 * Head of linked list of active lookup requests.
49 */
50 struct GNUNET_GNS_LookupRequest *lookup_head;
51
52 /**
53 * Tail of linked list of active lookup requests.
54 */
55 struct GNUNET_GNS_LookupRequest *lookup_tail;
56
57 /**
58 * Reconnect task
59 */
60 struct GNUNET_SCHEDULER_Task *reconnect_task;
61
62 /**
63 * How long do we wait until we try to reconnect?
64 */
65 struct GNUNET_TIME_Relative reconnect_backoff;
66
67 /**
68 * Request Id generator. Incremented by one for each request.
69 */
70 uint32_t r_id_gen;
71};
72
73
74#endif
diff --git a/src/service/gns/gns_tld_api.c b/src/service/gns/gns_tld_api.c
new file mode 100644
index 000000000..7a6864010
--- /dev/null
+++ b/src/service/gns/gns_tld_api.c
@@ -0,0 +1,352 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013, 2016, 2018 GNUnet e.V.
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 gns/gns_tld_api.c
22 * @brief library to access the GNS service, including TLD lookup
23 * @author Martin Schanzenbach
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_common.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_constants.h"
30#include "gnunet_arm_service.h"
31#include "gnunet_identity_service.h"
32#include "gnunet_protocols.h"
33#include "gnunet_dht_service.h"
34#include "gns.h"
35#include "gns_api.h"
36
37
38#define LOG(kind, ...) GNUNET_log_from (kind, "gns-tld-api", __VA_ARGS__)
39
40
41/**
42 * Handle to a lookup request
43 */
44struct GNUNET_GNS_LookupWithTldRequest
45{
46 /**
47 * handle to gns
48 */
49 struct GNUNET_GNS_Handle *gns_handle;
50
51 /**
52 * processor to call on lookup result
53 */
54 GNUNET_GNS_LookupResultProcessor2 lookup_proc;
55
56 /**
57 * Domain name we are resolving.
58 */
59 char *name;
60
61 /**
62 * @e lookup_proc closure
63 */
64 void *lookup_proc_cls;
65
66 /**
67 * Underlying GNS lookup.
68 */
69 struct GNUNET_GNS_LookupRequest *lr;
70
71 /**
72 * Lookup an ego with the identity service.
73 */
74 struct GNUNET_IDENTITY_EgoSuffixLookup *id_co;
75
76 /**
77 * Name of the longest matching ego found so far.
78 * Must be freed on termination.
79 */
80 char *longest_match;
81
82 /**
83 * Ego corresponding to @e longest_match.
84 */
85 struct GNUNET_IDENTITY_Ego *longest_match_ego;
86
87 /**
88 * Desired result record type.
89 */
90 uint32_t type;
91
92 /**
93 * Lookup options.
94 */
95 enum GNUNET_GNS_LocalOptions options;
96};
97
98
99/**
100 * Obtain the TLD of the given @a name.
101 *
102 * @param name a name
103 * @return the part of @a name after the last ".",
104 * or @a name if @a name does not contain a "."
105 */
106static const char *
107get_tld (const char *name)
108{
109 const char *tld;
110
111 tld = strrchr (name, (unsigned char) '.');
112 if (NULL == tld)
113 tld = name;
114 else
115 tld++; /* skip the '.' */
116 return tld;
117}
118
119
120/**
121 * Eat the "TLD" (last bit) of the given @a name.
122 *
123 * @param[in,out] name a name
124 * @param tld what to eat (can be more than just the tld)
125 */
126static void
127eat_tld (char *name, const char *tld)
128{
129 GNUNET_assert (0 < strlen (name));
130 if ((NULL == tld) || (strlen (name) == strlen (tld)))
131 {
132 strcpy (name, GNUNET_GNS_EMPTY_LABEL_AT);
133 }
134 else
135 {
136 GNUNET_assert (strlen (tld) < strlen (name));
137 name[strlen (name) - strlen (tld) - 1] = '\0';
138 }
139}
140
141
142/**
143 * Function called with the result of a GNS lookup.
144 *
145 * @param cls a `struct GNUNET_GNS_LookupWithTldRequest *`
146 * @param rd_count number of records returned
147 * @param rd array of @a rd_count records with the results
148 */
149static void
150process_lookup_result (void *cls,
151 uint32_t rd_count,
152 const struct GNUNET_GNSRECORD_Data *rd)
153{
154 struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
155
156 ltr->lr = NULL;
157 ltr->lookup_proc (ltr->lookup_proc_cls, GNUNET_YES, rd_count, rd);
158 GNUNET_GNS_lookup_with_tld_cancel (ltr);
159}
160
161
162/**
163 * Perform the actual resolution, starting with the zone
164 * identified by the given public key.
165 *
166 * @param pkey public key to use for the zone, can be NULL
167 */
168static void
169lookup_with_public_key (struct GNUNET_GNS_LookupWithTldRequest *ltr,
170 const struct GNUNET_CRYPTO_PublicKey *pkey)
171{
172 ltr->lr = GNUNET_GNS_lookup (ltr->gns_handle,
173 ltr->name,
174 pkey,
175 ltr->type,
176 ltr->options,
177 &process_lookup_result,
178 ltr);
179}
180
181
182/**
183 * Method called to with the ego we are to use for the lookup,
184 * when the ego is determined by a name.
185 *
186 * @param cls a `struct GNUNET_GNS_LookupWithTldRequest *`
187 * @param ego ego handle, NULL at the end of the iteration
188 * @param ctx context we could store data to associate with @e ego
189 * @param ego_name name of the ego
190 */
191static void
192identity_zone_cb (void *cls,
193 const struct GNUNET_CRYPTO_PrivateKey *priv,
194 const char *ego_name)
195{
196 struct GNUNET_GNS_LookupWithTldRequest *ltr = cls;
197 struct GNUNET_CRYPTO_PublicKey pkey;
198
199 ltr->id_co = NULL;
200 if (NULL == priv)
201 {
202 /* no matching ego found */
203 ltr->lookup_proc (ltr->lookup_proc_cls, GNUNET_NO, 0, NULL);
204 return;
205 }
206 /* Final case: TLD matches one of our egos */
207 if (0 == strcmp (ltr->name, ego_name))
208 {
209 /* name matches ego name perfectly, only "@" remains */
210 strcpy (ltr->name, GNUNET_GNS_EMPTY_LABEL_AT);
211 }
212 else
213 {
214 GNUNET_assert (strlen (ego_name) < strlen (ltr->name));
215 ltr->name[strlen (ltr->name) - strlen (ego_name) - 1] = '\0';
216 }
217 /* if the name is of the form 'label' (and not 'label.SUBDOMAIN'), never go to the DHT */
218 if (NULL == strchr (ltr->name, (unsigned char) '.'))
219 ltr->options = GNUNET_GNS_LO_NO_DHT;
220 else
221 ltr->options = GNUNET_GNS_LO_LOCAL_MASTER;
222 GNUNET_CRYPTO_key_get_public (priv, &pkey);
223 lookup_with_public_key (ltr, &pkey);
224}
225
226
227enum GNUNET_GenericReturnValue
228GNUNET_GNS_parse_ztld (const char *name,
229 struct GNUNET_CRYPTO_PublicKey *ztld_key)
230{
231 const char *tld;
232
233 /* start with trivial case: TLD is zkey */
234 tld = get_tld (name);
235 return GNUNET_CRYPTO_public_key_from_string (tld, ztld_key);
236}
237
238
239struct GNUNET_GNS_LookupWithTldRequest *
240GNUNET_GNS_lookup_with_tld (struct GNUNET_GNS_Handle *handle,
241 const char *name,
242 uint32_t type,
243 enum GNUNET_GNS_LocalOptions options,
244 GNUNET_GNS_LookupResultProcessor2 proc,
245 void *proc_cls)
246{
247 struct GNUNET_GNS_LookupWithTldRequest *ltr;
248 const char *tld;
249 char *dot_tld;
250 char *zonestr;
251 struct GNUNET_CRYPTO_PublicKey pkey;
252
253 ltr = GNUNET_new (struct GNUNET_GNS_LookupWithTldRequest);
254 ltr->gns_handle = handle;
255 ltr->name = GNUNET_strdup (name);
256 ltr->type = type;
257 ltr->options = options;
258 ltr->lookup_proc = proc;
259 ltr->lookup_proc_cls = proc_cls;
260 /* start with trivial case: TLD is zkey */
261 if (GNUNET_OK ==
262 GNUNET_GNS_parse_ztld (ltr->name, &pkey))
263 {
264 tld = get_tld (ltr->name);
265 LOG (GNUNET_ERROR_TYPE_DEBUG,
266 "`%s' seems to be a valid zone key\n", tld);
267 eat_tld (ltr->name, tld);
268 lookup_with_public_key (ltr, &pkey);
269 return ltr;
270 }
271
272 /* second case: domain is mapped in our configuration file */
273 for (const char *domain = name; NULL != domain;
274 domain = strchr (domain, (unsigned char) '.'))
275 {
276 if ('.' == domain[0])
277 domain++;
278 GNUNET_asprintf (&dot_tld, ".%s", domain);
279 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (handle->cfg,
280 "gns",
281 dot_tld,
282 &zonestr))
283 {
284 if (GNUNET_OK !=
285 GNUNET_CRYPTO_public_key_from_string (zonestr,
286 &pkey))
287 {
288 GNUNET_log_config_invalid (
289 GNUNET_ERROR_TYPE_ERROR,
290 "gns",
291 dot_tld,
292 _ ("Expected a base32-encoded public zone key\n"));
293 GNUNET_free (zonestr);
294 GNUNET_free (dot_tld);
295 GNUNET_free (ltr->name);
296 GNUNET_free (ltr);
297 return NULL;
298 }
299 eat_tld (ltr->name, &dot_tld[1]);
300 GNUNET_free (zonestr);
301 GNUNET_free (dot_tld);
302 lookup_with_public_key (ltr, &pkey);
303 return ltr;
304 }
305 GNUNET_free (dot_tld);
306 }
307 LOG (GNUNET_ERROR_TYPE_DEBUG,
308 "`%s' should be a valid ego\n", ltr->name);
309 ltr->id_co =
310 GNUNET_IDENTITY_ego_lookup_by_suffix (ltr->gns_handle->cfg,
311 ltr->name,
312 &identity_zone_cb,
313 ltr);
314 if (NULL == ltr->id_co)
315 {
316 GNUNET_free (ltr->name);
317 GNUNET_free (ltr);
318 return NULL;
319 }
320 return ltr;
321}
322
323
324/**
325 * Cancel pending lookup request
326 *
327 * @param ltr the lookup request to cancel
328 * @return closure from the lookup result processor
329 */
330void *
331GNUNET_GNS_lookup_with_tld_cancel (struct GNUNET_GNS_LookupWithTldRequest *ltr)
332{
333 void *ret = ltr->lookup_proc_cls;
334
335 if (NULL != ltr->id_co)
336 {
337 GNUNET_IDENTITY_ego_lookup_by_suffix_cancel (ltr->id_co);
338 ltr->id_co = NULL;
339 }
340 if (NULL != ltr->lr)
341 {
342 GNUNET_GNS_lookup_cancel (ltr->lr);
343 ltr->lr = NULL;
344 }
345 GNUNET_free (ltr->longest_match);
346 GNUNET_free (ltr->name);
347 GNUNET_free (ltr);
348 return ret;
349}
350
351
352/* end of gns_tld_api.c */
diff --git a/src/service/gns/gnunet-bcd.c b/src/service/gns/gnunet-bcd.c
new file mode 100644
index 000000000..0f7fb6507
--- /dev/null
+++ b/src/service/gns/gnunet-bcd.c
@@ -0,0 +1,677 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
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 gns/gnunet-bcd.c
23 * @author Christian Grothoff
24 * @brief HTTP server to create GNS business cards
25 */
26
27#include "platform.h"
28#include <microhttpd.h>
29#include "gnunet_util_lib.h"
30#include "gnunet_identity_service.h"
31#include "gnunet_mhd_compat.h"
32
33struct StaticResource
34{
35 /**
36 * Handle to file on disk.
37 */
38 struct GNUNET_DISK_FileHandle *handle;
39
40 /**
41 * Size in bytes of the file.
42 */
43 uint64_t size;
44
45 /**
46 * Cached response object to send to clients.
47 */
48 struct MHD_Response *response;
49};
50
51struct ParameterMap
52{
53 /**
54 * Name of the parameter from the request.
55 */
56 char *name;
57
58 /**
59 * Name of the definition in the TeX output.
60 */
61 char *definition;
62};
63
64/**
65 * Handle to the HTTP server as provided by libmicrohttpd
66 */
67static struct MHD_Daemon *httpd = NULL;
68
69/**
70 * Our primary task for the HTTPD.
71 */
72static struct GNUNET_SCHEDULER_Task *httpd_task = NULL;
73
74/**
75 * Index file resource (simple result).
76 */
77static struct StaticResource *index_simple = NULL;
78
79/**
80 * Index file resource (full result).
81 */
82static struct StaticResource *index_full = NULL;
83
84/**
85 * Error: invalid gns key.
86 */
87static struct StaticResource *key_error = NULL;
88
89/**
90 * Error: 404
91 */
92static struct StaticResource *notfound_error = NULL;
93
94/**
95 * Errors after receiving the form data.
96 */
97static struct StaticResource *internal_error = NULL;
98
99/**
100 * Other errors.
101 */
102static struct StaticResource *forbidden_error = NULL;
103
104/**
105 * Full path to the TeX template file (simple result)
106 */
107static char *tex_file_simple = NULL;
108
109/**
110 * Full path to the TeX template file (full result)
111 */
112static char *tex_file_full = NULL;
113
114/**
115 * Full path to the TeX template file (PNG result)
116 */
117static char *tex_file_png = NULL;
118
119/**
120 * Used as a sort of singleton to send exactly one 100 CONTINUE per request.
121 */
122static int continue_100 = 100;
123
124/**
125 * Map of names with TeX definitions, used during PDF generation.
126 */
127static const struct ParameterMap pmap[] = {
128 {"prefix", "prefix"},
129 {"name", "name"},
130 {"suffix", "suffix"},
131 {"street", "street"},
132 {"city", "city"},
133 {"phone", "phone"},
134 {"fax", "fax"},
135 {"email", "email"},
136 {"homepage", "homepage"},
137 {"org", "organization"},
138 {"department", "department"},
139 {"subdepartment", "subdepartment"},
140 {"jobtitle", "jobtitle"},
141 {NULL, NULL},
142};
143
144/**
145 * Port number.
146 */
147static uint16_t port = 8888;
148
149/**
150 * Task ran at shutdown to clean up everything.
151 *
152 * @param cls unused
153 */
154static void
155do_shutdown (void *cls)
156{
157 /* We cheat a bit here: the file descriptor is implicitly closed by MHD, so
158 calling `GNUNET_DISK_file_close' would generate a spurious warning message
159 in the log. Since that function does nothing but close the descriptor and
160 free the allocated memory, After destroying the response all that's left to
161 do is call `GNUNET_free'. */
162 if (NULL != index_simple)
163 {
164 MHD_destroy_response (index_simple->response);
165 GNUNET_free (index_simple->handle);
166 GNUNET_free (index_simple);
167 }
168 if (NULL != index_full)
169 {
170 MHD_destroy_response (index_full->response);
171 GNUNET_free (index_full->handle);
172 GNUNET_free (index_full);
173 }
174 if (NULL != key_error)
175 {
176 MHD_destroy_response (key_error->response);
177 GNUNET_free (key_error->handle);
178 GNUNET_free (key_error);
179 }
180 if (NULL != notfound_error)
181 {
182 MHD_destroy_response (notfound_error->response);
183 GNUNET_free (notfound_error->handle);
184 GNUNET_free (notfound_error);
185 }
186 if (NULL != internal_error)
187 {
188 MHD_destroy_response (internal_error->response);
189 GNUNET_free (internal_error->handle);
190 GNUNET_free (internal_error);
191 }
192 if (NULL != forbidden_error)
193 {
194 MHD_destroy_response (forbidden_error->response);
195 GNUNET_free (forbidden_error->handle);
196 GNUNET_free (forbidden_error);
197 }
198
199 if (NULL != httpd_task)
200 {
201 GNUNET_SCHEDULER_cancel (httpd_task);
202 }
203 if (NULL != httpd)
204 {
205 MHD_stop_daemon (httpd);
206 }
207}
208
209/**
210 * Called when the HTTP server has some pending operations.
211 *
212 * @param cls unused
213 */
214static void
215do_httpd (void *cls);
216
217/**
218 * Schedule a task to run MHD.
219 */
220static void
221run_httpd (void)
222{
223 fd_set rs;
224 fd_set ws;
225 fd_set es;
226
227 struct GNUNET_NETWORK_FDSet *grs = GNUNET_NETWORK_fdset_create ();
228 struct GNUNET_NETWORK_FDSet *gws = GNUNET_NETWORK_fdset_create ();
229 struct GNUNET_NETWORK_FDSet *ges = GNUNET_NETWORK_fdset_create ();
230
231 FD_ZERO (&rs);
232 FD_ZERO (&ws);
233 FD_ZERO (&es);
234
235 int max = -1;
236 GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
237
238 unsigned MHD_LONG_LONG timeout = 0;
239 struct GNUNET_TIME_Relative gtime = GNUNET_TIME_UNIT_FOREVER_REL;
240 if (MHD_YES == MHD_get_timeout (httpd, &timeout))
241 {
242 gtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
243 timeout);
244 }
245
246 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
247 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
248 GNUNET_NETWORK_fdset_copy_native (ges, &es, max + 1);
249
250 httpd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
251 gtime,
252 grs,
253 gws,
254 &do_httpd,
255 NULL);
256 GNUNET_NETWORK_fdset_destroy (grs);
257 GNUNET_NETWORK_fdset_destroy (gws);
258 GNUNET_NETWORK_fdset_destroy (ges);
259}
260
261/**
262 * Called when the HTTP server has some pending operations.
263 *
264 * @param cls unused
265 */
266static void
267do_httpd (void *cls)
268{
269 httpd_task = NULL;
270 MHD_run (httpd);
271 run_httpd ();
272}
273
274/**
275 * Send a response back to a connected client.
276 *
277 * @param cls unused
278 * @param connection the connection with the client
279 * @param url the requested address
280 * @param method the HTTP method used
281 * @param version the protocol version (including the "HTTP/" part)
282 * @param upload_data data sent with a POST request
283 * @param upload_data_size length in bytes of the POST data
284 * @param ptr used to pass data between request handling phases
285 * @return MHD_NO on error
286 */
287static MHD_RESULT
288create_response (void *cls,
289 struct MHD_Connection *connection,
290 const char *url,
291 const char *method,
292 const char *version,
293 const char *upload_data,
294 size_t *upload_data_size,
295 void **ptr)
296{
297 (void) cls;
298 (void) version;
299 (void) upload_data;
300 (void) upload_data_size;
301
302 bool isget = (0 == strcmp (method, MHD_HTTP_METHOD_GET));
303 bool ishead = (0 == strcmp (method, MHD_HTTP_METHOD_HEAD));
304
305 if (!isget && !ishead)
306 {
307 return MHD_queue_response (connection,
308 MHD_HTTP_NOT_IMPLEMENTED,
309 forbidden_error->response);
310 }
311
312 if (ishead)
313 {
314 /* Dedicated branch in case we want to provide a different result for some
315 reason (e.g. a non-web browser application using the web UI) */
316 return MHD_queue_response (connection,
317 MHD_HTTP_OK,
318 index_simple->response);
319 }
320
321 /* Send a 100 CONTINUE response to tell clients that the result of the
322 request might take some time */
323 if (NULL == *ptr)
324 {
325 *ptr = &continue_100;
326 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending 100 CONTINUE\n");
327 return MHD_YES;
328 }
329
330 if (0 == strcmp ("/", url))
331 {
332 return MHD_queue_response (connection,
333 MHD_HTTP_OK,
334 index_simple->response);
335 }
336
337 if (0 == strcmp ("/full", url))
338 {
339 return MHD_queue_response (connection,
340 MHD_HTTP_OK,
341 index_full->response);
342 }
343
344 bool isfull = (0 == strcmp ("/submit/full", url));
345 bool issimple = (0 == strcmp ("/submit/simple", url));
346
347 if (!isfull && !issimple)
348 {
349 return MHD_queue_response (connection,
350 MHD_HTTP_NOT_FOUND,
351 notfound_error->response);
352 }
353
354 const char *gpgfp = MHD_lookup_connection_value (connection,
355 MHD_GET_ARGUMENT_KIND,
356 "gpgfingerprint");
357 const char *gnsnick = MHD_lookup_connection_value (connection,
358 MHD_GET_ARGUMENT_KIND,
359 "gnsnick");
360 const char *gnskey = MHD_lookup_connection_value (connection,
361 MHD_GET_ARGUMENT_KIND,
362 "gnskey");
363 const char *qrpng = MHD_lookup_connection_value (connection,
364 MHD_GET_ARGUMENT_KIND,
365 "gnspng");
366
367 struct GNUNET_CRYPTO_PublicKey pk;
368 if (NULL == gnskey
369 || GNUNET_OK != GNUNET_CRYPTO_public_key_from_string (gnskey, &pk))
370 {
371 return MHD_queue_response (connection,
372 MHD_HTTP_BAD_REQUEST,
373 key_error->response);
374 }
375
376 char *tmpd = GNUNET_DISK_mkdtemp (gnskey);
377 if (NULL == tmpd)
378 {
379 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mktemp", gnskey);
380 return MHD_queue_response (connection,
381 MHD_HTTP_INTERNAL_SERVER_ERROR,
382 internal_error->response);
383 }
384
385 char *defpath = NULL;
386 GNUNET_asprintf (&defpath, "%s%s%s", tmpd, DIR_SEPARATOR_STR, "def.tex");
387
388 FILE *deffile = fopen (defpath, "w");
389 if (NULL == deffile)
390 {
391 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", defpath);
392 GNUNET_free (defpath);
393 GNUNET_DISK_directory_remove (tmpd);
394 GNUNET_free (tmpd);
395 return MHD_queue_response (connection,
396 MHD_HTTP_INTERNAL_SERVER_ERROR,
397 internal_error->response);
398 }
399
400 GNUNET_free (defpath);
401
402 for (size_t i=0; NULL!=pmap[i].name; ++i)
403 {
404 const char *value = MHD_lookup_connection_value (connection,
405 MHD_GET_ARGUMENT_KIND,
406 pmap[i].name);
407 fprintf (deffile,
408 "\\def\\%s{%s}\n",
409 pmap[i].definition,
410 (NULL == value) ? "" : value);
411 }
412
413 if (NULL != gpgfp)
414 {
415 size_t len = strlen (gpgfp);
416 char *line1 = GNUNET_strndup (gpgfp, len/2);
417 char *line2 = GNUNET_strdup (&gpgfp[len/2]);
418 fprintf (deffile,
419 "\\def\\gpglineone{%s}\n\\def\\gpglinetwo{%s}\n",
420 line1,
421 line2);
422 GNUNET_free (line1);
423 GNUNET_free (line2);
424 }
425
426 fprintf (deffile,
427 "\\def\\gns{%s/%s}\n",
428 gnskey,
429 (NULL == gnsnick) ? "" : gnsnick);
430
431 fclose (deffile);
432
433 char *command = NULL;
434 GNUNET_asprintf (&command,
435 "cd %s; cp %s gns-bcd.tex; "
436 "pdflatex %s gns-bcd.tex >/dev/null 2>&1",
437 tmpd,
438 (isfull) ? tex_file_full :
439 ((NULL == qrpng) ? tex_file_simple : tex_file_png),
440 (NULL == qrpng) ? "" : "-shell-escape");
441
442 int ret = system (command);
443
444 GNUNET_free (command);
445
446 if (WIFSIGNALED (ret) || 0 != WEXITSTATUS (ret))
447 {
448 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "system", command);
449 }
450
451 GNUNET_asprintf (&defpath,
452 "%s%s%s",
453 tmpd,
454 DIR_SEPARATOR_STR,
455 (NULL == qrpng) ? "gns-bcd.pdf" : "gns-bcd.png");
456
457 int pdf = open (defpath, O_RDONLY);
458 if (-1 == pdf)
459 {
460 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", defpath);
461 GNUNET_free (defpath);
462 GNUNET_DISK_directory_remove (tmpd);
463 GNUNET_free (tmpd);
464 return MHD_queue_response (connection,
465 MHD_HTTP_INTERNAL_SERVER_ERROR,
466 internal_error->response);
467 }
468
469 struct stat statret;
470 GNUNET_break (0 == stat (defpath, &statret));
471
472 GNUNET_free (defpath);
473
474 struct MHD_Response *pdfrs =
475 MHD_create_response_from_fd ((size_t) statret.st_size, pdf);
476 if (NULL == pdfrs)
477 {
478 GNUNET_break (0);
479 GNUNET_break (0 == close (pdf));
480 GNUNET_DISK_directory_remove (tmpd);
481 GNUNET_free (tmpd);
482 return MHD_queue_response (connection,
483 MHD_HTTP_INTERNAL_SERVER_ERROR,
484 internal_error->response);
485 }
486
487 GNUNET_assert (MHD_NO != MHD_add_response_header (pdfrs,
488 MHD_HTTP_HEADER_CONTENT_TYPE,
489 (NULL == qrpng) ? "application/pdf" : "image/png"));
490 GNUNET_assert (MHD_NO != MHD_add_response_header (pdfrs,
491 MHD_HTTP_HEADER_CONTENT_DISPOSITION,
492 (NULL == qrpng) ?
493 "attachment; filename=\"gns-business-card.pdf\"" :
494 "attachment; filename=\"gns-qr-code.png\""));
495 MHD_RESULT r = MHD_queue_response (connection, MHD_HTTP_OK, pdfrs);
496
497 MHD_destroy_response (pdfrs);
498 GNUNET_DISK_directory_remove (tmpd);
499 GNUNET_free (tmpd);
500
501 return r;
502}
503
504/**
505 * Open a file on disk and generate a response for it.
506 *
507 * @param name name of the file to open
508 * @param basedir directory where the file is located
509 * @return NULL on error
510 */
511static struct StaticResource *
512open_static_resource (const char *name, const char *basedir)
513{
514 char *fullname = NULL;
515 GNUNET_asprintf (&fullname, "%s%s%s", basedir, DIR_SEPARATOR_STR, name);
516
517 struct GNUNET_DISK_FileHandle *f =
518 GNUNET_DISK_file_open (fullname,
519 GNUNET_DISK_OPEN_READ,
520 GNUNET_DISK_PERM_NONE);
521
522 GNUNET_free (fullname);
523
524 if (NULL == f)
525 {
526 return NULL;
527 }
528
529 off_t size = 0;
530 if (GNUNET_SYSERR == GNUNET_DISK_file_handle_size (f, &size))
531 {
532 GNUNET_DISK_file_close (f);
533 return NULL;
534 }
535
536 struct MHD_Response *response = MHD_create_response_from_fd64 (size, f->fd);
537
538 if (NULL == response)
539 {
540 GNUNET_DISK_file_close (f);
541 return NULL;
542 }
543
544 struct StaticResource *res = GNUNET_new (struct StaticResource);
545 res->handle = f;
546 res->size = (uint64_t) size;
547 res->response = response;
548
549 return res;
550}
551
552/**
553 * Main function that will be run.
554 *
555 * @param cls closure
556 * @param args remaining command-line arguments
557 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
558 * @param c configuration
559 */
560static void
561run (void *cls,
562 char *const *args,
563 const char *cfgfile,
564 const struct GNUNET_CONFIGURATION_Handle *c)
565{
566 (void) cls;
567 (void) args;
568 (void) cfgfile;
569
570 if (0 == port)
571 {
572 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
573 _ ("Invalid port number %u\n"),
574 port);
575 GNUNET_SCHEDULER_shutdown ();
576 return;
577 }
578
579 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
580
581 char *datadir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
582 GNUNET_assert (NULL != datadir);
583
584 GNUNET_asprintf (&tex_file_full,
585 "%s%s%s",
586 datadir,
587 DIR_SEPARATOR_STR,
588 "gns-bcd.tex");
589 GNUNET_asprintf (&tex_file_simple,
590 "%s%s%s",
591 datadir,
592 DIR_SEPARATOR_STR,
593 "gns-bcd-simple.tex");
594 GNUNET_asprintf(&tex_file_png,
595 "%s%s%s",
596 datadir,
597 DIR_SEPARATOR_STR,
598 "gns-bcd-png.tex");
599
600 index_simple = open_static_resource ("gns-bcd-simple.html", datadir);
601 index_full = open_static_resource ("gns-bcd.html", datadir);
602 key_error = open_static_resource ("gns-bcd-invalid-key.html", datadir);
603 notfound_error = open_static_resource ("gns-bcd-not-found.html", datadir);
604 internal_error = open_static_resource ("gns-bcd-internal-error.html", datadir);
605 forbidden_error = open_static_resource ("gns-bcd-forbidden.html", datadir);
606
607 GNUNET_free (datadir);
608
609 if ((NULL == index_simple) || (NULL == index_full)
610 || (NULL == key_error) || (NULL == notfound_error)
611 || (NULL == internal_error) || (NULL == forbidden_error))
612 {
613 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
614 _ ("Unable to set up the daemon\n"));
615 GNUNET_SCHEDULER_shutdown ();
616 return;
617 }
618
619 int flags = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME;
620 do
621 {
622 httpd = MHD_start_daemon (flags,
623 port,
624 NULL, NULL,
625 &create_response, NULL,
626 MHD_OPTION_CONNECTION_LIMIT, 512,
627 MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2,
628 MHD_OPTION_CONNECTION_TIMEOUT, 60,
629 MHD_OPTION_CONNECTION_MEMORY_LIMIT, 16 * 1024,
630 MHD_OPTION_END);
631 flags = MHD_USE_DEBUG;
632 } while (NULL == httpd && flags != MHD_USE_DEBUG);
633
634 if (NULL == httpd)
635 {
636 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
637 _ ("Failed to start HTTP server\n"));
638 GNUNET_SCHEDULER_shutdown ();
639 return;
640 }
641
642 run_httpd ();
643}
644
645/**
646 * The main function for gnunet-gns.
647 *
648 * @param argc number of arguments from the command line
649 * @param argv command line arguments
650 * @return 0 ok, 1 on error
651 */
652int
653main (int argc, char *const *argv)
654{
655 struct GNUNET_GETOPT_CommandLineOption options[] = {
656 GNUNET_GETOPT_option_uint16 (
657 'p',
658 "port",
659 "PORT",
660 gettext_noop ("Run HTTP server on port PORT (default is 8888)"),
661 &port),
662 GNUNET_GETOPT_OPTION_END,
663 };
664
665 return ((GNUNET_OK ==
666 GNUNET_PROGRAM_run (argc,
667 argv,
668 "gnunet-bcd",
669 _ ("GNUnet HTTP server to create business cards"),
670 options,
671 &run,
672 NULL))
673 ? 0
674 : 1);
675}
676
677/* end of gnunet-bcd.c */
diff --git a/src/service/gns/gnunet-dns2gns.c b/src/service/gns/gnunet-dns2gns.c
new file mode 100644
index 000000000..8706bec3a
--- /dev/null
+++ b/src/service/gns/gnunet-dns2gns.c
@@ -0,0 +1,1024 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2013 GNUnet e.V.
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 gnunet-dns2gns.c
22 * @brief DNS server that translates DNS requests to GNS
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include <gnunet_util_lib.h>
27#include <gnunet_gns_service.h>
28#include "gnunet_vpn_service.h"
29#include "gns.h"
30
31/**
32 * Timeout for DNS requests.
33 */
34#define TIMEOUT GNUNET_TIME_UNIT_MINUTES
35
36/**
37 * Default timeout for VPN redirections.
38 */
39#define VPN_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
40
41
42struct Request;
43
44/**
45 * Closure for #vpn_allocation_cb.
46 */
47struct VpnContext
48{
49 /**
50 * Which resolution process are we processing.
51 */
52 struct Request *request;
53
54 /**
55 * Handle to the VPN request that we were performing.
56 */
57 struct GNUNET_VPN_RedirectionRequest *vpn_request;
58
59 /**
60 * Number of records serialized in @e rd_data.
61 */
62 unsigned int rd_count;
63
64 /**
65 * Serialized records.
66 */
67 char *rd_data;
68
69 /**
70 * Number of bytes in @e rd_data.
71 */
72 ssize_t rd_data_size;
73};
74
75
76/**
77 * Data kept per request.
78 */
79struct Request
80{
81 /**
82 * Socket to use for sending the reply.
83 */
84 struct GNUNET_NETWORK_Handle *lsock;
85
86 /**
87 * Destination address to use.
88 */
89 const void *addr;
90
91 /**
92 * Initially, this is the DNS request, it will then be
93 * converted to the DNS response.
94 */
95 struct GNUNET_DNSPARSER_Packet *packet;
96
97 /**
98 * Our GNS request handle.
99 */
100 struct GNUNET_GNS_LookupWithTldRequest *lookup;
101
102 /**
103 * Our DNS request handle
104 */
105 struct GNUNET_DNSSTUB_RequestSocket *dns_lookup;
106
107 /**
108 * Task run on timeout or shutdown to clean up without
109 * response.
110 */
111 struct GNUNET_SCHEDULER_Task *timeout_task;
112
113 /**
114 * Vpn resulution context
115 */
116 struct VpnContext *vpn_ctx;
117
118 /**
119 * Original UDP request message.
120 */
121 char *udp_msg;
122
123 /**
124 * Number of bytes in @e addr.
125 */
126 size_t addr_len;
127
128 /**
129 * Number of bytes in @e udp_msg.
130 */
131 size_t udp_msg_size;
132
133 /**
134 * ID of the original request.
135 */
136 uint16_t original_request_id;
137
138};
139
140/**
141 * The address to bind to
142 */
143static in_addr_t address;
144
145/**
146 * The IPv6 address to bind to
147 */
148static struct in6_addr address6;
149
150
151/**
152 * Handle to GNS resolver.
153 */
154struct GNUNET_GNS_Handle *gns;
155
156/**
157 * Our handle to the vpn service
158 */
159static struct GNUNET_VPN_Handle *vpn_handle;
160
161/**
162 * Stub resolver
163 */
164struct GNUNET_DNSSTUB_Context *dns_stub;
165
166/**
167 * Listen socket for IPv4.
168 */
169static struct GNUNET_NETWORK_Handle *listen_socket4;
170
171/**
172 * Listen socket for IPv6.
173 */
174static struct GNUNET_NETWORK_Handle *listen_socket6;
175
176/**
177 * Task for IPv4 socket.
178 */
179static struct GNUNET_SCHEDULER_Task *t4;
180
181/**
182 * Task for IPv6 socket.
183 */
184static struct GNUNET_SCHEDULER_Task *t6;
185
186/**
187 * IP of DNS server
188 */
189static char *dns_ip;
190
191/**
192 * UDP Port we listen on for inbound DNS requests.
193 */
194static unsigned long long listen_port = 53;
195
196/**
197 * Configuration to use.
198 */
199static const struct GNUNET_CONFIGURATION_Handle *cfg;
200
201
202/**
203 * Task run on shutdown. Cleans up everything.
204 *
205 * @param cls unused
206 */
207static void
208do_shutdown (void *cls)
209{
210 (void) cls;
211 if (NULL != t4)
212 {
213 GNUNET_SCHEDULER_cancel (t4);
214 t4 = NULL;
215 }
216 if (NULL != t6)
217 {
218 GNUNET_SCHEDULER_cancel (t6);
219 t6 = NULL;
220 }
221 if (NULL != listen_socket4)
222 {
223 GNUNET_NETWORK_socket_close (listen_socket4);
224 listen_socket4 = NULL;
225 }
226 if (NULL != listen_socket6)
227 {
228 GNUNET_NETWORK_socket_close (listen_socket6);
229 listen_socket6 = NULL;
230 }
231 if (NULL != gns)
232 {
233 GNUNET_GNS_disconnect (gns);
234 gns = NULL;
235 }
236 if (NULL != vpn_handle)
237 {
238 GNUNET_VPN_disconnect (vpn_handle);
239 vpn_handle = NULL;
240 }
241 if (NULL != dns_stub)
242 {
243 GNUNET_DNSSTUB_stop (dns_stub);
244 dns_stub = NULL;
245 }
246}
247
248
249/**
250 * Shuffle answers
251 * Fisher-Yates (aka Knuth) Shuffle
252 *
253 * @param request context for the request (with answers)
254 */
255static void
256shuffle_answers (struct Request *request)
257{
258 unsigned int idx = request->packet->num_answers;
259 unsigned int r_idx;
260 struct GNUNET_DNSPARSER_Record tmp_answer;
261
262 while (0 != idx)
263 {
264 r_idx = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
265 request->packet->num_answers);
266 idx--;
267 tmp_answer = request->packet->answers[idx];
268 memcpy (&request->packet->answers[idx], &request->packet->answers[r_idx],
269 sizeof (struct GNUNET_DNSPARSER_Record));
270 memcpy (&request->packet->answers[r_idx], &tmp_answer,
271 sizeof (struct GNUNET_DNSPARSER_Record));
272 }
273}
274
275
276/**
277 * Send the response for the given request and clean up.
278 *
279 * @param request context for the request.
280 */
281static void
282send_response (struct Request *request)
283{
284 char *buf;
285 size_t size;
286 ssize_t sret;
287
288 shuffle_answers (request);
289 if (GNUNET_SYSERR ==
290 GNUNET_DNSPARSER_pack (request->packet,
291 UINT16_MAX /* is this not too much? */,
292 &buf,
293 &size))
294 {
295 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
296 _ ("Failed to pack DNS response into UDP packet!\n"));
297 }
298 else
299 {
300 sret = GNUNET_NETWORK_socket_sendto (request->lsock,
301 buf,
302 size,
303 request->addr,
304 request->addr_len);
305 if ((sret < 0) ||
306 (size != (size_t) sret))
307 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
308 "sendto");
309 GNUNET_free (buf);
310 }
311 GNUNET_SCHEDULER_cancel (request->timeout_task);
312 GNUNET_DNSPARSER_free_packet (request->packet);
313 GNUNET_free (request->udp_msg);
314 GNUNET_free (request);
315}
316
317
318/**
319 * Task run on timeout. Cleans up request.
320 *
321 * @param cls `struct Request *` of the request to clean up
322 */
323static void
324do_timeout (void *cls)
325{
326 struct Request *request = cls;
327 struct VpnContext *vpn_ctx;
328
329 if (NULL != request->packet)
330 GNUNET_DNSPARSER_free_packet (request->packet);
331 if (NULL != request->lookup)
332 GNUNET_GNS_lookup_with_tld_cancel (request->lookup);
333 if (NULL != request->dns_lookup)
334 GNUNET_DNSSTUB_resolve_cancel (request->dns_lookup);
335 GNUNET_free (request->udp_msg);
336 if (NULL != (vpn_ctx = request->vpn_ctx))
337 {
338 GNUNET_VPN_cancel_request (vpn_ctx->vpn_request);
339 GNUNET_free (vpn_ctx->rd_data);
340 GNUNET_free (vpn_ctx);
341 }
342 GNUNET_free (request);
343}
344
345
346/**
347 * Iterator called on obtained result for a DNS lookup
348 *
349 * @param cls closure
350 * @param dns the DNS udp payload
351 * @param r size of the DNS payload
352 */
353static void
354dns_result_processor (void *cls,
355 const struct GNUNET_TUN_DnsHeader *dns,
356 size_t r)
357{
358 struct Request *request = cls;
359
360 if (NULL == dns)
361 {
362 /* DNSSTUB gave up, so we trigger timeout early */
363 GNUNET_SCHEDULER_cancel (request->timeout_task);
364 do_timeout (request);
365 return;
366 }
367 if (request->original_request_id != dns->id)
368 {
369 /* for a another query, ignore */
370 return;
371 }
372 request->packet = GNUNET_DNSPARSER_parse ((char *) dns,
373 r);
374 if (NULL == request->packet)
375 {
376 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
377 _ ("Failed to parse DNS response!\n"));
378 GNUNET_SCHEDULER_cancel (request->timeout_task);
379 do_timeout (request);
380 return;
381 }
382 GNUNET_DNSSTUB_resolve_cancel (request->dns_lookup);
383 send_response (request);
384}
385
386/**
387 * Callback invoked from the VPN service once a redirection is
388 * available. Provides the IP address that can now be used to
389 * reach the requested destination. Replaces the "VPN" record
390 * with the respective A/AAAA record and continues processing.
391 *
392 * @param cls closure
393 * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
394 * will match 'result_af' from the request
395 * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
396 * that the VPN allocated for the redirection;
397 * traffic to this IP will now be redirected to the
398 * specified target peer; NULL on error
399 */
400static void
401vpn_allocation_cb (void *cls,
402 int af,
403 const void *address)
404{
405 struct VpnContext *vpn_ctx = cls;
406 struct Request *request = vpn_ctx->request;
407 struct GNUNET_GNSRECORD_Data rd[vpn_ctx->rd_count];
408 unsigned int i;
409
410 vpn_ctx->vpn_request = NULL;
411 request->vpn_ctx = NULL;
412 GNUNET_assert (GNUNET_OK ==
413 GNUNET_GNSRECORD_records_deserialize (
414 (size_t) vpn_ctx->rd_data_size,
415 vpn_ctx->rd_data,
416 vpn_ctx->rd_count,
417 rd));
418 for (i = 0; i < vpn_ctx->rd_count; i++)
419 {
420 if (GNUNET_GNSRECORD_TYPE_VPN == rd[i].record_type)
421 {
422 switch (af)
423 {
424 case AF_INET:
425 rd[i].record_type = GNUNET_DNSPARSER_TYPE_A;
426 rd[i].data_size = sizeof(struct in_addr);
427 rd[i].expiration_time = GNUNET_TIME_relative_to_absolute (
428 VPN_TIMEOUT).abs_value_us;
429 rd[i].flags = 0;
430 rd[i].data = address;
431 break;
432
433 case AF_INET6:
434 rd[i].record_type = GNUNET_DNSPARSER_TYPE_AAAA;
435 rd[i].expiration_time = GNUNET_TIME_relative_to_absolute (
436 VPN_TIMEOUT).abs_value_us;
437 rd[i].flags = 0;
438 rd[i].data = address;
439 rd[i].data_size = sizeof(struct in6_addr);
440 break;
441
442 default:
443 GNUNET_assert (0);
444 }
445 break;
446 }
447 }
448 GNUNET_assert (i < vpn_ctx->rd_count);
449 if (0 == vpn_ctx->rd_count)
450 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
451 _ ("VPN returned empty result for `%s'\n"),
452 request->packet->queries[0].name);
453 send_response (request);
454 GNUNET_free (vpn_ctx->rd_data);
455 GNUNET_free (vpn_ctx);
456}
457
458
459
460/**
461 * Iterator called on obtained result for a GNS lookup.
462 *
463 * @param cls closure
464 * @param was_gns #GNUNET_NO if the TLD is not configured for GNS
465 * @param rd_count number of records in @a rd
466 * @param rd the records in reply
467 */
468static void
469result_processor (void *cls,
470 int was_gns,
471 uint32_t rd_count,
472 const struct GNUNET_GNSRECORD_Data *rd)
473{
474 struct Request *request = cls;
475 struct GNUNET_DNSPARSER_Packet *packet;
476 struct GNUNET_DNSPARSER_Record rec;
477 struct VpnContext *vpn_ctx;
478 const struct GNUNET_TUN_GnsVpnRecord *vpn;
479 const char *vname;
480 struct GNUNET_HashCode vhash;
481 int af;
482
483 request->lookup = NULL;
484 if (GNUNET_NO == was_gns)
485 {
486 /* TLD not configured for GNS, fall back to DNS */
487 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
488 "Using DNS resolver IP `%s' to resolve `%s'\n",
489 dns_ip,
490 request->packet->queries[0].name);
491 request->original_request_id = request->packet->id;
492 GNUNET_DNSPARSER_free_packet (request->packet);
493 request->packet = NULL;
494 request->dns_lookup = GNUNET_DNSSTUB_resolve (dns_stub,
495 request->udp_msg,
496 request->udp_msg_size,
497 &dns_result_processor,
498 request);
499 return;
500 }
501 packet = request->packet;
502 packet->flags.query_or_response = 1;
503 packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NO_ERROR;
504 packet->flags.checking_disabled = 0;
505 packet->flags.authenticated_data = 1;
506 packet->flags.zero = 0;
507 packet->flags.recursion_available = 1;
508 packet->flags.message_truncated = 0;
509 packet->flags.authoritative_answer = 0;
510 // packet->flags.opcode = GNUNET_TUN_DNS_OPCODE_STATUS; // ???
511 for (uint32_t i = 0; i < rd_count; i++)
512 {
513 rec.expiration_time.abs_value_us = rd[i].expiration_time;
514 switch (rd[i].record_type)
515 {
516 case GNUNET_DNSPARSER_TYPE_A:
517 GNUNET_assert (sizeof(struct in_addr) == rd[i].data_size);
518 rec.name = GNUNET_strdup (packet->queries[0].name);
519 rec.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
520 rec.type = GNUNET_DNSPARSER_TYPE_A;
521 rec.data.raw.data = GNUNET_new (struct in_addr);
522 GNUNET_memcpy (rec.data.raw.data,
523 rd[i].data,
524 rd[i].data_size);
525 rec.data.raw.data_len = sizeof(struct in_addr);
526 GNUNET_array_append (packet->answers,
527 packet->num_answers,
528 rec);
529 break;
530
531 case GNUNET_DNSPARSER_TYPE_AAAA:
532 GNUNET_assert (sizeof(struct in6_addr) == rd[i].data_size);
533 rec.name = GNUNET_strdup (packet->queries[0].name);
534 rec.data.raw.data = GNUNET_new (struct in6_addr);
535 rec.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
536 rec.type = GNUNET_DNSPARSER_TYPE_AAAA;
537 GNUNET_memcpy (rec.data.raw.data,
538 rd[i].data,
539 rd[i].data_size);
540 rec.data.raw.data_len = sizeof(struct in6_addr);
541 GNUNET_array_append (packet->answers,
542 packet->num_answers,
543 rec);
544 break;
545
546 case GNUNET_DNSPARSER_TYPE_CNAME:
547 rec.name = GNUNET_strdup (packet->queries[0].name);
548 rec.data.hostname = GNUNET_strdup (rd[i].data);
549 rec.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
550 rec.type = GNUNET_DNSPARSER_TYPE_CNAME;
551 GNUNET_memcpy (rec.data.hostname,
552 rd[i].data,
553 rd[i].data_size);
554 GNUNET_array_append (packet->answers,
555 packet->num_answers,
556 rec);
557 break;
558 case GNUNET_GNSRECORD_TYPE_VPN:
559 if ((GNUNET_DNSPARSER_TYPE_A != request->packet->queries[0].type) &&
560 (GNUNET_DNSPARSER_TYPE_AAAA != request->packet->queries[0].type))
561 break;
562 af = (GNUNET_DNSPARSER_TYPE_A == request->packet->queries[0].type) ?
563 AF_INET :
564 AF_INET6;
565 if (sizeof(struct GNUNET_TUN_GnsVpnRecord) >
566 rd[i].data_size)
567 {
568 GNUNET_break_op (0);
569 break;
570 }
571 vpn = (const struct GNUNET_TUN_GnsVpnRecord *) rd[i].data;
572 vname = (const char *) &vpn[1];
573 if ('\0' != vname[rd[i].data_size - 1 - sizeof(struct
574 GNUNET_TUN_GnsVpnRecord)
575 ])
576 {
577 GNUNET_break_op (0);
578 break;
579 }
580 GNUNET_TUN_service_name_to_hash (vname,
581 &vhash);
582 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
583 "Attempting VPN allocation for %s-%s (AF: %d, proto %d)\n",
584 GNUNET_i2s (&vpn->peer),
585 vname,
586 (int) af,
587 (int) ntohs (vpn->proto));
588 vpn_ctx = GNUNET_new (struct VpnContext);
589 request->vpn_ctx = vpn_ctx;
590 vpn_ctx->request = request;
591 vpn_ctx->rd_data_size = GNUNET_GNSRECORD_records_get_size (rd_count,
592 rd);
593 if (vpn_ctx->rd_data_size < 0)
594 {
595 GNUNET_break_op (0);
596 GNUNET_free (vpn_ctx);
597 break;
598 }
599 vpn_ctx->rd_data = GNUNET_malloc ((size_t) vpn_ctx->rd_data_size);
600 vpn_ctx->rd_count = rd_count;
601 GNUNET_assert (vpn_ctx->rd_data_size ==
602 GNUNET_GNSRECORD_records_serialize (rd_count,
603 rd,
604 (size_t) vpn_ctx
605 ->rd_data_size,
606 vpn_ctx->rd_data));
607 vpn_ctx->vpn_request = GNUNET_VPN_redirect_to_peer (vpn_handle,
608 af,
609 ntohs (
610 vpn->proto),
611 &vpn->peer,
612 &vhash,
613 GNUNET_TIME_relative_to_absolute (
614 VPN_TIMEOUT),
615 &
616 vpn_allocation_cb,
617 vpn_ctx);
618 return;
619
620
621 default:
622 /* skip */
623 break;
624 }
625 }
626 send_response (request);
627}
628
629
630/**
631 * Handle DNS request.
632 *
633 * @param lsock socket to use for sending the reply
634 * @param addr address to use for sending the reply
635 * @param addr_len number of bytes in @a addr
636 * @param udp_msg DNS request payload
637 * @param udp_msg_size number of bytes in @a udp_msg
638 */
639static void
640handle_request (struct GNUNET_NETWORK_Handle *lsock,
641 const void *addr,
642 size_t addr_len,
643 const char *udp_msg,
644 size_t udp_msg_size)
645{
646 struct Request *request;
647 struct GNUNET_DNSPARSER_Packet *packet;
648
649 packet = GNUNET_DNSPARSER_parse (udp_msg,
650 udp_msg_size);
651 if (NULL == packet)
652 {
653 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
654 _ ("Cannot parse DNS request from %s\n"),
655 GNUNET_a2s (addr, addr_len));
656 return;
657 }
658 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
659 "Received request for `%s' with flags %u, #answers %d, #auth %d, #additional %d\n",
660 packet->queries[0].name,
661 (unsigned int) packet->flags.query_or_response,
662 (int) packet->num_answers,
663 (int) packet->num_authority_records,
664 (int) packet->num_additional_records);
665 if ((0 != packet->flags.query_or_response) ||
666 (0 != packet->num_answers) ||
667 (0 != packet->num_authority_records))
668 {
669 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
670 _ ("Received malformed DNS request from %s\n"),
671 GNUNET_a2s (addr, addr_len));
672 GNUNET_DNSPARSER_free_packet (packet);
673 return;
674 }
675 if ((1 != packet->num_queries))
676 {
677 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
678 _ ("Received unsupported DNS request from %s\n"),
679 GNUNET_a2s (addr,
680 addr_len));
681 GNUNET_DNSPARSER_free_packet (packet);
682 return;
683 }
684 request = GNUNET_malloc (sizeof(struct Request) + addr_len);
685 request->lsock = lsock;
686 request->packet = packet;
687 request->addr = &request[1];
688 request->addr_len = addr_len;
689 GNUNET_memcpy (&request[1],
690 addr,
691 addr_len);
692 request->udp_msg_size = udp_msg_size;
693 request->udp_msg = GNUNET_memdup (udp_msg,
694 udp_msg_size);
695 request->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
696 &do_timeout,
697 request);
698 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
699 "Calling GNS on `%s'\n",
700 packet->queries[0].name);
701 request->lookup = GNUNET_GNS_lookup_with_tld (gns,
702 packet->queries[0].name,
703 packet->queries[0].type,
704 GNUNET_GNS_LO_DEFAULT,
705 &result_processor,
706 request);
707}
708
709
710/**
711 * Task to read IPv4 DNS packets.
712 *
713 * @param cls the 'listen_socket4'
714 */
715static void
716read_dns4 (void *cls)
717{
718 struct sockaddr_in v4;
719 socklen_t addrlen;
720 ssize_t size;
721 const struct GNUNET_SCHEDULER_TaskContext *tc;
722
723 GNUNET_assert (listen_socket4 == cls);
724 t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
725 listen_socket4,
726 &read_dns4,
727 listen_socket4);
728 tc = GNUNET_SCHEDULER_get_task_context ();
729 if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
730 return; /* shutdown? */
731 size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket4);
732 if (0 > size)
733 {
734 GNUNET_break (0);
735 return; /* read error!? */
736 }
737 {
738 char buf[size + 1];
739 ssize_t sret;
740
741 addrlen = sizeof(v4);
742 sret = GNUNET_NETWORK_socket_recvfrom (listen_socket4,
743 buf,
744 size + 1,
745 (struct sockaddr *) &v4,
746 &addrlen);
747 if (0 > sret)
748 {
749 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
750 "recvfrom");
751 return;
752 }
753 GNUNET_break (size != sret);
754 handle_request (listen_socket4,
755 &v4,
756 addrlen,
757 buf,
758 size);
759 }
760}
761
762
763/**
764 * Task to read IPv6 DNS packets.
765 *
766 * @param cls the 'listen_socket6'
767 */
768static void
769read_dns6 (void *cls)
770{
771 struct sockaddr_in6 v6;
772 socklen_t addrlen;
773 ssize_t size;
774 const struct GNUNET_SCHEDULER_TaskContext *tc;
775
776 GNUNET_assert (listen_socket6 == cls);
777 t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
778 listen_socket6,
779 &read_dns6,
780 listen_socket6);
781 tc = GNUNET_SCHEDULER_get_task_context ();
782 if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
783 return; /* shutdown? */
784 size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket6);
785 if (0 > size)
786 {
787 GNUNET_break (0);
788 return; /* read error!? */
789 }
790 {
791 char buf[size];
792 ssize_t sret;
793
794 addrlen = sizeof(v6);
795 sret = GNUNET_NETWORK_socket_recvfrom (listen_socket6,
796 buf,
797 size,
798 (struct sockaddr *) &v6,
799 &addrlen);
800 if (0 > sret)
801 {
802 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
803 "recvfrom");
804 return;
805 }
806 GNUNET_break (size != sret);
807 handle_request (listen_socket6,
808 &v6,
809 addrlen,
810 buf,
811 size);
812 }
813}
814
815
816/**
817 * Main function that will be run.
818 *
819 * @param cls closure
820 * @param args remaining command-line arguments
821 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
822 * @param c configuration
823 */
824static void
825run (void *cls,
826 char *const *args,
827 const char *cfgfile,
828 const struct GNUNET_CONFIGURATION_Handle *c)
829{
830 char *addr_str;
831
832 (void) cls;
833 (void) args;
834 (void) cfgfile;
835 cfg = c;
836 if (NULL == dns_ip)
837 {
838 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
839 _ ("No DNS server specified!\n"));
840 return;
841 }
842 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
843 NULL);
844 if (NULL == (gns = GNUNET_GNS_connect (cfg)))
845 return;
846 if (NULL == (vpn_handle = GNUNET_VPN_connect (cfg)))
847 return;
848 GNUNET_assert (NULL != (dns_stub = GNUNET_DNSSTUB_start (128)));
849 if (GNUNET_OK !=
850 GNUNET_DNSSTUB_add_dns_ip (dns_stub,
851 dns_ip))
852 {
853 GNUNET_DNSSTUB_stop (dns_stub);
854 GNUNET_GNS_disconnect (gns);
855 gns = NULL;
856 GNUNET_VPN_disconnect (vpn_handle);
857 vpn_handle = NULL;
858 return;
859 }
860
861 /* Get address to bind to */
862 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (c, "dns2gns",
863 "BIND_TO",
864 &addr_str))
865 {
866 // No address specified
867 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
868 "Don't know what to bind to...\n");
869 GNUNET_free (addr_str);
870 GNUNET_SCHEDULER_shutdown ();
871 return;
872 }
873 if (1 != inet_pton (AF_INET, addr_str, &address))
874 {
875 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
876 "Unable to parse address %s\n",
877 addr_str);
878 GNUNET_free (addr_str);
879 GNUNET_SCHEDULER_shutdown ();
880 return;
881 }
882 GNUNET_free (addr_str);
883 /* Get address to bind to */
884 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (c, "dns2gns",
885 "BIND_TO6",
886 &addr_str))
887 {
888 // No address specified
889 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
890 "Don't know what to bind6 to...\n");
891 GNUNET_free (addr_str);
892 GNUNET_SCHEDULER_shutdown ();
893 return;
894 }
895 if (1 != inet_pton (AF_INET6, addr_str, &address6))
896 {
897 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
898 "Unable to parse IPv6 address %s\n",
899 addr_str);
900 GNUNET_free (addr_str);
901 GNUNET_SCHEDULER_shutdown ();
902 return;
903 }
904 GNUNET_free (addr_str);
905 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (c, "dns2gns",
906 "PORT",
907 &listen_port))
908 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
909 "Listening on %llu\n", listen_port);
910
911 listen_socket4 = GNUNET_NETWORK_socket_create (PF_INET,
912 SOCK_DGRAM,
913 IPPROTO_UDP);
914 if (NULL != listen_socket4)
915 {
916 struct sockaddr_in v4;
917
918 memset (&v4, 0, sizeof(v4));
919 v4.sin_family = AF_INET;
920 v4.sin_addr.s_addr = address;
921#if HAVE_SOCKADDR_IN_SIN_LEN
922 v4.sin_len = sizeof(v4);
923#endif
924 v4.sin_port = htons (listen_port);
925 if (GNUNET_OK !=
926 GNUNET_NETWORK_socket_bind (listen_socket4,
927 (struct sockaddr *) &v4,
928 sizeof(v4)))
929 {
930 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
931 GNUNET_NETWORK_socket_close (listen_socket4);
932 listen_socket4 = NULL;
933 }
934 }
935 listen_socket6 = GNUNET_NETWORK_socket_create (PF_INET6,
936 SOCK_DGRAM,
937 IPPROTO_UDP);
938 if (NULL != listen_socket6)
939 {
940 struct sockaddr_in6 v6;
941
942 memset (&v6, 0, sizeof(v6));
943 v6.sin6_family = AF_INET6;
944 v6.sin6_addr = address6;
945#if HAVE_SOCKADDR_IN_SIN_LEN
946 v6.sin6_len = sizeof(v6);
947#endif
948 v6.sin6_port = htons (listen_port);
949 if (GNUNET_OK !=
950 GNUNET_NETWORK_socket_bind (listen_socket6,
951 (struct sockaddr *) &v6,
952 sizeof(v6)))
953 {
954 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
955 GNUNET_NETWORK_socket_close (listen_socket6);
956 listen_socket6 = NULL;
957 }
958 }
959 if ((NULL == listen_socket4) &&
960 (NULL == listen_socket6))
961 {
962 GNUNET_GNS_disconnect (gns);
963 gns = NULL;
964 GNUNET_VPN_disconnect (vpn_handle);
965 vpn_handle = NULL;
966 GNUNET_DNSSTUB_stop (dns_stub);
967 dns_stub = NULL;
968 return;
969 }
970 if (NULL != listen_socket4)
971 t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
972 listen_socket4,
973 &read_dns4,
974 listen_socket4);
975 if (NULL != listen_socket6)
976 t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
977 listen_socket6,
978 &read_dns6,
979 listen_socket6);
980}
981
982
983/**
984 * The main function for the dns2gns daemon.
985 *
986 * @param argc number of arguments from the command line
987 * @param argv command line arguments
988 * @return 0 ok, 1 on error
989 */
990int
991main (int argc,
992 char *const *argv)
993{
994 struct GNUNET_GETOPT_CommandLineOption options[] = {
995 GNUNET_GETOPT_option_string ('d',
996 "dns",
997 "IP",
998 gettext_noop (
999 "IP of recursive DNS resolver to use (required)"),
1000 &dns_ip),
1001 GNUNET_GETOPT_OPTION_END
1002 };
1003 int ret;
1004
1005 if (GNUNET_OK !=
1006 GNUNET_STRINGS_get_utf8_args (argc, argv,
1007 &argc, &argv))
1008 return 2;
1009 GNUNET_log_setup ("gnunet-dns2gns",
1010 "WARNING",
1011 NULL);
1012 ret =
1013 (GNUNET_OK ==
1014 GNUNET_PROGRAM_run (argc, argv,
1015 "gnunet-dns2gns",
1016 _ ("GNUnet DNS-to-GNS proxy (a DNS server)"),
1017 options,
1018 &run, NULL)) ? 0 : 1;
1019 GNUNET_free_nz ((void *) argv);
1020 return ret;
1021}
1022
1023
1024/* end of gnunet-dns2gns.c */
diff --git a/src/service/gns/gnunet-gns-benchmark.c b/src/service/gns/gnunet-gns-benchmark.c
new file mode 100644
index 000000000..b36a83f21
--- /dev/null
+++ b/src/service/gns/gnunet-gns-benchmark.c
@@ -0,0 +1,618 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2018 GNUnet e.V.
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 src/gns/gnunet-gns-benchmark.c
22 * @brief issue many queries to GNS and compute performance statistics
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include <gnunet_util_lib.h>
27#include <gnunet_gnsrecord_lib.h>
28#include <gnunet_gns_service.h>
29
30
31/**
32 * How long do we wait at least between requests by default?
33 */
34#define DEF_REQUEST_DELAY GNUNET_TIME_relative_multiply ( \
35 GNUNET_TIME_UNIT_MILLISECONDS, 1)
36
37/**
38 * How long do we wait until we consider a request failed by default?
39 */
40#define DEF_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 1)
41
42
43/**
44 * We distinguish between different categories of
45 * requests, for which we track statistics separately.
46 * However, this process does not change how it acts
47 * based on the category.
48 */
49enum RequestCategory
50{
51 RC_SHARED = 0,
52 RC_PRIVATE = 1,
53 /**
54 * Must be last and match number of categories.
55 */
56 RC_MAX = 2
57};
58
59
60/**
61 * Request we should make. We keep this struct in memory per request,
62 * thus optimizing it is crucial for the overall memory consumption of
63 * the zone importer.
64 */
65struct Request
66{
67 /**
68 * Active requests are kept in a DLL.
69 */
70 struct Request *next;
71
72 /**
73 * Active requests are kept in a DLL.
74 */
75 struct Request *prev;
76
77 /**
78 * Socket used to make the request, NULL if not active.
79 */
80 struct GNUNET_GNS_LookupWithTldRequest *lr;
81
82 /**
83 * Hostname we are resolving, allocated at the end of
84 * this struct (optimizing memory consumption by reducing
85 * total number of allocations).
86 */
87 const char *hostname;
88
89 /**
90 * While we are fetching the record, the value is set to the
91 * starting time of the GNS operation.
92 */
93 struct GNUNET_TIME_Absolute op_start_time;
94
95 /**
96 * Observed latency, set once we got a reply.
97 */
98 struct GNUNET_TIME_Relative latency;
99
100 /**
101 * Category of the request.
102 */
103 enum RequestCategory cat;
104};
105
106
107/**
108 * GNS handle.
109 */
110static struct GNUNET_GNS_Handle *gns;
111
112/**
113 * Number of lookups we performed overall per category.
114 */
115static unsigned int lookups[RC_MAX];
116
117/**
118 * Number of replies we got per category.
119 */
120static unsigned int replies[RC_MAX];
121
122/**
123 * Number of replies we got per category.
124 */
125static unsigned int failures[RC_MAX];
126
127/**
128 * Sum of the observed latencies of successful queries,
129 * per category.
130 */
131static struct GNUNET_TIME_Relative latency_sum[RC_MAX];
132
133/**
134 * Active requests are kept in a DLL.
135 */
136static struct Request *act_head;
137
138/**
139 * Active requests are kept in a DLL.
140 */
141static struct Request *act_tail;
142
143/**
144 * Completed successful requests are kept in a DLL.
145 */
146static struct Request *succ_head;
147
148/**
149 * Completed successful requests are kept in a DLL.
150 */
151static struct Request *succ_tail;
152
153/**
154 * Yet to be started requests are kept in a DLL.
155 */
156static struct Request *todo_head;
157
158/**
159 * Yet to be started requests are kept in a DLL.
160 */
161static struct Request *todo_tail;
162
163/**
164 * Main task.
165 */
166static struct GNUNET_SCHEDULER_Task *t;
167
168/**
169 * Delay between requests.
170 */
171static struct GNUNET_TIME_Relative request_delay;
172
173/**
174 * Timeout for requests.
175 */
176static struct GNUNET_TIME_Relative timeout;
177
178/**
179 * Number of requests we have concurrently active.
180 */
181static unsigned int active_cnt;
182
183/**
184 * Look for GNS2DNS records specifically?
185 */
186static int g2d;
187
188/**
189 * Free @a req and data structures reachable from it.
190 *
191 * @param req request to free
192 */
193static void
194free_request (struct Request *req)
195{
196 if (NULL != req->lr)
197 GNUNET_GNS_lookup_with_tld_cancel (req->lr);
198 GNUNET_free (req);
199}
200
201
202/**
203 * Function called with the result of a GNS resolution.
204 *
205 * @param cls closure with the `struct Request`
206 * @param gns_tld #GNUNET_YES if GNS lookup was attempted
207 * @param rd_count number of records in @a rd
208 * @param rd the records in reply
209 */
210static void
211process_result (void *cls,
212 int gns_tld,
213 uint32_t rd_count,
214 const struct GNUNET_GNSRECORD_Data *rd)
215{
216 struct Request *req = cls;
217
218 (void) gns_tld;
219 (void) rd_count;
220 (void) rd;
221 active_cnt--;
222 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
223 "Got response for request `%s'\n",
224 req->hostname);
225 req->lr = NULL;
226 req->latency = GNUNET_TIME_absolute_get_duration (req->op_start_time);
227 GNUNET_CONTAINER_DLL_remove (act_head,
228 act_tail,
229 req);
230 GNUNET_CONTAINER_DLL_insert (succ_head,
231 succ_tail,
232 req);
233 replies[req->cat]++;
234 latency_sum[req->cat]
235 = GNUNET_TIME_relative_add (latency_sum[req->cat],
236 req->latency);
237}
238
239
240/**
241 * Process request from the queue.
242 *
243 * @param cls NULL
244 */
245static void
246process_queue (void *cls)
247{
248 struct Request *req;
249 struct GNUNET_TIME_Relative duration;
250
251 (void) cls;
252 t = NULL;
253 /* check for expired requests */
254 while (NULL != (req = act_head))
255 {
256 duration = GNUNET_TIME_absolute_get_duration (req->op_start_time);
257 if (duration.rel_value_us < timeout.rel_value_us)
258 break;
259 GNUNET_CONTAINER_DLL_remove (act_head,
260 act_tail,
261 req);
262 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
263 "Failing request `%s' due to timeout\n",
264 req->hostname);
265 failures[req->cat]++;
266 active_cnt--;
267 free_request (req);
268 }
269 if (NULL == (req = todo_head))
270 {
271 struct GNUNET_TIME_Absolute at;
272
273 if (NULL == (req = act_head))
274 {
275 GNUNET_SCHEDULER_shutdown ();
276 return;
277 }
278 at = GNUNET_TIME_absolute_add (req->op_start_time,
279 timeout);
280 t = GNUNET_SCHEDULER_add_at (at,
281 &process_queue,
282 NULL);
283 return;
284 }
285 GNUNET_CONTAINER_DLL_remove (todo_head,
286 todo_tail,
287 req);
288 GNUNET_CONTAINER_DLL_insert_tail (act_head,
289 act_tail,
290 req);
291 lookups[req->cat]++;
292 active_cnt++;
293 req->op_start_time = GNUNET_TIME_absolute_get ();
294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
295 "Starting request `%s' (%u in parallel)\n",
296 req->hostname,
297 active_cnt);
298 req->lr = GNUNET_GNS_lookup_with_tld (gns,
299 req->hostname,
300 g2d
301 ? GNUNET_GNSRECORD_TYPE_GNS2DNS
302 : GNUNET_GNSRECORD_TYPE_ANY,
303 GNUNET_GNS_LO_DEFAULT,
304 &process_result,
305 req);
306 t = GNUNET_SCHEDULER_add_delayed (request_delay,
307 &process_queue,
308 NULL);
309}
310
311
312/**
313 * Compare two requests by latency for qsort().
314 *
315 * @param c1 pointer to `struct Request *`
316 * @param c2 pointer to `struct Request *`
317 * @return -1 if c1<c2, 1 if c1>c2, 0 if c1==c2.
318 */
319static int
320compare_req (const void *c1,
321 const void *c2)
322{
323 const struct Request *r1 = *(void **) c1;
324 const struct Request *r2 = *(void **) c2;
325
326 if (r1->latency.rel_value_us < r2->latency.rel_value_us)
327 return -1;
328 if (r1->latency.rel_value_us > r2->latency.rel_value_us)
329 return 1;
330 return 0;
331}
332
333
334/**
335 * Output statistics, then clean up and terminate the process.
336 *
337 * @param cls NULL
338 */
339static void
340do_shutdown (void *cls)
341{
342 struct Request *req;
343 struct Request **ra[RC_MAX];
344 unsigned int rp[RC_MAX];
345
346 (void) cls;
347 for (enum RequestCategory rc = 0; rc < RC_MAX; rc++)
348 {
349 ra[rc] = GNUNET_new_array (replies[rc],
350 struct Request *);
351 rp[rc] = 0;
352 }
353 for (req = succ_head; NULL != req; req = req->next)
354 {
355 GNUNET_assert (rp[req->cat] < replies[req->cat]);
356 ra[req->cat][rp[req->cat]++] = req;
357 }
358 for (enum RequestCategory rc = 0; rc < RC_MAX; rc++)
359 {
360 unsigned int off;
361
362 fprintf (stdout,
363 "Category %u\n",
364 rc);
365 fprintf (stdout,
366 "\tlookups: %u replies: %u failures: %u\n",
367 lookups[rc],
368 replies[rc],
369 failures[rc]);
370 if (0 == rp[rc])
371 continue;
372 qsort (ra[rc],
373 rp[rc],
374 sizeof(struct Request *),
375 &compare_req);
376 latency_sum[rc] = GNUNET_TIME_relative_divide (latency_sum[rc],
377 replies[rc]);
378 fprintf (stdout,
379 "\taverage: %s\n",
380 GNUNET_STRINGS_relative_time_to_string (latency_sum[rc],
381 GNUNET_YES));
382 off = rp[rc] * 50 / 100;
383 fprintf (stdout,
384 "\tmedian(50): %s\n",
385 GNUNET_STRINGS_relative_time_to_string (ra[rc][off]->latency,
386 GNUNET_YES));
387 off = rp[rc] * 75 / 100;
388 fprintf (stdout,
389 "\tquantile(75): %s\n",
390 GNUNET_STRINGS_relative_time_to_string (ra[rc][off]->latency,
391 GNUNET_YES));
392 off = rp[rc] * 90 / 100;
393 fprintf (stdout,
394 "\tquantile(90): %s\n",
395 GNUNET_STRINGS_relative_time_to_string (ra[rc][off]->latency,
396 GNUNET_YES));
397 off = rp[rc] * 99 / 100;
398 fprintf (stdout,
399 "\tquantile(99): %s\n",
400 GNUNET_STRINGS_relative_time_to_string (ra[rc][off]->latency,
401 GNUNET_YES));
402 GNUNET_free (ra[rc]);
403 }
404 if (NULL != t)
405 {
406 GNUNET_SCHEDULER_cancel (t);
407 t = NULL;
408 }
409 while (NULL != (req = act_head))
410 {
411 GNUNET_CONTAINER_DLL_remove (act_head,
412 act_tail,
413 req);
414 free_request (req);
415 }
416 while (NULL != (req = succ_head))
417 {
418 GNUNET_CONTAINER_DLL_remove (succ_head,
419 succ_tail,
420 req);
421 free_request (req);
422 }
423 while (NULL != (req = todo_head))
424 {
425 GNUNET_CONTAINER_DLL_remove (todo_head,
426 todo_tail,
427 req);
428 free_request (req);
429 }
430 if (NULL != gns)
431 {
432 GNUNET_GNS_disconnect (gns);
433 gns = NULL;
434 }
435}
436
437
438/**
439 * Add @a hostname to the list of requests to be made.
440 *
441 * @param hostname name to resolve
442 * @param cat category of the @a hostname
443 */
444static void
445queue (const char *hostname,
446 enum RequestCategory cat)
447{
448 struct Request *req;
449 const char *dot;
450 size_t hlen;
451
452 dot = strchr (hostname,
453 (unsigned char) '.');
454 if (NULL == dot)
455 {
456 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
457 "Refusing invalid hostname `%s' (lacks '.')\n",
458 hostname);
459 return;
460 }
461 hlen = strlen (hostname) + 1;
462 req = GNUNET_malloc (sizeof(struct Request) + hlen);
463 req->cat = cat;
464 req->hostname = (char *) &req[1];
465 GNUNET_memcpy (&req[1],
466 hostname,
467 hlen);
468 GNUNET_CONTAINER_DLL_insert (todo_head,
469 todo_tail,
470 req);
471}
472
473
474/**
475 * Begin processing hostnames from stdin.
476 *
477 * @param cls NULL
478 */
479static void
480process_stdin (void *cls)
481{
482 static struct GNUNET_TIME_Absolute last;
483 static uint64_t idot;
484 unsigned int cat;
485 char hn[256];
486 char in[270];
487
488 (void) cls;
489 t = NULL;
490 while (NULL !=
491 fgets (in,
492 sizeof(in),
493 stdin))
494 {
495 if (strlen (in) > 0)
496 hn[strlen (in) - 1] = '\0'; /* eat newline */
497 if ((2 != sscanf (in,
498 "%u %255s",
499 &cat,
500 hn)) ||
501 (cat >= RC_MAX))
502 {
503 fprintf (stderr,
504 "Malformed input line `%s', skipping\n",
505 in);
506 continue;
507 }
508 if (0 == idot)
509 last = GNUNET_TIME_absolute_get ();
510 idot++;
511 if (0 == idot % 100000)
512 {
513 struct GNUNET_TIME_Relative delta;
514
515 delta = GNUNET_TIME_absolute_get_duration (last);
516 last = GNUNET_TIME_absolute_get ();
517 fprintf (stderr,
518 "Read 100000 domain names in %s\n",
519 GNUNET_STRINGS_relative_time_to_string (delta,
520 GNUNET_YES));
521 }
522 queue (hn,
523 (enum RequestCategory) cat);
524 }
525 fprintf (stderr,
526 "Done reading %llu domain names\n",
527 (unsigned long long) idot);
528 t = GNUNET_SCHEDULER_add_now (&process_queue,
529 NULL);
530}
531
532
533/**
534 * Process requests from the queue, then if the queue is
535 * not empty, try again.
536 *
537 * @param cls NULL
538 * @param args remaining command-line arguments
539 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
540 * @param cfg configuration
541 */
542static void
543run (void *cls,
544 char *const *args,
545 const char *cfgfile,
546 const struct GNUNET_CONFIGURATION_Handle *cfg)
547{
548 (void) cls;
549 (void) args;
550 (void) cfgfile;
551 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
552 NULL);
553 gns = GNUNET_GNS_connect (cfg);
554 if (NULL == gns)
555 {
556 GNUNET_break (0);
557 GNUNET_SCHEDULER_shutdown ();
558 return;
559 }
560 t = GNUNET_SCHEDULER_add_now (&process_stdin,
561 NULL);
562}
563
564
565/**
566 * Call with list of names with numeric category to query.
567 *
568 * @param argc unused
569 * @param argv unused
570 * @return 0 on success
571 */
572int
573main (int argc,
574 char *const*argv)
575{
576 int ret = 0;
577 struct GNUNET_GETOPT_CommandLineOption options[] = {
578 GNUNET_GETOPT_option_relative_time ('d',
579 "delay",
580 "RELATIVETIME",
581 gettext_noop (
582 "how long to wait between queries"),
583 &request_delay),
584 GNUNET_GETOPT_option_relative_time ('t',
585 "timeout",
586 "RELATIVETIME",
587 gettext_noop (
588 "how long to wait for an answer"),
589 &timeout),
590 GNUNET_GETOPT_option_flag ('2',
591 "g2d",
592 gettext_noop (
593 "look for GNS2DNS records instead of ANY"),
594 &g2d),
595 GNUNET_GETOPT_OPTION_END
596 };
597
598 if (GNUNET_OK !=
599 GNUNET_STRINGS_get_utf8_args (argc, argv,
600 &argc, &argv))
601 return 2;
602 timeout = DEF_TIMEOUT;
603 request_delay = DEF_REQUEST_DELAY;
604 if (GNUNET_OK !=
605 GNUNET_PROGRAM_run (argc,
606 argv,
607 "gnunet-gns-benchmark",
608 "resolve GNS names and measure performance",
609 options,
610 &run,
611 NULL))
612 ret = 1;
613 GNUNET_free_nz ((void *) argv);
614 return ret;
615}
616
617
618/* end of gnunet-gns-benchmark.c */
diff --git a/src/service/gns/gnunet-gns-import.c b/src/service/gns/gnunet-gns-import.c
new file mode 100644
index 000000000..5d4602682
--- /dev/null
+++ b/src/service/gns/gnunet-gns-import.c
@@ -0,0 +1,498 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2013 GNUnet e.V.
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 gnunet-gns.c
22 * @brief binary version of gnunet-gns-import.sh
23 * (for OSes that have no POSIX shell).
24 * @author LRN
25 */
26#include "platform.h"
27#include <gnunet_util_lib.h>
28#include <gnunet_gnsrecord_lib.h>
29#include <gnunet_identity_service.h>
30#include <gnunet_namestore_service.h>
31
32/**
33 * Configuration we are using.
34 */
35static const struct GNUNET_CONFIGURATION_Handle *cfg;
36
37/**
38 * Handle to IDENTITY service.
39 */
40static struct GNUNET_IDENTITY_Handle *sh;
41
42/**
43 * Zone iterator for master zone
44 */
45struct GNUNET_NAMESTORE_ZoneIterator *list_it;
46
47/**
48 * Handle to the namestore.
49 */
50static struct GNUNET_NAMESTORE_Handle *ns;
51
52/**
53 * String version of PKEY for master-zone.
54 */
55static char *master_zone_pkey;
56
57/**
58 * Binary version of PKEY for master-zone.
59 */
60static struct GNUNET_CRYPTO_EcdsaPrivateKey master_pk;
61
62/**
63 * String version of PKEY for private-zone.
64 */
65static char *private_zone_pkey;
66
67/**
68 * String version of PKEY for pin-zone.
69 */
70static char *pin_zone_pkey =
71 "72QC35CO20UJN1E91KPJFNT9TG4CLKAPB4VK9S3Q758S9MLBRKOG";
72
73/**
74 * Set to GNUNET_YES if private record was found;
75 */
76static int found_private_rec = GNUNET_NO;
77
78/**
79 * Set to GNUNET_YES if pin record was found;
80 */
81static int found_pin_rec = GNUNET_NO;
82
83/**
84 * Exit code.
85 */
86static int ret;
87
88
89static int
90run_process_and_wait (enum GNUNET_OS_InheritStdioFlags std_inheritance,
91 struct GNUNET_DISK_PipeHandle *pipe_stdin,
92 struct GNUNET_DISK_PipeHandle *pipe_stdout,
93 enum GNUNET_OS_ProcessStatusType *st,
94 unsigned long *code,
95 const char *filename, ...)
96{
97 static struct GNUNET_OS_Process *p;
98 int arglen;
99 char *arg;
100 char *args;
101 char *argp;
102 va_list ap, apc1, apc2;
103
104 va_start (ap, filename);
105 va_copy (apc1, ap);
106 va_copy (apc2, ap);
107 arglen = 0;
108 while (NULL != (arg = va_arg (apc1, char *)))
109 arglen += strlen (arg) + 1;
110 va_end (apc1);
111 args = argp = GNUNET_malloc (arglen);
112 while (NULL != (arg = va_arg (apc2, char *)))
113 {
114 strcpy (argp, arg);
115 argp += strlen (arg);
116 *argp = ' ';
117 argp += 1;
118 }
119 va_end (apc2);
120 if (arglen > 0)
121 argp[-1] = '\0';
122 p = GNUNET_OS_start_process_va (std_inheritance,
123 pipe_stdin,
124 pipe_stdout,
125 NULL,
126 filename, ap);
127 va_end (ap);
128 if (NULL == p)
129 {
130 ret = 3;
131 fprintf (stderr, "Failed to run `%s'\n", args);
132 GNUNET_free (args);
133 return 1;
134 }
135
136 if (GNUNET_OK != GNUNET_OS_process_wait (p))
137 {
138 ret = 4;
139 fprintf (stderr, "Failed to wait for `%s'\n", args);
140 GNUNET_free (args);
141 return 1;
142 }
143
144 switch (GNUNET_OS_process_status (p, st, code))
145 {
146 case GNUNET_OK:
147 break;
148
149 case GNUNET_NO:
150 ret = 5;
151 fprintf (stderr, "`%s' is still running\n", args);
152 GNUNET_free (args);
153 return 1;
154
155 default:
156 case GNUNET_SYSERR:
157 ret = 6;
158 fprintf (stderr, "Failed to check the status of `%s'\n", args);
159 GNUNET_free (args);
160 return 1;
161 }
162 return 0;
163}
164
165
166static void
167check_pkey (unsigned int rd_len, const struct GNUNET_GNSRECORD_Data *rd,
168 char *pk, int *found_rec)
169{
170 int i;
171 struct GNUNET_CRYPTO_PublicKey pubkey;
172
173 for (i = 0; i < rd_len; i++)
174 {
175 char *s;
176 if (sizeof (uint32_t) > rd[i].data_size)
177 continue;
178 if (GNUNET_OK != GNUNET_GNSRECORD_identity_from_data (rd[i].data,
179 rd[i].data_size,
180 rd[i].record_type,
181 &pubkey))
182 continue;
183 s = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
184 rd[i].data,
185 rd[i].data_size);
186 if (NULL == s)
187 continue;
188 if (0 == strcmp (s, pk))
189 *found_rec = GNUNET_YES;
190 GNUNET_free (s);
191 }
192}
193
194
195/**
196 * Process a record that was stored in the namestore.
197 *
198 * @param cls closure
199 * @param zone_key private key of the zone
200 * @param rname name that is being mapped (at most 255 characters long)
201 * @param rd_len number of entries in @a rd array
202 * @param rd array of records with data to store
203 */
204static void
205zone_iterator (void *cls,
206 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
207 const char *rname, unsigned int rd_len,
208 const struct GNUNET_GNSRECORD_Data *rd)
209{
210 if (NULL != rname)
211 {
212 if (0 == strcmp (rname, "private"))
213 check_pkey (rd_len, rd, private_zone_pkey, &found_private_rec);
214 else if (0 == strcmp (rname, "pin"))
215 check_pkey (rd_len, rd, pin_zone_pkey, &found_pin_rec);
216 }
217 GNUNET_NAMESTORE_zone_iterator_next (list_it);
218}
219
220
221static void
222zone_iteration_error (void *cls)
223{
224 enum GNUNET_OS_ProcessStatusType st;
225 unsigned long code;
226
227 if (! found_private_rec)
228 {
229 if (0 != run_process_and_wait (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
230 NULL, NULL, &st, &code,
231 "gnunet-namestore",
232 "gnunet-namestore", "-z", "master-zone",
233 "-a", "-e", "never", "-n", "private", "-p",
234 "-t", "PKEY", "-V",
235 private_zone_pkey, NULL))
236 {
237 ret = 8;
238 return;
239 }
240 }
241 if (! found_pin_rec)
242 {
243 if (0 != run_process_and_wait (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
244 NULL, NULL, &st, &code,
245 "gnunet-namestore",
246 "gnunet-namestore", "-z", "master-zone",
247 "-a", "-e", "never", "-n", "pin", "-p", "-t",
248 "PKEY", "-V", pin_zone_pkey,
249 NULL))
250 {
251 ret = 10;
252 return;
253 }
254 }
255 list_it = NULL;
256 GNUNET_SCHEDULER_shutdown ();
257}
258
259
260static void
261zone_iteration_finished (void *cls)
262{
263}
264
265
266/**
267 * Get master-zone and private-zone keys.
268 *
269 * This function is initially called for all egos and then again
270 * whenever a ego's identifier changes or if it is deleted. At the
271 * end of the initial pass over all egos, the function is once called
272 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
273 * be invoked in the future or that there was an error.
274 *
275 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', this
276 * function is only called ONCE, and 'NULL' being passed in 'ego' does
277 * indicate an error (for example because name is taken or no default value is
278 * known). If 'ego' is non-NULL and if '*ctx' is set in those callbacks, the
279 * value WILL be passed to a subsequent call to the identity callback of
280 * 'GNUNET_IDENTITY_connect' (if that one was not NULL).
281 *
282 * When an identity is renamed, this function is called with the
283 * (known) ego but the NEW identifier.
284 *
285 * When an identity is deleted, this function is called with the
286 * (known) ego and "NULL" for the 'identifier'. In this case,
287 * the 'ego' is henceforth invalid (and the 'ctx' should also be
288 * cleaned up).
289 *
290 * @param cls closure
291 * @param ego ego handle
292 * @param ctx context for application to store data for this ego
293 * (during the lifetime of this process, initially NULL)
294 * @param identifier identifier assigned by the user for this ego,
295 * NULL if the user just deleted the ego and it
296 * must thus no longer be used
297 */
298static void
299get_ego (void *cls,
300 struct GNUNET_IDENTITY_Ego *ego,
301 void **ctx,
302 const char *identifier)
303{
304 static struct GNUNET_CRYPTO_EcdsaPublicKey pk;
305
306 if (NULL == ego)
307 {
308 if ((NULL == master_zone_pkey) ||
309 (NULL == private_zone_pkey) )
310 {
311 ret = 11;
312 GNUNET_SCHEDULER_shutdown ();
313 return;
314 }
315 list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
316 &master_pk,
317 &zone_iteration_error,
318 NULL, &zone_iterator, NULL,
319 &zone_iteration_finished,
320 NULL);
321 if (NULL == list_it)
322 {
323 ret = 12;
324 GNUNET_SCHEDULER_shutdown ();
325 }
326 return;
327 }
328 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
329 if (NULL != identifier)
330 {
331 if ((NULL == master_zone_pkey) && (0 == strcmp ("master-zone",
332 identifier)) )
333 {
334 master_zone_pkey = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
335 master_pk = *GNUNET_IDENTITY_ego_get_private_key (ego);
336 }
337 else if ((NULL == private_zone_pkey) && (0 == strcmp ("private-zone",
338 identifier)) )
339 private_zone_pkey = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
340 }
341}
342
343
344/**
345 * Task run on shutdown.
346 *
347 * @param cls NULL
348 */
349static void
350shutdown_task (void *cls)
351{
352 GNUNET_free (master_zone_pkey);
353 master_zone_pkey = NULL;
354 GNUNET_free (private_zone_pkey);
355 private_zone_pkey = NULL;
356 if (NULL != list_it)
357 {
358 GNUNET_NAMESTORE_zone_iteration_stop (list_it);
359 list_it = NULL;
360 }
361 if (NULL != ns)
362 {
363 GNUNET_NAMESTORE_disconnect (ns);
364 ns = NULL;
365 }
366 if (NULL != sh)
367 {
368 GNUNET_IDENTITY_disconnect (sh);
369 sh = NULL;
370 }
371}
372
373
374/**
375 * Main function that will be run.
376 *
377 * @param cls closure
378 * @param args remaining command-line arguments
379 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
380 * @param c configuration
381 */
382static void
383run (void *cls, char *const *args, const char *cfgfile,
384 const struct GNUNET_CONFIGURATION_Handle *c)
385{
386 enum GNUNET_OS_ProcessStatusType st;
387 unsigned long code;
388
389 cfg = c;
390
391 if (0 != run_process_and_wait (GNUNET_OS_INHERIT_STD_NONE,
392 NULL, NULL, &st, &code,
393 "gnunet-arm",
394 "gnunet-arm", "-I", NULL))
395 {
396 if (7 == ret)
397 fprintf (stderr,
398 "GNUnet is not running, please start GNUnet before running import\n");
399 return;
400 }
401
402 if (0 != run_process_and_wait (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
403 NULL, NULL, &st, &code,
404 "gnunet-identity",
405 "gnunet-identity", "-C", "master-zone", NULL))
406 return;
407
408 if (0 != run_process_and_wait (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
409 NULL, NULL, &st, &code,
410 "gnunet-identity",
411 "gnunet-identity", "-C", "private-zone", NULL))
412 return;
413
414 if (0 != run_process_and_wait (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
415 NULL, NULL, &st, &code,
416 "gnunet-identity",
417 "gnunet-identity", "-C", "sks-zone", NULL))
418 return;
419
420 if (0 != run_process_and_wait (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
421 NULL, NULL, &st, &code,
422 "gnunet-identity",
423 "gnunet-identity", "-e", "master-zone", "-s",
424 "gns-master", NULL))
425 return;
426
427 if (0 != run_process_and_wait (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
428 NULL, NULL, &st, &code,
429 "gnunet-identity",
430 "gnunet-identity", "-e", "master-zone", "-s",
431 "namestore", NULL))
432 return;
433
434 if (0 != run_process_and_wait (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
435 NULL, NULL, &st, &code,
436 "gnunet-identity",
437 "gnunet-identity", "-e", "master-zone", "-s",
438 "gns-proxy", NULL))
439 return;
440
441 if (0 != run_process_and_wait (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
442 NULL, NULL, &st, &code,
443 "gnunet-identity",
444 "gnunet-identity", "-e", "master-zone", "-s",
445 "gns-intercept", NULL))
446 return;
447
448 if (0 != run_process_and_wait (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
449 NULL, NULL, &st, &code,
450 "gnunet-identity",
451 "gnunet-identity", "-e", "private-zone", "-s",
452 "gns-private", NULL))
453 return;
454
455 if (0 != run_process_and_wait (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
456 NULL, NULL, &st, &code,
457 "gnunet-identity",
458 "gnunet-identity", "-e", "sks-zone", "-s",
459 "fs-sks", NULL))
460 return;
461
462 ns = GNUNET_NAMESTORE_connect (cfg);
463 sh = GNUNET_IDENTITY_connect (cfg, &get_ego, NULL);
464 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
465}
466
467
468/**
469 * The main function for gnunet-gns.
470 *
471 * @param argc number of arguments from the command line
472 * @param argv command line arguments
473 * @return 0 ok, 1 on error
474 */
475int
476main (int argc, char *const *argv)
477{
478 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
479 GNUNET_GETOPT_OPTION_END
480 };
481 int r;
482
483 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
484 return 2;
485
486 GNUNET_log_setup ("gnunet-gns-import", "WARNING", NULL);
487 ret = 0;
488 r = GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-import",
489 _ (
490 "This program will import some GNS authorities into your GNS namestore."),
491 options,
492 &run, NULL);
493 GNUNET_free_nz ((void *) argv);
494 return GNUNET_OK == r ? ret : 1;
495}
496
497
498/* end of gnunet-gns-import.c */
diff --git a/src/service/gns/gnunet-gns-proxy.c b/src/service/gns/gnunet-gns-proxy.c
new file mode 100644
index 000000000..b38f0a425
--- /dev/null
+++ b/src/service/gns/gnunet-gns-proxy.c
@@ -0,0 +1,3912 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2018 GNUnet e.V.
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 * @author Martin Schanzenbach
22 * @author Christian Grothoff
23 * @file src/gns/gnunet-gns-proxy.c
24 * @brief HTTP(S) proxy that rewrites URIs and fakes certificates to make GNS work
25 * with legacy browsers
26 *
27 * TODO:
28 * - double-check queueing logic
29 */
30#include "platform.h"
31#include <microhttpd.h>
32/* Just included for the right curl.h */
33#include "gnunet_curl_lib.h"
34#include <gnutls/gnutls.h>
35#include <gnutls/x509.h>
36#include <gnutls/abstract.h>
37#include <gnutls/crypto.h>
38#if HAVE_GNUTLS_DANE
39#include <gnutls/dane.h>
40#endif
41#include <regex.h>
42#include "gnunet_util_lib.h"
43#include "gnunet_gns_service.h"
44#include "gnunet_identity_service.h"
45#include "gns.h"
46#include "gnunet_mhd_compat.h"
47
48/**
49 * Default Socks5 listen port.
50 */
51#define GNUNET_GNS_PROXY_PORT 7777
52
53/**
54 * Maximum supported length for a URI.
55 * Should die. @deprecated
56 */
57#define MAX_HTTP_URI_LENGTH 2048
58
59/**
60 * Maximum number of DANE records we support
61 * per domain name (and port and protocol).
62 */
63#define MAX_DANES 32
64
65/**
66 * Size of the buffer for the data upload / download. Must be
67 * enough for curl, thus CURL_MAX_WRITE_SIZE is needed here (16k).
68 */
69#define IO_BUFFERSIZE CURL_MAX_WRITE_SIZE
70
71/**
72 * Size of the read/write buffers for Socks. Uses
73 * 256 bytes for the hostname (at most), plus a few
74 * bytes overhead for the messages.
75 */
76#define SOCKS_BUFFERSIZE (256 + 32)
77
78/**
79 * Port for plaintext HTTP.
80 */
81#define HTTP_PORT 80
82
83/**
84 * Port for HTTPS.
85 */
86#define HTTPS_PORT 443
87
88/**
89 * Largest allowed size for a PEM certificate.
90 */
91#define MAX_PEM_SIZE (10 * 1024)
92
93/**
94 * After how long do we clean up unused MHD TLS instances?
95 */
96#define MHD_CACHE_TIMEOUT GNUNET_TIME_relative_multiply ( \
97 GNUNET_TIME_UNIT_MINUTES, 5)
98
99/**
100 * After how long do we clean up Socks5 handles that failed to show any activity
101 * with their respective MHD instance?
102 */
103#define HTTP_HANDSHAKE_TIMEOUT GNUNET_TIME_relative_multiply ( \
104 GNUNET_TIME_UNIT_SECONDS, 15)
105
106
107/**
108 * Log curl error.
109 *
110 * @param level log level
111 * @param fun name of curl_easy-function that gave the error
112 * @param rc return code from curl
113 */
114#define LOG_CURL_EASY(level, fun, rc) \
115 GNUNET_log (level, \
116 _ ("%s failed at %s:%d: `%s'\n"), \
117 fun, \
118 __FILE__, \
119 __LINE__, \
120 curl_easy_strerror (rc))
121
122
123/* *************** Socks protocol definitions (move to TUN?) ****************** */
124
125/**
126 * Which SOCKS version do we speak?
127 */
128#define SOCKS_VERSION_5 0x05
129
130/**
131 * Flag to set for 'no authentication'.
132 */
133#define SOCKS_AUTH_NONE 0
134
135
136/**
137 * Commands in Socks5.
138 */
139enum Socks5Commands
140{
141 /**
142 * Establish TCP/IP stream.
143 */
144 SOCKS5_CMD_TCP_STREAM = 1,
145
146 /**
147 * Establish TCP port binding.
148 */
149 SOCKS5_CMD_TCP_PORT = 2,
150
151 /**
152 * Establish UDP port binding.
153 */
154 SOCKS5_CMD_UDP_PORT = 3
155};
156
157
158/**
159 * Address types in Socks5.
160 */
161enum Socks5AddressType
162{
163 /**
164 * IPv4 address.
165 */
166 SOCKS5_AT_IPV4 = 1,
167
168 /**
169 * IPv4 address.
170 */
171 SOCKS5_AT_DOMAINNAME = 3,
172
173 /**
174 * IPv6 address.
175 */
176 SOCKS5_AT_IPV6 = 4
177};
178
179
180/**
181 * Status codes in Socks5 response.
182 */
183enum Socks5StatusCode
184{
185 SOCKS5_STATUS_REQUEST_GRANTED = 0,
186 SOCKS5_STATUS_GENERAL_FAILURE = 1,
187 SOCKS5_STATUS_CONNECTION_NOT_ALLOWED_BY_RULE = 2,
188 SOCKS5_STATUS_NETWORK_UNREACHABLE = 3,
189 SOCKS5_STATUS_HOST_UNREACHABLE = 4,
190 SOCKS5_STATUS_CONNECTION_REFUSED_BY_HOST = 5,
191 SOCKS5_STATUS_TTL_EXPIRED = 6,
192 SOCKS5_STATUS_COMMAND_NOT_SUPPORTED = 7,
193 SOCKS5_STATUS_ADDRESS_TYPE_NOT_SUPPORTED = 8
194};
195
196
197/**
198 * Client hello in Socks5 protocol.
199 */
200struct Socks5ClientHelloMessage
201{
202 /**
203 * Should be #SOCKS_VERSION_5.
204 */
205 uint8_t version;
206
207 /**
208 * How many authentication methods does the client support.
209 */
210 uint8_t num_auth_methods;
211
212 /* followed by supported authentication methods, 1 byte per method */
213};
214
215
216/**
217 * Server hello in Socks5 protocol.
218 */
219struct Socks5ServerHelloMessage
220{
221 /**
222 * Should be #SOCKS_VERSION_5.
223 */
224 uint8_t version;
225
226 /**
227 * Chosen authentication method, for us always #SOCKS_AUTH_NONE,
228 * which skips the authentication step.
229 */
230 uint8_t auth_method;
231};
232
233
234/**
235 * Client socks request in Socks5 protocol.
236 */
237struct Socks5ClientRequestMessage
238{
239 /**
240 * Should be #SOCKS_VERSION_5.
241 */
242 uint8_t version;
243
244 /**
245 * Command code, we only uspport #SOCKS5_CMD_TCP_STREAM.
246 */
247 uint8_t command;
248
249 /**
250 * Reserved, always zero.
251 */
252 uint8_t resvd;
253
254 /**
255 * Address type, an `enum Socks5AddressType`.
256 */
257 uint8_t addr_type;
258
259 /*
260 * Followed by either an ip4/ipv6 address or a domain name with a
261 * length field (uint8_t) in front (depending on @e addr_type).
262 * followed by port number in network byte order (uint16_t).
263 */
264};
265
266
267/**
268 * Server response to client requests in Socks5 protocol.
269 */
270struct Socks5ServerResponseMessage
271{
272 /**
273 * Should be #SOCKS_VERSION_5.
274 */
275 uint8_t version;
276
277 /**
278 * Status code, an `enum Socks5StatusCode`
279 */
280 uint8_t reply;
281
282 /**
283 * Always zero.
284 */
285 uint8_t reserved;
286
287 /**
288 * Address type, an `enum Socks5AddressType`.
289 */
290 uint8_t addr_type;
291
292 /*
293 * Followed by either an ip4/ipv6 address or a domain name with a
294 * length field (uint8_t) in front (depending on @e addr_type).
295 * followed by port number in network byte order (uint16_t).
296 */
297};
298
299
300/* *********************** Datastructures for HTTP handling ****************** */
301
302/**
303 * A structure for CA cert/key
304 */
305struct ProxyCA
306{
307 /**
308 * The certificate
309 */
310 gnutls_x509_crt_t cert;
311
312 /**
313 * The private key
314 */
315 gnutls_x509_privkey_t key;
316};
317
318
319/**
320 * Structure for GNS certificates
321 */
322struct ProxyGNSCertificate
323{
324 /**
325 * The certificate as PEM
326 */
327 char cert[MAX_PEM_SIZE];
328
329 /**
330 * The private key as PEM
331 */
332 char key[MAX_PEM_SIZE];
333};
334
335
336/**
337 * A structure for all running Httpds
338 */
339struct MhdHttpList
340{
341 /**
342 * DLL for httpds
343 */
344 struct MhdHttpList *prev;
345
346 /**
347 * DLL for httpds
348 */
349 struct MhdHttpList *next;
350
351 /**
352 * the domain name to server (only important for TLS)
353 */
354 char *domain;
355
356 /**
357 * The daemon handle
358 */
359 struct MHD_Daemon *daemon;
360
361 /**
362 * Optional proxy certificate used
363 */
364 struct ProxyGNSCertificate *proxy_cert;
365
366 /**
367 * The task ID
368 */
369 struct GNUNET_SCHEDULER_Task *httpd_task;
370
371 /**
372 * is this an ssl daemon?
373 */
374 int is_ssl;
375};
376
377
378/* ***************** Datastructures for Socks handling **************** */
379
380
381/**
382 * The socks phases.
383 */
384enum SocksPhase
385{
386 /**
387 * We're waiting to get the client hello.
388 */
389 SOCKS5_INIT,
390
391 /**
392 * We're waiting to get the initial request.
393 */
394 SOCKS5_REQUEST,
395
396 /**
397 * We are currently resolving the destination.
398 */
399 SOCKS5_RESOLVING,
400
401 /**
402 * We're in transfer mode.
403 */
404 SOCKS5_DATA_TRANSFER,
405
406 /**
407 * Finish writing the write buffer, then clean up.
408 */
409 SOCKS5_WRITE_THEN_CLEANUP,
410
411 /**
412 * Socket has been passed to MHD, do not close it anymore.
413 */
414 SOCKS5_SOCKET_WITH_MHD,
415
416 /**
417 * We've started receiving upload data from MHD.
418 */
419 SOCKS5_SOCKET_UPLOAD_STARTED,
420
421 /**
422 * We've finished receiving upload data from MHD.
423 */
424 SOCKS5_SOCKET_UPLOAD_DONE,
425
426 /**
427 * We've finished uploading data via CURL and can now download.
428 */
429 SOCKS5_SOCKET_DOWNLOAD_STARTED,
430
431 /**
432 * We've finished receiving download data from cURL.
433 */
434 SOCKS5_SOCKET_DOWNLOAD_DONE
435};
436
437
438/**
439 * A header list
440 */
441struct HttpResponseHeader
442{
443 /**
444 * DLL
445 */
446 struct HttpResponseHeader *next;
447
448 /**
449 * DLL
450 */
451 struct HttpResponseHeader *prev;
452
453 /**
454 * Header type
455 */
456 char *type;
457
458 /**
459 * Header value
460 */
461 char *value;
462};
463
464/**
465 * A structure for socks requests
466 */
467struct Socks5Request
468{
469 /**
470 * DLL.
471 */
472 struct Socks5Request *next;
473
474 /**
475 * DLL.
476 */
477 struct Socks5Request *prev;
478
479 /**
480 * The client socket
481 */
482 struct GNUNET_NETWORK_Handle *sock;
483
484 /**
485 * Handle to GNS lookup, during #SOCKS5_RESOLVING phase.
486 */
487 struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
488
489 /**
490 * Client socket read task
491 */
492 struct GNUNET_SCHEDULER_Task *rtask;
493
494 /**
495 * Client socket write task
496 */
497 struct GNUNET_SCHEDULER_Task *wtask;
498
499 /**
500 * Timeout task
501 */
502 struct GNUNET_SCHEDULER_Task *timeout_task;
503
504 /**
505 * Read buffer
506 */
507 char rbuf[SOCKS_BUFFERSIZE];
508
509 /**
510 * Write buffer
511 */
512 char wbuf[SOCKS_BUFFERSIZE];
513
514 /**
515 * Buffer we use for moving data between MHD and curl (in both directions).
516 */
517 char io_buf[IO_BUFFERSIZE];
518
519 /**
520 * MHD HTTP instance handling this request, NULL for none.
521 */
522 struct MhdHttpList *hd;
523
524 /**
525 * MHD connection for this request.
526 */
527 struct MHD_Connection *con;
528
529 /**
530 * MHD response object for this request.
531 */
532 struct MHD_Response *response;
533
534 /**
535 * the domain name to server (only important for TLS)
536 */
537 char *domain;
538
539 /**
540 * DNS Legacy Host Name as given by GNS, NULL if not given.
541 */
542 char *leho;
543
544 /**
545 * Payload of the DANE records encountered.
546 */
547 char *dane_data[MAX_DANES + 1];
548
549 /**
550 * The URL to fetch
551 */
552 char *url;
553
554 /**
555 * Handle to cURL
556 */
557 CURL *curl;
558
559 /**
560 * HTTP request headers for the curl request.
561 */
562 struct curl_slist *headers;
563
564 /**
565 * DNS->IP mappings resolved through GNS
566 */
567 struct curl_slist *hosts;
568
569 /**
570 * HTTP response code to give to MHD for the response.
571 */
572 unsigned int response_code;
573
574 /**
575 * Number of bytes in @e dane_data.
576 */
577 int dane_data_len[MAX_DANES + 1];
578
579 /**
580 * Number of entries used in @e dane_data_len
581 * and @e dane_data.
582 */
583 unsigned int num_danes;
584
585 /**
586 * Number of bytes already in read buffer
587 */
588 size_t rbuf_len;
589
590 /**
591 * Number of bytes already in write buffer
592 */
593 size_t wbuf_len;
594
595 /**
596 * Number of bytes already in the IO buffer.
597 */
598 size_t io_len;
599
600 /**
601 * Once known, what's the target address for the connection?
602 */
603 struct sockaddr_storage destination_address;
604
605 /**
606 * The socks state
607 */
608 enum SocksPhase state;
609
610 /**
611 * Desired destination port.
612 */
613 uint16_t port;
614
615 /**
616 * Headers from response
617 */
618 struct HttpResponseHeader *header_head;
619
620 /**
621 * Headers from response
622 */
623 struct HttpResponseHeader *header_tail;
624
625 /**
626 * X.509 Certificate status
627 */
628 int ssl_checked;
629
630 /**
631 * Was the hostname resolved via GNS?
632 */
633 int is_gns;
634
635 /**
636 * This is (probably) a TLS connection
637 */
638 int is_tls;
639
640 /**
641 * Did we suspend MHD processing?
642 */
643 int suspended;
644
645 /**
646 * Did we pause CURL processing?
647 */
648 int curl_paused;
649};
650
651
652/* *********************** Globals **************************** */
653
654/**
655 * The address to bind to
656 */
657static in_addr_t address;
658
659/**
660 * The IPv6 address to bind to
661 */
662static struct in6_addr address6;
663
664/**
665 * The port the proxy is running on (default 7777)
666 */
667static uint16_t port = GNUNET_GNS_PROXY_PORT;
668
669/**
670 * The CA file (pem) to use for the proxy CA
671 */
672static char *cafile_opt;
673
674/**
675 * The listen socket of the proxy for IPv4
676 */
677static struct GNUNET_NETWORK_Handle *lsock4;
678
679/**
680 * The listen socket of the proxy for IPv6
681 */
682static struct GNUNET_NETWORK_Handle *lsock6;
683
684/**
685 * The listen task ID for IPv4
686 */
687static struct GNUNET_SCHEDULER_Task *ltask4;
688
689/**
690 * The listen task ID for IPv6
691 */
692static struct GNUNET_SCHEDULER_Task *ltask6;
693
694/**
695 * The cURL download task (curl multi API).
696 */
697static struct GNUNET_SCHEDULER_Task *curl_download_task;
698
699/**
700 * The cURL multi handle
701 */
702static CURLM *curl_multi;
703
704/**
705 * Handle to the GNS service
706 */
707static struct GNUNET_GNS_Handle *gns_handle;
708
709/**
710 * Disable IPv6.
711 */
712static int disable_v6;
713
714/**
715 * DLL for http/https daemons
716 */
717static struct MhdHttpList *mhd_httpd_head;
718
719/**
720 * DLL for http/https daemons
721 */
722static struct MhdHttpList *mhd_httpd_tail;
723
724/**
725 * Daemon for HTTP (we have one per X.509 certificate, and then one for
726 * all HTTP connections; this is the one for HTTP, not HTTPS).
727 */
728static struct MhdHttpList *httpd;
729
730/**
731 * DLL of active socks requests.
732 */
733static struct Socks5Request *s5r_head;
734
735/**
736 * DLL of active socks requests.
737 */
738static struct Socks5Request *s5r_tail;
739
740/**
741 * The CA for X.509 certificate generation
742 */
743static struct ProxyCA proxy_ca;
744
745/**
746 * Response we return on cURL failures.
747 */
748static struct MHD_Response *curl_failure_response;
749
750/**
751 * Our configuration.
752 */
753static const struct GNUNET_CONFIGURATION_Handle *cfg;
754
755
756/* ************************* Global helpers ********************* */
757
758
759/**
760 * Run MHD now, we have extra data ready for the callback.
761 *
762 * @param hd the daemon to run now.
763 */
764static void
765run_mhd_now (struct MhdHttpList *hd);
766
767
768/**
769 * Clean up s5r handles.
770 *
771 * @param s5r the handle to destroy
772 */
773static void
774cleanup_s5r (struct Socks5Request *s5r)
775{
776 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
777 "Cleaning up socks request\n");
778 if (NULL != s5r->curl)
779 {
780 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
781 "Cleaning up cURL handle\n");
782 curl_multi_remove_handle (curl_multi,
783 s5r->curl);
784 curl_easy_cleanup (s5r->curl);
785 s5r->curl = NULL;
786 }
787 if (s5r->suspended)
788 {
789 s5r->suspended = GNUNET_NO;
790 MHD_resume_connection (s5r->con);
791 }
792 curl_slist_free_all (s5r->headers);
793 if (NULL != s5r->hosts)
794 {
795 curl_slist_free_all (s5r->hosts);
796 }
797 if ((NULL != s5r->response) &&
798 (curl_failure_response != s5r->response))
799 {
800 MHD_destroy_response (s5r->response);
801 s5r->response = NULL;
802 }
803 if (NULL != s5r->rtask)
804 {
805 GNUNET_SCHEDULER_cancel (s5r->rtask);
806 s5r->rtask = NULL;
807 }
808 if (NULL != s5r->timeout_task)
809 {
810 GNUNET_SCHEDULER_cancel (s5r->timeout_task);
811 s5r->timeout_task = NULL;
812 }
813 if (NULL != s5r->wtask)
814 {
815 GNUNET_SCHEDULER_cancel (s5r->wtask);
816 s5r->wtask = NULL;
817 }
818 if (NULL != s5r->gns_lookup)
819 {
820 GNUNET_GNS_lookup_with_tld_cancel (s5r->gns_lookup);
821 s5r->gns_lookup = NULL;
822 }
823 if (NULL != s5r->sock)
824 {
825 if (SOCKS5_SOCKET_WITH_MHD <= s5r->state)
826 GNUNET_NETWORK_socket_free_memory_only_ (s5r->sock);
827 else
828 GNUNET_NETWORK_socket_close (s5r->sock);
829 s5r->sock = NULL;
830 }
831 GNUNET_CONTAINER_DLL_remove (s5r_head,
832 s5r_tail,
833 s5r);
834 GNUNET_free (s5r->domain);
835 GNUNET_free (s5r->leho);
836 GNUNET_free (s5r->url);
837 for (unsigned int i = 0; i < s5r->num_danes; i++)
838 GNUNET_free (s5r->dane_data[i]);
839 GNUNET_free (s5r);
840}
841
842
843/* ************************* HTTP handling with cURL *********************** */
844
845static void
846curl_download_prepare ();
847
848
849/**
850 * Callback for MHD response generation. This function is called from
851 * MHD whenever MHD expects to get data back. Copies data from the
852 * io_buf, if available.
853 *
854 * @param cls closure with our `struct Socks5Request`
855 * @param pos in buffer
856 * @param buf where to copy data
857 * @param max available space in @a buf
858 * @return number of bytes written to @a buf
859 */
860static ssize_t
861mhd_content_cb (void *cls,
862 uint64_t pos,
863 char*buf,
864 size_t max)
865{
866 struct Socks5Request *s5r = cls;
867 size_t bytes_to_copy;
868
869 if ((SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) ||
870 (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state))
871 {
872 /* we're still not done with the upload, do not yet
873 start the download, the IO buffer is still full
874 with upload data. */
875 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
876 "Pausing MHD download %s%s, not yet ready for download\n",
877 s5r->domain,
878 s5r->url);
879 return 0; /* not yet ready for data download */
880 }
881 bytes_to_copy = GNUNET_MIN (max,
882 s5r->io_len);
883 if ((0 == bytes_to_copy) &&
884 (SOCKS5_SOCKET_DOWNLOAD_DONE != s5r->state))
885 {
886 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
887 "Pausing MHD download %s%s, no data available\n",
888 s5r->domain,
889 s5r->url);
890 if (NULL != s5r->curl)
891 {
892 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
893 "Continuing CURL interaction for %s%s\n",
894 s5r->domain,
895 s5r->url);
896 if (GNUNET_YES == s5r->curl_paused)
897 {
898 s5r->curl_paused = GNUNET_NO;
899 curl_easy_pause (s5r->curl,
900 CURLPAUSE_CONT);
901 }
902 curl_download_prepare ();
903 }
904 if (GNUNET_NO == s5r->suspended)
905 {
906 MHD_suspend_connection (s5r->con);
907 s5r->suspended = GNUNET_YES;
908 }
909 return 0; /* more data later */
910 }
911 if ((0 == bytes_to_copy) &&
912 (SOCKS5_SOCKET_DOWNLOAD_DONE == s5r->state))
913 {
914 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
915 "Completed MHD download %s%s\n",
916 s5r->domain,
917 s5r->url);
918 return MHD_CONTENT_READER_END_OF_STREAM;
919 }
920 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
921 "Writing %llu/%llu bytes to %s%s\n",
922 (unsigned long long) bytes_to_copy,
923 (unsigned long long) s5r->io_len,
924 s5r->domain,
925 s5r->url);
926 GNUNET_memcpy (buf,
927 s5r->io_buf,
928 bytes_to_copy);
929 memmove (s5r->io_buf,
930 &s5r->io_buf[bytes_to_copy],
931 s5r->io_len - bytes_to_copy);
932 s5r->io_len -= bytes_to_copy;
933 if ((NULL != s5r->curl) &&
934 (GNUNET_YES == s5r->curl_paused))
935 {
936 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
937 "Continuing CURL interaction for %s%s\n",
938 s5r->domain,
939 s5r->url);
940 s5r->curl_paused = GNUNET_NO;
941 curl_easy_pause (s5r->curl,
942 CURLPAUSE_CONT);
943 }
944 return bytes_to_copy;
945}
946
947
948/**
949 * Check that the website has presented us with a valid X.509 certificate.
950 * The certificate must either match the domain name or the LEHO name
951 * (or, if available, the TLSA record).
952 *
953 * @param s5r request to check for.
954 * @return #GNUNET_OK if the certificate is valid
955 */
956static int
957check_ssl_certificate (struct Socks5Request *s5r)
958{
959 unsigned int cert_list_size;
960 const gnutls_datum_t *chainp;
961 const struct curl_tlssessioninfo *tlsinfo;
962 char certdn[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 3];
963 size_t size;
964 gnutls_x509_crt_t x509_cert;
965 int rc;
966 const char *name;
967
968 s5r->ssl_checked = GNUNET_YES;
969 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
970 "Checking X.509 certificate\n");
971 if (CURLE_OK !=
972 curl_easy_getinfo (s5r->curl,
973 CURLINFO_TLS_SSL_PTR,
974 &tlsinfo))
975 return GNUNET_SYSERR;
976 if (CURLSSLBACKEND_GNUTLS != tlsinfo->backend)
977 {
978 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
979 _ ("Unsupported CURL TLS backend %d\n"),
980 tlsinfo->backend);
981 return GNUNET_SYSERR;
982 }
983 chainp = gnutls_certificate_get_peers (tlsinfo->internals,
984 &cert_list_size);
985 if ((! chainp) ||
986 (0 == cert_list_size))
987 return GNUNET_SYSERR;
988
989 size = sizeof(certdn);
990 /* initialize an X.509 certificate structure. */
991 gnutls_x509_crt_init (&x509_cert);
992 gnutls_x509_crt_import (x509_cert,
993 chainp,
994 GNUTLS_X509_FMT_DER);
995
996 if (0 != (rc = gnutls_x509_crt_get_dn_by_oid (x509_cert,
997 GNUTLS_OID_X520_COMMON_NAME,
998 0, /* the first and only one */
999 0 /* no DER encoding */,
1000 certdn,
1001 &size)))
1002 {
1003 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1004 _ ("Failed to fetch CN from cert: %s\n"),
1005 gnutls_strerror (rc));
1006 gnutls_x509_crt_deinit (x509_cert);
1007 return GNUNET_SYSERR;
1008 }
1009 /* check for TLSA/DANE records */
1010#if HAVE_GNUTLS_DANE
1011 if (0 != s5r->num_danes)
1012 {
1013 dane_state_t dane_state;
1014 dane_query_t dane_query;
1015 unsigned int verify;
1016
1017 /* FIXME: add flags to gnutls to NOT read UNBOUND_ROOT_KEY_FILE here! */
1018 if (0 != (rc = dane_state_init (&dane_state,
1019#ifdef DANE_F_IGNORE_DNSSEC
1020 DANE_F_IGNORE_DNSSEC |
1021#endif
1022 DANE_F_IGNORE_LOCAL_RESOLVER)))
1023 {
1024 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1025 _ ("Failed to initialize DANE: %s\n"),
1026 dane_strerror (rc));
1027 gnutls_x509_crt_deinit (x509_cert);
1028 return GNUNET_SYSERR;
1029 }
1030 s5r->dane_data[s5r->num_danes] = NULL;
1031 s5r->dane_data_len[s5r->num_danes] = 0;
1032 if (0 != (rc = dane_raw_tlsa (dane_state,
1033 &dane_query,
1034 s5r->dane_data,
1035 s5r->dane_data_len,
1036 GNUNET_YES,
1037 GNUNET_NO)))
1038 {
1039 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1040 _ ("Failed to parse DANE record: %s\n"),
1041 dane_strerror (rc));
1042 dane_state_deinit (dane_state);
1043 gnutls_x509_crt_deinit (x509_cert);
1044 return GNUNET_SYSERR;
1045 }
1046 if (0 != (rc = dane_verify_crt_raw (dane_state,
1047 chainp,
1048 cert_list_size,
1049 gnutls_certificate_type_get (
1050 tlsinfo->internals),
1051 dane_query,
1052 0, 0,
1053 &verify)))
1054 {
1055 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1056 _ ("Failed to verify TLS connection using DANE: %s\n"),
1057 dane_strerror (rc));
1058 dane_query_deinit (dane_query);
1059 dane_state_deinit (dane_state);
1060 gnutls_x509_crt_deinit (x509_cert);
1061 return GNUNET_SYSERR;
1062 }
1063 if (0 != verify)
1064 {
1065 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1066 _ (
1067 "Failed DANE verification failed with GnuTLS verify status code: %u\n"),
1068 verify);
1069 dane_query_deinit (dane_query);
1070 dane_state_deinit (dane_state);
1071 gnutls_x509_crt_deinit (x509_cert);
1072 return GNUNET_SYSERR;
1073 }
1074 dane_query_deinit (dane_query);
1075 dane_state_deinit (dane_state);
1076 /* success! */
1077 }
1078 else
1079#endif
1080 {
1081 /* try LEHO or ordinary domain name X509 verification */
1082 name = s5r->domain;
1083 if (NULL != s5r->leho)
1084 name = s5r->leho;
1085 if (NULL != name)
1086 {
1087 if (0 == (rc = gnutls_x509_crt_check_hostname (x509_cert,
1088 name)))
1089 {
1090 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1091 _ (
1092 "TLS certificate subject name (%s) does not match `%s': %d\n"),
1093 certdn,
1094 name,
1095 rc);
1096 gnutls_x509_crt_deinit (x509_cert);
1097 return GNUNET_SYSERR;
1098 }
1099 }
1100 else
1101 {
1102 /* we did not even have the domain name!? */
1103 GNUNET_break (0);
1104 return GNUNET_SYSERR;
1105 }
1106 }
1107 gnutls_x509_crt_deinit (x509_cert);
1108 return GNUNET_OK;
1109}
1110
1111
1112/**
1113 * We're getting an HTTP response header from cURL. Convert it to the
1114 * MHD response headers. Mostly copies the headers, but makes special
1115 * adjustments to "Set-Cookie" and "Location" headers as those may need
1116 * to be changed from the LEHO to the domain the browser expects.
1117 *
1118 * @param buffer curl buffer with a single line of header data; not 0-terminated!
1119 * @param size curl blocksize
1120 * @param nmemb curl blocknumber
1121 * @param cls our `struct Socks5Request *`
1122 * @return size of processed bytes
1123 */
1124static size_t
1125curl_check_hdr (void *buffer,
1126 size_t size,
1127 size_t nmemb,
1128 void *cls)
1129{
1130 struct Socks5Request *s5r = cls;
1131 struct HttpResponseHeader *header;
1132 size_t bytes = size * nmemb;
1133 char *ndup;
1134 const char *hdr_type;
1135 const char *cookie_domain;
1136 char *hdr_val;
1137 char *new_cookie_hdr;
1138 char *new_location;
1139 size_t offset;
1140 size_t delta_cdomain;
1141 int domain_matched;
1142 char *tok;
1143
1144 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1145 "Receiving HTTP response header from CURL\n");
1146 /* first, check TLS certificate */
1147 if ((GNUNET_YES != s5r->ssl_checked) &&
1148 (GNUNET_YES == s5r->is_tls))
1149 // (HTTPS_PORT == s5r->port))
1150 {
1151 if (GNUNET_OK != check_ssl_certificate (s5r))
1152 return 0;
1153 }
1154 ndup = GNUNET_strndup (buffer,
1155 bytes);
1156 hdr_type = strtok (ndup,
1157 ":");
1158 if (NULL == hdr_type)
1159 {
1160 GNUNET_free (ndup);
1161 return bytes;
1162 }
1163 hdr_val = strtok (NULL,
1164 "");
1165 if (NULL == hdr_val)
1166 {
1167 GNUNET_free (ndup);
1168 return bytes;
1169 }
1170 if (' ' == *hdr_val)
1171 hdr_val++;
1172
1173 /* custom logic for certain header types */
1174 new_cookie_hdr = NULL;
1175 if ((NULL != s5r->leho) &&
1176 (0 == strcasecmp (hdr_type,
1177 MHD_HTTP_HEADER_SET_COOKIE)))
1178
1179 {
1180 new_cookie_hdr = GNUNET_malloc (strlen (hdr_val)
1181 + strlen (s5r->domain) + 1);
1182 offset = 0;
1183 domain_matched = GNUNET_NO; /* make sure we match domain at most once */
1184 for (tok = strtok (hdr_val, ";"); NULL != tok; tok = strtok (NULL, ";"))
1185 {
1186 if ((0 == strncasecmp (tok,
1187 " domain",
1188 strlen (" domain"))) &&
1189 (GNUNET_NO == domain_matched))
1190 {
1191 domain_matched = GNUNET_YES;
1192 cookie_domain = tok + strlen (" domain") + 1;
1193 if (strlen (cookie_domain) < strlen (s5r->leho))
1194 {
1195 delta_cdomain = strlen (s5r->leho) - strlen (cookie_domain);
1196 if (0 == strcasecmp (cookie_domain,
1197 s5r->leho + delta_cdomain))
1198 {
1199 offset += sprintf (new_cookie_hdr + offset,
1200 " domain=%s;",
1201 s5r->domain);
1202 continue;
1203 }
1204 }
1205 else if (0 == strcmp (cookie_domain,
1206 s5r->leho))
1207 {
1208 offset += sprintf (new_cookie_hdr + offset,
1209 " domain=%s;",
1210 s5r->domain);
1211 continue;
1212 }
1213 else if (('.' == cookie_domain[0]) &&
1214 (0 == strcmp (&cookie_domain[1],
1215 s5r->leho)))
1216 {
1217 offset += sprintf (new_cookie_hdr + offset,
1218 " domain=.%s;",
1219 s5r->domain);
1220 continue;
1221 }
1222 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1223 _ ("Cookie domain `%s' supplied by server is invalid\n"),
1224 tok);
1225 }
1226 GNUNET_memcpy (new_cookie_hdr + offset,
1227 tok,
1228 strlen (tok));
1229 offset += strlen (tok);
1230 new_cookie_hdr[offset++] = ';';
1231 }
1232 hdr_val = new_cookie_hdr;
1233 hdr_val[offset] = '\0';
1234 }
1235
1236 new_location = NULL;
1237 if (0 == strcasecmp (MHD_HTTP_HEADER_TRANSFER_ENCODING,
1238 hdr_type))
1239 {
1240 /* Ignore transfer encoding, set automatically by MHD if required */
1241 goto cleanup;
1242 }
1243 if ((0 == strcasecmp (MHD_HTTP_HEADER_LOCATION,
1244 hdr_type)))
1245 {
1246 char *leho_host;
1247
1248 GNUNET_asprintf (&leho_host,
1249 (GNUNET_YES != s5r->is_tls) // (HTTPS_PORT != s5r->port)
1250 ? "http://%s"
1251 : "https://%s",
1252 s5r->leho);
1253 if (0 == strncmp (leho_host,
1254 hdr_val,
1255 strlen (leho_host)))
1256 {
1257 GNUNET_asprintf (&new_location,
1258 "%s%s%s",
1259 (GNUNET_YES != s5r->is_tls) // (HTTPS_PORT != s5r->port)
1260 ? "http://"
1261 : "https://",
1262 s5r->domain,
1263 hdr_val + strlen (leho_host));
1264 hdr_val = new_location;
1265 }
1266 GNUNET_free (leho_host);
1267 }
1268 else if (0 == strcasecmp (MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
1269 hdr_type))
1270 {
1271 char *leho_host;
1272
1273 GNUNET_asprintf (&leho_host,
1274 (GNUNET_YES != s5r->is_tls) // (HTTPS_PORT != s5r->port)
1275 ? "http://%s"
1276 : "https://%s",
1277 s5r->leho);
1278 if (0 == strncmp (leho_host,
1279 hdr_val,
1280 strlen (leho_host)))
1281 {
1282 GNUNET_asprintf (&new_location,
1283 "%s%s",
1284 (GNUNET_YES != s5r->is_tls) // (HTTPS_PORT != s5r->port)
1285 ? "http://"
1286 : "https://",
1287 s5r->domain);
1288 hdr_val = new_location;
1289 }
1290 GNUNET_free (leho_host);
1291 }
1292
1293 /* MHD does not allow certain characters in values, remove those */
1294 if (NULL != (tok = strchr (hdr_val, '\n')))
1295 *tok = '\0';
1296 if (NULL != (tok = strchr (hdr_val, '\r')))
1297 *tok = '\0';
1298 if (NULL != (tok = strchr (hdr_val, '\t')))
1299 *tok = '\0';
1300 if (0 != strlen (hdr_val)) /* Rely in MHD to set those */
1301 {
1302 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1303 "Adding header %s: %s to MHD response\n",
1304 hdr_type,
1305 hdr_val);
1306 header = GNUNET_new (struct HttpResponseHeader);
1307 header->type = GNUNET_strdup (hdr_type);
1308 header->value = GNUNET_strdup (hdr_val);
1309 GNUNET_CONTAINER_DLL_insert (s5r->header_head,
1310 s5r->header_tail,
1311 header);
1312 }
1313 cleanup:
1314 GNUNET_free (ndup);
1315 GNUNET_free (new_cookie_hdr);
1316 GNUNET_free (new_location);
1317 return bytes;
1318}
1319
1320
1321/**
1322 * Create an MHD response object in @a s5r matching the
1323 * information we got from curl.
1324 *
1325 * @param s5r the request for which we convert the response
1326 * @return #GNUNET_OK on success, #GNUNET_SYSERR if response was
1327 * already initialized before
1328 */
1329static int
1330create_mhd_response_from_s5r (struct Socks5Request *s5r)
1331{
1332 long resp_code;
1333 double content_length;
1334
1335 if (NULL != s5r->response)
1336 {
1337 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1338 "Response already set!\n");
1339 return GNUNET_SYSERR;
1340 }
1341
1342 GNUNET_break (CURLE_OK ==
1343 curl_easy_getinfo (s5r->curl,
1344 CURLINFO_RESPONSE_CODE,
1345 &resp_code));
1346 GNUNET_break (CURLE_OK ==
1347 curl_easy_getinfo (s5r->curl,
1348 CURLINFO_CONTENT_LENGTH_DOWNLOAD_T,
1349 &content_length));
1350 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1351 "Creating MHD response with code %d and size %d for %s%s\n",
1352 (int) resp_code,
1353 (int) content_length,
1354 s5r->domain,
1355 s5r->url);
1356 s5r->response_code = resp_code;
1357 s5r->response = MHD_create_response_from_callback ((-1 == content_length)
1358 ? MHD_SIZE_UNKNOWN
1359 : content_length,
1360 IO_BUFFERSIZE,
1361 &mhd_content_cb,
1362 s5r,
1363 NULL);
1364 for (struct HttpResponseHeader *header = s5r->header_head;
1365 NULL != header;
1366 header = header->next)
1367 {
1368 if (0 == strcasecmp (header->type,
1369 MHD_HTTP_HEADER_CONTENT_LENGTH))
1370 continue; /* MHD won't let us mess with those, for good reason */
1371 if ((0 == strcasecmp (header->type,
1372 MHD_HTTP_HEADER_TRANSFER_ENCODING)) &&
1373 ((0 == strcasecmp (header->value,
1374 "identity")) ||
1375 (0 == strcasecmp (header->value,
1376 "chunked"))))
1377 continue; /* MHD won't let us mess with those, for good reason */
1378 if (MHD_YES !=
1379 MHD_add_response_header (s5r->response,
1380 header->type,
1381 header->value))
1382 {
1383 GNUNET_break (0);
1384 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1385 "Failed to add header `%s:%s'\n",
1386 header->type,
1387 header->value);
1388 }
1389 }
1390 /* force connection to be closed after each request, as we
1391 do not support HTTP pipelining (yet, FIXME!) */
1392 /*GNUNET_break (MHD_YES ==
1393 MHD_add_response_header (s5r->response,
1394 MHD_HTTP_HEADER_CONNECTION,
1395 "close"));*/
1396 MHD_resume_connection (s5r->con);
1397 s5r->suspended = GNUNET_NO;
1398 return GNUNET_OK;
1399}
1400
1401
1402/**
1403 * Handle response payload data from cURL. Copies it into our `io_buf` to make
1404 * it available to MHD.
1405 *
1406 * @param ptr pointer to the data
1407 * @param size number of blocks of data
1408 * @param nmemb blocksize
1409 * @param ctx our `struct Socks5Request *`
1410 * @return number of bytes handled
1411 */
1412static size_t
1413curl_download_cb (void *ptr,
1414 size_t size,
1415 size_t nmemb,
1416 void*ctx)
1417{
1418 struct Socks5Request *s5r = ctx;
1419 size_t total = size * nmemb;
1420
1421 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1422 "Receiving %ux%u bytes for `%s%s' from cURL to download\n",
1423 (unsigned int) size,
1424 (unsigned int) nmemb,
1425 s5r->domain,
1426 s5r->url);
1427 if (NULL == s5r->response)
1428 GNUNET_assert (GNUNET_OK ==
1429 create_mhd_response_from_s5r (s5r));
1430 if ((SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) &&
1431 (0 == s5r->io_len))
1432 {
1433 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1434 "Previous upload finished... starting DOWNLOAD.\n");
1435 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
1436 }
1437 if ((SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) ||
1438 (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state))
1439 {
1440 /* we're still not done with the upload, do not yet
1441 start the download, the IO buffer is still full
1442 with upload data. */
1443 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1444 "Pausing CURL download `%s%s', waiting for UPLOAD to finish\n",
1445 s5r->domain,
1446 s5r->url);
1447 s5r->curl_paused = GNUNET_YES;
1448 return CURL_WRITEFUNC_PAUSE; /* not yet ready for data download */
1449 }
1450 if (sizeof(s5r->io_buf) - s5r->io_len < total)
1451 {
1452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1453 "Pausing CURL `%s%s' download, not enough space %llu %llu %llu\n",
1454 s5r->domain,
1455 s5r->url,
1456 (unsigned long long) sizeof(s5r->io_buf),
1457 (unsigned long long) s5r->io_len,
1458 (unsigned long long) total);
1459 s5r->curl_paused = GNUNET_YES;
1460 return CURL_WRITEFUNC_PAUSE; /* not enough space */
1461 }
1462 GNUNET_memcpy (&s5r->io_buf[s5r->io_len],
1463 ptr,
1464 total);
1465 s5r->io_len += total;
1466 if (GNUNET_YES == s5r->suspended)
1467 {
1468 MHD_resume_connection (s5r->con);
1469 s5r->suspended = GNUNET_NO;
1470 }
1471 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1472 "Received %llu bytes of payload via cURL from %s\n",
1473 (unsigned long long) total,
1474 s5r->domain);
1475 if (s5r->io_len == total)
1476 run_mhd_now (s5r->hd);
1477 return total;
1478}
1479
1480
1481/**
1482 * cURL callback for uploaded (PUT/POST) data. Copies it into our `io_buf`
1483 * to make it available to MHD.
1484 *
1485 * @param buf where to write the data
1486 * @param size number of bytes per member
1487 * @param nmemb number of members available in @a buf
1488 * @param cls our `struct Socks5Request` that generated the data
1489 * @return number of bytes copied to @a buf
1490 */
1491static size_t
1492curl_upload_cb (void *buf,
1493 size_t size,
1494 size_t nmemb,
1495 void *cls)
1496{
1497 struct Socks5Request *s5r = cls;
1498 size_t len = size * nmemb;
1499 size_t to_copy;
1500
1501 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1502 "Receiving %ux%u bytes for `%s%s' from cURL to upload\n",
1503 (unsigned int) size,
1504 (unsigned int) nmemb,
1505 s5r->domain,
1506 s5r->url);
1507
1508 if ((0 == s5r->io_len) &&
1509 (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state))
1510 {
1511 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1512 "Pausing CURL UPLOAD %s%s, need more data\n",
1513 s5r->domain,
1514 s5r->url);
1515 return CURL_READFUNC_PAUSE;
1516 }
1517 if ((0 == s5r->io_len) &&
1518 (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state))
1519 {
1520 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
1521 if (GNUNET_YES == s5r->curl_paused)
1522 {
1523 s5r->curl_paused = GNUNET_NO;
1524 curl_easy_pause (s5r->curl,
1525 CURLPAUSE_CONT);
1526 }
1527 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1528 "Completed CURL UPLOAD %s%s\n",
1529 s5r->domain,
1530 s5r->url);
1531 return 0; /* upload finished, can now download */
1532 }
1533 if ((SOCKS5_SOCKET_UPLOAD_STARTED != s5r->state) &&
1534 (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state))
1535 {
1536 GNUNET_break (0);
1537 return CURL_READFUNC_ABORT;
1538 }
1539 to_copy = GNUNET_MIN (s5r->io_len,
1540 len);
1541 GNUNET_memcpy (buf,
1542 s5r->io_buf,
1543 to_copy);
1544 memmove (s5r->io_buf,
1545 &s5r->io_buf[to_copy],
1546 s5r->io_len - to_copy);
1547 s5r->io_len -= to_copy;
1548 if (s5r->io_len + to_copy == sizeof(s5r->io_buf))
1549 run_mhd_now (s5r->hd); /* got more space for upload now */
1550 return to_copy;
1551}
1552
1553
1554/* ************************** main loop of cURL interaction ****************** */
1555
1556
1557/**
1558 * Task that is run when we are ready to receive more data
1559 * from curl
1560 *
1561 * @param cls closure
1562 */
1563static void
1564curl_task_download (void *cls);
1565
1566
1567/**
1568 * Ask cURL for the select() sets and schedule cURL operations.
1569 */
1570static void
1571curl_download_prepare ()
1572{
1573 CURLMcode mret;
1574 fd_set rs;
1575 fd_set ws;
1576 fd_set es;
1577 int max;
1578 struct GNUNET_NETWORK_FDSet *grs;
1579 struct GNUNET_NETWORK_FDSet *gws;
1580 long to;
1581 struct GNUNET_TIME_Relative rtime;
1582
1583 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1584 "Scheduling CURL interaction\n");
1585 if (NULL != curl_download_task)
1586 {
1587 GNUNET_SCHEDULER_cancel (curl_download_task);
1588 curl_download_task = NULL;
1589 }
1590 max = -1;
1591 FD_ZERO (&rs);
1592 FD_ZERO (&ws);
1593 FD_ZERO (&es);
1594 if (CURLM_OK != (mret = curl_multi_fdset (curl_multi,
1595 &rs,
1596 &ws,
1597 &es,
1598 &max)))
1599 {
1600 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1601 "%s failed at %s:%d: `%s'\n",
1602 "curl_multi_fdset", __FILE__, __LINE__,
1603 curl_multi_strerror (mret));
1604 return;
1605 }
1606 to = -1;
1607 GNUNET_break (CURLM_OK ==
1608 curl_multi_timeout (curl_multi,
1609 &to));
1610 if (-1 == to)
1611 rtime = GNUNET_TIME_UNIT_FOREVER_REL;
1612 else
1613 rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
1614 to);
1615 if (-1 != max)
1616 {
1617 grs = GNUNET_NETWORK_fdset_create ();
1618 gws = GNUNET_NETWORK_fdset_create ();
1619 GNUNET_NETWORK_fdset_copy_native (grs,
1620 &rs,
1621 max + 1);
1622 GNUNET_NETWORK_fdset_copy_native (gws,
1623 &ws,
1624 max + 1);
1625 curl_download_task = GNUNET_SCHEDULER_add_select (
1626 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1627 rtime,
1628 grs,
1629 gws,
1630 &curl_task_download,
1631 curl_multi);
1632 GNUNET_NETWORK_fdset_destroy (gws);
1633 GNUNET_NETWORK_fdset_destroy (grs);
1634 }
1635 else
1636 {
1637 curl_download_task = GNUNET_SCHEDULER_add_delayed (rtime,
1638 &curl_task_download,
1639 curl_multi);
1640 }
1641}
1642
1643
1644/**
1645 * Task that is run when we are ready to receive more data from curl.
1646 *
1647 * @param cls closure, NULL
1648 */
1649static void
1650curl_task_download (void *cls)
1651{
1652 int running;
1653 int msgnum;
1654 struct CURLMsg *msg;
1655 CURLMcode mret;
1656 struct Socks5Request *s5r;
1657
1658 curl_download_task = NULL;
1659 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1660 "Running CURL interaction\n");
1661 do
1662 {
1663 running = 0;
1664 mret = curl_multi_perform (curl_multi,
1665 &running);
1666 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1667 "Checking CURL multi status: %d\n",
1668 mret);
1669 while (NULL != (msg = curl_multi_info_read (curl_multi,
1670 &msgnum)))
1671 {
1672 GNUNET_break (CURLE_OK ==
1673 curl_easy_getinfo (msg->easy_handle,
1674 CURLINFO_PRIVATE,
1675 (char **) &s5r));
1676 if (NULL == s5r)
1677 {
1678 GNUNET_break (0);
1679 continue;
1680 }
1681 switch (msg->msg)
1682 {
1683 case CURLMSG_NONE:
1684 /* documentation says this is not used */
1685 GNUNET_break (0);
1686 break;
1687
1688 case CURLMSG_DONE:
1689 switch (msg->data.result)
1690 {
1691 case CURLE_OK:
1692 case CURLE_GOT_NOTHING:
1693 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1694 "CURL download %s%s completed.\n",
1695 s5r->domain,
1696 s5r->url);
1697 if (NULL == s5r->response)
1698 {
1699 GNUNET_assert (GNUNET_OK ==
1700 create_mhd_response_from_s5r (s5r));
1701 }
1702 s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;
1703 if (GNUNET_YES == s5r->suspended)
1704 {
1705 MHD_resume_connection (s5r->con);
1706 s5r->suspended = GNUNET_NO;
1707 }
1708 run_mhd_now (s5r->hd);
1709 break;
1710
1711 default:
1712 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1713 "Download curl %s%s failed: %s\n",
1714 s5r->domain,
1715 s5r->url,
1716 curl_easy_strerror (msg->data.result));
1717 /* FIXME: indicate error somehow? close MHD connection badly as well? */
1718 s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;
1719 if (GNUNET_YES == s5r->suspended)
1720 {
1721 MHD_resume_connection (s5r->con);
1722 s5r->suspended = GNUNET_NO;
1723 }
1724 run_mhd_now (s5r->hd);
1725 break;
1726 }
1727 if (NULL == s5r->response)
1728 s5r->response = curl_failure_response;
1729 break;
1730
1731 case CURLMSG_LAST:
1732 /* documentation says this is not used */
1733 GNUNET_break (0);
1734 break;
1735
1736 default:
1737 /* unexpected status code */
1738 GNUNET_break (0);
1739 break;
1740 }
1741 }
1742 ;
1743 }
1744 while (mret == CURLM_CALL_MULTI_PERFORM);
1745 if (CURLM_OK != mret)
1746 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1747 "%s failed at %s:%d: `%s'\n",
1748 "curl_multi_perform", __FILE__, __LINE__,
1749 curl_multi_strerror (mret));
1750 if (0 == running)
1751 {
1752 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1753 "Suspending cURL multi loop, no more events pending\n");
1754 if (NULL != curl_download_task)
1755 {
1756 GNUNET_SCHEDULER_cancel (curl_download_task);
1757 curl_download_task = NULL;
1758 }
1759 return; /* nothing more in progress */
1760 }
1761 curl_download_prepare ();
1762}
1763
1764
1765/* ********************************* MHD response generation ******************* */
1766
1767
1768/**
1769 * Read HTTP request header field from the request. Copies the fields
1770 * over to the 'headers' that will be given to curl. However, 'Host'
1771 * is substituted with the LEHO if present. We also change the
1772 * 'Connection' header value to "close" as the proxy does not support
1773 * pipelining.
1774 *
1775 * @param cls our `struct Socks5Request`
1776 * @param kind value kind
1777 * @param key field key
1778 * @param value field value
1779 * @return #MHD_YES to continue to iterate
1780 */
1781static int
1782con_val_iter (void *cls,
1783 enum MHD_ValueKind kind,
1784 const char *key,
1785 const char *value)
1786{
1787 struct Socks5Request *s5r = cls;
1788 char *hdr;
1789
1790 if ((0 == strcasecmp (MHD_HTTP_HEADER_HOST,
1791 key)) &&
1792 (NULL != s5r->leho))
1793 value = s5r->leho;
1794 GNUNET_asprintf (&hdr,
1795 "%s: %s",
1796 key,
1797 value);
1798 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1799 "Adding HEADER `%s' to HTTP request\n",
1800 hdr);
1801 s5r->headers = curl_slist_append (s5r->headers,
1802 hdr);
1803 GNUNET_free (hdr);
1804 return MHD_YES;
1805}
1806
1807
1808/**
1809 * Main MHD callback for handling requests.
1810 *
1811 * @param cls unused
1812 * @param con MHD connection handle
1813 * @param url the url in the request
1814 * @param meth the HTTP method used ("GET", "PUT", etc.)
1815 * @param ver the HTTP version string ("HTTP/1.1" for version 1.1, etc.)
1816 * @param upload_data the data being uploaded (excluding HEADERS,
1817 * for a POST that fits into memory and that is encoded
1818 * with a supported encoding, the POST data will NOT be
1819 * given in upload_data and is instead available as
1820 * part of MHD_get_connection_values; very large POST
1821 * data *will* be made available incrementally in
1822 * upload_data)
1823 * @param upload_data_size set initially to the size of the
1824 * @a upload_data provided; the method must update this
1825 * value to the number of bytes NOT processed;
1826 * @param con_cls pointer to location where we store the `struct Request`
1827 * @return #MHD_YES if the connection was handled successfully,
1828 * #MHD_NO if the socket must be closed due to a serious
1829 * error while handling the request
1830 */
1831static MHD_RESULT
1832create_response (void *cls,
1833 struct MHD_Connection *con,
1834 const char *url,
1835 const char *meth,
1836 const char *ver,
1837 const char *upload_data,
1838 size_t *upload_data_size,
1839 void **con_cls)
1840{
1841 struct Socks5Request *s5r = *con_cls;
1842 char *curlurl;
1843 char ipstring[INET6_ADDRSTRLEN];
1844 char ipaddr[INET6_ADDRSTRLEN + 2];
1845 char *curl_hosts;
1846 const struct sockaddr *sa;
1847 const struct sockaddr_in *s4;
1848 const struct sockaddr_in6 *s6;
1849 uint16_t port;
1850 size_t left;
1851
1852 if (NULL == s5r)
1853 {
1854 GNUNET_break (0);
1855 return MHD_NO;
1856 }
1857 s5r->con = con;
1858 /* Fresh connection. */
1859 if (SOCKS5_SOCKET_WITH_MHD == s5r->state)
1860 {
1861 /* first time here, initialize curl handle */
1862 if (s5r->is_gns)
1863 {
1864 sa = (const struct sockaddr *) &s5r->destination_address;
1865 switch (sa->sa_family)
1866 {
1867 case AF_INET:
1868 s4 = (const struct sockaddr_in *) &s5r->destination_address;
1869 if (NULL == inet_ntop (AF_INET,
1870 &s4->sin_addr,
1871 ipstring,
1872 sizeof(ipstring)))
1873 {
1874 GNUNET_break (0);
1875 return MHD_NO;
1876 }
1877 GNUNET_snprintf (ipaddr,
1878 sizeof(ipaddr),
1879 "%s",
1880 ipstring);
1881 port = ntohs (s4->sin_port);
1882 break;
1883
1884 case AF_INET6:
1885 s6 = (const struct sockaddr_in6 *) &s5r->destination_address;
1886 if (NULL == inet_ntop (AF_INET6,
1887 &s6->sin6_addr,
1888 ipstring,
1889 sizeof(ipstring)))
1890 {
1891 GNUNET_break (0);
1892 return MHD_NO;
1893 }
1894 GNUNET_snprintf (ipaddr,
1895 sizeof(ipaddr),
1896 "%s",
1897 ipstring);
1898 port = ntohs (s6->sin6_port);
1899 break;
1900
1901 default:
1902 GNUNET_break (0);
1903 return MHD_NO;
1904 }
1905 GNUNET_asprintf (&curl_hosts,
1906 "%s:%d:%s",
1907 s5r->leho,
1908 port,
1909 ipaddr);
1910 s5r->hosts = curl_slist_append (NULL,
1911 curl_hosts);
1912 GNUNET_free (curl_hosts);
1913 }
1914 else
1915 {
1916 port = s5r->port;
1917 }
1918 if (NULL == s5r->curl)
1919 s5r->curl = curl_easy_init ();
1920 if (NULL == s5r->curl)
1921 return MHD_queue_response (con,
1922 MHD_HTTP_INTERNAL_SERVER_ERROR,
1923 curl_failure_response);
1924 curl_easy_setopt (s5r->curl,
1925 CURLOPT_HEADERFUNCTION,
1926 &curl_check_hdr);
1927 curl_easy_setopt (s5r->curl,
1928 CURLOPT_HEADERDATA,
1929 s5r);
1930 curl_easy_setopt (s5r->curl,
1931 CURLOPT_FOLLOWLOCATION,
1932 0);
1933 if (s5r->is_gns)
1934 curl_easy_setopt (s5r->curl,
1935 CURLOPT_IPRESOLVE,
1936 CURL_IPRESOLVE_V4);
1937 curl_easy_setopt (s5r->curl,
1938 CURLOPT_CONNECTTIMEOUT,
1939 600L);
1940 curl_easy_setopt (s5r->curl,
1941 CURLOPT_TIMEOUT,
1942 600L);
1943 curl_easy_setopt (s5r->curl,
1944 CURLOPT_NOSIGNAL,
1945 1L);
1946 curl_easy_setopt (s5r->curl,
1947 CURLOPT_HTTP_CONTENT_DECODING,
1948 0);
1949 curl_easy_setopt (s5r->curl,
1950 CURLOPT_NOSIGNAL,
1951 1L);
1952 curl_easy_setopt (s5r->curl,
1953 CURLOPT_PRIVATE,
1954 s5r);
1955 curl_easy_setopt (s5r->curl,
1956 CURLOPT_VERBOSE,
1957 0L);
1958 /**
1959 * Pre-populate cache to resolve Hostname.
1960 * This is necessary as the DNS name in the CURLOPT_URL is used
1961 * for SNI http://de.wikipedia.org/wiki/Server_Name_Indication
1962 */
1963 if ((NULL != s5r->leho) &&
1964 (NULL != s5r->hosts))
1965 {
1966 curl_easy_setopt (s5r->curl,
1967 CURLOPT_RESOLVE,
1968 s5r->hosts);
1969 }
1970 if (s5r->is_gns)
1971 {
1972 GNUNET_asprintf (&curlurl,
1973 (GNUNET_YES != s5r->is_tls) // (HTTPS_PORT != s5r->port)
1974 ? "http://%s:%d%s"
1975 : "https://%s:%d%s",
1976 (NULL != s5r->leho)
1977 ? s5r->leho
1978 : ipaddr,
1979 port,
1980 s5r->url);
1981 }
1982 else
1983 {
1984 GNUNET_asprintf (&curlurl,
1985 (GNUNET_YES != s5r->is_tls) // (HTTPS_PORT != s5r->port)
1986 ? "http://%s:%d%s"
1987 : "https://%s:%d%s",
1988 s5r->domain,
1989 port,
1990 s5r->url);
1991 }
1992 curl_easy_setopt (s5r->curl,
1993 CURLOPT_URL,
1994 curlurl);
1995 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1996 "Launching %s CURL interaction, fetching `%s'\n",
1997 (s5r->is_gns) ? "GNS" : "DNS",
1998 curlurl);
1999 GNUNET_free (curlurl);
2000 if (0 == strcasecmp (meth,
2001 MHD_HTTP_METHOD_PUT))
2002 {
2003 s5r->state = SOCKS5_SOCKET_UPLOAD_STARTED;
2004 curl_easy_setopt (s5r->curl,
2005 CURLOPT_UPLOAD,
2006 1L);
2007 curl_easy_setopt (s5r->curl,
2008 CURLOPT_WRITEFUNCTION,
2009 &curl_download_cb);
2010 curl_easy_setopt (s5r->curl,
2011 CURLOPT_WRITEDATA,
2012 s5r);
2013 GNUNET_assert (CURLE_OK ==
2014 curl_easy_setopt (s5r->curl,
2015 CURLOPT_READFUNCTION,
2016 &curl_upload_cb));
2017 curl_easy_setopt (s5r->curl,
2018 CURLOPT_READDATA,
2019 s5r);
2020 {
2021 const char *us;
2022 long upload_size = 0;
2023
2024 us = MHD_lookup_connection_value (con,
2025 MHD_HEADER_KIND,
2026 MHD_HTTP_HEADER_CONTENT_LENGTH);
2027 if ((1 == sscanf (us,
2028 "%ld",
2029 &upload_size)) &&
2030 (upload_size >= 0))
2031 {
2032 curl_easy_setopt (s5r->curl,
2033 CURLOPT_INFILESIZE,
2034 upload_size);
2035 }
2036 }
2037 }
2038 else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2039 {
2040 s5r->state = SOCKS5_SOCKET_UPLOAD_STARTED;
2041 curl_easy_setopt (s5r->curl,
2042 CURLOPT_POST,
2043 1L);
2044 curl_easy_setopt (s5r->curl,
2045 CURLOPT_WRITEFUNCTION,
2046 &curl_download_cb);
2047 curl_easy_setopt (s5r->curl,
2048 CURLOPT_WRITEDATA,
2049 s5r);
2050 curl_easy_setopt (s5r->curl,
2051 CURLOPT_READFUNCTION,
2052 &curl_upload_cb);
2053 curl_easy_setopt (s5r->curl,
2054 CURLOPT_READDATA,
2055 s5r);
2056 {
2057 const char *us;
2058 long upload_size;
2059
2060 upload_size = 0;
2061 us = MHD_lookup_connection_value (con,
2062 MHD_HEADER_KIND,
2063 MHD_HTTP_HEADER_CONTENT_LENGTH);
2064 if ((NULL != us) &&
2065 (1 == sscanf (us,
2066 "%ld",
2067 &upload_size)) &&
2068 (upload_size >= 0))
2069 {
2070 curl_easy_setopt (s5r->curl,
2071 CURLOPT_INFILESIZE,
2072 upload_size);
2073 }
2074 else
2075 {
2076 curl_easy_setopt (s5r->curl,
2077 CURLOPT_INFILESIZE,
2078 upload_size);
2079 }
2080 }
2081 }
2082 else if (0 == strcasecmp (meth,
2083 MHD_HTTP_METHOD_HEAD))
2084 {
2085 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
2086 curl_easy_setopt (s5r->curl,
2087 CURLOPT_NOBODY,
2088 1L);
2089 }
2090 else if (0 == strcasecmp (meth,
2091 MHD_HTTP_METHOD_OPTIONS))
2092 {
2093 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
2094 curl_easy_setopt (s5r->curl,
2095 CURLOPT_CUSTOMREQUEST,
2096 "OPTIONS");
2097 curl_easy_setopt (s5r->curl,
2098 CURLOPT_WRITEFUNCTION,
2099 &curl_download_cb);
2100 curl_easy_setopt (s5r->curl,
2101 CURLOPT_WRITEDATA,
2102 s5r);
2103 }
2104 else if (0 == strcasecmp (meth,
2105 MHD_HTTP_METHOD_GET))
2106 {
2107 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
2108 curl_easy_setopt (s5r->curl,
2109 CURLOPT_HTTPGET,
2110 1L);
2111 curl_easy_setopt (s5r->curl,
2112 CURLOPT_WRITEFUNCTION,
2113 &curl_download_cb);
2114 curl_easy_setopt (s5r->curl,
2115 CURLOPT_WRITEDATA,
2116 s5r);
2117 }
2118 else if (0 == strcasecmp (meth,
2119 MHD_HTTP_METHOD_DELETE))
2120 {
2121 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
2122 curl_easy_setopt (s5r->curl,
2123 CURLOPT_CUSTOMREQUEST,
2124 "DELETE");
2125 curl_easy_setopt (s5r->curl,
2126 CURLOPT_WRITEFUNCTION,
2127 &curl_download_cb);
2128 curl_easy_setopt (s5r->curl,
2129 CURLOPT_WRITEDATA,
2130 s5r);
2131 }
2132 else
2133 {
2134 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2135 _ ("Unsupported HTTP method `%s'\n"),
2136 meth);
2137 curl_easy_cleanup (s5r->curl);
2138 s5r->curl = NULL;
2139 return MHD_NO;
2140 }
2141
2142 if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_0))
2143 {
2144 curl_easy_setopt (s5r->curl,
2145 CURLOPT_HTTP_VERSION,
2146 CURL_HTTP_VERSION_1_0);
2147 }
2148 else if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_1))
2149 {
2150 curl_easy_setopt (s5r->curl,
2151 CURLOPT_HTTP_VERSION,
2152 CURL_HTTP_VERSION_1_1);
2153 }
2154 else
2155 {
2156 curl_easy_setopt (s5r->curl,
2157 CURLOPT_HTTP_VERSION,
2158 CURL_HTTP_VERSION_NONE);
2159 }
2160
2161 if (GNUNET_YES == s5r->is_tls) // (HTTPS_PORT == s5r->port)
2162 {
2163 curl_easy_setopt (s5r->curl,
2164 CURLOPT_USE_SSL,
2165 CURLUSESSL_ALL);
2166 if (0 < s5r->num_danes)
2167 curl_easy_setopt (s5r->curl,
2168 CURLOPT_SSL_VERIFYPEER,
2169 0L);
2170 else
2171 curl_easy_setopt (s5r->curl,
2172 CURLOPT_SSL_VERIFYPEER,
2173 1L);
2174 /* Disable cURL checking the hostname, as we will check ourselves
2175 as only we have the domain name or the LEHO or the DANE record */
2176 curl_easy_setopt (s5r->curl,
2177 CURLOPT_SSL_VERIFYHOST,
2178 0L);
2179 }
2180 else
2181 {
2182 curl_easy_setopt (s5r->curl,
2183 CURLOPT_USE_SSL,
2184 CURLUSESSL_NONE);
2185 }
2186
2187 if (CURLM_OK !=
2188 curl_multi_add_handle (curl_multi,
2189 s5r->curl))
2190 {
2191 GNUNET_break (0);
2192 curl_easy_cleanup (s5r->curl);
2193 s5r->curl = NULL;
2194 return MHD_NO;
2195 }
2196 MHD_get_connection_values (con,
2197 MHD_HEADER_KIND,
2198 (MHD_KeyValueIterator) & con_val_iter,
2199 s5r);
2200 curl_easy_setopt (s5r->curl,
2201 CURLOPT_HTTPHEADER,
2202 s5r->headers);
2203 curl_download_prepare ();
2204 return MHD_YES;
2205 }
2206
2207 /* continuing to process request */
2208 if (0 != *upload_data_size)
2209 {
2210 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2211 "Processing %u bytes UPLOAD\n",
2212 (unsigned int) *upload_data_size);
2213
2214 /* FIXME: This must be set or a header with Transfer-Encoding: chunked. Else
2215 * upload callback is not called!
2216 */
2217 curl_easy_setopt (s5r->curl,
2218 CURLOPT_POSTFIELDSIZE,
2219 *upload_data_size);
2220
2221 left = GNUNET_MIN (*upload_data_size,
2222 sizeof(s5r->io_buf) - s5r->io_len);
2223 GNUNET_memcpy (&s5r->io_buf[s5r->io_len],
2224 upload_data,
2225 left);
2226 s5r->io_len += left;
2227 *upload_data_size -= left;
2228 GNUNET_assert (NULL != s5r->curl);
2229 if (GNUNET_YES == s5r->curl_paused)
2230 {
2231 s5r->curl_paused = GNUNET_NO;
2232 curl_easy_pause (s5r->curl,
2233 CURLPAUSE_CONT);
2234 }
2235 return MHD_YES;
2236 }
2237 if (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state)
2238 {
2239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2240 "Finished processing UPLOAD\n");
2241 s5r->state = SOCKS5_SOCKET_UPLOAD_DONE;
2242 }
2243 if (NULL == s5r->response)
2244 {
2245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2246 "Waiting for HTTP response for %s%s...\n",
2247 s5r->domain,
2248 s5r->url);
2249 MHD_suspend_connection (con);
2250 s5r->suspended = GNUNET_YES;
2251 return MHD_YES;
2252 }
2253 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2254 "Queueing response for %s%s with MHD\n",
2255 s5r->domain,
2256 s5r->url);
2257 run_mhd_now (s5r->hd);
2258 return MHD_queue_response (con,
2259 s5r->response_code,
2260 s5r->response);
2261}
2262
2263
2264/* ******************** MHD HTTP setup and event loop ******************** */
2265
2266
2267/**
2268 * Function called when MHD decides that we are done with a request.
2269 *
2270 * @param cls NULL
2271 * @param connection connection handle
2272 * @param con_cls value as set by the last call to
2273 * the MHD_AccessHandlerCallback, should be our `struct Socks5Request *`
2274 * @param toe reason for request termination (ignored)
2275 */
2276static void
2277mhd_completed_cb (void *cls,
2278 struct MHD_Connection *connection,
2279 void **con_cls,
2280 enum MHD_RequestTerminationCode toe)
2281{
2282 struct Socks5Request *s5r = *con_cls;
2283
2284 if (NULL == s5r)
2285 return;
2286 if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
2287 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2288 "MHD encountered error handling request: %d\n",
2289 toe);
2290 if (NULL != s5r->curl)
2291 {
2292 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2293 "Removing cURL handle (MHD interaction complete)\n");
2294 curl_multi_remove_handle (curl_multi,
2295 s5r->curl);
2296 curl_slist_free_all (s5r->headers);
2297 s5r->headers = NULL;
2298 curl_easy_reset (s5r->curl);
2299 s5r->rbuf_len = 0;
2300 s5r->wbuf_len = 0;
2301 s5r->io_len = 0;
2302 curl_download_prepare ();
2303 }
2304 if ((NULL != s5r->response) &&
2305 (curl_failure_response != s5r->response))
2306 MHD_destroy_response (s5r->response);
2307 for (struct HttpResponseHeader *header = s5r->header_head;
2308 NULL != header;
2309 header = s5r->header_head)
2310 {
2311 GNUNET_CONTAINER_DLL_remove (s5r->header_head,
2312 s5r->header_tail,
2313 header);
2314 GNUNET_free (header->type);
2315 GNUNET_free (header->value);
2316 GNUNET_free (header);
2317 }
2318 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2319 "Finished request for %s\n",
2320 s5r->url);
2321 GNUNET_free (s5r->url);
2322 s5r->state = SOCKS5_SOCKET_WITH_MHD;
2323 s5r->url = NULL;
2324 s5r->response = NULL;
2325 *con_cls = NULL;
2326}
2327
2328
2329/**
2330 * Function called when MHD connection is opened or closed.
2331 *
2332 * @param cls NULL
2333 * @param connection connection handle
2334 * @param con_cls value as set by the last call to
2335 * the MHD_AccessHandlerCallback, should be our `struct Socks5Request *`
2336 * @param toe connection notification type
2337 */
2338static void
2339mhd_connection_cb (void *cls,
2340 struct MHD_Connection *connection,
2341 void **con_cls,
2342 enum MHD_ConnectionNotificationCode cnc)
2343{
2344 struct Socks5Request *s5r;
2345 const union MHD_ConnectionInfo *ci;
2346 int sock;
2347
2348 switch (cnc)
2349 {
2350 case MHD_CONNECTION_NOTIFY_STARTED:
2351 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection started...\n");
2352 ci = MHD_get_connection_info (connection,
2353 MHD_CONNECTION_INFO_CONNECTION_FD);
2354 if (NULL == ci)
2355 {
2356 GNUNET_break (0);
2357 return;
2358 }
2359 sock = ci->connect_fd;
2360 for (s5r = s5r_head; NULL != s5r; s5r = s5r->next)
2361 {
2362 if (GNUNET_NETWORK_get_fd (s5r->sock) == sock)
2363 {
2364 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2365 "Context set...\n");
2366 s5r->ssl_checked = GNUNET_NO;
2367 *con_cls = s5r;
2368 break;
2369 }
2370 }
2371 break;
2372
2373 case MHD_CONNECTION_NOTIFY_CLOSED:
2374 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2375 "Connection closed... cleaning up\n");
2376 s5r = *con_cls;
2377 if (NULL == s5r)
2378 {
2379 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2380 "Connection stale!\n");
2381 return;
2382 }
2383 cleanup_s5r (s5r);
2384 curl_download_prepare ();
2385 *con_cls = NULL;
2386 break;
2387
2388 default:
2389 GNUNET_break (0);
2390 }
2391}
2392
2393
2394/**
2395 * Function called when MHD first processes an incoming connection.
2396 * Gives us the respective URI information.
2397 *
2398 * We use this to associate the `struct MHD_Connection` with our
2399 * internal `struct Socks5Request` data structure (by checking
2400 * for matching sockets).
2401 *
2402 * @param cls the HTTP server handle (a `struct MhdHttpList`)
2403 * @param url the URL that is being requested
2404 * @param connection MHD connection object for the request
2405 * @return the `struct Socks5Request` that this @a connection is for
2406 */
2407static void *
2408mhd_log_callback (void *cls,
2409 const char *url,
2410 struct MHD_Connection *connection)
2411{
2412 struct Socks5Request *s5r;
2413 const union MHD_ConnectionInfo *ci;
2414
2415 ci = MHD_get_connection_info (connection,
2416 MHD_CONNECTION_INFO_SOCKET_CONTEXT);
2417 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing %s\n", url);
2418 if (NULL == ci)
2419 {
2420 GNUNET_break (0);
2421 return NULL;
2422 }
2423 s5r = ci->socket_context;
2424 if (NULL != s5r->url)
2425 {
2426 GNUNET_break (0);
2427 return NULL;
2428 }
2429 s5r->url = GNUNET_strdup (url);
2430 if (NULL != s5r->timeout_task)
2431 {
2432 GNUNET_SCHEDULER_cancel (s5r->timeout_task);
2433 s5r->timeout_task = NULL;
2434 }
2435 GNUNET_assert (s5r->state == SOCKS5_SOCKET_WITH_MHD);
2436 return s5r;
2437}
2438
2439
2440/**
2441 * Kill the given MHD daemon.
2442 *
2443 * @param hd daemon to stop
2444 */
2445static void
2446kill_httpd (struct MhdHttpList *hd)
2447{
2448 GNUNET_CONTAINER_DLL_remove (mhd_httpd_head,
2449 mhd_httpd_tail,
2450 hd);
2451 GNUNET_free (hd->domain);
2452 MHD_stop_daemon (hd->daemon);
2453 if (NULL != hd->httpd_task)
2454 {
2455 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2456 hd->httpd_task = NULL;
2457 }
2458 GNUNET_free (hd->proxy_cert);
2459 if (hd == httpd)
2460 httpd = NULL;
2461 GNUNET_free (hd);
2462}
2463
2464
2465/**
2466 * Task run whenever HTTP server is idle for too long. Kill it.
2467 *
2468 * @param cls the `struct MhdHttpList *`
2469 */
2470static void
2471kill_httpd_task (void *cls)
2472{
2473 struct MhdHttpList *hd = cls;
2474
2475 hd->httpd_task = NULL;
2476 kill_httpd (hd);
2477}
2478
2479
2480/**
2481 * Task run whenever HTTP server operations are pending.
2482 *
2483 * @param cls the `struct MhdHttpList *` of the daemon that is being run
2484 */
2485static void
2486do_httpd (void *cls);
2487
2488
2489/**
2490 * Schedule MHD. This function should be called initially when an
2491 * MHD is first getting its client socket, and will then automatically
2492 * always be called later whenever there is work to be done.
2493 *
2494 * @param hd the daemon to schedule
2495 */
2496static void
2497schedule_httpd (struct MhdHttpList *hd)
2498{
2499 fd_set rs;
2500 fd_set ws;
2501 fd_set es;
2502 struct GNUNET_NETWORK_FDSet *wrs;
2503 struct GNUNET_NETWORK_FDSet *wws;
2504 int max;
2505 int haveto;
2506 MHD_UNSIGNED_LONG_LONG timeout;
2507 struct GNUNET_TIME_Relative tv;
2508
2509 FD_ZERO (&rs);
2510 FD_ZERO (&ws);
2511 FD_ZERO (&es);
2512 max = -1;
2513 if (MHD_YES !=
2514 MHD_get_fdset (hd->daemon,
2515 &rs,
2516 &ws,
2517 &es,
2518 &max))
2519 {
2520 kill_httpd (hd);
2521 return;
2522 }
2523 haveto = MHD_get_timeout (hd->daemon,
2524 &timeout);
2525 if (MHD_YES == haveto)
2526 tv.rel_value_us = (uint64_t) timeout * 1000LL;
2527 else
2528 tv = GNUNET_TIME_UNIT_FOREVER_REL;
2529 if (-1 != max)
2530 {
2531 wrs = GNUNET_NETWORK_fdset_create ();
2532 wws = GNUNET_NETWORK_fdset_create ();
2533 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2534 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2535 }
2536 else
2537 {
2538 wrs = NULL;
2539 wws = NULL;
2540 }
2541 if (NULL != hd->httpd_task)
2542 {
2543 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2544 hd->httpd_task = NULL;
2545 }
2546 if ((MHD_YES != haveto) &&
2547 (-1 == max) &&
2548 (hd != httpd))
2549 {
2550 /* daemon is idle, kill after timeout */
2551 hd->httpd_task = GNUNET_SCHEDULER_add_delayed (MHD_CACHE_TIMEOUT,
2552 &kill_httpd_task,
2553 hd);
2554 }
2555 else
2556 {
2557 hd->httpd_task =
2558 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
2559 tv, wrs, wws,
2560 &do_httpd, hd);
2561 }
2562 if (NULL != wrs)
2563 GNUNET_NETWORK_fdset_destroy (wrs);
2564 if (NULL != wws)
2565 GNUNET_NETWORK_fdset_destroy (wws);
2566}
2567
2568
2569static void
2570do_httpd (void *cls)
2571{
2572 struct MhdHttpList *hd = cls;
2573
2574 hd->httpd_task = NULL;
2575 MHD_run (hd->daemon);
2576 schedule_httpd (hd);
2577}
2578
2579
2580/**
2581 * Run MHD now, we have extra data ready for the callback.
2582 *
2583 * @param hd the daemon to run now.
2584 */
2585static void
2586run_mhd_now (struct MhdHttpList *hd)
2587{
2588 if (NULL != hd->httpd_task)
2589 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2590 hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd,
2591 hd);
2592}
2593
2594
2595/**
2596 * Read file in filename
2597 *
2598 * @param filename file to read
2599 * @param size pointer where filesize is stored
2600 * @return NULL on error
2601 */
2602static void*
2603load_file (const char*filename,
2604 unsigned int*size)
2605{
2606 void *buffer;
2607 uint64_t fsize;
2608
2609 if (GNUNET_OK !=
2610 GNUNET_DISK_file_size (filename,
2611 &fsize,
2612 GNUNET_YES,
2613 GNUNET_YES))
2614 return NULL;
2615 if (fsize > MAX_PEM_SIZE)
2616 return NULL;
2617 *size = (unsigned int) fsize;
2618 buffer = GNUNET_malloc (*size);
2619 if (fsize !=
2620 GNUNET_DISK_fn_read (filename,
2621 buffer,
2622 (size_t) fsize))
2623 {
2624 GNUNET_free (buffer);
2625 return NULL;
2626 }
2627 return buffer;
2628}
2629
2630
2631/**
2632 * Load PEM key from file
2633 *
2634 * @param key where to store the data
2635 * @param keyfile path to the PEM file
2636 * @return #GNUNET_OK on success
2637 */
2638static int
2639load_key_from_file (gnutls_x509_privkey_t key,
2640 const char*keyfile)
2641{
2642 gnutls_datum_t key_data;
2643 int ret;
2644
2645 key_data.data = load_file (keyfile,
2646 &key_data.size);
2647 if (NULL == key_data.data)
2648 return GNUNET_SYSERR;
2649 ret = gnutls_x509_privkey_import (key, &key_data,
2650 GNUTLS_X509_FMT_PEM);
2651 if (GNUTLS_E_SUCCESS != ret)
2652 {
2653 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2654 _ ("Unable to import private key from file `%s'\n"),
2655 keyfile);
2656 }
2657 GNUNET_free (key_data.data);
2658 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2659}
2660
2661
2662/**
2663 * Load cert from file
2664 *
2665 * @param crt struct to store data in
2666 * @param certfile path to pem file
2667 * @return #GNUNET_OK on success
2668 */
2669static int
2670load_cert_from_file (gnutls_x509_crt_t crt,
2671 const char*certfile)
2672{
2673 gnutls_datum_t cert_data;
2674 int ret;
2675
2676 cert_data.data = load_file (certfile,
2677 &cert_data.size);
2678 if (NULL == cert_data.data)
2679 return GNUNET_SYSERR;
2680 ret = gnutls_x509_crt_import (crt,
2681 &cert_data,
2682 GNUTLS_X509_FMT_PEM);
2683 if (GNUTLS_E_SUCCESS != ret)
2684 {
2685 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2686 _ ("Unable to import certificate from `%s'\n"),
2687 certfile);
2688 }
2689 GNUNET_free (cert_data.data);
2690 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2691}
2692
2693
2694/**
2695 * Generate new certificate for specific name
2696 *
2697 * @param name the subject name to generate a cert for
2698 * @return a struct holding the PEM data, NULL on error
2699 */
2700static struct ProxyGNSCertificate *
2701generate_gns_certificate (const char *name)
2702{
2703 unsigned int serial;
2704 size_t key_buf_size;
2705 size_t cert_buf_size;
2706 gnutls_x509_crt_t request;
2707 time_t etime;
2708 struct tm *tm_data;
2709 struct ProxyGNSCertificate *pgc;
2710
2711 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2712 "Generating x.509 certificate for `%s'\n",
2713 name);
2714 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_init (&request));
2715 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request,
2716 proxy_ca.key));
2717 pgc = GNUNET_new (struct ProxyGNSCertificate);
2718 gnutls_x509_crt_set_dn_by_oid (request,
2719 GNUTLS_OID_X520_COUNTRY_NAME,
2720 0,
2721 "ZZ",
2722 strlen ("ZZ"));
2723 gnutls_x509_crt_set_dn_by_oid (request,
2724 GNUTLS_OID_X520_ORGANIZATION_NAME,
2725 0,
2726 "GNU Name System",
2727 strlen ("GNU Name System"));
2728 gnutls_x509_crt_set_dn_by_oid (request,
2729 GNUTLS_OID_X520_COMMON_NAME,
2730 0,
2731 name,
2732 strlen (name));
2733 gnutls_x509_crt_set_subject_alternative_name (request,
2734 GNUTLS_SAN_DNSNAME,
2735 name);
2736 GNUNET_break (GNUTLS_E_SUCCESS ==
2737 gnutls_x509_crt_set_version (request,
2738 3));
2739 gnutls_rnd (GNUTLS_RND_NONCE,
2740 &serial,
2741 sizeof(serial));
2742 gnutls_x509_crt_set_serial (request,
2743 &serial,
2744 sizeof(serial));
2745 etime = time (NULL);
2746 tm_data = localtime (&etime);
2747 tm_data->tm_hour--;
2748 etime = mktime (tm_data);
2749 gnutls_x509_crt_set_activation_time (request,
2750 etime);
2751 tm_data->tm_year++;
2752 etime = mktime (tm_data);
2753 gnutls_x509_crt_set_expiration_time (request,
2754 etime);
2755 gnutls_x509_crt_sign2 (request,
2756 proxy_ca.cert,
2757 proxy_ca.key,
2758 GNUTLS_DIG_SHA512,
2759 0);
2760 key_buf_size = sizeof(pgc->key);
2761 cert_buf_size = sizeof(pgc->cert);
2762 gnutls_x509_crt_export (request,
2763 GNUTLS_X509_FMT_PEM,
2764 pgc->cert,
2765 &cert_buf_size);
2766 gnutls_x509_privkey_export (proxy_ca.key,
2767 GNUTLS_X509_FMT_PEM,
2768 pgc->key,
2769 &key_buf_size);
2770 gnutls_x509_crt_deinit (request);
2771 return pgc;
2772}
2773
2774
2775/**
2776 * Function called by MHD with errors, suppresses them all.
2777 *
2778 * @param cls closure
2779 * @param fm format string (`printf()`-style)
2780 * @param ap arguments to @a fm
2781 */
2782static void
2783mhd_error_log_callback (void *cls,
2784 const char *fm,
2785 va_list ap)
2786{
2787 /* do nothing */
2788}
2789
2790
2791/**
2792 * Lookup (or create) an TLS MHD instance for a particular domain.
2793 *
2794 * @param domain the domain the TLS daemon has to serve
2795 * @return NULL on error
2796 */
2797static struct MhdHttpList *
2798lookup_ssl_httpd (const char*domain)
2799{
2800 struct MhdHttpList *hd;
2801 struct ProxyGNSCertificate *pgc;
2802
2803 if (NULL == domain)
2804 {
2805 GNUNET_break (0);
2806 return NULL;
2807 }
2808 for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2809 if ((NULL != hd->domain) &&
2810 (0 == strcmp (hd->domain, domain)))
2811 return hd;
2812 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2813 "Starting fresh MHD HTTPS instance for domain `%s'\n",
2814 domain);
2815 pgc = generate_gns_certificate (domain);
2816 hd = GNUNET_new (struct MhdHttpList);
2817 hd->is_ssl = GNUNET_YES;
2818 hd->domain = GNUNET_strdup (domain);
2819 hd->proxy_cert = pgc;
2820 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL
2821 | MHD_USE_NO_LISTEN_SOCKET
2822 | MHD_ALLOW_SUSPEND_RESUME,
2823 0,
2824 NULL, NULL,
2825 &create_response, hd,
2826 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned
2827 int) 16,
2828 MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb,
2829 NULL,
2830 MHD_OPTION_NOTIFY_CONNECTION,
2831 &mhd_connection_cb, NULL,
2832 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2833 NULL,
2834 MHD_OPTION_EXTERNAL_LOGGER,
2835 &mhd_error_log_callback, NULL,
2836 MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2837 MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2838 MHD_OPTION_END);
2839 if (NULL == hd->daemon)
2840 {
2841 GNUNET_free (pgc);
2842 GNUNET_free (hd);
2843 return NULL;
2844 }
2845 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head,
2846 mhd_httpd_tail,
2847 hd);
2848 return hd;
2849}
2850
2851
2852/**
2853 * Task run when a Socks5Request somehow fails to be associated with
2854 * an MHD connection (e.g. because the client never speaks HTTP after
2855 * the SOCKS5 handshake). Clean up.
2856 *
2857 * @param cls the `struct Socks5Request *`
2858 */
2859static void
2860timeout_s5r_handshake (void *cls)
2861{
2862 struct Socks5Request *s5r = cls;
2863
2864 s5r->timeout_task = NULL;
2865 cleanup_s5r (s5r);
2866}
2867
2868
2869/**
2870 * We're done with the Socks5 protocol, now we need to pass the
2871 * connection data through to the final destination, either
2872 * direct (if the protocol might not be HTTP), or via MHD
2873 * (if the port looks like it should be HTTP).
2874 *
2875 * @param s5r socks request that has reached the final stage
2876 */
2877static void
2878setup_data_transfer (struct Socks5Request *s5r)
2879{
2880 struct MhdHttpList *hd;
2881 int fd;
2882 const struct sockaddr *addr;
2883 socklen_t len;
2884 char *domain;
2885
2886 if (GNUNET_YES == s5r->is_tls)
2887 {
2888 GNUNET_asprintf (&domain,
2889 "%s",
2890 s5r->domain);
2891 hd = lookup_ssl_httpd (domain);
2892 if (NULL == hd)
2893 {
2894 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2895 _ ("Failed to start HTTPS server for `%s'\n"),
2896 s5r->domain);
2897 cleanup_s5r (s5r);
2898 GNUNET_free (domain);
2899 return;
2900 }
2901 }
2902 else
2903 {
2904 domain = NULL;
2905 GNUNET_assert (NULL != httpd);
2906 hd = httpd;
2907 }
2908 fd = GNUNET_NETWORK_get_fd (s5r->sock);
2909 addr = GNUNET_NETWORK_get_addr (s5r->sock);
2910 len = GNUNET_NETWORK_get_addrlen (s5r->sock);
2911 s5r->state = SOCKS5_SOCKET_WITH_MHD;
2912 if (MHD_YES !=
2913 MHD_add_connection (hd->daemon,
2914 fd,
2915 addr,
2916 len))
2917 {
2918 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2919 _ ("Failed to pass client to MHD\n"));
2920 cleanup_s5r (s5r);
2921 GNUNET_free (domain);
2922 return;
2923 }
2924 s5r->hd = hd;
2925 schedule_httpd (hd);
2926 s5r->timeout_task = GNUNET_SCHEDULER_add_delayed (HTTP_HANDSHAKE_TIMEOUT,
2927 &timeout_s5r_handshake,
2928 s5r);
2929 GNUNET_free (domain);
2930}
2931
2932
2933/* ********************* SOCKS handling ************************* */
2934
2935
2936/**
2937 * Write data from buffer to socks5 client, then continue with state machine.
2938 *
2939 * @param cls the closure with the `struct Socks5Request`
2940 */
2941static void
2942do_write (void *cls)
2943{
2944 struct Socks5Request *s5r = cls;
2945 ssize_t len;
2946
2947 s5r->wtask = NULL;
2948 len = GNUNET_NETWORK_socket_send (s5r->sock,
2949 s5r->wbuf,
2950 s5r->wbuf_len);
2951 if (len <= 0)
2952 {
2953 /* write error: connection closed, shutdown, etc.; just clean up */
2954 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2955 "Write Error\n");
2956 cleanup_s5r (s5r);
2957 return;
2958 }
2959 memmove (s5r->wbuf,
2960 &s5r->wbuf[len],
2961 s5r->wbuf_len - len);
2962 s5r->wbuf_len -= len;
2963 if (s5r->wbuf_len > 0)
2964 {
2965 /* not done writing */
2966 s5r->wtask =
2967 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2968 s5r->sock,
2969 &do_write, s5r);
2970 return;
2971 }
2972
2973 /* we're done writing, continue with state machine! */
2974
2975 switch (s5r->state)
2976 {
2977 case SOCKS5_INIT:
2978 GNUNET_assert (0);
2979 break;
2980
2981 case SOCKS5_REQUEST:
2982 GNUNET_assert (NULL != s5r->rtask);
2983 break;
2984
2985 case SOCKS5_DATA_TRANSFER:
2986 setup_data_transfer (s5r);
2987 return;
2988
2989 case SOCKS5_WRITE_THEN_CLEANUP:
2990 cleanup_s5r (s5r);
2991 return;
2992
2993 default:
2994 GNUNET_break (0);
2995 break;
2996 }
2997}
2998
2999
3000/**
3001 * Return a server response message indicating a failure to the client.
3002 *
3003 * @param s5r request to return failure code for
3004 * @param sc status code to return
3005 */
3006static void
3007signal_socks_failure (struct Socks5Request *s5r,
3008 enum Socks5StatusCode sc)
3009{
3010 struct Socks5ServerResponseMessage *s_resp;
3011
3012 GNUNET_break (0 == s5r->wbuf_len); /* Should happen first in any transmission, right? */
3013 GNUNET_assert (SOCKS_BUFFERSIZE - s5r->wbuf_len >=
3014 sizeof(struct Socks5ServerResponseMessage));
3015 s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len];
3016 memset (s_resp, 0, sizeof(struct Socks5ServerResponseMessage));
3017 s_resp->version = SOCKS_VERSION_5;
3018 s_resp->reply = sc;
3019 s5r->state = SOCKS5_WRITE_THEN_CLEANUP;
3020 if (NULL != s5r->wtask)
3021 s5r->wtask =
3022 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
3023 s5r->sock,
3024 &do_write, s5r);
3025}
3026
3027
3028/**
3029 * Return a server response message indicating success.
3030 *
3031 * @param s5r request to return success status message for
3032 */
3033static void
3034signal_socks_success (struct Socks5Request *s5r)
3035{
3036 struct Socks5ServerResponseMessage *s_resp;
3037
3038 s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len];
3039 s_resp->version = SOCKS_VERSION_5;
3040 s_resp->reply = SOCKS5_STATUS_REQUEST_GRANTED;
3041 s_resp->reserved = 0;
3042 s_resp->addr_type = SOCKS5_AT_IPV4;
3043 /* zero out IPv4 address and port */
3044 memset (&s_resp[1],
3045 0,
3046 sizeof(struct in_addr) + sizeof(uint16_t));
3047 s5r->wbuf_len += sizeof(struct Socks5ServerResponseMessage)
3048 + sizeof(struct in_addr) + sizeof(uint16_t);
3049 if (NULL == s5r->wtask)
3050 s5r->wtask =
3051 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
3052 s5r->sock,
3053 &do_write, s5r);
3054}
3055
3056
3057/**
3058 * Process GNS results for target domain.
3059 *
3060 * @param cls the `struct Socks5Request *`
3061 * @param tld #GNUNET_YES if this was a GNS TLD.
3062 * @param rd_count number of records returned
3063 * @param rd record data
3064 */
3065static void
3066handle_gns_result (void *cls,
3067 int tld,
3068 uint32_t rd_count,
3069 const struct GNUNET_GNSRECORD_Data *rd)
3070{
3071 struct Socks5Request *s5r = cls;
3072 const struct GNUNET_GNSRECORD_Data *r;
3073 int got_ip;
3074
3075 s5r->gns_lookup = NULL;
3076 s5r->is_gns = tld;
3077 got_ip = GNUNET_NO;
3078 for (uint32_t i = 0; i < rd_count; i++)
3079 {
3080 r = &rd[i];
3081 switch (r->record_type)
3082 {
3083 case GNUNET_DNSPARSER_TYPE_A:
3084 {
3085 struct sockaddr_in *in;
3086
3087 if (sizeof(struct in_addr) != r->data_size)
3088 {
3089 GNUNET_break_op (0);
3090 break;
3091 }
3092 if (GNUNET_YES == got_ip)
3093 break;
3094 if (GNUNET_OK !=
3095 GNUNET_NETWORK_test_pf (PF_INET))
3096 break;
3097 got_ip = GNUNET_YES;
3098 in = (struct sockaddr_in *) &s5r->destination_address;
3099 in->sin_family = AF_INET;
3100 GNUNET_memcpy (&in->sin_addr,
3101 r->data,
3102 r->data_size);
3103 in->sin_port = htons (s5r->port);
3104#if HAVE_SOCKADDR_IN_SIN_LEN
3105 in->sin_len = sizeof(*in);
3106#endif
3107 }
3108 break;
3109
3110 case GNUNET_DNSPARSER_TYPE_AAAA:
3111 {
3112 struct sockaddr_in6 *in;
3113
3114 if (sizeof(struct in6_addr) != r->data_size)
3115 {
3116 GNUNET_break_op (0);
3117 break;
3118 }
3119 if (GNUNET_YES == got_ip)
3120 break;
3121 if (GNUNET_YES == disable_v6)
3122 break;
3123 if (GNUNET_OK !=
3124 GNUNET_NETWORK_test_pf (PF_INET6))
3125 break;
3126 /* FIXME: allow user to disable IPv6 per configuration option... */
3127 got_ip = GNUNET_YES;
3128 in = (struct sockaddr_in6 *) &s5r->destination_address;
3129 in->sin6_family = AF_INET6;
3130 GNUNET_memcpy (&in->sin6_addr,
3131 r->data,
3132 r->data_size);
3133 in->sin6_port = htons (s5r->port);
3134#if HAVE_SOCKADDR_IN_SIN_LEN
3135 in->sin6_len = sizeof(*in);
3136#endif
3137 }
3138 break;
3139
3140 case GNUNET_GNSRECORD_TYPE_VPN:
3141 GNUNET_break (0); /* should have been translated within GNS */
3142 break;
3143
3144 case GNUNET_GNSRECORD_TYPE_LEHO:
3145 GNUNET_free (s5r->leho);
3146 s5r->leho = GNUNET_strndup (r->data,
3147 r->data_size);
3148 break;
3149
3150 case GNUNET_GNSRECORD_TYPE_BOX:
3151 {
3152 const struct GNUNET_GNSRECORD_BoxRecord *box;
3153
3154 if (r->data_size < sizeof(struct GNUNET_GNSRECORD_BoxRecord))
3155 {
3156 GNUNET_break_op (0);
3157 break;
3158 }
3159 box = r->data;
3160 if ((ntohl (box->record_type) != GNUNET_DNSPARSER_TYPE_TLSA) ||
3161 (ntohs (box->protocol) != IPPROTO_TCP) ||
3162 (ntohs (box->service) != s5r->port))
3163 break; /* BOX record does not apply */
3164 if (s5r->num_danes >= MAX_DANES)
3165 {
3166 GNUNET_break (0); /* MAX_DANES too small */
3167 break;
3168 }
3169 s5r->is_tls = GNUNET_YES; /* This should be TLS */
3170 s5r->dane_data_len[s5r->num_danes]
3171 = r->data_size - sizeof(struct GNUNET_GNSRECORD_BoxRecord);
3172 s5r->dane_data[s5r->num_danes]
3173 = GNUNET_memdup (&box[1],
3174 s5r->dane_data_len[s5r->num_danes]);
3175 s5r->num_danes++;
3176 break;
3177 }
3178
3179 default:
3180 /* don't care */
3181 break;
3182 }
3183 }
3184 if ((GNUNET_YES != got_ip) &&
3185 (GNUNET_YES == tld))
3186 {
3187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3188 "Name resolution failed to yield useful IP address.\n");
3189 signal_socks_failure (s5r,
3190 SOCKS5_STATUS_GENERAL_FAILURE);
3191 return;
3192 }
3193 s5r->state = SOCKS5_DATA_TRANSFER;
3194 signal_socks_success (s5r);
3195}
3196
3197
3198/**
3199 * Remove the first @a len bytes from the beginning of the read buffer.
3200 *
3201 * @param s5r the handle clear the read buffer for
3202 * @param len number of bytes in read buffer to advance
3203 */
3204static void
3205clear_from_s5r_rbuf (struct Socks5Request *s5r,
3206 size_t len)
3207{
3208 GNUNET_assert (len <= s5r->rbuf_len);
3209 memmove (s5r->rbuf,
3210 &s5r->rbuf[len],
3211 s5r->rbuf_len - len);
3212 s5r->rbuf_len -= len;
3213}
3214
3215
3216/**
3217 * Read data from incoming Socks5 connection
3218 *
3219 * @param cls the closure with the `struct Socks5Request`
3220 */
3221static void
3222do_s5r_read (void *cls)
3223{
3224 struct Socks5Request *s5r = cls;
3225 const struct Socks5ClientHelloMessage *c_hello;
3226 struct Socks5ServerHelloMessage *s_hello;
3227 const struct Socks5ClientRequestMessage *c_req;
3228 ssize_t rlen;
3229 size_t alen;
3230 const struct GNUNET_SCHEDULER_TaskContext *tc;
3231
3232 s5r->rtask = NULL;
3233 tc = GNUNET_SCHEDULER_get_task_context ();
3234 if ((NULL != tc->read_ready) &&
3235 (GNUNET_NETWORK_fdset_isset (tc->read_ready,
3236 s5r->sock)))
3237 {
3238 rlen = GNUNET_NETWORK_socket_recv (s5r->sock,
3239 &s5r->rbuf[s5r->rbuf_len],
3240 sizeof(s5r->rbuf) - s5r->rbuf_len);
3241 if (rlen <= 0)
3242 {
3243 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3244 "socks5 client disconnected.\n");
3245 cleanup_s5r (s5r);
3246 return;
3247 }
3248 s5r->rbuf_len += rlen;
3249 }
3250 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3251 s5r->sock,
3252 &do_s5r_read, s5r);
3253 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3254 "Processing %zu bytes of socks data in state %d\n",
3255 s5r->rbuf_len,
3256 s5r->state);
3257 switch (s5r->state)
3258 {
3259 case SOCKS5_INIT:
3260 c_hello = (const struct Socks5ClientHelloMessage*) &s5r->rbuf;
3261 if ((s5r->rbuf_len < sizeof(struct Socks5ClientHelloMessage)) ||
3262 (s5r->rbuf_len < sizeof(struct Socks5ClientHelloMessage)
3263 + c_hello->num_auth_methods))
3264 return; /* need more data */
3265 if (SOCKS_VERSION_5 != c_hello->version)
3266 {
3267 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3268 _ ("Unsupported socks version %d\n"),
3269 (int) c_hello->version);
3270 cleanup_s5r (s5r);
3271 return;
3272 }
3273 clear_from_s5r_rbuf (s5r,
3274 sizeof(struct Socks5ClientHelloMessage)
3275 + c_hello->num_auth_methods);
3276 GNUNET_assert (0 == s5r->wbuf_len);
3277 s_hello = (struct Socks5ServerHelloMessage *) &s5r->wbuf;
3278 s5r->wbuf_len = sizeof(struct Socks5ServerHelloMessage);
3279 s_hello->version = SOCKS_VERSION_5;
3280 s_hello->auth_method = SOCKS_AUTH_NONE;
3281 GNUNET_assert (NULL == s5r->wtask);
3282 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
3283 s5r->sock,
3284 &do_write, s5r);
3285 s5r->state = SOCKS5_REQUEST;
3286 return;
3287
3288 case SOCKS5_REQUEST:
3289 c_req = (const struct Socks5ClientRequestMessage *) &s5r->rbuf;
3290 if (s5r->rbuf_len < sizeof(struct Socks5ClientRequestMessage))
3291 return;
3292 switch (c_req->command)
3293 {
3294 case SOCKS5_CMD_TCP_STREAM:
3295 /* handled below */
3296 break;
3297
3298 default:
3299 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3300 _ ("Unsupported socks command %d\n"),
3301 (int) c_req->command);
3302 signal_socks_failure (s5r,
3303 SOCKS5_STATUS_COMMAND_NOT_SUPPORTED);
3304 return;
3305 }
3306 switch (c_req->addr_type)
3307 {
3308 case SOCKS5_AT_IPV4:
3309 {
3310 const struct in_addr *v4 = (const struct in_addr *) &c_req[1];
3311 const uint16_t *port = (const uint16_t *) &v4[1];
3312 struct sockaddr_in *in;
3313
3314 s5r->port = ntohs (*port);
3315 alen = sizeof(struct in_addr);
3316 if (s5r->rbuf_len < sizeof(struct Socks5ClientRequestMessage)
3317 + alen + sizeof(uint16_t))
3318 return; /* need more data */
3319 in = (struct sockaddr_in *) &s5r->destination_address;
3320 in->sin_family = AF_INET;
3321 in->sin_addr = *v4;
3322 in->sin_port = *port;
3323#if HAVE_SOCKADDR_IN_SIN_LEN
3324 in->sin_len = sizeof(*in);
3325#endif
3326 s5r->state = SOCKS5_DATA_TRANSFER;
3327 }
3328 break;
3329
3330 case SOCKS5_AT_IPV6:
3331 {
3332 const struct in6_addr *v6 = (const struct in6_addr *) &c_req[1];
3333 const uint16_t *port = (const uint16_t *) &v6[1];
3334 struct sockaddr_in6 *in;
3335
3336 s5r->port = ntohs (*port);
3337 alen = sizeof(struct in6_addr);
3338 if (s5r->rbuf_len < sizeof(struct Socks5ClientRequestMessage)
3339 + alen + sizeof(uint16_t))
3340 return; /* need more data */
3341 in = (struct sockaddr_in6 *) &s5r->destination_address;
3342 in->sin6_family = AF_INET6;
3343 in->sin6_addr = *v6;
3344 in->sin6_port = *port;
3345#if HAVE_SOCKADDR_IN_SIN_LEN
3346 in->sin6_len = sizeof(*in);
3347#endif
3348 s5r->state = SOCKS5_DATA_TRANSFER;
3349 }
3350 break;
3351
3352 case SOCKS5_AT_DOMAINNAME:
3353 {
3354 const uint8_t *dom_len;
3355 const char *dom_name;
3356 const uint16_t *port;
3357
3358 dom_len = (const uint8_t *) &c_req[1];
3359 alen = *dom_len + 1;
3360 if (s5r->rbuf_len < sizeof(struct Socks5ClientRequestMessage)
3361 + alen + sizeof(uint16_t))
3362 return; /* need more data */
3363 dom_name = (const char *) &dom_len[1];
3364 port = (const uint16_t *) &dom_name[*dom_len];
3365 s5r->domain = GNUNET_strndup (dom_name,
3366 *dom_len);
3367 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3368 "Requested connection is to %s:%d\n",
3369 // (HTTPS_PORT == s5r->port) ? "s" : "",
3370 s5r->domain,
3371 ntohs (*port));
3372 s5r->state = SOCKS5_RESOLVING;
3373 s5r->port = ntohs (*port);
3374 s5r->is_tls = (HTTPS_PORT == s5r->port) ? GNUNET_YES : GNUNET_NO;
3375 s5r->gns_lookup = GNUNET_GNS_lookup_with_tld (gns_handle,
3376 s5r->domain,
3377 GNUNET_DNSPARSER_TYPE_A,
3378 GNUNET_GNS_LO_LOCAL_MASTER /* only cached */,
3379 &handle_gns_result,
3380 s5r);
3381 break;
3382 }
3383
3384 default:
3385 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3386 _ ("Unsupported socks address type %d\n"),
3387 (int) c_req->addr_type);
3388 signal_socks_failure (s5r,
3389 SOCKS5_STATUS_ADDRESS_TYPE_NOT_SUPPORTED);
3390 return;
3391 }
3392 clear_from_s5r_rbuf (s5r,
3393 sizeof(struct Socks5ClientRequestMessage)
3394 + alen + sizeof(uint16_t));
3395 if (0 != s5r->rbuf_len)
3396 {
3397 /* read more bytes than healthy, why did the client send more!? */
3398 GNUNET_break_op (0);
3399 signal_socks_failure (s5r,
3400 SOCKS5_STATUS_GENERAL_FAILURE);
3401 return;
3402 }
3403 if (SOCKS5_DATA_TRANSFER == s5r->state)
3404 {
3405 /* if we are not waiting for GNS resolution, signal success */
3406 signal_socks_success (s5r);
3407 }
3408 /* We are done reading right now */
3409 GNUNET_SCHEDULER_cancel (s5r->rtask);
3410 s5r->rtask = NULL;
3411 return;
3412
3413 case SOCKS5_RESOLVING:
3414 GNUNET_assert (0);
3415 return;
3416
3417 case SOCKS5_DATA_TRANSFER:
3418 GNUNET_assert (0);
3419 return;
3420
3421 default:
3422 GNUNET_assert (0);
3423 return;
3424 }
3425}
3426
3427
3428/**
3429 * Accept new incoming connections
3430 *
3431 * @param cls the closure with the lsock4 or lsock6
3432 */
3433static void
3434do_accept (void *cls)
3435{
3436 struct GNUNET_NETWORK_Handle *lsock = cls;
3437 struct GNUNET_NETWORK_Handle *s;
3438 struct Socks5Request *s5r;
3439
3440 GNUNET_assert (NULL != lsock);
3441 if (lsock == lsock4)
3442 ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3443 lsock,
3444 &do_accept,
3445 lsock);
3446 else if (lsock == lsock6)
3447 ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3448 lsock,
3449 &do_accept,
3450 lsock);
3451 else
3452 GNUNET_assert (0);
3453 s = GNUNET_NETWORK_socket_accept (lsock,
3454 NULL,
3455 NULL);
3456 if (NULL == s)
3457 {
3458 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
3459 "accept");
3460 return;
3461 }
3462 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3463 "Got an inbound connection, waiting for data\n");
3464 s5r = GNUNET_new (struct Socks5Request);
3465 GNUNET_CONTAINER_DLL_insert (s5r_head,
3466 s5r_tail,
3467 s5r);
3468 s5r->sock = s;
3469 s5r->state = SOCKS5_INIT;
3470 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3471 s5r->sock,
3472 &do_s5r_read,
3473 s5r);
3474}
3475
3476
3477/* ******************* General / main code ********************* */
3478
3479
3480/**
3481 * Task run on shutdown
3482 *
3483 * @param cls closure
3484 */
3485static void
3486do_shutdown (void *cls)
3487{
3488 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3489 "Shutting down...\n");
3490 /* MHD requires resuming before destroying the daemons */
3491 for (struct Socks5Request *s5r = s5r_head;
3492 NULL != s5r;
3493 s5r = s5r->next)
3494 {
3495 if (s5r->suspended)
3496 {
3497 s5r->suspended = GNUNET_NO;
3498 MHD_resume_connection (s5r->con);
3499 }
3500 }
3501 while (NULL != mhd_httpd_head)
3502 kill_httpd (mhd_httpd_head);
3503 while (NULL != s5r_head)
3504 cleanup_s5r (s5r_head);
3505 if (NULL != lsock4)
3506 {
3507 GNUNET_NETWORK_socket_close (lsock4);
3508 lsock4 = NULL;
3509 }
3510 if (NULL != lsock6)
3511 {
3512 GNUNET_NETWORK_socket_close (lsock6);
3513 lsock6 = NULL;
3514 }
3515 if (NULL != curl_multi)
3516 {
3517 curl_multi_cleanup (curl_multi);
3518 curl_multi = NULL;
3519 }
3520 if (NULL != gns_handle)
3521 {
3522 GNUNET_GNS_disconnect (gns_handle);
3523 gns_handle = NULL;
3524 }
3525 if (NULL != curl_download_task)
3526 {
3527 GNUNET_SCHEDULER_cancel (curl_download_task);
3528 curl_download_task = NULL;
3529 }
3530 if (NULL != ltask4)
3531 {
3532 GNUNET_SCHEDULER_cancel (ltask4);
3533 ltask4 = NULL;
3534 }
3535 if (NULL != ltask6)
3536 {
3537 GNUNET_SCHEDULER_cancel (ltask6);
3538 ltask6 = NULL;
3539 }
3540 gnutls_x509_crt_deinit (proxy_ca.cert);
3541 gnutls_x509_privkey_deinit (proxy_ca.key);
3542 gnutls_global_deinit ();
3543}
3544
3545
3546/**
3547 * Create an IPv4 listen socket bound to our port.
3548 *
3549 * @return NULL on error
3550 */
3551static struct GNUNET_NETWORK_Handle *
3552bind_v4 ()
3553{
3554 struct GNUNET_NETWORK_Handle *ls;
3555 struct sockaddr_in sa4;
3556 int eno;
3557
3558 memset (&sa4, 0, sizeof(sa4));
3559 sa4.sin_family = AF_INET;
3560 sa4.sin_port = htons (port);
3561 sa4.sin_addr.s_addr = address;
3562#if HAVE_SOCKADDR_IN_SIN_LEN
3563 sa4.sin_len = sizeof(sa4);
3564#endif
3565 ls = GNUNET_NETWORK_socket_create (AF_INET,
3566 SOCK_STREAM,
3567 0);
3568 if (NULL == ls)
3569 return NULL;
3570 if (GNUNET_OK !=
3571 GNUNET_NETWORK_socket_bind (ls,
3572 (const struct sockaddr *) &sa4,
3573 sizeof(sa4)))
3574 {
3575 eno = errno;
3576 GNUNET_NETWORK_socket_close (ls);
3577 errno = eno;
3578 return NULL;
3579 }
3580 return ls;
3581}
3582
3583
3584/**
3585 * Create an IPv6 listen socket bound to our port.
3586 *
3587 * @return NULL on error
3588 */
3589static struct GNUNET_NETWORK_Handle *
3590bind_v6 ()
3591{
3592 struct GNUNET_NETWORK_Handle *ls;
3593 struct sockaddr_in6 sa6;
3594 int eno;
3595
3596 memset (&sa6, 0, sizeof(sa6));
3597 sa6.sin6_family = AF_INET6;
3598 sa6.sin6_port = htons (port);
3599 sa6.sin6_addr = address6;
3600#if HAVE_SOCKADDR_IN_SIN_LEN
3601 sa6.sin6_len = sizeof(sa6);
3602#endif
3603 ls = GNUNET_NETWORK_socket_create (AF_INET6,
3604 SOCK_STREAM,
3605 0);
3606 if (NULL == ls)
3607 return NULL;
3608 if (GNUNET_OK !=
3609 GNUNET_NETWORK_socket_bind (ls,
3610 (const struct sockaddr *) &sa6,
3611 sizeof(sa6)))
3612 {
3613 eno = errno;
3614 GNUNET_NETWORK_socket_close (ls);
3615 errno = eno;
3616 return NULL;
3617 }
3618 return ls;
3619}
3620
3621
3622/**
3623 * Main function that will be run
3624 *
3625 * @param cls closure
3626 * @param args remaining command-line arguments
3627 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3628 * @param c configuration
3629 */
3630static void
3631run (void *cls,
3632 char *const *args,
3633 const char *cfgfile,
3634 const struct GNUNET_CONFIGURATION_Handle *c)
3635{
3636 char*cafile_cfg = NULL;
3637 char*cafile;
3638 char*addr_str;
3639 struct MhdHttpList *hd;
3640
3641 cfg = c;
3642
3643 /* Get address to bind to */
3644 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "gns-proxy",
3645 "BIND_TO",
3646 &addr_str))
3647 {
3648 // No address specified
3649 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3650 "Don't know what to bind to...\n");
3651 GNUNET_free (addr_str);
3652 GNUNET_SCHEDULER_shutdown ();
3653 return;
3654 }
3655 if (1 != inet_pton (AF_INET, addr_str, &address))
3656 {
3657 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3658 "Unable to parse address %s\n",
3659 addr_str);
3660 GNUNET_free (addr_str);
3661 GNUNET_SCHEDULER_shutdown ();
3662 return;
3663 }
3664 GNUNET_free (addr_str);
3665 /* Get address to bind to */
3666 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "gns-proxy",
3667 "BIND_TO6",
3668 &addr_str))
3669 {
3670 // No address specified
3671 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3672 "Don't know what to bind6 to...\n");
3673 GNUNET_free (addr_str);
3674 GNUNET_SCHEDULER_shutdown ();
3675 return;
3676 }
3677 if (1 != inet_pton (AF_INET6, addr_str, &address6))
3678 {
3679 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3680 "Unable to parse IPv6 address %s\n",
3681 addr_str);
3682 GNUNET_free (addr_str);
3683 GNUNET_SCHEDULER_shutdown ();
3684 return;
3685 }
3686 GNUNET_free (addr_str);
3687
3688 if (NULL == (curl_multi = curl_multi_init ()))
3689 {
3690 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3691 "Failed to create cURL multi handle!\n");
3692 return;
3693 }
3694 cafile = cafile_opt;
3695 if (NULL == cafile)
3696 {
3697 if (GNUNET_OK !=
3698 GNUNET_CONFIGURATION_get_value_filename (cfg,
3699 "gns-proxy",
3700 "PROXY_CACERT",
3701 &cafile_cfg))
3702 {
3703 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
3704 "gns-proxy",
3705 "PROXY_CACERT");
3706 return;
3707 }
3708 cafile = cafile_cfg;
3709 }
3710 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3711 "Using `%s' as CA\n",
3712 cafile);
3713
3714 gnutls_global_init ();
3715 gnutls_x509_crt_init (&proxy_ca.cert);
3716 gnutls_x509_privkey_init (&proxy_ca.key);
3717
3718 if ((GNUNET_OK !=
3719 load_cert_from_file (proxy_ca.cert,
3720 cafile)) ||
3721 (GNUNET_OK !=
3722 load_key_from_file (proxy_ca.key,
3723 cafile)))
3724 {
3725 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3726 _ ("Failed to load X.509 key and certificate from `%s'\n"),
3727 cafile);
3728 gnutls_x509_crt_deinit (proxy_ca.cert);
3729 gnutls_x509_privkey_deinit (proxy_ca.key);
3730 gnutls_global_deinit ();
3731 GNUNET_free (cafile_cfg);
3732 return;
3733 }
3734 GNUNET_free (cafile_cfg);
3735 if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
3736 {
3737 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3738 "Unable to connect to GNS!\n");
3739 gnutls_x509_crt_deinit (proxy_ca.cert);
3740 gnutls_x509_privkey_deinit (proxy_ca.key);
3741 gnutls_global_deinit ();
3742 return;
3743 }
3744 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
3745 NULL);
3746
3747 /* Open listen socket for socks proxy */
3748 lsock6 = bind_v6 ();
3749 if (NULL == lsock6)
3750 {
3751 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
3752 "bind");
3753 }
3754 else
3755 {
3756 if (GNUNET_OK !=
3757 GNUNET_NETWORK_socket_listen (lsock6,
3758 5))
3759 {
3760 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
3761 "listen");
3762 GNUNET_NETWORK_socket_close (lsock6);
3763 lsock6 = NULL;
3764 }
3765 else
3766 {
3767 ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3768 lsock6,
3769 &do_accept,
3770 lsock6);
3771 }
3772 }
3773 lsock4 = bind_v4 ();
3774 if (NULL == lsock4)
3775 {
3776 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
3777 "bind");
3778 }
3779 else
3780 {
3781 if (GNUNET_OK !=
3782 GNUNET_NETWORK_socket_listen (lsock4,
3783 5))
3784 {
3785 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
3786 "listen");
3787 GNUNET_NETWORK_socket_close (lsock4);
3788 lsock4 = NULL;
3789 }
3790 else
3791 {
3792 ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3793 lsock4,
3794 &do_accept,
3795 lsock4);
3796 }
3797 }
3798 if ((NULL == lsock4) &&
3799 (NULL == lsock6))
3800 {
3801 GNUNET_SCHEDULER_shutdown ();
3802 return;
3803 }
3804 if (CURLSSLSET_OK != curl_global_sslset (CURLSSLBACKEND_GNUTLS,
3805 NULL,
3806 NULL))
3807 {
3808 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3809 "cURL does not support the GnuTLS backend\n");
3810
3811 }
3812 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3813 {
3814 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3815 "cURL global init failed!\n");
3816 GNUNET_SCHEDULER_shutdown ();
3817 return;
3818 }
3819 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3820 "Proxy listens on port %u\n",
3821 (unsigned int) port);
3822
3823 /* start MHD daemon for HTTP */
3824 hd = GNUNET_new (struct MhdHttpList);
3825 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET
3826 | MHD_ALLOW_SUSPEND_RESUME,
3827 0,
3828 NULL, NULL,
3829 &create_response, hd,
3830 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned
3831 int) 16,
3832 MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb,
3833 NULL,
3834 MHD_OPTION_NOTIFY_CONNECTION,
3835 &mhd_connection_cb, NULL,
3836 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
3837 NULL,
3838 MHD_OPTION_END);
3839 if (NULL == hd->daemon)
3840 {
3841 GNUNET_free (hd);
3842 GNUNET_SCHEDULER_shutdown ();
3843 return;
3844 }
3845 httpd = hd;
3846 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head,
3847 mhd_httpd_tail,
3848 hd);
3849}
3850
3851
3852/**
3853 * The main function for gnunet-gns-proxy.
3854 *
3855 * @param argc number of arguments from the command line
3856 * @param argv command line arguments
3857 * @return 0 ok, 1 on error
3858 */
3859int
3860main (int argc,
3861 char *const *argv)
3862{
3863 struct GNUNET_GETOPT_CommandLineOption options[] = {
3864 GNUNET_GETOPT_option_uint16 ('p',
3865 "port",
3866 NULL,
3867 gettext_noop (
3868 "listen on specified port (default: 7777)"),
3869 &port),
3870 GNUNET_GETOPT_option_string ('a',
3871 "authority",
3872 NULL,
3873 gettext_noop ("pem file to use as CA"),
3874 &cafile_opt),
3875 GNUNET_GETOPT_option_flag ('6',
3876 "disable-ivp6",
3877 gettext_noop ("disable use of IPv6"),
3878 &disable_v6),
3879
3880 GNUNET_GETOPT_OPTION_END
3881 };
3882 static const char*page =
3883 "<html><head><title>gnunet-gns-proxy</title>"
3884 "</head><body>cURL fail</body></html>";
3885 int ret;
3886
3887 if (GNUNET_OK !=
3888 GNUNET_STRINGS_get_utf8_args (argc, argv,
3889 &argc, &argv))
3890 return 2;
3891 GNUNET_log_setup ("gnunet-gns-proxy",
3892 "WARNING",
3893 NULL);
3894 curl_failure_response
3895 = MHD_create_response_from_buffer (strlen (page),
3896 (void *) page,
3897 MHD_RESPMEM_PERSISTENT);
3898
3899 ret =
3900 (GNUNET_OK ==
3901 GNUNET_PROGRAM_run (argc, argv,
3902 "gnunet-gns-proxy",
3903 _ ("GNUnet GNS proxy"),
3904 options,
3905 &run, NULL)) ? 0 : 1;
3906 MHD_destroy_response (curl_failure_response);
3907 GNUNET_free_nz ((char *) argv);
3908 return ret;
3909}
3910
3911
3912/* end of gnunet-gns-proxy.c */
diff --git a/src/service/gns/gnunet-service-gns.c b/src/service/gns/gnunet-service-gns.c
new file mode 100644
index 000000000..aaf82a557
--- /dev/null
+++ b/src/service/gns/gnunet-service-gns.c
@@ -0,0 +1,618 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011-2018 GNUnet e.V.
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 gns/gnunet-service-gns.c
22 * @brief GNU Name System (main service)
23 * @author Martin Schanzenbach
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_dns_service.h"
29#include "gnunet_dht_service.h"
30#include "gnunet_namecache_service.h"
31#include "gnunet_gnsrecord_lib.h"
32#include "gnunet_gns_service.h"
33#include "gnunet_statistics_service.h"
34#include "gns.h"
35#include "gnunet-service-gns_resolver.h"
36#include "gnunet-service-gns_interceptor.h"
37#include "gnunet_protocols.h"
38
39
40/**
41 * GnsClient prototype
42 */
43struct GnsClient;
44
45/**
46 * Handle to a lookup operation from client via API.
47 */
48struct ClientLookupHandle
49{
50 /**
51 * We keep these in a DLL.
52 */
53 struct ClientLookupHandle *next;
54
55 /**
56 * We keep these in a DLL.
57 */
58 struct ClientLookupHandle *prev;
59
60 /**
61 * Client handle
62 */
63 struct GnsClient *gc;
64
65 /**
66 * Active handle for the lookup.
67 */
68 struct GNS_ResolverHandle *lookup;
69
70 /**
71 * request id
72 */
73 uint32_t request_id;
74};
75
76
77/**
78 * Information we track per connected client.
79 */
80struct GnsClient
81{
82 /**
83 * The client
84 */
85 struct GNUNET_SERVICE_Client *client;
86
87 /**
88 * The MQ
89 */
90 struct GNUNET_MQ_Handle *mq;
91
92 /**
93 * Head of the DLL.
94 */
95 struct ClientLookupHandle *clh_head;
96
97 /**
98 * Tail of the DLL.
99 */
100 struct ClientLookupHandle *clh_tail;
101};
102
103
104/**
105 * Representation of a TLD, mapping the respective TLD string
106 * (e.g. ".gnu") to the respective public key of the zone.
107 */
108struct GNS_TopLevelDomain
109{
110 /**
111 * Kept in a DLL, as there are unlikely enough of these to
112 * warrant a hash map.
113 */
114 struct GNS_TopLevelDomain *next;
115
116 /**
117 * Kept in a DLL, as there are unlikely enough of these to
118 * warrant a hash map.
119 */
120 struct GNS_TopLevelDomain *prev;
121
122 /**
123 * Public key associated with the @a tld.
124 */
125 struct GNUNET_CRYPTO_PublicKey pkey;
126
127 /**
128 * Top-level domain as a string, including leading ".".
129 */
130 char *tld;
131};
132
133
134/**
135 * Our handle to the DHT
136 */
137static struct GNUNET_DHT_Handle *dht_handle;
138
139/**
140 * Our handle to the namecache service
141 */
142static struct GNUNET_NAMECACHE_Handle *namecache_handle;
143
144/**
145 * #GNUNET_YES if ipv6 is supported
146 */
147static int v6_enabled;
148
149/**
150 * #GNUNET_YES if ipv4 is supported
151 */
152static int v4_enabled;
153
154/**
155 * Handle to the statistics service
156 */
157static struct GNUNET_STATISTICS_Handle *statistics;
158
159/**
160 * Head of DLL of TLDs we map to GNS zones.
161 */
162static struct GNS_TopLevelDomain *tld_head;
163
164/**
165 * Tail of DLL of TLDs we map to GNS zones.
166 */
167static struct GNS_TopLevelDomain *tld_tail;
168
169
170/**
171 * Find GNS zone belonging to TLD @a tld.
172 *
173 * @param tld_str top-level domain to look up
174 * @param[out] pkey public key to set
175 * @return #GNUNET_YES if @a tld was found #GNUNET_NO if not
176 */
177int
178GNS_find_tld (const char *tld_str,
179 struct GNUNET_CRYPTO_PublicKey *pkey)
180{
181 if ('\0' == *tld_str)
182 return GNUNET_NO;
183 for (struct GNS_TopLevelDomain *tld = tld_head;
184 NULL != tld;
185 tld = tld->next)
186 {
187 if (0 == strcasecmp (tld_str,
188 tld->tld))
189 {
190 *pkey = tld->pkey;
191 return GNUNET_YES;
192 }
193 }
194 if (GNUNET_OK ==
195 GNUNET_GNSRECORD_zkey_to_pkey (tld_str + 1,
196 pkey))
197 return GNUNET_YES; /* TLD string *was* the public key */
198 return GNUNET_NO;
199}
200
201
202/**
203 * Obtain the TLD of the given @a name.
204 *
205 * @param name a name
206 * @return the part of @a name after the last ".",
207 * or @a name if @a name does not contain a "."
208 */
209const char *
210GNS_get_tld (const char *name)
211{
212 const char *tld;
213
214 tld = strrchr (name,
215 (unsigned char) '.');
216 if (NULL == tld)
217 tld = name;
218 else
219 tld++; /* skip the '.' */
220 return tld;
221}
222
223
224/**
225 * Task run during shutdown.
226 *
227 * @param cls unused, NULL
228 */
229static void
230shutdown_task (void *cls)
231{
232 struct GNS_TopLevelDomain *tld;
233
234 (void) cls;
235 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
236 "Shutting down!\n");
237 GNS_interceptor_done ();
238 GNS_resolver_done ();
239 if (NULL != statistics)
240 {
241 GNUNET_STATISTICS_destroy (statistics,
242 GNUNET_NO);
243 statistics = NULL;
244 }
245 if (NULL != namecache_handle)
246 {
247 GNUNET_NAMECACHE_disconnect (namecache_handle);
248 namecache_handle = NULL;
249 }
250 if (NULL != dht_handle)
251 {
252 GNUNET_DHT_disconnect (dht_handle);
253 dht_handle = NULL;
254 }
255 while (NULL != (tld = tld_head))
256 {
257 GNUNET_CONTAINER_DLL_remove (tld_head,
258 tld_tail,
259 tld);
260 GNUNET_free (tld->tld);
261 GNUNET_free (tld);
262 }
263}
264
265
266/**
267 * Called whenever a client is disconnected.
268 *
269 * @param cls closure
270 * @param client identification of the client
271 * @param app_ctx @a client
272 */
273static void
274client_disconnect_cb (void *cls,
275 struct GNUNET_SERVICE_Client *client,
276 void *app_ctx)
277{
278 struct ClientLookupHandle *clh;
279 struct GnsClient *gc = app_ctx;
280
281 (void) cls;
282 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
283 "Client %p disconnected\n",
284 client);
285 while (NULL != (clh = gc->clh_head))
286 {
287 if (NULL != clh->lookup)
288 GNS_resolver_lookup_cancel (clh->lookup);
289 GNUNET_CONTAINER_DLL_remove (gc->clh_head,
290 gc->clh_tail,
291 clh);
292 GNUNET_free (clh);
293 }
294 GNUNET_free (gc);
295}
296
297
298/**
299 * Add a client to our list of active clients.
300 *
301 * @param cls NULL
302 * @param client client to add
303 * @param mq message queue for @a client
304 * @return internal namestore client structure for this client
305 */
306static void *
307client_connect_cb (void *cls,
308 struct GNUNET_SERVICE_Client *client,
309 struct GNUNET_MQ_Handle *mq)
310{
311 struct GnsClient *gc;
312
313 (void) cls;
314 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
315 "Client %p connected\n",
316 client);
317 gc = GNUNET_new (struct GnsClient);
318 gc->client = client;
319 gc->mq = mq;
320 return gc;
321}
322
323
324/**
325 * Reply to client with the result from our lookup.
326 *
327 * @param cls the closure (our client lookup handle)
328 * @param rd_count the number of records in @a rd
329 * @param rd the record data
330 */
331static void
332send_lookup_response (void *cls,
333 uint32_t rd_count,
334 const struct GNUNET_GNSRECORD_Data *rd)
335{
336 struct ClientLookupHandle *clh = cls;
337 struct GnsClient *gc = clh->gc;
338 struct GNUNET_MQ_Envelope *env;
339 struct LookupResultMessage *rmsg;
340 ssize_t len;
341
342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
343 "Sending LOOKUP_RESULT message with %u results\n",
344 (unsigned int) rd_count);
345 len = GNUNET_GNSRECORD_records_get_size (rd_count,
346 rd);
347 if (len < 0)
348 {
349 GNUNET_break (0);
350 GNUNET_SERVICE_client_drop (gc->client);
351 return;
352 }
353 if (len > UINT16_MAX - sizeof(*rmsg))
354 {
355 GNUNET_break (0);
356 GNUNET_SERVICE_client_drop (gc->client);
357 return;
358 }
359 env = GNUNET_MQ_msg_extra (rmsg,
360 len,
361 GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
362 rmsg->id = clh->request_id;
363 rmsg->rd_count = htonl (rd_count);
364 GNUNET_assert (len ==
365 GNUNET_GNSRECORD_records_serialize (rd_count,
366 rd,
367 len,
368 (char *) &rmsg[1]));
369 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (gc->client),
370 env);
371 GNUNET_CONTAINER_DLL_remove (gc->clh_head,
372 gc->clh_tail,
373 clh);
374 GNUNET_free (clh);
375 GNUNET_STATISTICS_update (statistics,
376 "Completed lookups", 1,
377 GNUNET_NO);
378 GNUNET_STATISTICS_update (statistics,
379 "Records resolved",
380 rd_count,
381 GNUNET_NO);
382}
383
384
385/**
386 * Checks a #GNUNET_MESSAGE_TYPE_GNS_LOOKUP message
387 *
388 * @param cls client sending the message
389 * @param l_msg message of type `struct LookupMessage`
390 * @return #GNUNET_OK if @a l_msg is well-formed
391 */
392static int
393check_lookup (void *cls,
394 const struct LookupMessage *l_msg)
395{
396 size_t nlen;
397 size_t klen;
398
399 (void) cls;
400 klen = ntohl (l_msg->key_len);
401 nlen = ntohs (l_msg->header.size) - sizeof(struct LookupMessage) - klen;
402 if (nlen > GNUNET_DNSPARSER_MAX_NAME_LENGTH)
403 {
404 GNUNET_break (0);
405 return GNUNET_SYSERR;
406 }
407 return GNUNET_OK;
408}
409
410
411/**
412 * Handle lookup requests from client
413 *
414 * @param cls the closure
415 * @param sh_msg the message
416 */
417static void
418handle_lookup (void *cls,
419 const struct LookupMessage *sh_msg)
420{
421 struct GnsClient *gc = cls;
422 struct ClientLookupHandle *clh;
423 struct GNUNET_CRYPTO_PublicKey zone;
424 const char *name;
425 size_t key_len;
426 size_t read;
427
428 GNUNET_SERVICE_client_continue (gc->client);
429 key_len = ntohl (sh_msg->key_len);
430 clh = GNUNET_new (struct ClientLookupHandle);
431 GNUNET_CONTAINER_DLL_insert (gc->clh_head,
432 gc->clh_tail,
433 clh);
434 clh->gc = gc;
435 clh->request_id = sh_msg->id;
436 if ((GNUNET_SYSERR ==
437 GNUNET_CRYPTO_read_public_key_from_buffer (&sh_msg[1],
438 key_len,
439 &zone,
440 &read)) ||
441 (read != key_len))
442 {
443 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
444 "LOOKUP: Failed to read zone key!");
445 send_lookup_response (clh,
446 0,
447 NULL);
448 return;
449 }
450 name = (const char *) &sh_msg[1] + key_len;
451 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
452 "Received LOOKUP `%s' message\n",
453 name);
454 if ((GNUNET_DNSPARSER_TYPE_A == ntohl (sh_msg->type)) &&
455 (GNUNET_OK != v4_enabled))
456 {
457 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
458 "LOOKUP: Query for A record but AF_INET not supported!");
459 send_lookup_response (clh,
460 0,
461 NULL);
462 return;
463 }
464 if ((GNUNET_DNSPARSER_TYPE_AAAA == ntohl (sh_msg->type)) &&
465 (GNUNET_OK != v6_enabled))
466 {
467 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
468 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
469 send_lookup_response (clh,
470 0,
471 NULL);
472 return;
473 }
474 clh->lookup = GNS_resolver_lookup (&zone,
475 ntohl (sh_msg->type),
476 name,
477 (enum GNUNET_GNS_LocalOptions) ntohs (
478 sh_msg->options),
479 ntohs (sh_msg->recursion_depth_limit),
480 &send_lookup_response, clh);
481 GNUNET_STATISTICS_update (statistics,
482 "Lookup attempts",
483 1, GNUNET_NO);
484}
485
486
487/**
488 * Reads the configuration and populates TLDs
489 *
490 * @param cls unused
491 * @param section name of section in config, always "gns"
492 * @param option name of the option, TLDs start with "."
493 * @param value value for the option, public key for TLDs
494 */
495static void
496read_service_conf (void *cls,
497 const char *section,
498 const char *option,
499 const char *value)
500{
501 struct GNUNET_CRYPTO_PublicKey pk;
502 struct GNS_TopLevelDomain *tld;
503
504 (void) cls;
505 (void) section;
506 if (option[0] != '.')
507 return;
508 if (GNUNET_OK !=
509 GNUNET_STRINGS_string_to_data (value,
510 strlen (value),
511 &pk,
512 sizeof(pk)))
513 {
514 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
515 section,
516 option,
517 _ (
518 "Properly base32-encoded public key required"));
519 return;
520 }
521 tld = GNUNET_new (struct GNS_TopLevelDomain);
522 tld->tld = GNUNET_strdup (&option[1]);
523 tld->pkey = pk;
524 GNUNET_CONTAINER_DLL_insert (tld_head,
525 tld_tail,
526 tld);
527}
528
529
530/**
531 * Process GNS requests.
532 *
533 * @param cls closure
534 * @param server the initialized server
535 * @param c configuration to use
536 */
537static void
538run (void *cls,
539 const struct GNUNET_CONFIGURATION_Handle *c,
540 struct GNUNET_SERVICE_Handle *service)
541{
542 unsigned long long max_parallel_bg_queries = 16;
543
544 GNUNET_CONFIGURATION_iterate_section_values (c,
545 "gns",
546 &read_service_conf,
547 NULL);
548 v6_enabled = GNUNET_NETWORK_test_pf (PF_INET6);
549 v4_enabled = GNUNET_NETWORK_test_pf (PF_INET);
550 namecache_handle = GNUNET_NAMECACHE_connect (c);
551 if (NULL == namecache_handle)
552 {
553 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
554 _ ("Failed to connect to the namecache!\n"));
555 GNUNET_SCHEDULER_shutdown ();
556 return;
557 }
558 if (GNUNET_OK ==
559 GNUNET_CONFIGURATION_get_value_number (c,
560 "gns",
561 "MAX_PARALLEL_BACKGROUND_QUERIES",
562 &max_parallel_bg_queries))
563 {
564 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
565 "Number of allowed parallel background queries: %llu\n",
566 max_parallel_bg_queries);
567 }
568 dht_handle = GNUNET_DHT_connect (c,
569 (unsigned int) max_parallel_bg_queries);
570 if (NULL == dht_handle)
571 {
572 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
573 _ ("Could not connect to DHT!\n"));
574 GNUNET_SCHEDULER_add_now (&shutdown_task,
575 NULL);
576 return;
577 }
578 GNS_resolver_init (namecache_handle,
579 dht_handle,
580 c,
581 max_parallel_bg_queries);
582 if ((GNUNET_YES ==
583 GNUNET_CONFIGURATION_get_value_yesno (c,
584 "gns",
585 "INTERCEPT_DNS")) &&
586 (GNUNET_SYSERR ==
587 GNS_interceptor_init (c)))
588 {
589 GNUNET_break (0);
590 GNUNET_SCHEDULER_add_now (&shutdown_task,
591 NULL);
592 return;
593 }
594 statistics = GNUNET_STATISTICS_create ("gns",
595 c);
596 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
597 NULL);
598}
599
600
601/**
602 * Define "main" method using service macro.
603 */
604GNUNET_SERVICE_MAIN
605 ("gns",
606 GNUNET_SERVICE_OPTION_NONE,
607 &run,
608 &client_connect_cb,
609 &client_disconnect_cb,
610 NULL,
611 GNUNET_MQ_hd_var_size (lookup,
612 GNUNET_MESSAGE_TYPE_GNS_LOOKUP,
613 struct LookupMessage,
614 NULL),
615 GNUNET_MQ_handler_end ());
616
617
618/* end of gnunet-service-gns.c */
diff --git a/src/service/gns/gnunet-service-gns.h b/src/service/gns/gnunet-service-gns.h
new file mode 100644
index 000000000..13e28349c
--- /dev/null
+++ b/src/service/gns/gnunet-service-gns.h
@@ -0,0 +1,54 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2018 GNUnet e.V.
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 gns/gnunet-service-gns.h
22 * @brief GNU Name System (main service)
23 * @author Martin Schanzenbach
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_GNS_H
27#define GNUNET_SERVICE_GNS_H
28
29#include "gnunet_identity_service.h"
30
31/**
32 * Find GNS zone belonging to TLD @a tld.
33 *
34 * @param tld_str top-level domain to look up
35 * @param[out] pkey public key to set
36 * @return #GNUNET_YES if @a tld was found #GNUNET_NO if not
37 */
38int
39GNS_find_tld (const char *tld_str,
40 struct GNUNET_CRYPTO_PublicKey *pkey);
41
42
43/**
44 * Obtain the TLD of the given @a name.
45 *
46 * @param name a name
47 * @return the part of @a name after the last ".",
48 * or @a name if @a name does not contain a "."
49 */
50const char *
51GNS_get_tld (const char *name);
52
53
54#endif
diff --git a/src/service/gns/gnunet-service-gns_interceptor.c b/src/service/gns/gnunet-service-gns_interceptor.c
new file mode 100644
index 000000000..ecd1b5475
--- /dev/null
+++ b/src/service/gns/gnunet-service-gns_interceptor.c
@@ -0,0 +1,430 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013 GNUnet e.V.
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 gns/gnunet-service-gns_interceptor.c
22 * @brief GNUnet GNS interceptor logic
23 * @author Martin Schanzenbach
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_dns_service.h"
29#include "gnunet-service-gns.h"
30#include "gnunet-service-gns_resolver.h"
31#include "gnunet-service-gns_interceptor.h"
32#include "gns.h"
33
34
35/**
36 * How deep do we allow recursions to go before we abort?
37 */
38#define MAX_RECURSION 256
39
40
41/**
42 * Handle to a DNS intercepted
43 * reslution request
44 */
45struct InterceptLookupHandle
46{
47 /**
48 * We keep these in a DLL.
49 */
50 struct InterceptLookupHandle *next;
51
52 /**
53 * We keep these in a DLL.
54 */
55 struct InterceptLookupHandle *prev;
56
57 /**
58 * the request handle to reply to
59 */
60 struct GNUNET_DNS_RequestHandle *request_handle;
61
62 /**
63 * the dns parser packet received
64 */
65 struct GNUNET_DNSPARSER_Packet *packet;
66
67 /**
68 * Handle for the lookup operation.
69 */
70 struct GNS_ResolverHandle *lookup;
71};
72
73
74/**
75 * Our handle to the DNS handler library
76 */
77static struct GNUNET_DNS_Handle *dns_handle;
78
79/**
80 * Head of the DLL.
81 */
82static struct InterceptLookupHandle *ilh_head;
83
84/**
85 * Tail of the DLL.
86 */
87static struct InterceptLookupHandle *ilh_tail;
88
89
90/**
91 * Reply to dns request with the result from our lookup.
92 *
93 * @param cls the closure to the request (an InterceptLookupHandle)
94 * @param rd_count the number of records to return
95 * @param rd the record data
96 */
97static void
98reply_to_dns (void *cls, uint32_t rd_count,
99 const struct GNUNET_GNSRECORD_Data *rd)
100{
101 struct InterceptLookupHandle *ilh = cls;
102 struct GNUNET_DNSPARSER_Packet *packet = ilh->packet;
103 struct GNUNET_DNSPARSER_Query *query = &packet->queries[0];
104 uint32_t i;
105 size_t len;
106 int ret;
107 char *buf;
108 unsigned int num_answers;
109 unsigned int skip_answers;
110 unsigned int skip_additional;
111 size_t off = 0;
112
113 /* Put records in the DNS packet */
114 num_answers = 0;
115 for (i = 0; i < rd_count; i++)
116 if (rd[i].record_type == query->type)
117 num_answers++;
118 skip_answers = 0;
119 skip_additional = 0;
120
121 {
122 struct GNUNET_DNSPARSER_Record answer_records[num_answers];
123 struct GNUNET_DNSPARSER_Record additional_records[rd_count - num_answers];
124
125 packet->answers = answer_records;
126 packet->additional_records = additional_records;
127 /* FIXME: need to handle #GNUNET_GNSRECORD_RF_SHADOW_RECORD option
128 (by ignoring records where this flag is set if there is any
129 other record of that type in the result set) */
130 for (i = 0; i < rd_count; i++)
131 {
132 if (rd[i].record_type == query->type)
133 {
134 answer_records[i - skip_answers].name = query->name;
135 answer_records[i - skip_answers].type = rd[i].record_type;
136 switch (rd[i].record_type)
137 {
138 case GNUNET_DNSPARSER_TYPE_NS:
139 case GNUNET_DNSPARSER_TYPE_CNAME:
140 case GNUNET_DNSPARSER_TYPE_PTR:
141 answer_records[i - skip_answers].data.hostname
142 = GNUNET_DNSPARSER_parse_name (rd[i].data,
143 rd[i].data_size,
144 &off);
145 if ((off != rd[i].data_size) ||
146 (NULL == answer_records[i].data.hostname))
147 {
148 GNUNET_break_op (0);
149 skip_answers++;
150 }
151 break;
152
153 case GNUNET_DNSPARSER_TYPE_SOA:
154 answer_records[i - skip_answers].data.soa
155 = GNUNET_DNSPARSER_parse_soa (rd[i].data,
156 rd[i].data_size,
157 &off);
158 if ((off != rd[i].data_size) ||
159 (NULL == answer_records[i].data.soa))
160 {
161 GNUNET_break_op (0);
162 skip_answers++;
163 }
164 break;
165
166 case GNUNET_DNSPARSER_TYPE_SRV:
167 /* FIXME: SRV is not yet supported */
168 skip_answers++;
169 break;
170
171 case GNUNET_DNSPARSER_TYPE_URI:
172 /* FIXME: URI is not yet supported */
173 skip_answers++;
174 break;
175
176 case GNUNET_DNSPARSER_TYPE_MX:
177 answer_records[i - skip_answers].data.mx
178 = GNUNET_DNSPARSER_parse_mx (rd[i].data,
179 rd[i].data_size,
180 &off);
181 if ((off != rd[i].data_size) ||
182 (NULL == answer_records[i].data.hostname))
183 {
184 GNUNET_break_op (0);
185 skip_answers++;
186 }
187 break;
188
189 default:
190 answer_records[i - skip_answers].data.raw.data_len = rd[i].data_size;
191 answer_records[i - skip_answers].data.raw.data = (char *) rd[i].data;
192 break;
193 }
194 GNUNET_break (0 == (rd[i - skip_answers].flags
195 & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION));
196 answer_records[i - skip_answers].expiration_time.abs_value_us =
197 rd[i].expiration_time;
198 answer_records[i - skip_answers].dns_traffic_class =
199 GNUNET_TUN_DNS_CLASS_INTERNET;
200 }
201 else
202 {
203 additional_records[i - skip_additional].name = query->name;
204 additional_records[i - skip_additional].type = rd[i].record_type;
205 switch (rd[i].record_type)
206 {
207 case GNUNET_DNSPARSER_TYPE_NS:
208 case GNUNET_DNSPARSER_TYPE_CNAME:
209 case GNUNET_DNSPARSER_TYPE_PTR:
210 additional_records[i - skip_additional].data.hostname
211 = GNUNET_DNSPARSER_parse_name (rd[i].data,
212 rd[i].data_size,
213 &off);
214 if ((off != rd[i].data_size) ||
215 (NULL == additional_records[i].data.hostname))
216 {
217 GNUNET_break_op (0);
218 skip_additional++;
219 }
220 break;
221
222 case GNUNET_DNSPARSER_TYPE_SOA:
223 additional_records[i - skip_additional].data.soa
224 = GNUNET_DNSPARSER_parse_soa (rd[i].data,
225 rd[i].data_size,
226 &off);
227 if ((off != rd[i].data_size) ||
228 (NULL == additional_records[i].data.hostname))
229 {
230 GNUNET_break_op (0);
231 skip_additional++;
232 }
233 break;
234
235 case GNUNET_DNSPARSER_TYPE_MX:
236 additional_records[i - skip_additional].data.mx
237 = GNUNET_DNSPARSER_parse_mx (rd[i].data,
238 rd[i].data_size,
239 &off);
240 if ((off != rd[i].data_size) ||
241 (NULL == additional_records[i].data.hostname))
242 {
243 GNUNET_break_op (0);
244 skip_additional++;
245 }
246 break;
247
248 case GNUNET_DNSPARSER_TYPE_SRV:
249 /* FIXME: SRV is not yet supported */
250 skip_answers++;
251 break;
252
253 case GNUNET_DNSPARSER_TYPE_URI:
254 additional_records[i - skip_additional].data.uri
255 = GNUNET_DNSPARSER_parse_uri (rd[i].data,
256 rd[i].data_size,
257 &off);
258 if ((off != rd[i].data_size) ||
259 (NULL == additional_records[i].data.uri))
260 {
261 GNUNET_break_op (0);
262 skip_additional++;
263 }
264 break;
265
266 default:
267 additional_records[i - skip_additional].data.raw.data_len =
268 rd[i].data_size;
269 additional_records[i - skip_additional].data.raw.data =
270 (char *) rd[i].data;
271 break;
272 }
273 GNUNET_break (0 == (rd[i - skip_additional].flags
274 & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION));
275 additional_records[i - skip_additional].expiration_time.abs_value_us =
276 rd[i].expiration_time;
277 additional_records[i - skip_additional].dns_traffic_class =
278 GNUNET_TUN_DNS_CLASS_INTERNET;
279 }
280 }
281 packet->num_answers = num_answers - skip_answers;
282 packet->num_additional_records = rd_count - num_answers - skip_additional;
283 packet->flags.authoritative_answer = 1;
284 if (NULL == rd)
285 packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NAME_ERROR;
286 else
287 packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NO_ERROR;
288 packet->flags.query_or_response = 1;
289 ret = GNUNET_DNSPARSER_pack (packet,
290 1024, /* maximum allowed size for DNS reply */
291 &buf,
292 &len);
293 if (GNUNET_OK != ret)
294 {
295 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
296 _ ("Error converting GNS response to DNS response!\n"));
297 if (GNUNET_NO == ret)
298 GNUNET_free (buf);
299 }
300 else
301 {
302 GNUNET_DNS_request_answer (ilh->request_handle,
303 len,
304 buf);
305 GNUNET_free (buf);
306 }
307 packet->num_answers = 0;
308 packet->answers = NULL;
309 packet->num_additional_records = 0;
310 packet->additional_records = NULL;
311 GNUNET_DNSPARSER_free_packet (packet);
312 }
313 GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
314 GNUNET_free (ilh);
315}
316
317
318/**
319 * The DNS request handler. Called for every incoming DNS request.
320 *
321 * @param cls closure, unused
322 * @param rh request handle to user for reply
323 * @param request_length number of bytes in @a request
324 * @param request UDP payload of the DNS request
325 */
326static void
327handle_dns_request (void *cls,
328 struct GNUNET_DNS_RequestHandle *rh,
329 size_t request_length,
330 const char *request)
331{
332 struct GNUNET_DNSPARSER_Packet *p;
333 struct InterceptLookupHandle *ilh;
334 struct GNUNET_CRYPTO_PublicKey zone;
335
336 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
337 "Hijacked a DNS request. Processing.\n");
338 if (NULL == (p = GNUNET_DNSPARSER_parse (request, request_length)))
339 {
340 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
341 "Received malformed DNS packet, leaving it untouched.\n");
342 GNUNET_DNS_request_forward (rh);
343 return;
344 }
345
346 /* Check TLD and decide if we or legacy dns is responsible */
347 if (1 != p->num_queries)
348 {
349 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
350 "Not exactly one query in DNS packet. Forwarding untouched.\n");
351 GNUNET_DNS_request_forward (rh);
352 GNUNET_DNSPARSER_free_packet (p);
353 return;
354 }
355
356 /* Check for GNS TLDs. */
357 if (GNUNET_YES ==
358 GNS_find_tld (GNS_get_tld (p->queries[0].name),
359 &zone))
360 {
361 /* Start resolution in GNS */
362 ilh = GNUNET_new (struct InterceptLookupHandle);
363 GNUNET_CONTAINER_DLL_insert (ilh_head,
364 ilh_tail,
365 ilh);
366 ilh->packet = p;
367 ilh->request_handle = rh;
368 ilh->lookup = GNS_resolver_lookup (&zone,
369 p->queries[0].type,
370 p->queries[0].name,
371 GNUNET_GNS_LO_DEFAULT,
372 MAX_RECURSION,
373 &reply_to_dns, ilh);
374 return;
375 }
376 /* This request does not concern us. Forward to real DNS. */
377 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
378 "Request for `%s' is forwarded to DNS untouched.\n",
379 p->queries[0].name);
380 GNUNET_DNS_request_forward (rh);
381 GNUNET_DNSPARSER_free_packet (p);
382}
383
384
385int
386GNS_interceptor_init (const struct GNUNET_CONFIGURATION_Handle *c)
387{
388 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
389 "DNS hijacking enabled. Connecting to DNS service.\n");
390 dns_handle = GNUNET_DNS_connect (c,
391 GNUNET_DNS_FLAG_PRE_RESOLUTION,
392 &handle_dns_request,
393 NULL);
394 if (NULL == dns_handle)
395 {
396 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
397 _ ("Failed to connect to the DNS service!\n"));
398 return GNUNET_SYSERR;
399 }
400 return GNUNET_YES;
401}
402
403
404/**
405 * Disconnect from interceptor
406 */
407void
408GNS_interceptor_done ()
409{
410 struct InterceptLookupHandle *ilh;
411
412 while (NULL != (ilh = ilh_head))
413 {
414 GNUNET_CONTAINER_DLL_remove (ilh_head,
415 ilh_tail,
416 ilh);
417 GNS_resolver_lookup_cancel (ilh->lookup);
418 GNUNET_DNS_request_drop (ilh->request_handle);
419 GNUNET_DNSPARSER_free_packet (ilh->packet);
420 GNUNET_free (ilh);
421 }
422 if (NULL != dns_handle)
423 {
424 GNUNET_DNS_disconnect (dns_handle);
425 dns_handle = NULL;
426 }
427}
428
429
430/* end of gnunet-service-gns_interceptor.c */
diff --git a/src/service/gns/gnunet-service-gns_interceptor.h b/src/service/gns/gnunet-service-gns_interceptor.h
new file mode 100644
index 000000000..7f9733c2a
--- /dev/null
+++ b/src/service/gns/gnunet-service-gns_interceptor.h
@@ -0,0 +1,46 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 2012 GNUnet e.V.
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 gns/gnunet-service-gns_interceptor.h
22 * @brief GNUnet GNS service
23 * @author Martin Schanzenbach
24 */
25#ifndef GNUNET_GNS_INTERCEPTOR_H
26#define GNUNET_GNS_INTERCEPTOR_H
27
28#include "gnunet_util_lib.h"
29
30
31/**
32 * Initialize DNS interceptor
33 *
34 * @param c the configuration
35 * @return #GNUNET_YES on success #GNUNET_SYSERR on error
36 */
37int
38GNS_interceptor_init (const struct GNUNET_CONFIGURATION_Handle *c);
39
40/**
41 * Stops the interceptor
42 */
43void
44GNS_interceptor_done (void);
45
46#endif
diff --git a/src/service/gns/gnunet-service-gns_resolver.c b/src/service/gns/gnunet-service-gns_resolver.c
new file mode 100644
index 000000000..b14500ab7
--- /dev/null
+++ b/src/service/gns/gnunet-service-gns_resolver.c
@@ -0,0 +1,3173 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011-2013 GNUnet e.V.
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 gns/gnunet-service-gns_resolver.c
23 * @brief GNU Name System resolver logic
24 * @author Martin Schanzenbach
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#if HAVE_LIBIDN2
29#if HAVE_IDN2_H
30#include <idn2.h>
31#elif HAVE_IDN2_IDN2_H
32#include <idn2/idn2.h>
33#endif
34#elif HAVE_LIBIDN
35#if HAVE_IDNA_H
36#include <idna.h>
37#elif HAVE_IDN_IDNA_H
38#include <idn/idna.h>
39#endif
40#endif
41#include "gnunet_util_lib.h"
42#include "gnunet_dht_service.h"
43#include "gnunet_gnsrecord_lib.h"
44#include "gnunet_namecache_service.h"
45#include "gnunet_dns_service.h"
46#include "gnunet_resolver_service.h"
47#include "gnunet_revocation_service.h"
48#include "gnunet_gns_service.h"
49#include "gns.h"
50#include "gnunet-service-gns.h"
51#include "gnunet-service-gns_resolver.h"
52#include "gnu_name_system_protocols.h"
53#include "gnu_name_system_service_ports.h"
54
55
56/**
57 * Default DHT timeout for lookups.
58 */
59#define DHT_LOOKUP_TIMEOUT GNUNET_TIME_relative_multiply ( \
60 GNUNET_TIME_UNIT_SECONDS, 60)
61
62/**
63 * Default timeout for DNS lookups.
64 */
65#define DNS_LOOKUP_TIMEOUT GNUNET_TIME_relative_multiply ( \
66 GNUNET_TIME_UNIT_SECONDS, 15)
67
68/**
69 * DHT replication level
70 */
71#define DHT_GNS_REPLICATION_LEVEL 10
72
73
74/**
75 * DLL to hold the authority chain we had to pass in the resolution
76 * process.
77 */
78struct AuthorityChain;
79
80
81/**
82 * Element of a resolution process for looking up the
83 * responsible DNS server hostname in a GNS2DNS recursive
84 * resolution.
85 */
86struct Gns2DnsPending
87{
88 /**
89 * Kept in a DLL.
90 */
91 struct Gns2DnsPending *next;
92
93 /**
94 * Kept in a DLL.
95 */
96 struct Gns2DnsPending *prev;
97
98 /**
99 * Context this activity belongs with.
100 */
101 struct AuthorityChain *ac;
102
103 /**
104 * Handle for the resolution of the IP part of the
105 * GNS2DNS record. Will return to us the addresses
106 * of the DNS resolver to use.
107 */
108 struct GNS_ResolverHandle *rh;
109
110 /**
111 * Handle for DNS resolution of the DNS nameserver.
112 */
113 struct GNUNET_RESOLVER_RequestHandle *dns_rh;
114
115 /**
116 * How many results did we get?
117 */
118 unsigned int num_results;
119};
120
121
122/**
123 * Handle to a currently pending resolution. On result (positive or
124 * negative) the #GNS_ResultProcessor is called.
125 */
126struct GNS_ResolverHandle;
127
128
129/**
130 * DLL to hold the authority chain we had to pass in the resolution
131 * process.
132 */
133struct AuthorityChain
134{
135 /**
136 * This is a DLL.
137 */
138 struct AuthorityChain *prev;
139
140 /**
141 * This is a DLL.
142 */
143 struct AuthorityChain *next;
144
145 /**
146 * Resolver handle this entry in the chain belongs to.
147 */
148 struct GNS_ResolverHandle *rh;
149
150 /**
151 * label/name corresponding to the authority
152 */
153 char *label;
154
155 /**
156 * #GNUNET_YES if the authority was a GNS authority,
157 * #GNUNET_NO if the authority was a DNS authority.
158 */
159 int gns_authority;
160
161 /**
162 * Information about the resolver authority for this label.
163 */
164 union
165 {
166 /**
167 * The zone of the GNS authority
168 */
169 struct GNUNET_CRYPTO_PublicKey gns_authority;
170
171 struct
172 {
173 /**
174 * Domain of the DNS resolver that is the authority.
175 * (appended to construct the DNS name to resolve;
176 * this is NOT the DNS name of the DNS server!).
177 */
178 char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
179
180 /**
181 * List of resolutions of the 'ip' of the name server that
182 * are still pending.
183 */
184 struct Gns2DnsPending *gp_head;
185
186 /**
187 * Tail of list of resolutions of the 'ip' of the name server that
188 * are still pending.
189 */
190 struct Gns2DnsPending *gp_tail;
191
192 /**
193 * Handle to perform DNS lookups with this authority (in GNS2DNS handling).
194 */
195 struct GNUNET_DNSSTUB_Context *dns_handle;
196
197 /**
198 * Did we succeed in getting an IP address for *any* of the DNS servers listed?
199 * Once we do, we can start with DNS queries.
200 */
201 int found;
202
203 /**
204 * Did we start the recursive resolution via DNS?
205 */
206 int launched;
207 } dns_authority;
208 } authority_info;
209};
210
211
212/**
213 * A result we got from DNS.
214 */
215struct DnsResult
216{
217 /**
218 * Kept in DLL.
219 */
220 struct DnsResult *next;
221
222 /**
223 * Kept in DLL.
224 */
225 struct DnsResult *prev;
226
227 /**
228 * Binary value stored in the DNS record (appended to this struct)
229 */
230 const void *data;
231
232 /**
233 * Expiration time for the DNS record, 0 if we didn't
234 * get anything useful (i.e. 'gethostbyname()' was used).
235 */
236 uint64_t expiration_time;
237
238 /**
239 * Number of bytes in @e data.
240 */
241 size_t data_size;
242
243 /**
244 * Type of the GNS/DNS record.
245 */
246 uint32_t record_type;
247};
248
249
250/**
251 * Handle to a currently pending resolution. On result (positive or
252 * negative) the #GNS_ResultProcessor is called.
253 */
254struct GNS_ResolverHandle
255{
256 /**
257 * DLL
258 */
259 struct GNS_ResolverHandle *next;
260
261 /**
262 * DLL
263 */
264 struct GNS_ResolverHandle *prev;
265
266 /**
267 * The top-level GNS authoritative zone to query
268 */
269 struct GNUNET_CRYPTO_PublicKey authority_zone;
270
271 /**
272 * called when resolution phase finishes
273 */
274 GNS_ResultProcessor proc;
275
276 /**
277 * closure passed to @e proc
278 */
279 void *proc_cls;
280
281 /**
282 * Handle for DHT lookups. should be NULL if no lookups are in progress
283 */
284 struct GNUNET_DHT_GetHandle *get_handle;
285
286
287 /**
288 * Socket for a DNS request, NULL if none is active.
289 */
290 struct GNUNET_DNSSTUB_RequestSocket *dns_request;
291
292 /**
293 * Handle for standard DNS resolution, NULL if none is active.
294 */
295 struct GNUNET_RESOLVER_RequestHandle *std_resolve;
296
297 /**
298 * Pending Namecache lookup task
299 */
300 struct GNUNET_NAMECACHE_QueueEntry *namecache_qe;
301
302 /**
303 * Pending revocation check.
304 */
305 struct GNUNET_REVOCATION_Query *rev_check;
306
307 /**
308 * Heap node associated with this lookup. Used to limit number of
309 * concurrent requests.
310 */
311 struct GNUNET_CONTAINER_HeapNode *dht_heap_node;
312
313 /**
314 * DLL to store the authority chain
315 */
316 struct AuthorityChain *ac_head;
317
318 /**
319 * DLL to store the authority chain
320 */
321 struct AuthorityChain *ac_tail;
322
323 /**
324 * ID of a task associated with the resolution process.
325 */
326 struct GNUNET_SCHEDULER_Task *task_id;
327
328 /**
329 * The name to resolve
330 */
331 char *name;
332
333 /**
334 * Legacy Hostname to use if we encountered GNS2DNS record
335 * and thus can deduct the LEHO from that transition.
336 */
337 char *leho;
338
339 /**
340 * DLL of results we got from DNS.
341 */
342 struct DnsResult *dns_result_head;
343
344 /**
345 * DLL of results we got from DNS.
346 */
347 struct DnsResult *dns_result_tail;
348
349 /**
350 * Current offset in @e name where we are resolving.
351 */
352 size_t name_resolution_pos;
353
354 /**
355 * Use only cache
356 */
357 enum GNUNET_GNS_LocalOptions options;
358
359 /**
360 * For SRV and TLSA records, the number of the
361 * protocol specified in the name. 0 if no protocol was given.
362 */
363 int protocol;
364
365 /**
366 * For SRV and TLSA records, the number of the
367 * service specified in the name. 0 if no service was given.
368 */
369 int service;
370
371 /**
372 * For SMIMEA,OPENPGPKEY... records. NULL if no _ prefix was given.
373 */
374 char *prefix;
375
376
377 /**
378 * Desired type for the resolution.
379 */
380 int record_type;
381
382 /**
383 * We increment the loop limiter for each step in a recursive
384 * resolution. If it passes our @e loop_threshold (e.g. due to
385 * self-recursion in the resolution, i.e CNAME fun), we stop.
386 */
387 unsigned int loop_limiter;
388
389 /**
390 * Maximum value of @e loop_limiter allowed by client.
391 */
392 unsigned int loop_threshold;
393
394 /**
395 * 16 bit random ID we used in the @e dns_request.
396 */
397 uint16_t original_dns_id;
398};
399
400
401/**
402 * Active namestore caching operations.
403 */
404struct CacheOps
405{
406 /**
407 * Organized in a DLL.
408 */
409 struct CacheOps *next;
410
411 /**
412 * Organized in a DLL.
413 */
414 struct CacheOps *prev;
415
416 /**
417 * Pending Namestore caching task.
418 */
419 struct GNUNET_NAMECACHE_QueueEntry *namecache_qe_cache;
420};
421
422
423/**
424 * Our handle to the namecache service
425 */
426static struct GNUNET_NAMECACHE_Handle *namecache_handle;
427
428/**
429 * Resolver handle to the dht
430 */
431static struct GNUNET_DHT_Handle *dht_handle;
432
433/**
434 * Heap for limiting parallel DHT lookups
435 */
436static struct GNUNET_CONTAINER_Heap *dht_lookup_heap;
437
438/**
439 * Maximum amount of parallel queries to the DHT
440 */
441static unsigned long long max_allowed_background_queries;
442
443/**
444 * Head of resolver lookup list
445 */
446static struct GNS_ResolverHandle *rlh_head;
447
448/**
449 * Tail of resolver lookup list
450 */
451static struct GNS_ResolverHandle *rlh_tail;
452
453/**
454 * Organized in a DLL.
455 */
456static struct CacheOps *co_head;
457
458/**
459 * Organized in a DLL.
460 */
461static struct CacheOps *co_tail;
462
463/**
464 * Use namecache
465 */
466static int disable_cache;
467
468/**
469 * Global configuration.
470 */
471static const struct GNUNET_CONFIGURATION_Handle *cfg;
472
473
474/**
475 * Determine if this name is canonical (is a legal name in a zone, without delegation);
476 * note that we do not test that the name does not contain illegal characters, we only
477 * test for delegation. Note that service records (like _foo._srv) are canonical names
478 * even though they consist of multiple labels.
479 *
480 * Examples:
481 * a.b.gnu = not canonical
482 * a = canonical
483 * _foo._srv = canonical
484 * _f.bar = not canonical
485 *
486 * @param name the name to test
487 * @return #GNUNET_YES if canonical
488 */
489/* dead, but keep for now */ int
490is_canonical (const char *name)
491{
492 const char *pos;
493 const char *dot;
494
495 if (NULL == strchr (name,
496 (unsigned char) '.'))
497 return GNUNET_YES;
498 if ('_' != name[0])
499 return GNUNET_NO;
500 pos = &name[1];
501 while (NULL != (dot = strchr (pos,
502 (unsigned char) '.')))
503 if ('_' != dot[1])
504 return GNUNET_NO;
505 else
506 pos = dot + 1;
507 return GNUNET_YES;
508}
509
510
511/* ************************** Resolution **************************** */
512
513/**
514 * Expands a name ending in .+ with the zone of origin.
515 *
516 * @param rh resolution context
517 * @param name name to modify (to be free'd or returned)
518 * @return updated name
519 */
520static char *
521translate_dot_plus (struct GNS_ResolverHandle *rh,
522 char *name)
523{
524 char *ret;
525 size_t s_len = strlen (name);
526
527 if (0 != strcmp (&name[s_len - 2],
528 ".+"))
529 return name; /* did not end in ".+" */
530 GNUNET_assert (GNUNET_YES == rh->ac_tail->gns_authority);
531 GNUNET_asprintf (&ret,
532 "%.*s.%s",
533 (int) (s_len - 2),
534 name,
535 GNUNET_GNSRECORD_pkey_to_zkey (
536 &rh->ac_tail->authority_info.gns_authority));
537 GNUNET_free (name);
538 return ret;
539}
540
541
542/**
543 * Wrapper around #GNS_resolver_lookup_cancel() as a task.
544 * Used for delayed cleanup so we can unwind the stack first.
545 *
546 * @param cls the `struct GNS_ResolverHandle`
547 */
548static void
549GNS_resolver_lookup_cancel_ (void *cls)
550{
551 struct GNS_ResolverHandle *rh = cls;
552
553 rh->task_id = NULL;
554 GNS_resolver_lookup_cancel (rh);
555}
556
557
558/**
559 * Function called to asynchronously fail a resolution.
560 *
561 * @param rh the resolution to fail
562 */
563static void
564fail_resolution (struct GNS_ResolverHandle *rh)
565{
566 rh->proc (rh->proc_cls,
567 0,
568 NULL);
569 GNUNET_assert (NULL == rh->task_id);
570 rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_,
571 rh);
572}
573
574
575/**
576 * Function called when a resolution times out.
577 *
578 * @param cls the `struct GNS_ResolverHandle`
579 */
580static void
581timeout_resolution (void *cls)
582{
583 struct GNS_ResolverHandle *rh = cls;
584
585 rh->task_id = NULL;
586 fail_resolution (rh);
587}
588
589
590/**
591 * Function called to receive the protocol number for a service.
592 *
593 * @param name name of the protocol
594*/
595static int
596resolver_getprotobyname (const char *name)
597{
598 struct protoent *pe = getprotobyname (name);
599 if (pe == NULL)
600 {
601 return GNUNET_GNS_protocol_name_to_number (name);
602 }
603 return pe->p_proto;
604}
605
606
607/**
608 * Function called to receive the port number for a service.
609 *
610 * @param name name of the service
611 * @param proto name of the protocol
612*/
613static int
614resolver_getservbyname (const char *name, const char *proto)
615{
616 struct servent *se = getservbyname (name, proto);
617 if (se == NULL)
618 {
619 return htons (GNUNET_GNS_service_port_name_to_number (name));
620 }
621 return se->s_port;
622}
623
624
625/**
626 * Get the next, rightmost label from the name that we are trying to resolve,
627 * and update the resolution position accordingly. Labels usually consist
628 * of up to 63 characters without a period ("."); however, we use a special
629 * convention to support resource records where the domain name
630 * includes a label starting with '_'. The syntax (see RFC 8552) here is
631 * "someLabel._Label.Name" and in this special case we include the "someLabel._Label" in the rightmost label.
632 * Thus, for "_443._tcp.foo.bar" we first return the label "bar" and then
633 * the label "_443._tcp.foo". The special case is detected by the
634 * presence of one label beginning with an underscore. The rightmost label
635 * beginning with an underscore, is combined with the label to its right
636 * (and the "." is preserved). If the label is in the syntax of
637 * "_PORT._PROTOCOL" (e.g. "_443._tcp") we also extract the port and protocol.
638 * In this implementation, the more specific case is handled first.
639 *
640 * @param rh handle to the resolution operation to get the next label from
641 * @return NULL if there are no more labels
642 */
643static char *
644resolver_lookup_get_next_label (struct GNS_ResolverHandle *rh)
645{
646 const char *rp;
647 const char *dot;
648 size_t len;
649 char *ret;
650 char *srv_name;
651 char *proto_name;
652 int protocol;
653 int service;
654
655 if (0 == rh->name_resolution_pos)
656 return NULL;
657 dot = memrchr (rh->name,
658 (int) '.',
659 rh->name_resolution_pos);
660 if (NULL == dot)
661 {
662 /* done, this was the last one */
663 len = rh->name_resolution_pos;
664 rp = rh->name;
665 rh->name_resolution_pos = 0;
666 }
667 else if ('_' == dot[1])
668 {
669 /**
670 * Do not advance a label. This seems to be a name only consisting
671 * of a prefix. Indicating a BOX record (_443,_tcp)
672 * Or some version of an SBOX record (HEX,_smimeacert)
673 * Which means, it is a BOX/SBOX under the empty label.
674 * leaving name_resolution_pos as is and returning empty label.
675 */
676 rp = GNUNET_GNS_EMPTY_LABEL_AT;
677 len = strlen (GNUNET_GNS_EMPTY_LABEL_AT);
678 }
679 else
680 {
681 /* advance by one label */
682 len = rh->name_resolution_pos - (dot - rh->name) - 1;
683 rp = dot + 1;
684 rh->name_resolution_pos = dot - rh->name;
685 }
686 rh->protocol = 0;
687 rh->service = 0;
688 rh->prefix = NULL;
689 ret = GNUNET_strndup (rp, len);
690 /** If we have labels starting with underscore with label on
691 * the right (SRV/DANE/BOX case), determine port/protocol;
692 * The format of `rh->name` must be "_PORT._PROTOCOL".
693 */
694 if (('_' == rh->name[0]) &&
695 (NULL != (dot = memrchr (rh->name,
696 (int) '.',
697 rh->name_resolution_pos))) &&
698 ('_' == dot[1]) &&
699 (NULL == memrchr (rh->name,
700 (int) '.',
701 dot - rh->name)))
702 {
703 srv_name = GNUNET_strndup (&rh->name[1],
704 (dot - rh->name) - 1);
705 proto_name = GNUNET_strndup (&dot[2],
706 rh->name_resolution_pos - (dot - rh->name)
707 - 2);
708 protocol = resolver_getprotobyname (proto_name);
709 if (0 == protocol)
710 {
711 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
712 _ (
713 "Protocol `%s' unknown, skipping labels as BOX retain as SBOX.\n"),
714 proto_name);
715 GNUNET_free (proto_name);
716 GNUNET_free (srv_name);
717 rh->prefix = GNUNET_strndup (rh->name, rh->name_resolution_pos);
718 rh->name_resolution_pos = 0;
719 return ret;
720 }
721 service = resolver_getservbyname (srv_name,
722 proto_name);
723 if (0 == service)
724 {
725 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
726 _ (
727 "Service `%s' unknown for protocol `%s', trying as number.\n"),
728 srv_name,
729 proto_name);
730 if (1 != sscanf (srv_name, "%u", &rh->service))
731 {
732 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
733 _ (
734 "Service `%s' not a port, skipping service labels as BOX retain as SBOX.\n"),
735 srv_name);
736 GNUNET_free (proto_name);
737 GNUNET_free (srv_name);
738 rh->prefix = GNUNET_strndup (rh->name, rh->name_resolution_pos);
739 rh->name_resolution_pos = 0;
740 return ret;
741 }
742 }
743 else
744 {
745 rh->service = ntohs (service);
746 }
747 rh->protocol = protocol;
748 GNUNET_free (proto_name);
749 GNUNET_free (srv_name);
750 }
751 /**
752 * If we have labels starting with underscore with label on
753 * the right, copy prefix to rh->prefix;
754 * The format of `rh->name` must be "*._label" or "_label",
755 * where label is a string without '.'
756 */
757 if ((NULL != (dot = memrchr (rh->name,
758 (int) '.',
759 rh->name_resolution_pos)) && '_' == dot[1]) ||
760 ((NULL == memrchr (rh->name,
761 (int) '.',
762 rh->name_resolution_pos)) && '_' == rh->name[0]))
763 {
764 rh->prefix = GNUNET_strndup (rh->name, rh->name_resolution_pos);
765 rh->name_resolution_pos = 0;
766 }
767 return ret;
768}
769
770
771/**
772 * Gives the cumulative result obtained to the callback and clean up the request.
773 *
774 * @param rh resolution process that has culminated in a result
775 */
776static void
777transmit_lookup_dns_result (struct GNS_ResolverHandle *rh)
778{
779 struct DnsResult *pos;
780 unsigned int n;
781 unsigned int i;
782
783 n = 0;
784 for (pos = rh->dns_result_head; NULL != pos; pos = pos->next)
785 n++;
786 {
787 struct GNUNET_GNSRECORD_Data rd[n];
788
789 i = 0;
790 for (pos = rh->dns_result_head; NULL != pos; pos = pos->next)
791 {
792 rd[i].data = pos->data;
793 rd[i].data_size = pos->data_size;
794 rd[i].record_type = pos->record_type;
795 rd[i].flags = GNUNET_GNSRECORD_RF_NONE;
796 /**
797 * If this is a LEHO, we added this before. It must be a supplemental
798 * record #LSD0001
799 */
800 if (GNUNET_GNSRECORD_TYPE_LEHO == rd[i].record_type)
801 rd[i].flags |= GNUNET_GNSRECORD_RF_SUPPLEMENTAL;
802 if (0 == pos->expiration_time)
803 {
804 rd[i].flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
805 rd[i].expiration_time = 0;
806 }
807 else
808 {
809 rd[i].expiration_time = pos->expiration_time;
810 }
811 i++;
812 }
813 GNUNET_assert (i == n);
814 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
815 "Transmitting standard DNS result with %u records\n",
816 n);
817 rh->proc (rh->proc_cls,
818 n,
819 rd);
820 }
821 GNS_resolver_lookup_cancel (rh);
822}
823
824
825/**
826 * Add a result from DNS to the records to be returned to the application.
827 *
828 * @param rh resolution request to extend with a result
829 * @param expiration_time expiration time for the answer
830 * @param record_type DNS record type of the answer
831 * @param data_size number of bytes in @a data
832 * @param data binary data to return in DNS record
833 */
834static void
835add_dns_result (struct GNS_ResolverHandle *rh,
836 uint64_t expiration_time,
837 uint32_t record_type,
838 size_t data_size,
839 const void *data)
840{
841 struct DnsResult *res;
842
843 res = GNUNET_malloc (sizeof(struct DnsResult) + data_size);
844 res->expiration_time = expiration_time;
845 res->data_size = data_size;
846 res->record_type = record_type;
847 res->data = &res[1];
848 GNUNET_memcpy (&res[1],
849 data,
850 data_size);
851 GNUNET_CONTAINER_DLL_insert (rh->dns_result_head,
852 rh->dns_result_tail,
853 res);
854}
855
856
857/**
858 * We had to do a DNS lookup. Convert the result (if any) and return
859 * it.
860 *
861 * @param cls closure with the `struct GNS_ResolverHandle`
862 * @param addr one of the addresses of the host, NULL for the last address
863 * @param addrlen length of the address
864 */
865static void
866handle_dns_result (void *cls,
867 const struct sockaddr *addr,
868 socklen_t addrlen)
869{
870 struct GNS_ResolverHandle *rh = cls;
871 const struct sockaddr_in *sa4;
872 const struct sockaddr_in6 *sa6;
873
874 if (NULL == addr)
875 {
876 rh->std_resolve = NULL;
877 transmit_lookup_dns_result (rh);
878 return;
879 }
880 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
881 "Received %u bytes of DNS IP data\n",
882 addrlen);
883 switch (addr->sa_family)
884 {
885 case AF_INET:
886 sa4 = (const struct sockaddr_in *) addr;
887 add_dns_result (rh,
888 0 /* expiration time is unknown */,
889 GNUNET_DNSPARSER_TYPE_A,
890 sizeof(struct in_addr),
891 &sa4->sin_addr);
892 break;
893
894 case AF_INET6:
895 sa6 = (const struct sockaddr_in6 *) addr;
896 add_dns_result (rh,
897 0 /* expiration time is unknown */,
898 GNUNET_DNSPARSER_TYPE_AAAA,
899 sizeof(struct in6_addr),
900 &sa6->sin6_addr);
901 break;
902
903 default:
904 GNUNET_break (0);
905 break;
906 }
907}
908
909
910/**
911 * Task scheduled to continue with the resolution process.
912 *
913 * @param cls the 'struct GNS_ResolverHandle' of the resolution
914 */
915static void
916recursive_resolution (void *cls);
917
918
919/**
920 * Begin the resolution process from 'name', starting with
921 * the identification of the zone specified by 'name'.
922 *
923 * @param cls closure with `struct GNS_ResolverHandle *rh`
924 */
925static void
926start_resolver_lookup (void *cls);
927
928
929/**
930 * Function called with the result of a DNS resolution.
931 *
932 * @param cls the request handle of the resolution that
933 * we were attempting to make
934 * @param dns dns response, never NULL
935 * @param dns_len number of bytes in @a dns
936 */
937static void
938dns_result_parser (void *cls,
939 const struct GNUNET_TUN_DnsHeader *dns,
940 size_t dns_len)
941{
942 struct GNS_ResolverHandle *rh = cls;
943 struct GNUNET_DNSPARSER_Packet *p;
944 const struct GNUNET_DNSPARSER_Record *rec;
945 unsigned int rd_count;
946
947 if (NULL == dns)
948 {
949 rh->dns_request = NULL;
950 GNUNET_SCHEDULER_cancel (rh->task_id);
951 rh->task_id = NULL;
952 fail_resolution (rh);
953 return;
954 }
955 if (rh->original_dns_id != dns->id)
956 {
957 /* DNS answer, but for another query */
958 return;
959 }
960 p = GNUNET_DNSPARSER_parse ((const char *) dns,
961 dns_len);
962 if (NULL == p)
963 {
964 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
965 _ ("Failed to parse DNS response\n"));
966 return;
967 }
968
969 /* We got a result from DNS */
970 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
971 "Received DNS response for `%s' with %u answers\n",
972 rh->ac_tail->label,
973 (unsigned int) p->num_answers);
974 if ((p->num_answers > 0) &&
975 (GNUNET_DNSPARSER_TYPE_CNAME == p->answers[0].type) &&
976 (GNUNET_DNSPARSER_TYPE_CNAME != rh->record_type))
977 {
978 int af;
979
980 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
981 "Got CNAME `%s' from DNS for `%s'\n",
982 p->answers[0].data.hostname,
983 rh->name);
984 if (NULL != rh->std_resolve)
985 {
986 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
987 "Multiple CNAME results from DNS resolving `%s'! Not really allowed...\n",
988 rh->name);
989 GNUNET_RESOLVER_request_cancel (rh->std_resolve);
990 }
991 GNUNET_free (rh->name);
992 rh->name = GNUNET_strdup (p->answers[0].data.hostname);
993 rh->name_resolution_pos = strlen (rh->name);
994 switch (rh->record_type)
995 {
996 case GNUNET_DNSPARSER_TYPE_A:
997 af = AF_INET;
998 break;
999
1000 case GNUNET_DNSPARSER_TYPE_AAAA:
1001 af = AF_INET6;
1002 break;
1003
1004 default:
1005 af = AF_UNSPEC;
1006 break;
1007 }
1008 if (NULL != rh->leho)
1009 add_dns_result (rh,
1010 GNUNET_TIME_UNIT_HOURS.rel_value_us,
1011 GNUNET_GNSRECORD_TYPE_LEHO,
1012 strlen (rh->leho),
1013 rh->leho);
1014 rh->std_resolve = GNUNET_RESOLVER_ip_get (rh->name,
1015 af,
1016 DNS_LOOKUP_TIMEOUT,
1017 &handle_dns_result,
1018 rh);
1019 GNUNET_DNSPARSER_free_packet (p);
1020 GNUNET_DNSSTUB_resolve_cancel (rh->dns_request);
1021 rh->dns_request = NULL;
1022 return;
1023 }
1024
1025 /* convert from (parsed) DNS to (binary) GNS format! */
1026 rd_count = p->num_answers + p->num_authority_records
1027 + p->num_additional_records;
1028 {
1029 struct GNUNET_GNSRECORD_Data rd[rd_count + 1]; /* +1 for LEHO */
1030 int skip;
1031 char buf[UINT16_MAX];
1032 size_t buf_off;
1033 size_t buf_start;
1034
1035 buf_off = 0;
1036 skip = 0;
1037 memset (rd,
1038 0,
1039 sizeof(rd));
1040 for (unsigned int i = 0; i < rd_count; i++)
1041 {
1042 if (i < p->num_answers)
1043 rec = &p->answers[i];
1044 else if (i < p->num_answers + p->num_authority_records)
1045 rec = &p->authority_records[i - p->num_answers];
1046 else
1047 rec = &p->additional_records[i - p->num_answers
1048 - p->num_authority_records];
1049 /* As we copied the full DNS name to 'rh->ac_tail->label', this
1050 should be the correct check to see if this record is actually
1051 a record for our label... */
1052 if (0 != strcmp (rec->name,
1053 rh->ac_tail->label))
1054 {
1055 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1056 "Dropping record `%s', does not match desired name `%s'\n",
1057 rec->name,
1058 rh->ac_tail->label);
1059 skip++;
1060 continue;
1061 }
1062 rd[i - skip].record_type = rec->type;
1063 rd[i - skip].expiration_time = rec->expiration_time.abs_value_us;
1064 switch (rec->type)
1065 {
1066 case GNUNET_DNSPARSER_TYPE_A:
1067 if (rec->data.raw.data_len != sizeof(struct in_addr))
1068 {
1069 GNUNET_break_op (0);
1070 skip++;
1071 continue;
1072 }
1073 rd[i - skip].data_size = rec->data.raw.data_len;
1074 rd[i - skip].data = rec->data.raw.data;
1075 break;
1076
1077 case GNUNET_DNSPARSER_TYPE_AAAA:
1078 if (rec->data.raw.data_len != sizeof(struct in6_addr))
1079 {
1080 GNUNET_break_op (0);
1081 skip++;
1082 continue;
1083 }
1084 rd[i - skip].data_size = rec->data.raw.data_len;
1085 rd[i - skip].data = rec->data.raw.data;
1086 break;
1087
1088 case GNUNET_DNSPARSER_TYPE_CNAME:
1089 case GNUNET_DNSPARSER_TYPE_PTR:
1090 case GNUNET_DNSPARSER_TYPE_NS:
1091 buf_start = buf_off;
1092 if (GNUNET_OK !=
1093 GNUNET_DNSPARSER_builder_add_name (buf,
1094 sizeof(buf),
1095 &buf_off,
1096 rec->data.hostname))
1097 {
1098 GNUNET_break (0);
1099 skip++;
1100 continue;
1101 }
1102 rd[i - skip].data_size = buf_off - buf_start;
1103 rd[i - skip].data = &buf[buf_start];
1104 break;
1105
1106 case GNUNET_DNSPARSER_TYPE_SOA:
1107 buf_start = buf_off;
1108 if (GNUNET_OK !=
1109 GNUNET_DNSPARSER_builder_add_soa (buf,
1110 sizeof(buf),
1111 &buf_off,
1112 rec->data.soa))
1113 {
1114 GNUNET_break (0);
1115 skip++;
1116 continue;
1117 }
1118 rd[i - skip].data_size = buf_off - buf_start;
1119 rd[i - skip].data = &buf[buf_start];
1120 break;
1121
1122 case GNUNET_DNSPARSER_TYPE_MX:
1123 buf_start = buf_off;
1124 if (GNUNET_OK !=
1125 GNUNET_DNSPARSER_builder_add_mx (buf,
1126 sizeof(buf),
1127 &buf_off,
1128 rec->data.mx))
1129 {
1130 GNUNET_break (0);
1131 skip++;
1132 continue;
1133 }
1134 rd[i - skip].data_size = buf_off - buf_start;
1135 rd[i - skip].data = &buf[buf_start];
1136 break;
1137
1138 case GNUNET_DNSPARSER_TYPE_SRV:
1139 buf_start = buf_off;
1140 if (GNUNET_OK !=
1141 GNUNET_DNSPARSER_builder_add_srv (buf,
1142 sizeof(buf),
1143 &buf_off,
1144 rec->data.srv))
1145 {
1146 GNUNET_break (0);
1147 skip++;
1148 continue;
1149 }
1150 rd[i - skip].data_size = buf_off - buf_start;
1151 rd[i - skip].data = &buf[buf_start];
1152 break;
1153
1154 case GNUNET_DNSPARSER_TYPE_URI:
1155 buf_start = buf_off;
1156 if (GNUNET_OK !=
1157 GNUNET_DNSPARSER_builder_add_uri (buf,
1158 sizeof(buf),
1159 &buf_off,
1160 rec->data.uri))
1161 {
1162 GNUNET_break (0);
1163 skip++;
1164 continue;
1165 }
1166 rd[i - skip].data_size = buf_off - buf_start;
1167 rd[i - skip].data = &buf[buf_start];
1168 break;
1169
1170 default:
1171 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1172 _ ("Skipping record of unsupported type %d\n"),
1173 rec->type);
1174 skip++;
1175 continue;
1176 }
1177 } /* end of for all records in answer */
1178 if (NULL != rh->leho)
1179 {
1180 rd[rd_count - skip].record_type = GNUNET_GNSRECORD_TYPE_LEHO;
1181 rd[rd_count - skip].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1182 rd[rd_count - skip].flags |= GNUNET_GNSRECORD_RF_SUPPLEMENTAL;
1183 rd[rd_count - skip].expiration_time = GNUNET_TIME_UNIT_HOURS.rel_value_us;
1184 rd[rd_count - skip].data = rh->leho;
1185 rd[rd_count - skip].data_size = strlen (rh->leho);
1186 skip--; /* skip one LESS */
1187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1188 "Adding LEHO %s\n",
1189 rh->leho);
1190 }
1191 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1192 "Returning DNS response for `%s' with %u answers\n",
1193 rh->ac_tail->label,
1194 (unsigned int) (rd_count - skip));
1195 rh->proc (rh->proc_cls,
1196 rd_count - skip,
1197 rd);
1198 GNUNET_DNSSTUB_resolve_cancel (rh->dns_request);
1199 rh->dns_request = NULL;
1200 }
1201 GNUNET_DNSPARSER_free_packet (p);
1202 if (NULL != rh->task_id)
1203 GNUNET_SCHEDULER_cancel (rh->task_id); /* should be timeout task */
1204 rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_,
1205 rh);
1206}
1207
1208
1209/**
1210 * Perform recursive DNS resolution. Asks the given DNS resolver to
1211 * resolve "rh->dns_name", possibly recursively proceeding following
1212 * NS delegations, CNAMES, etc., until 'rh->loop_limiter' bounds us or
1213 * we find the answer.
1214 *
1215 * @param rh resolution information
1216 */
1217static void
1218recursive_dns_resolution (struct GNS_ResolverHandle *rh)
1219{
1220 struct AuthorityChain *ac;
1221 struct GNUNET_DNSPARSER_Query *query;
1222 struct GNUNET_DNSPARSER_Packet *p;
1223 char *dns_request;
1224 size_t dns_request_length;
1225 int ret;
1226
1227 ac = rh->ac_tail;
1228 GNUNET_assert (NULL != ac);
1229 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1230 "Starting DNS lookup for `%s'\n",
1231 ac->label);
1232 GNUNET_assert (GNUNET_NO == ac->gns_authority);
1233 query = GNUNET_new (struct GNUNET_DNSPARSER_Query);
1234 query->name = GNUNET_strdup (ac->label);
1235 query->type = rh->record_type;
1236 query->dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
1237 p = GNUNET_new (struct GNUNET_DNSPARSER_Packet);
1238 p->queries = query;
1239 p->num_queries = 1;
1240 p->id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
1241 UINT16_MAX);
1242 p->flags.opcode = GNUNET_TUN_DNS_OPCODE_QUERY;
1243 p->flags.recursion_desired = 1;
1244 ret = GNUNET_DNSPARSER_pack (p,
1245 1024,
1246 &dns_request,
1247 &dns_request_length);
1248 if (GNUNET_OK != ret)
1249 {
1250 GNUNET_break (0);
1251 rh->proc (rh->proc_cls,
1252 0,
1253 NULL);
1254 GNUNET_assert (NULL == rh->task_id);
1255 rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_,
1256 rh);
1257 }
1258 else
1259 {
1260 rh->original_dns_id = p->id;
1261 GNUNET_assert (NULL != ac->authority_info.dns_authority.dns_handle);
1262 GNUNET_assert (NULL == rh->dns_request);
1263 rh->leho = GNUNET_strdup (ac->label);
1264 rh->dns_request = GNUNET_DNSSTUB_resolve (
1265 ac->authority_info.dns_authority.dns_handle,
1266 dns_request,
1267 dns_request_length,
1268 &dns_result_parser,
1269 rh);
1270 rh->task_id = GNUNET_SCHEDULER_add_delayed (DNS_LOOKUP_TIMEOUT,
1271 &timeout_resolution,
1272 rh);
1273 }
1274 if (GNUNET_SYSERR != ret)
1275 GNUNET_free (dns_request);
1276 GNUNET_DNSPARSER_free_packet (p);
1277}
1278
1279
1280/**
1281 * We encountered a REDIRECT record during our resolution.
1282 * Merge it into our chain.
1283 *
1284 * @param rh resolution we are performing
1285 * @param rname value of the redirect record we got for the current
1286 * authority chain tail
1287 */
1288static void
1289handle_gns_redirect_result (struct GNS_ResolverHandle *rh,
1290 const char *rname)
1291{
1292 size_t nlen;
1293 char *res;
1294 const char *tld;
1295 struct AuthorityChain *ac;
1296 int af;
1297 struct GNUNET_CRYPTO_PublicKey zone;
1298
1299 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1300 "Handling GNS REDIRECT result `%s'\n",
1301 rname);
1302 nlen = strlen (rname);
1303 tld = GNS_get_tld (rname);
1304 if (0 == strcmp ("+", tld))
1305 {
1306 /* REDIRECT resolution continues relative to current domain */
1307 if (0 == rh->name_resolution_pos)
1308 {
1309 res = GNUNET_strndup (rname, nlen - 2);
1310 rh->name_resolution_pos = nlen - 2;
1311 }
1312 else
1313 {
1314 GNUNET_asprintf (&res,
1315 "%.*s.%.*s",
1316 (int) rh->name_resolution_pos,
1317 rh->name,
1318 (int) (nlen - 2),
1319 rname);
1320 rh->name_resolution_pos = strlen (res);
1321 }
1322 GNUNET_free (rh->name);
1323 rh->name = res;
1324 ac = GNUNET_new (struct AuthorityChain);
1325 ac->rh = rh;
1326 ac->gns_authority = GNUNET_YES;
1327 ac->authority_info.gns_authority =
1328 rh->ac_tail->authority_info.gns_authority;
1329 ac->label = resolver_lookup_get_next_label (rh);
1330 /* add AC to tail */
1331 GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head,
1332 rh->ac_tail,
1333 ac);
1334 rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution,
1335 rh);
1336 return;
1337 }
1338 if (GNUNET_OK == GNUNET_GNSRECORD_zkey_to_pkey (tld, &zone))
1339 {
1340 /* REDIRECT resolution continues relative to current domain */
1341 if (0 == rh->name_resolution_pos)
1342 {
1343 GNUNET_asprintf (&res,
1344 "%.*s",
1345 (int) (strlen (rname) - (strlen (tld) + 1)),
1346 rname);
1347 }
1348 else
1349 {
1350 GNUNET_asprintf (&res,
1351 "%.*s.%.*s",
1352 (int) rh->name_resolution_pos,
1353 rh->name,
1354 (int) (strlen (rname) - (strlen (tld) + 1)),
1355 rname);
1356 }
1357 rh->name_resolution_pos = strlen (res);
1358 GNUNET_free (rh->name);
1359 rh->name = res;
1360 ac = GNUNET_new (struct AuthorityChain);
1361 ac->rh = rh;
1362 ac->gns_authority = GNUNET_YES;
1363 ac->authority_info.gns_authority = zone;
1364 ac->label = resolver_lookup_get_next_label (rh);
1365 /* add AC to tail */
1366 GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head,
1367 rh->ac_tail,
1368 ac);
1369 rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution,
1370 rh);
1371 return;
1372 }
1373
1374 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1375 "Got REDIRECT `%s' from GNS for `%s'\n",
1376 rname,
1377 rh->name);
1378 if (NULL != rh->std_resolve)
1379 {
1380 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1381 "Multiple REDIRECT results from GNS resolving `%s'! Not really allowed...\n",
1382 rh->name);
1383 GNUNET_RESOLVER_request_cancel (rh->std_resolve);
1384 }
1385 /* name is absolute, go to DNS */
1386 GNUNET_free (rh->name);
1387 rh->name = GNUNET_strdup (rname);
1388 rh->name_resolution_pos = strlen (rh->name);
1389 switch (rh->record_type)
1390 {
1391 case GNUNET_DNSPARSER_TYPE_A:
1392 af = AF_INET;
1393 break;
1394
1395 case GNUNET_DNSPARSER_TYPE_AAAA:
1396 af = AF_INET6;
1397 break;
1398
1399 default:
1400 af = AF_UNSPEC;
1401 break;
1402 }
1403 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1404 "Doing standard DNS lookup for `%s'\n",
1405 rh->name);
1406
1407 rh->std_resolve = GNUNET_RESOLVER_ip_get (rh->name,
1408 af,
1409 DNS_LOOKUP_TIMEOUT,
1410 &handle_dns_result,
1411 rh);
1412}
1413
1414
1415/**
1416 * We encountered a CNAME record during our resolution.
1417 * Merge it into our chain.
1418 *
1419 * @param rh resolution we are performing
1420 * @param cname value of the cname record we got for the current
1421 * authority chain tail
1422 */
1423static void
1424handle_gns_cname_result (struct GNS_ResolverHandle *rh,
1425 const char *cname)
1426{
1427 int af;
1428
1429 GNUNET_free (rh->name);
1430 rh->name = GNUNET_strdup (cname);
1431 rh->name_resolution_pos = strlen (rh->name);
1432 switch (rh->record_type)
1433 {
1434 case GNUNET_DNSPARSER_TYPE_A:
1435 af = AF_INET;
1436 break;
1437
1438 case GNUNET_DNSPARSER_TYPE_AAAA:
1439 af = AF_INET6;
1440 break;
1441
1442 default:
1443 af = AF_UNSPEC;
1444 break;
1445 }
1446 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1447 "Doing standard DNS lookup for `%s'\n",
1448 rh->name);
1449
1450 rh->std_resolve = GNUNET_RESOLVER_ip_get (rh->name,
1451 af,
1452 DNS_LOOKUP_TIMEOUT,
1453 &handle_dns_result,
1454 rh);
1455}
1456
1457
1458/**
1459 * Process records that were decrypted from a block.
1460 *
1461 * @param cls closure with the 'struct GNS_ResolverHandle'
1462 * @param rd_count number of entries in @a rd array
1463 * @param rd array of records with data to store
1464 */
1465static void
1466handle_gns_resolution_result (void *cls,
1467 unsigned int rd_count,
1468 const struct GNUNET_GNSRECORD_Data *rd);
1469
1470
1471/**
1472 * We have resolved one or more of the nameservers for a
1473 * GNS2DNS lookup. Once we have some of them, begin using
1474 * the DNSSTUB resolver.
1475 *
1476 * @param ac context for GNS2DNS resolution
1477 */
1478static void
1479continue_with_gns2dns (struct AuthorityChain *ac)
1480{
1481 struct GNS_ResolverHandle *rh = ac->rh;
1482
1483 if ((NULL != ac->authority_info.dns_authority.gp_head) &&
1484 (GNUNET_NO == ac->authority_info.dns_authority.found))
1485 return; /* more pending and none found yet */
1486 if (GNUNET_NO == ac->authority_info.dns_authority.found)
1487 {
1488 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1489 "Failed to resolve DNS server for `%s' in GNS2DNS resolution\n",
1490 ac->authority_info.dns_authority.name);
1491 fail_resolution (rh);
1492 return;
1493 }
1494 if (GNUNET_NO != ac->authority_info.dns_authority.launched)
1495 return; /* already running, do not launch again! */
1496 /* recurse */
1497 ac->authority_info.dns_authority.launched = GNUNET_YES;
1498 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1499 "Will continue resolution using DNS to resolve `%s'\n",
1500 ac->label);
1501 GNUNET_assert (NULL == rh->task_id);
1502 rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution,
1503 rh);
1504}
1505
1506
1507/**
1508 * We've resolved the IP address for the DNS resolver to use
1509 * after encountering a GNS2DNS record.
1510 *
1511 * @param cls the `struct Gns2DnsPending` used for this request
1512 * @param rd_count number of records in @a rd
1513 * @param rd addresses for the DNS resolver (presumably)
1514 */
1515static void
1516handle_gns2dns_result (void *cls,
1517 unsigned int rd_count,
1518 const struct GNUNET_GNSRECORD_Data *rd)
1519{
1520 struct Gns2DnsPending *gp = cls;
1521 struct AuthorityChain *ac = gp->ac;
1522
1523 GNUNET_CONTAINER_DLL_remove (ac->authority_info.dns_authority.gp_head,
1524 ac->authority_info.dns_authority.gp_tail,
1525 gp);
1526 /* enable cleanup of 'rh' handle that automatically comes after we return,
1527 and which expects 'rh' to be in the #rlh_head DLL. */
1528 if (NULL != gp->rh)
1529 {
1530 GNUNET_CONTAINER_DLL_insert (rlh_head,
1531 rlh_tail,
1532 gp->rh);
1533 gp->rh = NULL;
1534 }
1535 GNUNET_free (gp);
1536 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1537 "Received %u results for IP address of DNS server for GNS2DNS transition\n",
1538 rd_count);
1539 /* find suitable A/AAAA record */
1540 for (unsigned int j = 0; j < rd_count; j++)
1541 {
1542 switch (rd[j].record_type)
1543 {
1544 case GNUNET_DNSPARSER_TYPE_A:
1545 {
1546 struct sockaddr_in v4;
1547
1548 if (sizeof(struct in_addr) != rd[j].data_size)
1549 {
1550 GNUNET_break_op (0);
1551 continue;
1552 }
1553 memset (&v4,
1554 0,
1555 sizeof(v4));
1556 v4.sin_family = AF_INET;
1557 v4.sin_port = htons (53);
1558#if HAVE_SOCKADDR_IN_SIN_LEN
1559 v4.sin_len = (u_char) sizeof(v4);
1560#endif
1561 GNUNET_memcpy (&v4.sin_addr,
1562 rd[j].data,
1563 sizeof(struct in_addr));
1564 if (GNUNET_OK ==
1565 GNUNET_DNSSTUB_add_dns_sa (
1566 ac->authority_info.dns_authority.dns_handle,
1567 (const struct sockaddr *) &v4))
1568 ac->authority_info.dns_authority.found = GNUNET_YES;
1569 break;
1570 }
1571
1572 case GNUNET_DNSPARSER_TYPE_AAAA:
1573 {
1574 struct sockaddr_in6 v6;
1575
1576 if (sizeof(struct in6_addr) != rd[j].data_size)
1577 {
1578 GNUNET_break_op (0);
1579 continue;
1580 }
1581 /* FIXME: might want to check if we support IPv6 here,
1582 and otherwise skip this one and hope we find another */
1583 memset (&v6,
1584 0,
1585 sizeof(v6));
1586 v6.sin6_family = AF_INET6;
1587 v6.sin6_port = htons (53);
1588#if HAVE_SOCKADDR_IN_SIN_LEN
1589 v6.sin6_len = (u_char) sizeof(v6);
1590#endif
1591 GNUNET_memcpy (&v6.sin6_addr,
1592 rd[j].data,
1593 sizeof(struct in6_addr));
1594 if (GNUNET_OK ==
1595 GNUNET_DNSSTUB_add_dns_sa (
1596 ac->authority_info.dns_authority.dns_handle,
1597 (const struct sockaddr *) &v6))
1598 ac->authority_info.dns_authority.found = GNUNET_YES;
1599 break;
1600 }
1601
1602 default:
1603 break;
1604 }
1605 }
1606 continue_with_gns2dns (ac);
1607}
1608
1609
1610/**
1611 * Function called by the resolver for each address obtained from DNS.
1612 *
1613 * @param cls closure, a `struct Gns2DnsPending *`
1614 * @param addr one of the addresses of the host, NULL for the last address
1615 * @param addrlen length of @a addr
1616 */
1617static void
1618handle_gns2dns_ip (void *cls,
1619 const struct sockaddr *addr,
1620 socklen_t addrlen)
1621{
1622 struct Gns2DnsPending *gp = cls;
1623 struct AuthorityChain *ac = gp->ac;
1624 struct sockaddr_storage ss;
1625 struct sockaddr_in *v4;
1626 struct sockaddr_in6 *v6;
1627
1628 if (NULL == addr)
1629 {
1630 /* DNS resolution finished */
1631 if (0 == gp->num_results)
1632 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1633 "Failed to use DNS to resolve name of DNS resolver\n");
1634 GNUNET_CONTAINER_DLL_remove (ac->authority_info.dns_authority.gp_head,
1635 ac->authority_info.dns_authority.gp_tail,
1636 gp);
1637 GNUNET_free (gp);
1638 continue_with_gns2dns (ac);
1639 return;
1640 }
1641 GNUNET_memcpy (&ss,
1642 addr,
1643 addrlen);
1644 switch (ss.ss_family)
1645 {
1646 case AF_INET:
1647 v4 = (struct sockaddr_in *) &ss;
1648 v4->sin_port = htons (53);
1649 gp->num_results++;
1650 break;
1651
1652 case AF_INET6:
1653 v6 = (struct sockaddr_in6 *) &ss;
1654 v6->sin6_port = htons (53);
1655 gp->num_results++;
1656 break;
1657
1658 default:
1659 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1660 "Unsupported AF %d\n",
1661 ss.ss_family);
1662 return;
1663 }
1664 if (GNUNET_OK ==
1665 GNUNET_DNSSTUB_add_dns_sa (ac->authority_info.dns_authority.dns_handle,
1666 (struct sockaddr *) &ss))
1667 ac->authority_info.dns_authority.found = GNUNET_YES;
1668}
1669
1670
1671/**
1672 * We found a REDIRECT record, perform recursive resolution on it.
1673 *
1674 * @param rh resolution handle
1675 * @param rd record with CNAME to resolve recursively
1676 */
1677static void
1678recursive_redirect_resolution (struct GNS_ResolverHandle *rh,
1679 const struct GNUNET_GNSRECORD_Data *rd)
1680{
1681 handle_gns_redirect_result (rh,
1682 rd->data);
1683}
1684
1685
1686/**
1687 * We found a CNAME record, perform recursive resolution on it.
1688 *
1689 * @param rh resolution handle
1690 * @param rd record with CNAME to resolve recursively
1691 */
1692static void
1693recursive_cname_resolution (struct GNS_ResolverHandle *rh,
1694 const struct GNUNET_GNSRECORD_Data *rd)
1695{
1696 char *cname;
1697 size_t off;
1698
1699 off = 0;
1700 cname = GNUNET_DNSPARSER_parse_name (rd->data,
1701 rd->data_size,
1702 &off);
1703 if ((NULL == cname) ||
1704 (off != rd->data_size))
1705 {
1706 GNUNET_break_op (0); /* record not well-formed */
1707 GNUNET_free (cname);
1708 fail_resolution (rh);
1709 return;
1710 }
1711 handle_gns_cname_result (rh,
1712 cname);
1713 GNUNET_free (cname);
1714}
1715
1716
1717/**
1718 * We found a PKEY record, perform recursive resolution on it.
1719 *
1720 * @param rh resolution handle
1721 * @param rd record with PKEY to resolve recursively
1722 */
1723static void
1724recursive_pkey_resolution (struct GNS_ResolverHandle *rh,
1725 const struct GNUNET_GNSRECORD_Data *rd)
1726{
1727 struct AuthorityChain *ac;
1728 struct GNUNET_CRYPTO_PublicKey auth;
1729
1730 /* delegation to another zone */
1731 if (GNUNET_OK != GNUNET_GNSRECORD_identity_from_data (rd->data,
1732 rd->data_size,
1733 rd->record_type,
1734 &auth))
1735 {
1736 GNUNET_break_op (0);
1737 fail_resolution (rh);
1738 return;
1739 }
1740 /* expand authority chain */
1741 ac = GNUNET_new (struct AuthorityChain);
1742 ac->rh = rh;
1743 ac->gns_authority = GNUNET_YES;
1744 ac->authority_info.gns_authority = auth;
1745 ac->label = resolver_lookup_get_next_label (rh);
1746 /* add AC to tail */
1747 GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head,
1748 rh->ac_tail,
1749 ac);
1750 /* recurse */
1751 rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution,
1752 rh);
1753}
1754
1755
1756/**
1757 * We found one or more GNS2DNS records, perform recursive resolution on it.
1758 * (to be precise, one or more records in @a rd is GNS2DNS, there may be others,
1759 * so this function still needs to check which ones are GNS2DNS).
1760 *
1761 * @param rh resolution handle
1762 * @param rd_count length of the @a rd array
1763 * @param rd record with PKEY to resolve recursively
1764 * @return #GNUNET_OK if this worked, #GNUNET_SYSERR if no GNS2DNS records were in @a rd
1765 */
1766static int
1767recursive_gns2dns_resolution (struct GNS_ResolverHandle *rh,
1768 unsigned int rd_count,
1769 const struct GNUNET_GNSRECORD_Data *rd)
1770{
1771 struct AuthorityChain *ac;
1772 const char *tld;
1773 char *ns;
1774
1775 ns = NULL;
1776 /* expand authority chain */
1777 ac = GNUNET_new (struct AuthorityChain);
1778 ac->rh = rh;
1779 ac->authority_info.dns_authority.dns_handle = GNUNET_DNSSTUB_start (4);
1780
1781 for (unsigned int i = 0; i < rd_count; i++)
1782 {
1783 char *ip;
1784 char *n;
1785 size_t off;
1786 struct Gns2DnsPending *gp;
1787 struct GNUNET_CRYPTO_PublicKey zone;
1788 struct sockaddr_in v4;
1789 struct sockaddr_in6 v6;
1790
1791 if (GNUNET_GNSRECORD_TYPE_GNS2DNS != rd[i].record_type)
1792 {
1793 /**
1794 * Records other than GNS2DNS not allowed
1795 */
1796 GNUNET_free (ns);
1797 GNUNET_free (ac);
1798 return GNUNET_SYSERR;
1799 }
1800 off = 0;
1801 n = GNUNET_DNSPARSER_parse_name (rd[i].data,
1802 rd[i].data_size,
1803 &off);
1804 ip = GNUNET_strdup (&((const char *) rd[i].data)[off]);
1805 if ((NULL == n) ||
1806 (NULL == ip))
1807 {
1808 GNUNET_break_op (0);
1809 GNUNET_free (n);
1810 GNUNET_free (ip);
1811 continue;
1812 }
1813
1814 off += strlen (ip) + 1;
1815
1816 if (off != rd[i].data_size)
1817 {
1818 GNUNET_break_op (0);
1819 GNUNET_free (n);
1820 GNUNET_free (ip);
1821 continue;
1822 }
1823 /* resolve 'ip' to determine the IP(s) of the DNS
1824 resolver to use for lookup of 'ns' */
1825 if (NULL != ns)
1826 {
1827 if (0 != strcasecmp (ns,
1828 n))
1829 {
1830 /* NS values must all be the same for all GNS2DNS records,
1831 anything else leads to insanity */
1832 GNUNET_break_op (0);
1833 GNUNET_free (n);
1834 GNUNET_free (ip);
1835 continue;
1836 }
1837 GNUNET_free (n);
1838 }
1839 else
1840 {
1841 ns = n;
1842 }
1843
1844 /* check if 'ip' is already an IPv4/IPv6 address */
1845 if ((1 == inet_pton (AF_INET,
1846 ip,
1847 &v4)) ||
1848 (1 == inet_pton (AF_INET6,
1849 ip,
1850 &v6)))
1851 {
1852 GNUNET_break (GNUNET_OK ==
1853 GNUNET_DNSSTUB_add_dns_ip (
1854 ac->authority_info.dns_authority.dns_handle,
1855 ip));
1856 ac->authority_info.dns_authority.found = GNUNET_YES;
1857 GNUNET_free (ip);
1858 continue;
1859 }
1860 tld = GNS_get_tld (ip);
1861 if ((0 != strcmp (tld, "+")) &&
1862 (GNUNET_OK != GNUNET_GNSRECORD_zkey_to_pkey (tld, &zone)))
1863 {
1864 /* 'ip' is a DNS name */
1865 gp = GNUNET_new (struct Gns2DnsPending);
1866 gp->ac = ac;
1867 GNUNET_CONTAINER_DLL_insert (ac->authority_info.dns_authority.gp_head,
1868 ac->authority_info.dns_authority.gp_tail,
1869 gp);
1870 gp->dns_rh = GNUNET_RESOLVER_ip_get (ip,
1871 AF_UNSPEC,
1872 GNUNET_TIME_UNIT_FOREVER_REL,
1873 &handle_gns2dns_ip,
1874 gp);
1875 GNUNET_free (ip);
1876 continue;
1877 }
1878 /* 'ip' should be a GNS name */
1879 gp = GNUNET_new (struct Gns2DnsPending);
1880 gp->ac = ac;
1881 GNUNET_CONTAINER_DLL_insert (ac->authority_info.dns_authority.gp_head,
1882 ac->authority_info.dns_authority.gp_tail,
1883 gp);
1884 gp->rh = GNUNET_new (struct GNS_ResolverHandle);
1885 if (0 == strcmp (tld, "+"))
1886 {
1887 ip = translate_dot_plus (rh,
1888 ip);
1889 tld = GNS_get_tld (ip);
1890 if (GNUNET_OK !=
1891 GNUNET_GNSRECORD_zkey_to_pkey (tld,
1892 &zone))
1893 {
1894 GNUNET_break_op (0);
1895 GNUNET_free (ip);
1896 continue;
1897 }
1898 }
1899 gp->rh->authority_zone = zone;
1900 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1901 "Resolving `%s' to determine IP address of DNS server for GNS2DNS transition for `%s'\n",
1902 ip,
1903 ns);
1904 gp->rh->name = ip;
1905 gp->rh->name_resolution_pos = strlen (ip) - strlen (tld) - 1;
1906 gp->rh->proc = &handle_gns2dns_result;
1907 gp->rh->proc_cls = gp;
1908 gp->rh->record_type = GNUNET_GNSRECORD_TYPE_ANY;
1909 gp->rh->options = GNUNET_GNS_LO_DEFAULT;
1910 gp->rh->loop_limiter = rh->loop_limiter + 1;
1911 gp->rh->loop_threshold = rh->loop_threshold;
1912 gp->rh->task_id
1913 = GNUNET_SCHEDULER_add_now (&start_resolver_lookup,
1914 gp->rh);
1915 } /* end 'for all records' */
1916
1917 if (NULL == ns)
1918 {
1919 /* not a single GNS2DNS record found */
1920 GNUNET_free (ac);
1921 return GNUNET_SYSERR;
1922 }
1923 GNUNET_assert (strlen (ns) <= GNUNET_DNSPARSER_MAX_NAME_LENGTH);
1924 strcpy (ac->authority_info.dns_authority.name,
1925 ns);
1926 /* for DNS recursion, the label is the full DNS name,
1927 created from the remainder of the GNS name and the
1928 name in the NS record */
1929 GNUNET_asprintf (&ac->label,
1930 "%.*s%s%s",
1931 (int) rh->name_resolution_pos,
1932 rh->name,
1933 (0 != rh->name_resolution_pos) ? "." : "",
1934 ns);
1935 GNUNET_free (ns);
1936
1937 {
1938 /* the GNS name is UTF-8 and may include multibyte chars.
1939 * We have to convert the combined name to a DNS-compatible IDNA.
1940 */
1941 char *tmp = ac->label;
1942
1943 if (IDNA_SUCCESS != idna_to_ascii_8z (tmp,
1944 &ac->label,
1945 IDNA_ALLOW_UNASSIGNED))
1946 {
1947 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1948 _ ("Name `%s' cannot be converted to IDNA."),
1949 tmp);
1950 GNUNET_free (tmp);
1951 GNUNET_free (ac);
1952 return GNUNET_SYSERR;
1953 }
1954 GNUNET_free (tmp);
1955 }
1956
1957 GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head,
1958 rh->ac_tail,
1959 ac);
1960 if (strlen (ac->label) > GNUNET_DNSPARSER_MAX_NAME_LENGTH)
1961 {
1962 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1963 _ ("GNS lookup resulted in DNS name that is too long (`%s')\n"),
1964 ac->label);
1965 GNUNET_free (ac->label);
1966 GNUNET_free (ac);
1967 return GNUNET_SYSERR;
1968 }
1969 continue_with_gns2dns (ac);
1970 return GNUNET_OK;
1971}
1972
1973
1974static void
1975handle_gns_resolution_result (void *cls,
1976 unsigned int rd_count,
1977 const struct GNUNET_GNSRECORD_Data *rd)
1978{
1979 struct GNS_ResolverHandle *rh = cls;
1980 char *cname;
1981 char scratch[UINT16_MAX];
1982 size_t scratch_off;
1983 size_t scratch_start;
1984 size_t off;
1985 struct GNUNET_GNSRECORD_Data rd_new[rd_count];
1986 unsigned int rd_off;
1987
1988 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1989 "Resolution succeeded for `%s' in zone %s, got %u records\n",
1990 rh->ac_tail->label,
1991 GNUNET_GNSRECORD_z2s (&rh->ac_tail->authority_info.gns_authority),
1992 rd_count);
1993 if (0 == rd_count)
1994 {
1995 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1996 _ ("GNS lookup failed (zero records found for `%s')\n"),
1997 rh->name);
1998 fail_resolution (rh);
1999 return;
2000 }
2001
2002 if (0 == rh->name_resolution_pos)
2003 {
2004 /* top-level match, are we done yet? */
2005 if ((rd_count > 0) &&
2006 (GNUNET_DNSPARSER_TYPE_CNAME == rd[0].record_type) &&
2007 (GNUNET_DNSPARSER_TYPE_CNAME != rh->record_type))
2008 {
2009 off = 0;
2010 cname = GNUNET_DNSPARSER_parse_name (rd[0].data,
2011 rd[0].data_size,
2012 &off);
2013 if ((NULL == cname) ||
2014 (off != rd[0].data_size))
2015 {
2016 GNUNET_break_op (0);
2017 GNUNET_free (cname);
2018 fail_resolution (rh);
2019 return;
2020 }
2021 handle_gns_cname_result (rh,
2022 cname);
2023 GNUNET_free (cname);
2024 return;
2025 }
2026 if ((rd_count > 0) &&
2027 (GNUNET_GNSRECORD_TYPE_REDIRECT == rd[0].record_type) &&
2028 (GNUNET_GNSRECORD_TYPE_REDIRECT != rh->record_type))
2029 {
2030 handle_gns_redirect_result (rh,
2031 rd[0].data);
2032 return;
2033 }
2034
2035
2036 /* If A/AAAA was requested,
2037 * but we got a GNS2DNS record */
2038 if ((GNUNET_DNSPARSER_TYPE_A == rh->record_type) ||
2039 (GNUNET_DNSPARSER_TYPE_AAAA == rh->record_type))
2040 {
2041 for (unsigned int i = 0; i < rd_count; i++)
2042 {
2043 switch (rd[i].record_type)
2044 {
2045 case GNUNET_GNSRECORD_TYPE_GNS2DNS:
2046 {
2047 /* delegation to DNS */
2048 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2049 "Found GNS2DNS record, delegating to DNS!\n");
2050 if (GNUNET_OK ==
2051 recursive_gns2dns_resolution (rh,
2052 rd_count,
2053 rd))
2054 return;
2055 else
2056 goto fail;
2057 }
2058
2059 default:
2060 break;
2061 } /* end: switch */
2062 } /* end: for rd */
2063 } /* end: name_resolution_pos */
2064 /* convert relative names in record values to absolute names,
2065 using 'scratch' array for memory allocations */
2066 scratch_off = 0;
2067 rd_off = 0;
2068 for (unsigned int i = 0; i < rd_count; i++)
2069 {
2070 GNUNET_assert (rd_off <= i);
2071 if ((((0 != rh->protocol) &&
2072 (0 != rh->service)) || (NULL != rh->prefix)) &&
2073 (GNUNET_GNSRECORD_TYPE_BOX != rd[i].record_type &&
2074 GNUNET_GNSRECORD_TYPE_SBOX != rd[i].record_type))
2075 if (GNUNET_GNSRECORD_TYPE_PKEY != rd[i].record_type &&
2076 GNUNET_GNSRECORD_TYPE_EDKEY != rd[i].record_type)
2077 continue;
2078 /* we _only_ care about boxed records */
2079
2080 GNUNET_assert (rd_off < rd_count);
2081 rd_new[rd_off] = rd[i];
2082 /* Check if the embedded name(s) end in "+", and if so,
2083 replace the "+" with the zone at "ac_tail", changing the name
2084 to a ".ZONEKEY". The name is allocated on the 'scratch' array,
2085 so we can free it afterwards. */
2086 switch (rd[i].record_type)
2087 {
2088 case GNUNET_GNSRECORD_TYPE_REDIRECT:
2089 {
2090 char *rname;
2091 rname = GNUNET_strndup (rd[i].data, rd[i].data_size);
2092 rname = translate_dot_plus (rh, rname);
2093 GNUNET_break (NULL != rname);
2094 scratch_start = scratch_off;
2095 memcpy (&scratch[scratch_start], rname, strlen (rname) + 1);
2096 scratch_off += strlen (rname) + 1;
2097 GNUNET_assert (rd_off < rd_count);
2098 rd_new[rd_off].data = &scratch[scratch_start];
2099 rd_new[rd_off].data_size = scratch_off - scratch_start;
2100 rd_off++;
2101 GNUNET_free (rname);
2102 }
2103 break;
2104
2105 case GNUNET_DNSPARSER_TYPE_CNAME:
2106 {
2107 char *cname;
2108
2109 off = 0;
2110 cname = GNUNET_DNSPARSER_parse_name (rd[i].data,
2111 rd[i].data_size,
2112 &off);
2113 if ((NULL == cname) ||
2114 (off != rd[i].data_size))
2115 {
2116 GNUNET_break_op (0); /* record not well-formed */
2117 }
2118 else
2119 {
2120 cname = translate_dot_plus (rh, cname);
2121 GNUNET_break (NULL != cname);
2122 scratch_start = scratch_off;
2123 if (GNUNET_OK !=
2124 GNUNET_DNSPARSER_builder_add_name (scratch,
2125 sizeof(scratch),
2126 &scratch_off,
2127 cname))
2128 {
2129 GNUNET_break (0);
2130 }
2131 else
2132 {
2133 GNUNET_assert (rd_off < rd_count);
2134 rd_new[rd_off].data = &scratch[scratch_start];
2135 rd_new[rd_off].data_size = scratch_off - scratch_start;
2136 rd_off++;
2137 }
2138 }
2139 GNUNET_free (cname);
2140 }
2141 break;
2142
2143 case GNUNET_DNSPARSER_TYPE_SOA:
2144 {
2145 struct GNUNET_DNSPARSER_SoaRecord *soa;
2146
2147 off = 0;
2148 soa = GNUNET_DNSPARSER_parse_soa (rd[i].data,
2149 rd[i].data_size,
2150 &off);
2151 if ((NULL == soa) ||
2152 (off != rd[i].data_size))
2153 {
2154 GNUNET_break_op (0); /* record not well-formed */
2155 }
2156 else
2157 {
2158 soa->mname = translate_dot_plus (rh, soa->mname);
2159 soa->rname = translate_dot_plus (rh, soa->rname);
2160 scratch_start = scratch_off;
2161 if (GNUNET_OK !=
2162 GNUNET_DNSPARSER_builder_add_soa (scratch,
2163 sizeof(scratch),
2164 &scratch_off,
2165 soa))
2166 {
2167 GNUNET_break (0);
2168 }
2169 else
2170 {
2171 GNUNET_assert (rd_off < rd_count);
2172 rd_new[rd_off].data = &scratch[scratch_start];
2173 rd_new[rd_off].data_size = scratch_off - scratch_start;
2174 rd_off++;
2175 }
2176 }
2177 if (NULL != soa)
2178 GNUNET_DNSPARSER_free_soa (soa);
2179 }
2180 break;
2181
2182 case GNUNET_DNSPARSER_TYPE_MX:
2183 {
2184 struct GNUNET_DNSPARSER_MxRecord *mx;
2185
2186 off = 0;
2187 mx = GNUNET_DNSPARSER_parse_mx (rd[i].data,
2188 rd[i].data_size,
2189 &off);
2190 if ((NULL == mx) ||
2191 (off != rd[i].data_size))
2192 {
2193 GNUNET_break_op (0); /* record not well-formed */
2194 }
2195 else
2196 {
2197 mx->mxhost = translate_dot_plus (rh, mx->mxhost);
2198 scratch_start = scratch_off;
2199 if (GNUNET_OK !=
2200 GNUNET_DNSPARSER_builder_add_mx (scratch,
2201 sizeof(scratch),
2202 &scratch_off,
2203 mx))
2204 {
2205 GNUNET_break (0);
2206 }
2207 else
2208 {
2209 GNUNET_assert (rd_off < rd_count);
2210 rd_new[rd_off].data = &scratch[scratch_start];
2211 rd_new[rd_off].data_size = scratch_off - scratch_start;
2212 rd_off++;
2213 }
2214 }
2215 if (NULL != mx)
2216 GNUNET_DNSPARSER_free_mx (mx);
2217 }
2218 break;
2219
2220 case GNUNET_DNSPARSER_TYPE_SRV:
2221 {
2222 struct GNUNET_DNSPARSER_SrvRecord *srv;
2223
2224 off = 0;
2225 srv = GNUNET_DNSPARSER_parse_srv (rd[i].data,
2226 rd[i].data_size,
2227 &off);
2228 if ((NULL == srv) ||
2229 (off != rd[i].data_size))
2230 {
2231 GNUNET_break_op (0); /* record not well-formed */
2232 }
2233 else
2234 {
2235 srv->target = translate_dot_plus (rh, srv->target);
2236 scratch_start = scratch_off;
2237 if (GNUNET_OK !=
2238 GNUNET_DNSPARSER_builder_add_srv (scratch,
2239 sizeof(scratch),
2240 &scratch_off,
2241 srv))
2242 {
2243 GNUNET_break (0);
2244 }
2245 else
2246 {
2247 GNUNET_assert (rd_off < rd_count);
2248 rd_new[rd_off].data = &scratch[scratch_start];
2249 rd_new[rd_off].data_size = scratch_off - scratch_start;
2250 rd_off++;
2251 }
2252 }
2253 if (NULL != srv)
2254 GNUNET_DNSPARSER_free_srv (srv);
2255 }
2256 break;
2257
2258 case GNUNET_DNSPARSER_TYPE_URI:
2259 {
2260 struct GNUNET_DNSPARSER_UriRecord *uri;
2261
2262 off = 0;
2263 uri = GNUNET_DNSPARSER_parse_uri (rd[i].data,
2264 rd[i].data_size,
2265 &off);
2266 if ((NULL == uri) ||
2267 (off != rd[i].data_size))
2268 {
2269 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2270 _ ("Failed to deserialize URI record with target\n"));
2271 GNUNET_break_op (0); /* record not well-formed */
2272 }
2273 else
2274 {
2275 scratch_start = scratch_off;
2276 if (GNUNET_OK !=
2277 GNUNET_DNSPARSER_builder_add_uri (scratch,
2278 sizeof(scratch),
2279 &scratch_off,
2280 uri))
2281 {
2282 GNUNET_break (0);
2283 }
2284 else
2285 {
2286 GNUNET_assert (rd_off < rd_count);
2287 rd_new[rd_off].data = &scratch[scratch_start];
2288 rd_new[rd_off].data_size = scratch_off - scratch_start;
2289 rd_off++;
2290 }
2291 }
2292 if (NULL != uri)
2293 GNUNET_DNSPARSER_free_uri (uri);
2294 }
2295 break;
2296
2297 case GNUNET_GNSRECORD_TYPE_PKEY:
2298 case GNUNET_GNSRECORD_TYPE_EDKEY:
2299 {
2300 struct GNUNET_CRYPTO_PublicKey pubkey;
2301 if (rd[i].data_size < sizeof(uint32_t))
2302 {
2303 GNUNET_break_op (0);
2304 break;
2305 }
2306 if (GNUNET_OK !=
2307 GNUNET_GNSRECORD_identity_from_data (rd[i].data,
2308 rd[i].data_size,
2309 rd[i].record_type,
2310 &pubkey))
2311 {
2312 GNUNET_break_op (0);
2313 break;
2314 }
2315 rd_off++;
2316 if (rd[i].record_type != rh->record_type)
2317 {
2318 /* try to resolve "@" */
2319 struct AuthorityChain *ac;
2320
2321 ac = GNUNET_new (struct AuthorityChain);
2322 ac->rh = rh;
2323 ac->gns_authority = GNUNET_YES;
2324 ac->authority_info.gns_authority = pubkey;
2325 ac->label = GNUNET_strdup (GNUNET_GNS_EMPTY_LABEL_AT);
2326 GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head,
2327 rh->ac_tail,
2328 ac);
2329 rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution,
2330 rh);
2331 return;
2332 }
2333 }
2334 break;
2335
2336 case GNUNET_GNSRECORD_TYPE_GNS2DNS:
2337 {
2338 /* delegation to DNS */
2339 if (GNUNET_GNSRECORD_TYPE_GNS2DNS == rh->record_type)
2340 {
2341 rd_off++;
2342 break; /* do not follow to DNS, we wanted the GNS2DNS record! */
2343 }
2344 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2345 "Found GNS2DNS record, delegating to DNS!\n");
2346 if (GNUNET_OK ==
2347 recursive_gns2dns_resolution (rh,
2348 rd_count,
2349 rd))
2350 return;
2351 else
2352 goto fail;
2353 }
2354
2355 case GNUNET_GNSRECORD_TYPE_BOX:
2356 {
2357 /* unbox SRV/TLSA records if a specific one was requested */
2358 if ((0 != rh->protocol) &&
2359 (0 != rh->service) &&
2360 (rd[i].data_size >= sizeof(struct GNUNET_GNSRECORD_BoxRecord)))
2361 {
2362 const struct GNUNET_GNSRECORD_BoxRecord *box;
2363
2364 box = rd[i].data;
2365 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2366 "Got BOX record, checking if parameters match... %u/%u vs %u/%u\n",
2367 ntohs (box->protocol), ntohs (box->service),
2368 rh->protocol, rh->service);
2369 if ((ntohs (box->protocol) == rh->protocol) &&
2370 (ntohs (box->service) == rh->service))
2371 {
2372 /* Box matches, unbox! */
2373 GNUNET_assert (rd_off < rd_count);
2374 rd_new[rd_off].record_type = ntohl (box->record_type);
2375 rd_new[rd_off].data_size -= sizeof(struct
2376 GNUNET_GNSRECORD_BoxRecord);
2377 rd_new[rd_off].data = &box[1];
2378 rd_off++;
2379 }
2380 }
2381 else
2382 {
2383 /* no specific protocol/service specified, preserve all BOX
2384 records (for modern, GNS-enabled applications) */
2385 rd_off++;
2386 }
2387 break;
2388 }
2389 case GNUNET_GNSRECORD_TYPE_SBOX:
2390 {
2391 /* unbox SBOX records if a specific one was requested */
2392 if ((rh->prefix != NULL) &&
2393 (rd[i].data_size >= sizeof(struct GNUNET_GNSRECORD_SBoxRecord)))
2394 {
2395 const struct GNUNET_GNSRECORD_SBoxRecord *box;
2396
2397 box = rd[i].data;
2398 const char *prefix = rd[i].data + sizeof(struct
2399 GNUNET_GNSRECORD_SBoxRecord);
2400 size_t prefix_len = strnlen (prefix, rd[i].data_size - sizeof(struct
2401 GNUNET_GNSRECORD_SBoxRecord))
2402 + 1;
2403 if (prefix_len - 1 >= rd[i].data_size - sizeof(struct
2404 GNUNET_GNSRECORD_SBoxRecord))
2405 {
2406 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2407 "SBOX record with invalid prefix length, maybe not null-terminated\n");
2408 continue;
2409 }
2410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2411 "Got SBOX record, checking if prefixes match... %s vs %s\n",
2412 prefix, rh->prefix);
2413 if (strcmp (rh->prefix, prefix) == 0)
2414 {
2415 /* Box matches, unbox! */
2416 GNUNET_assert (rd_off < rd_count);
2417 rd_new[rd_off].record_type = ntohl (box->record_type);
2418 rd_new[rd_off].data_size -= sizeof(struct
2419 GNUNET_GNSRECORD_SBoxRecord)
2420 + prefix_len;
2421 rd_new[rd_off].data = rd[i].data
2422 + sizeof(struct GNUNET_GNSRECORD_SBoxRecord)
2423 + prefix_len;
2424 rd_off++;
2425 }
2426 }
2427 else
2428 {
2429 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2430 _ (
2431 "GNS no specific protocol/service specified, preserve all SBOX `%s')\n"),
2432 rh->name);
2433 /* no specific protocol/service specified, preserve all SBOX
2434 records (for modern, GNS-enabled applications) */
2435 rd_off++;
2436 }
2437 break;
2438 }
2439 default:
2440 rd_off++;
2441 break;
2442 } /* end: switch */
2443 } /* end: for rd_count */
2444
2445 GNUNET_free (rh->prefix);
2446 rh->prefix = NULL;
2447
2448 /* yes, we are done, return result */
2449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2450 "Returning GNS response for `%s' with %u answers\n",
2451 rh->ac_tail->label,
2452 rd_off);
2453 rh->proc (rh->proc_cls,
2454 rd_off,
2455 rd_new);
2456 rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_,
2457 rh);
2458 return;
2459 }
2460
2461 switch (rd[0].record_type)
2462 {
2463 case GNUNET_GNSRECORD_TYPE_REDIRECT:
2464 GNUNET_break_op (1 == rd_count); /* REDIRECT should be unique */
2465 recursive_redirect_resolution (rh,
2466 &rd[0]);
2467 return;
2468
2469 case GNUNET_DNSPARSER_TYPE_CNAME:
2470 GNUNET_break_op (1 == rd_count); /* CNAME should be unique */
2471 recursive_cname_resolution (rh,
2472 &rd[0]);
2473 return;
2474
2475 case GNUNET_GNSRECORD_TYPE_PKEY:
2476 case GNUNET_GNSRECORD_TYPE_EDKEY:
2477 GNUNET_break_op (1 == rd_count); /* PKEY should be unique */
2478 recursive_pkey_resolution (rh,
2479 &rd[0]);
2480 return;
2481
2482 case GNUNET_GNSRECORD_TYPE_GNS2DNS:
2483 if (GNUNET_OK ==
2484 recursive_gns2dns_resolution (rh,
2485 rd_count,
2486 rd))
2487 return;
2488 break;
2489 default:
2490 if (GNUNET_YES != GNUNET_GNSRECORD_is_critical (rd[0].record_type))
2491 return;
2492 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2493 _ ("Unable to process critical delegation record\n"));
2494 break;
2495 }
2496fail:
2497 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2498 _ ("GNS lookup recursion failed (no delegation record found)\n"));
2499 fail_resolution (rh);
2500}
2501
2502
2503/**
2504 * Function called once the namestore has completed the request for
2505 * caching a block.
2506 *
2507 * @param cls closure with the `struct CacheOps`
2508 * @param success #GNUNET_OK on success
2509 * @param emsg error message
2510 */
2511static void
2512namecache_cache_continuation (void *cls,
2513 int32_t success,
2514 const char *emsg)
2515{
2516 struct CacheOps *co = cls;
2517
2518 co->namecache_qe_cache = NULL;
2519 if (GNUNET_OK != success)
2520 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2521 _ ("Failed to cache GNS resolution: %s\n"),
2522 emsg);
2523 GNUNET_CONTAINER_DLL_remove (co_head,
2524 co_tail,
2525 co);
2526 GNUNET_free (co);
2527}
2528
2529
2530/**
2531 * Iterator called on each result obtained for a DHT
2532 * operation that expects a reply
2533 *
2534 * @param cls closure with the `struct GNS_ResolverHandle`
2535 * @param exp when will this value expire
2536 * @param key key of the result
2537 * @param trunc_peer truncated peer, NULL if not truncated
2538 * @param get_path peers on reply path (or NULL if not recorded)
2539 * [0] = datastore's first neighbor, [length - 1] = local peer
2540 * @param get_path_length number of entries in @a get_path
2541 * @param put_path peers on the PUT path (or NULL if not recorded)
2542 * [0] = origin, [length - 1] = datastore
2543 * @param put_path_length number of entries in @a put_path
2544 * @param type type of the result
2545 * @param size number of bytes in data
2546 * @param data pointer to the result data
2547 */
2548static void
2549handle_dht_response (void *cls,
2550 struct GNUNET_TIME_Absolute exp,
2551 const struct GNUNET_HashCode *key,
2552 const struct GNUNET_PeerIdentity *trunc_peer,
2553 const struct GNUNET_DHT_PathElement *get_path,
2554 unsigned int get_path_length,
2555 const struct GNUNET_DHT_PathElement *put_path,
2556 unsigned int put_path_length,
2557 enum GNUNET_BLOCK_Type type,
2558 size_t size,
2559 const void *data)
2560{
2561 struct GNS_ResolverHandle *rh = cls;
2562 struct AuthorityChain *ac = rh->ac_tail;
2563 const struct GNUNET_GNSRECORD_Block *block;
2564 struct CacheOps *co;
2565
2566 (void) exp;
2567 (void) key;
2568 (void) get_path;
2569 (void) get_path_length;
2570 (void) put_path;
2571 (void) put_path_length;
2572 (void) type;
2573 GNUNET_DHT_get_stop (rh->get_handle);
2574 rh->get_handle = NULL;
2575 GNUNET_CONTAINER_heap_remove_node (rh->dht_heap_node);
2576 rh->dht_heap_node = NULL;
2577 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2578 "Handling response from the DHT\n");
2579 if (size < sizeof(struct GNUNET_GNSRECORD_Block))
2580 {
2581 /* how did this pass DHT block validation!? */
2582 GNUNET_break (0);
2583 fail_resolution (rh);
2584 return;
2585 }
2586 block = data;
2587 if (size != GNUNET_GNSRECORD_block_get_size (block))
2588 {
2589 /* how did this pass DHT block validation!? */
2590 GNUNET_break (0);
2591 fail_resolution (rh);
2592 return;
2593 }
2594 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2595 "Decrypting DHT block of size %llu for `%s', expires %s\n",
2596 (unsigned long long) GNUNET_GNSRECORD_block_get_size (block),
2597 rh->name,
2598 GNUNET_STRINGS_absolute_time_to_string (exp));
2599 if (GNUNET_OK !=
2600 GNUNET_GNSRECORD_block_decrypt (block,
2601 &ac->authority_info.gns_authority,
2602 ac->label,
2603 &handle_gns_resolution_result,
2604 rh))
2605 {
2606 GNUNET_break_op (0); /* block was ill-formed */
2607 fail_resolution (rh);
2608 return;
2609 }
2610 if (0 == GNUNET_TIME_absolute_get_remaining (
2611 GNUNET_GNSRECORD_block_get_expiration (block)).
2612 rel_value_us)
2613 {
2614 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2615 "Received expired block from the DHT, will not cache it.\n");
2616 return;
2617 }
2618 if (GNUNET_YES == disable_cache)
2619 return;
2620 /* Cache well-formed blocks */
2621 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2622 "Caching response from the DHT in namecache\n");
2623 co = GNUNET_new (struct CacheOps);
2624 co->namecache_qe_cache = GNUNET_NAMECACHE_block_cache (namecache_handle,
2625 block,
2626 &
2627 namecache_cache_continuation,
2628 co);
2629 GNUNET_CONTAINER_DLL_insert (co_head,
2630 co_tail,
2631 co);
2632}
2633
2634
2635/**
2636 * Initiate a DHT query for a set of GNS records.
2637 *
2638 * @param rh resolution handle
2639 * @param query key to use in the DHT lookup
2640 */
2641static void
2642start_dht_request (struct GNS_ResolverHandle *rh,
2643 const struct GNUNET_HashCode *query)
2644{
2645 struct GNS_ResolverHandle *rx;
2646
2647 GNUNET_assert (NULL == rh->get_handle);
2648 rh->get_handle = GNUNET_DHT_get_start (dht_handle,
2649 GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
2650 query,
2651 DHT_GNS_REPLICATION_LEVEL,
2652 GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
2653 NULL, 0,
2654 &handle_dht_response, rh);
2655 rh->dht_heap_node = GNUNET_CONTAINER_heap_insert (dht_lookup_heap,
2656 rh,
2657 GNUNET_TIME_absolute_get ().
2658 abs_value_us);
2659 if (GNUNET_CONTAINER_heap_get_size (dht_lookup_heap) >
2660 max_allowed_background_queries)
2661 {
2662 /* fail longest-standing DHT request */
2663 rx = GNUNET_CONTAINER_heap_remove_root (dht_lookup_heap);
2664 rx->dht_heap_node = NULL;
2665 GNUNET_assert (NULL != rx);
2666 fail_resolution (rx);
2667 }
2668}
2669
2670
2671/**
2672 * Process a records that were decrypted from a block that we got from
2673 * the namecache. Simply calls #handle_gns_resolution_result().
2674 *
2675 * @param cls closure with the `struct GNS_ResolverHandle`
2676 * @param rd_count number of entries in @a rd array
2677 * @param rd array of records with data to store
2678 */
2679static void
2680handle_gns_namecache_resolution_result (void *cls,
2681 unsigned int rd_count,
2682 const struct GNUNET_GNSRECORD_Data *rd)
2683{
2684 struct GNS_ResolverHandle *rh = cls;
2685
2686 if (0 == rd_count)
2687 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2688 _ ("GNS namecache returned empty result for `%s'\n"),
2689 rh->name);
2690 handle_gns_resolution_result (rh,
2691 rd_count,
2692 rd);
2693}
2694
2695
2696/**
2697 * Process a record that was stored in the namecache.
2698 *
2699 * @param cls closure with the `struct GNS_ResolverHandle`
2700 * @param block block that was stored in the namecache
2701 */
2702static void
2703handle_namecache_block_response (void *cls,
2704 const struct GNUNET_GNSRECORD_Block *block)
2705{
2706 struct GNS_ResolverHandle *rh = cls;
2707 struct AuthorityChain *ac = rh->ac_tail;
2708 const char *label = ac->label;
2709 const struct GNUNET_CRYPTO_PublicKey *auth =
2710 &ac->authority_info.gns_authority;
2711 struct GNUNET_HashCode query;
2712
2713 GNUNET_assert (NULL != rh->namecache_qe);
2714 rh->namecache_qe = NULL;
2715 if (NULL == block)
2716 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2717 "No block found\n");
2718 else
2719 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2720 "Got block with expiration %s\n",
2721 GNUNET_STRINGS_absolute_time_to_string (
2722 GNUNET_GNSRECORD_block_get_expiration (block)));
2723 if (((GNUNET_GNS_LO_DEFAULT == rh->options) ||
2724 ((GNUNET_GNS_LO_LOCAL_MASTER == rh->options) &&
2725 (ac != rh->ac_head))) &&
2726 ((NULL == block) ||
2727 (0 == GNUNET_TIME_absolute_get_remaining (
2728 GNUNET_GNSRECORD_block_get_expiration (block)).
2729 rel_value_us)))
2730 {
2731 /* namecache knows nothing; try DHT lookup */
2732 GNUNET_GNSRECORD_query_from_public_key (auth,
2733 label,
2734 &query);
2735 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2736 "Starting DHT lookup for `%s' in zone `%s' under key `%s'\n",
2737 ac->label,
2738 GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority),
2739 GNUNET_h2s (&query));
2740 start_dht_request (rh, &query);
2741 return;
2742 }
2743
2744 if ((NULL == block) ||
2745 (0 == GNUNET_TIME_absolute_get_remaining (
2746 GNUNET_GNSRECORD_block_get_expiration (block)).
2747 rel_value_us))
2748 {
2749 /* DHT not permitted and no local result, fail */
2750 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2751 "Resolution failed for `%s' in zone %s (DHT lookup not permitted by configuration)\n",
2752 ac->label,
2753 GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority));
2754 fail_resolution (rh);
2755 return;
2756 }
2757 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2758 "Received result from namecache for label `%s'\n",
2759 ac->label);
2760
2761 if (GNUNET_OK !=
2762 GNUNET_GNSRECORD_block_decrypt (block,
2763 auth,
2764 label,
2765 &handle_gns_namecache_resolution_result,
2766 rh))
2767 {
2768 GNUNET_break_op (0); /* block was ill-formed */
2769 /* try DHT instead */
2770 GNUNET_GNSRECORD_query_from_public_key (auth,
2771 label,
2772 &query);
2773 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2774 "Starting DHT lookup for `%s' in zone `%s' under key `%s'\n",
2775 ac->label,
2776 GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority),
2777 GNUNET_h2s (&query));
2778 start_dht_request (rh, &query);
2779 return;
2780 }
2781}
2782
2783
2784/**
2785 * Lookup tail of our authority chain in the namecache.
2786 *
2787 * @param rh query we are processing
2788 */
2789static void
2790recursive_gns_resolution_namecache (struct GNS_ResolverHandle *rh)
2791{
2792 struct AuthorityChain *ac = rh->ac_tail;
2793 struct GNUNET_HashCode query;
2794
2795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2796 "Starting GNS resolution for `%s' in zone %s\n",
2797 ac->label,
2798 GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority));
2799 GNUNET_GNSRECORD_query_from_public_key (&ac->authority_info.gns_authority,
2800 ac->label,
2801 &query);
2802 if (GNUNET_YES != disable_cache)
2803 {
2804 rh->namecache_qe
2805 = GNUNET_NAMECACHE_lookup_block (namecache_handle,
2806 &query,
2807 &handle_namecache_block_response,
2808 rh);
2809 GNUNET_assert (NULL != rh->namecache_qe);
2810 }
2811 else
2812 {
2813 start_dht_request (rh,
2814 &query);
2815 }
2816}
2817
2818
2819/**
2820 * Function called with the result from a revocation check.
2821 *
2822 * @param cls the `struct GNS_ResovlerHandle`
2823 * @param is_valid #GNUNET_YES if the zone was not yet revoked
2824 */
2825static void
2826handle_revocation_result (void *cls,
2827 int is_valid)
2828{
2829 struct GNS_ResolverHandle *rh = cls;
2830 struct AuthorityChain *ac = rh->ac_tail;
2831
2832 rh->rev_check = NULL;
2833 if (GNUNET_YES != is_valid)
2834 {
2835 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2836 _ ("Zone %s was revoked, resolution fails\n"),
2837 GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority));
2838 fail_resolution (rh);
2839 return;
2840 }
2841 recursive_gns_resolution_namecache (rh);
2842}
2843
2844
2845/**
2846 * Perform revocation check on tail of our authority chain.
2847 *
2848 * @param rh query we are processing
2849 */
2850static void
2851recursive_gns_resolution_revocation (struct GNS_ResolverHandle *rh)
2852{
2853 struct AuthorityChain *ac = rh->ac_tail;
2854
2855 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2856 "Starting revocation check for zone %s\n",
2857 GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority));
2858 rh->rev_check = GNUNET_REVOCATION_query (cfg,
2859 &ac->authority_info.gns_authority,
2860 &handle_revocation_result,
2861 rh);
2862 GNUNET_assert (NULL != rh->rev_check);
2863}
2864
2865
2866static void
2867recursive_resolution (void *cls)
2868{
2869 struct GNS_ResolverHandle *rh = cls;
2870
2871 rh->task_id = NULL;
2872 if (rh->loop_threshold < rh->loop_limiter++)
2873 {
2874 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2875 "Encountered unbounded recursion resolving `%s'\n",
2876 rh->name);
2877 fail_resolution (rh);
2878 return;
2879 }
2880 if (GNUNET_YES == rh->ac_tail->gns_authority)
2881 recursive_gns_resolution_revocation (rh);
2882 else
2883 recursive_dns_resolution (rh);
2884}
2885
2886
2887static void
2888start_resolver_lookup (void *cls)
2889{
2890 struct GNS_ResolverHandle *rh = cls;
2891 struct AuthorityChain *ac;
2892 struct in_addr v4;
2893 struct in6_addr v6;
2894
2895 rh->task_id = NULL;
2896 if (1 == inet_pton (AF_INET,
2897 rh->name,
2898 &v4))
2899 {
2900 /* name is IPv4 address, pretend it's an A record */
2901 struct GNUNET_GNSRECORD_Data rd;
2902
2903 rd.data = &v4;
2904 rd.data_size = sizeof(v4);
2905 rd.expiration_time = UINT64_MAX;
2906 rd.record_type = GNUNET_DNSPARSER_TYPE_A;
2907 rd.flags = 0;
2908 rh->proc (rh->proc_cls,
2909 1,
2910 &rd);
2911 GNUNET_assert (NULL == rh->task_id);
2912 rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_,
2913 rh);
2914 return;
2915 }
2916 if (1 == inet_pton (AF_INET6,
2917 rh->name,
2918 &v6))
2919 {
2920 /* name is IPv6 address, pretend it's an AAAA record */
2921 struct GNUNET_GNSRECORD_Data rd;
2922
2923 rd.data = &v6;
2924 rd.data_size = sizeof(v6);
2925 rd.expiration_time = UINT64_MAX;
2926 rd.record_type = GNUNET_DNSPARSER_TYPE_AAAA;
2927 rd.flags = 0;
2928 rh->proc (rh->proc_cls,
2929 1,
2930 &rd);
2931 GNUNET_assert (NULL == rh->task_id);
2932 rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_,
2933 rh);
2934 return;
2935 }
2936
2937 ac = GNUNET_new (struct AuthorityChain);
2938 ac->rh = rh;
2939 ac->label = resolver_lookup_get_next_label (rh);
2940 if (NULL == ac->label)
2941 /* name was just the "TLD", so we default to label
2942 #GNUNET_GNS_EMPTY_LABEL_AT */
2943 ac->label = GNUNET_strdup (GNUNET_GNS_EMPTY_LABEL_AT);
2944 ac->gns_authority = GNUNET_YES;
2945 ac->authority_info.gns_authority = rh->authority_zone;
2946 GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head,
2947 rh->ac_tail,
2948 ac);
2949 rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution,
2950 rh);
2951}
2952
2953
2954/**
2955 * Lookup of a record in a specific zone calls lookup result processor
2956 * on result.
2957 *
2958 * @param zone the zone to perform the lookup in
2959 * @param record_type the record type to look up
2960 * @param name the name to look up
2961 * @param options local options to control local lookup
2962 * @param recursion_depth_limit how many zones to traverse
2963 * at most
2964 * @param proc the processor to call on result
2965 * @param proc_cls the closure to pass to @a proc
2966 * @return handle to cancel operation
2967 */
2968struct GNS_ResolverHandle *
2969GNS_resolver_lookup (const struct GNUNET_CRYPTO_PublicKey *zone,
2970 uint32_t record_type,
2971 const char *name,
2972 enum GNUNET_GNS_LocalOptions options,
2973 uint16_t recursion_depth_limit,
2974 GNS_ResultProcessor proc,
2975 void *proc_cls)
2976{
2977 struct GNS_ResolverHandle *rh;
2978
2979 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2980 "Starting lookup for `%s'\n",
2981 name);
2982 rh = GNUNET_new (struct GNS_ResolverHandle);
2983 GNUNET_CONTAINER_DLL_insert (rlh_head,
2984 rlh_tail,
2985 rh);
2986 rh->authority_zone = *zone;
2987 rh->proc = proc;
2988 rh->proc_cls = proc_cls;
2989 rh->options = options;
2990 rh->record_type = record_type;
2991 rh->name = GNUNET_strdup (name);
2992 rh->name_resolution_pos = strlen (name);
2993 rh->loop_threshold = recursion_depth_limit;
2994 rh->task_id = GNUNET_SCHEDULER_add_now (&start_resolver_lookup,
2995 rh);
2996 return rh;
2997}
2998
2999
3000/**
3001 * Cancel active resolution (i.e. client disconnected).
3002 *
3003 * @param rh resolution to abort
3004 */
3005void
3006GNS_resolver_lookup_cancel (struct GNS_ResolverHandle *rh)
3007{
3008 struct DnsResult *dr;
3009 struct AuthorityChain *ac;
3010
3011 GNUNET_CONTAINER_DLL_remove (rlh_head,
3012 rlh_tail,
3013 rh);
3014 if (NULL != rh->dns_request)
3015 {
3016 GNUNET_DNSSTUB_resolve_cancel (rh->dns_request);
3017 rh->dns_request = NULL;
3018 }
3019 while (NULL != (ac = rh->ac_head))
3020 {
3021 GNUNET_CONTAINER_DLL_remove (rh->ac_head,
3022 rh->ac_tail,
3023 ac);
3024 if (GNUNET_NO == ac->gns_authority)
3025 {
3026 struct Gns2DnsPending *gp;
3027
3028 while (NULL != (gp = ac->authority_info.dns_authority.gp_head))
3029 {
3030 GNUNET_CONTAINER_DLL_remove (ac->authority_info.dns_authority.gp_head,
3031 ac->authority_info.dns_authority.gp_tail,
3032 gp);
3033 if (NULL != gp->rh)
3034 {
3035 /* rh->g2dc->rh is NOT in the DLL yet, so to enable us
3036 using GNS_resolver_lookup_cancel here, we need to
3037 add it first... */
3038 GNUNET_CONTAINER_DLL_insert (rlh_head,
3039 rlh_tail,
3040 gp->rh);
3041 GNUNET_assert (NULL == gp->rh->task_id);
3042 gp->rh->task_id = GNUNET_SCHEDULER_add_now (
3043 &GNS_resolver_lookup_cancel_,
3044 gp->rh);
3045 gp->rh = NULL;
3046 }
3047 if (NULL != gp->dns_rh)
3048 {
3049 GNUNET_RESOLVER_request_cancel (gp->dns_rh);
3050 gp->dns_rh = NULL;
3051 }
3052 GNUNET_free (gp);
3053 }
3054 GNUNET_DNSSTUB_stop (ac->authority_info.dns_authority.dns_handle);
3055 }
3056 GNUNET_free (ac->label);
3057 GNUNET_free (ac);
3058 }
3059 if (NULL != rh->task_id)
3060 {
3061 GNUNET_SCHEDULER_cancel (rh->task_id);
3062 rh->task_id = NULL;
3063 }
3064 if (NULL != rh->get_handle)
3065 {
3066 GNUNET_DHT_get_stop (rh->get_handle);
3067 rh->get_handle = NULL;
3068 }
3069 if (NULL != rh->dht_heap_node)
3070 {
3071 GNUNET_CONTAINER_heap_remove_node (rh->dht_heap_node);
3072 rh->dht_heap_node = NULL;
3073 }
3074 if (NULL != rh->namecache_qe)
3075 {
3076 GNUNET_NAMECACHE_cancel (rh->namecache_qe);
3077 rh->namecache_qe = NULL;
3078 }
3079 if (NULL != rh->rev_check)
3080 {
3081 GNUNET_REVOCATION_query_cancel (rh->rev_check);
3082 rh->rev_check = NULL;
3083 }
3084 if (NULL != rh->std_resolve)
3085 {
3086 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3087 "Canceling standard DNS resolution\n");
3088 GNUNET_RESOLVER_request_cancel (rh->std_resolve);
3089 rh->std_resolve = NULL;
3090 }
3091 while (NULL != (dr = rh->dns_result_head))
3092 {
3093 GNUNET_CONTAINER_DLL_remove (rh->dns_result_head,
3094 rh->dns_result_tail,
3095 dr);
3096 GNUNET_free (dr);
3097 }
3098 if (NULL != rh->prefix)
3099 {
3100 GNUNET_free (rh->prefix);
3101 rh->prefix = NULL;
3102 }
3103 GNUNET_free (rh->leho);
3104 GNUNET_free (rh->name);
3105 GNUNET_free (rh);
3106}
3107
3108
3109/* ***************** Resolver initialization ********************* */
3110
3111
3112/**
3113 * Initialize the resolver
3114 *
3115 * @param nc the namecache handle
3116 * @param dht the dht handle
3117 * @param c configuration handle
3118 * @param max_bg_queries maximum number of parallel background queries in dht
3119 */
3120void
3121GNS_resolver_init (struct GNUNET_NAMECACHE_Handle *nc,
3122 struct GNUNET_DHT_Handle *dht,
3123 const struct GNUNET_CONFIGURATION_Handle *c,
3124 unsigned long long max_bg_queries)
3125{
3126 cfg = c;
3127 namecache_handle = nc;
3128 dht_handle = dht;
3129 dht_lookup_heap =
3130 GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
3131 max_allowed_background_queries = max_bg_queries;
3132 disable_cache = GNUNET_CONFIGURATION_get_value_yesno (cfg,
3133 "namecache",
3134 "DISABLE");
3135 if (GNUNET_YES == disable_cache)
3136 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3137 "Namecache disabled\n");
3138}
3139
3140
3141/**
3142 * Shutdown resolver
3143 */
3144void
3145GNS_resolver_done ()
3146{
3147 struct GNS_ResolverHandle *rh;
3148 struct CacheOps *co;
3149
3150 /* abort active resolutions */
3151 while (NULL != (rh = rlh_head))
3152 {
3153 rh->proc (rh->proc_cls,
3154 0,
3155 NULL);
3156 GNS_resolver_lookup_cancel (rh);
3157 }
3158 while (NULL != (co = co_head))
3159 {
3160 GNUNET_CONTAINER_DLL_remove (co_head,
3161 co_tail,
3162 co);
3163 GNUNET_NAMECACHE_cancel (co->namecache_qe_cache);
3164 GNUNET_free (co);
3165 }
3166 GNUNET_CONTAINER_heap_destroy (dht_lookup_heap);
3167 dht_lookup_heap = NULL;
3168 dht_handle = NULL;
3169 namecache_handle = NULL;
3170}
3171
3172
3173/* end of gnunet-service-gns_resolver.c */
diff --git a/src/service/gns/gnunet-service-gns_resolver.h b/src/service/gns/gnunet-service-gns_resolver.h
new file mode 100644
index 000000000..908af58e7
--- /dev/null
+++ b/src/service/gns/gnunet-service-gns_resolver.h
@@ -0,0 +1,106 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2020 GNUnet e.V.
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 gns/gnunet-service-gns_resolver.h
22 * @brief GNUnet GNS service
23 * @author Martin Schanzenbach
24 */
25#ifndef GNS_RESOLVER_H
26#define GNS_RESOLVER_H
27#include "gns.h"
28#include "gnunet_dht_service.h"
29#include "gnunet_gns_service.h"
30#include "gnunet_namecache_service.h"
31
32/**
33 * Initialize the resolver subsystem.
34 * MUST be called before #GNS_resolver_lookup.
35 *
36 * @param nc the namecache handle
37 * @param dht handle to the dht
38 * @param c configuration handle
39 * @param max_bg_queries maximum amount of background queries
40 */
41void
42GNS_resolver_init (struct GNUNET_NAMECACHE_Handle *nc,
43 struct GNUNET_DHT_Handle *dht,
44 const struct GNUNET_CONFIGURATION_Handle *c,
45 unsigned long long max_bg_queries);
46
47
48/**
49 * Cleanup resolver: Terminate pending lookups
50 */
51void
52GNS_resolver_done (void);
53
54
55/**
56 * Handle for an active request.
57 */
58struct GNS_ResolverHandle;
59
60
61/**
62 * Function called with results for a GNS resolution.
63 *
64 * @param cls closure
65 * @param rd_count number of records in @a rd
66 * @param rd records returned for the lookup
67 */
68typedef void
69(*GNS_ResultProcessor)(void *cls,
70 uint32_t rd_count,
71 const struct GNUNET_GNSRECORD_Data *rd);
72
73
74/**
75 * Lookup of a record in a specific zone
76 * calls RecordLookupProcessor on result or timeout
77 *
78 * @param zone the zone to perform the lookup in
79 * @param record_type the record type to look up
80 * @param name the name to look up
81 * @param options options set to control local lookup
82 * @param recursion_depth_limit how many zones to traverse
83 * at most
84 * @param proc the processor to call
85 * @param proc_cls the closure to pass to @a proc
86 * @return handle to cancel operation
87 */
88struct GNS_ResolverHandle *
89GNS_resolver_lookup (const struct GNUNET_CRYPTO_PublicKey *zone,
90 uint32_t record_type,
91 const char *name,
92 enum GNUNET_GNS_LocalOptions options,
93 uint16_t recursion_depth_limit,
94 GNS_ResultProcessor proc,
95 void *proc_cls);
96
97
98/**
99 * Cancel active resolution (i.e. client disconnected).
100 *
101 * @param rh resolution to abort
102 */
103void
104GNS_resolver_lookup_cancel (struct GNS_ResolverHandle *rh);
105
106#endif
diff --git a/src/service/gns/gnunet_w32nsp_lib.h b/src/service/gns/gnunet_w32nsp_lib.h
new file mode 100644
index 000000000..9d19ff2aa
--- /dev/null
+++ b/src/service/gns/gnunet_w32nsp_lib.h
@@ -0,0 +1,10 @@
1#if ! defined(GNUNET_W32NSP_LIB_H)
2#define GNUNET_W32NSP_LIB_H
3
4#include <basetyps.h>
5
6/* E0D24085-622C-4A93-9A0018-034469DE28DA */
7DEFINE_GUID (GNUNET_NAMESPACE_PROVIDER_DNS, 0xE0D24085L, 0x622C, 0x4A93, 0x9A,
8 0x18, 0x03, 0x44, 0x69, 0xDE, 0x28, 0xDA);
9
10#endif /* GNUNET_W32NSP_LIB_H */
diff --git a/src/service/gns/meson.build b/src/service/gns/meson.build
new file mode 100644
index 000000000..a12d7e918
--- /dev/null
+++ b/src/service/gns/meson.build
@@ -0,0 +1,113 @@
1libgnunetgns_src = ['gns_api.c', 'gns_tld_api.c']
2
3gnunetservicegns_src = ['gnunet-service-gns.c',
4 'gnunet-service-gns_resolver.c',
5 'gnunet-service-gns_interceptor.c']
6
7gnunetgnsproxy_src = ['gnunet-gns-proxy.c']
8
9configure_file(input : 'gns.conf.in',
10 output : 'gns.conf',
11 configuration : cdata,
12 install: true,
13 install_dir: pkgcfgdir)
14configure_file(input : 'tlds.conf',
15 output : 'tlds.conf',
16 configuration : cdata,
17 install: true,
18 install_dir: pkgcfgdir)
19
20if get_option('monolith')
21 foreach p : libgnunetgns_src + gnunetservicegns_src
22 gnunet_src += 'gns/' + p
23 endforeach
24endif
25
26libgnunetgns = library('gnunetgns',
27 libgnunetgns_src,
28 soversion: '0',
29 version: '0.0.0',
30 dependencies: [libgnunetutil_dep,
31 libgnunetgnsrecord_dep,
32 libgnunetidentity_dep],
33 include_directories: [incdir, configuration_inc],
34 install: true,
35 install_dir: get_option('libdir'))
36libgnunetgns_dep = declare_dependency(link_with : libgnunetgns)
37pkg.generate(libgnunetgns, url: 'https://www.gnunet.org',
38 description : 'Provides API to access the GNU Name System')
39
40executable ('gnunet-gns-proxy',
41 gnunetgnsproxy_src,
42 dependencies: [libgnunetgns_dep,
43 libgnunetutil_dep,
44 mhd_dep,
45 idn_dep,
46 curl_dep,
47 gnutls_dep,
48 libgnunetidentity_dep],
49 include_directories: [incdir, configuration_inc],
50 install: true,
51 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
52
53executable ('gnunet-service-gns',
54 gnunetservicegns_src,
55 dependencies: [libgnunetgns_dep,
56 libgnunetutil_dep,
57 libgnunetstatistics_dep,
58 libgnunetcore_dep,
59 libgnunetdht_dep,
60 libgnunetdns_dep,
61 idn_dep,
62 libgnunetidentity_dep,
63 libgnunetnamecache_dep,
64 libgnunetrevocation_dep,
65 libgnunetgnsrecord_dep,
66 libgnunetcadet_dep,
67 libgnunetblock_dep],
68 include_directories: [incdir, configuration_inc],
69 install: true,
70 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
71executable ('gnunet-bcd',
72 ['gnunet-bcd.c'],
73 dependencies: [libgnunetgns_dep,
74 libgnunetutil_dep,
75 libgnunetstatistics_dep,
76 libgnunetcore_dep,
77 libgnunetdht_dep,
78 libgnunetdns_dep,
79 mhd_dep,
80 idn_dep,
81 libgnunetidentity_dep,
82 libgnunetnamecache_dep,
83 libgnunetrevocation_dep,
84 libgnunetgnsrecord_dep,
85 libgnunetcadet_dep,
86 libgnunetblock_dep],
87 include_directories: [incdir, configuration_inc],
88 install: true,
89 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
90executable ('gnunet-dns2gns',
91 ['gnunet-dns2gns.c'],
92 dependencies: [libgnunetgns_dep,
93 libgnunetutil_dep,
94 libgnunetstatistics_dep,
95 libgnunetvpn_dep,
96 libgnunetcore_dep,
97 libgnunetdht_dep,
98 libgnunetdns_dep,
99 idn_dep,
100 libgnunetidentity_dep,
101 libgnunetnamecache_dep,
102 libgnunetrevocation_dep,
103 libgnunetgnsrecord_dep,
104 libgnunetcadet_dep,
105 libgnunetblock_dep],
106 include_directories: [incdir, configuration_inc],
107 install: true,
108 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
109
110
111if have_nss
112 subdir('nss')
113endif
diff --git a/src/service/gns/nss/Makefile.am b/src/service/gns/nss/Makefile.am
new file mode 100644
index 000000000..af0a8a2e2
--- /dev/null
+++ b/src/service/gns/nss/Makefile.am
@@ -0,0 +1,43 @@
1# This Makefile.am is in the public domain
2# $Id$
3#
4# This file taken and modified from nss-gns.
5#
6# nss-gns is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as
8# published by the Free Software Foundation; either version 3 of the
9# License, or (at your option) any later version.
10#
11# nss-gns is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14# General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public
17# License along with nss-gns; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19# USA.
20
21EXTRA_DIST = map-file
22
23AM_LDFLAGS=-avoid-version -module -export-dynamic
24
25lib_LTLIBRARIES = \
26 libnss_gns.la \
27 libnss_gns4.la \
28 libnss_gns6.la
29
30sources = nss_gns_query.h nss_gns_query.c
31
32# GNU Libc
33libnss_gns_la_SOURCES= $(sources) nss_gns.c
34libnss_gns_la_CFLAGS=$(AM_CFLAGS) -D_GNU_SOURCE
35libnss_gns_la_LDFLAGS=$(AM_LDFLAGS) -shrext .so.2 -Wl,-version-script=$(srcdir)/map-file
36
37libnss_gns4_la_SOURCES=$(libnss_gns_la_SOURCES)
38libnss_gns4_la_CFLAGS=$(libnss_gns_la_CFLAGS) -DNSS_IPV4_ONLY=1
39libnss_gns4_la_LDFLAGS=$(libnss_gns_la_LDFLAGS)
40
41libnss_gns6_la_SOURCES=$(libnss_gns_la_SOURCES)
42libnss_gns6_la_CFLAGS=$(libnss_gns_la_CFLAGS) -DNSS_IPV6_ONLY=1
43libnss_gns6_la_LDFLAGS=$(libnss_gns_la_LDFLAGS)
diff --git a/src/service/gns/nss/map-file b/src/service/gns/nss/map-file
new file mode 100644
index 000000000..476d0ac3e
--- /dev/null
+++ b/src/service/gns/nss/map-file
@@ -0,0 +1,14 @@
1NSSGNS_0 {
2global:
3_nss_gns_gethostbyaddr_r;
4_nss_gns4_gethostbyaddr_r;
5_nss_gns6_gethostbyaddr_r;
6_nss_gns_gethostbyname_r;
7_nss_gns4_gethostbyname_r;
8_nss_gns6_gethostbyname_r;
9_nss_gns_gethostbyname2_r;
10_nss_gns4_gethostbyname2_r;
11_nss_gns6_gethostbyname2_r;
12local:
13*;
14};
diff --git a/src/service/gns/nss/meson.build b/src/service/gns/nss/meson.build
new file mode 100644
index 000000000..7fd00ceb1
--- /dev/null
+++ b/src/service/gns/nss/meson.build
@@ -0,0 +1,34 @@
1# FIXME:
2#
3# EXTRA_DIST = map-file
4# AM_LDFLAGS=-avoid-version -module -export-dynamic
5#
6shared_library('nss_gns',
7 ['nss_gns_query.c', 'nss_gns.c'],
8 soversion: '2',
9 dependencies: [libgnunetutil_dep,
10 libgnunetgnsrecord_dep],
11 #link_args: ['-fno-version', '-module', '-export-dynamic', '-shrext', '.so.2', '-W', 'l'],
12 include_directories: [incdir, configuration_inc],
13 install: true,
14 install_dir: get_option('libdir'))
15shared_library('nss_gns4',
16 ['nss_gns_query.c', 'nss_gns.c'],
17 soversion: '2',
18 c_args: ['-DNSS_IPV4_ONLY=1'],
19 dependencies: [libgnunetutil_dep,
20 libgnunetgnsrecord_dep],
21 #link_args: ['-fno-version', '-module', '-export-dynamic', '-shrext', '.so.2', '-W', 'l'],
22 include_directories: [incdir, configuration_inc],
23 install: true,
24 install_dir: get_option('libdir'))
25shared_library('nss_gns6',
26 ['nss_gns_query.c', 'nss_gns.c'],
27 c_args: ['-DNSS_IPV6_ONLY=1'],
28 soversion: '2',
29 dependencies: [libgnunetutil_dep,
30 libgnunetgnsrecord_dep],
31 #link_args: ['-fno-version', '-module', '-export-dynamic', '-shrext', '.so.2', '-W', 'l'],
32 include_directories: [incdir, configuration_inc],
33 install: true,
34 install_dir: get_option('libdir'))
diff --git a/src/service/gns/nss/nss_gns.c b/src/service/gns/nss/nss_gns.c
new file mode 100644
index 000000000..b05cfff55
--- /dev/null
+++ b/src/service/gns/nss/nss_gns.c
@@ -0,0 +1,252 @@
1/***
2 This file is part of nss-gns.
3
4 Parts taken from: nss.c in nss-mdns
5
6 nss-mdns is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 3 of the License,
9 or (at your option) any later version.
10
11 nss-mdns is distributed in the hope that it will be useful, but1
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with nss-mdns; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 USA.
20 ***/
21
22#include <gnunet_private_config.h>
23#include <unistd.h>
24#include <errno.h>
25#include <string.h>
26#include <assert.h>
27#include <netdb.h>
28#include <sys/socket.h>
29#include <nss.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <errno.h>
33
34#include "nss_gns_query.h"
35
36#include <arpa/inet.h>
37
38/** macro to align idx to 32bit boundary */
39#define ALIGN(idx) do { \
40 if (idx % sizeof(void*)) \
41 idx += (sizeof(void*) - idx % sizeof(void*)); /* Align on 32 bit boundary */ \
42} while (0)
43
44
45/**
46 * The gethostbyname hook executed by nsswitch
47 *
48 * @param name the name to resolve
49 * @param af the address family to resolve
50 * @param result the result hostent
51 * @param buffer the result buffer
52 * @param buflen length of the buffer
53 * @param errnop idk
54 * @param h_errnop idk
55 * @return a nss_status code
56 */
57enum nss_status
58_nss_gns_gethostbyname2_r (const char *name,
59 int af,
60 struct hostent *result,
61 char *buffer,
62 size_t buflen,
63 int *errnop,
64 int *h_errnop)
65{
66 struct userdata u;
67 enum nss_status status = NSS_STATUS_UNAVAIL;
68 int i;
69 size_t address_length;
70 size_t l;
71 size_t idx;
72 size_t astart;
73
74 if (af == AF_UNSPEC)
75#ifdef NSS_IPV6_ONLY
76 af = AF_INET6;
77#else
78 af = AF_INET;
79#endif
80
81#ifdef NSS_IPV4_ONLY
82 if (af != AF_INET)
83#elif NSS_IPV6_ONLY
84 if (af != AF_INET6)
85#else
86 if ((af != AF_INET) &&
87 (af != AF_INET6))
88#endif
89 {
90 *errnop = EINVAL;
91 *h_errnop = NO_RECOVERY;
92
93 goto finish;
94 }
95
96 address_length = (af == AF_INET) ? sizeof(ipv4_address_t) :
97 sizeof(ipv6_address_t);
98 if (buflen <
99 sizeof(char*) /* alias names */
100 + strlen (name) + 1)
101 { /* official name */
102 *errnop = ERANGE;
103 *h_errnop = NO_RECOVERY;
104 status = NSS_STATUS_TRYAGAIN;
105
106 goto finish;
107 }
108 u.count = 0;
109 u.data_len = 0;
110 i = gns_resolve_name (af,
111 name,
112 &u);
113 if (-1 == i)
114 {
115 *errnop = errno;
116 status = NSS_STATUS_UNAVAIL;
117 *h_errnop = NO_RECOVERY;
118 goto finish;
119 }
120 if (-2 == i)
121 {
122 *errnop = ENOENT;
123 *h_errnop = NO_RECOVERY;
124 status = NSS_STATUS_UNAVAIL;
125 goto finish;
126 }
127 if (-3 == i)
128 {
129 *errnop = ETIMEDOUT;
130 *h_errnop = HOST_NOT_FOUND;
131 status = NSS_STATUS_NOTFOUND;
132 goto finish;
133 }
134 if (0 == u.count)
135 {
136 *errnop = 0; /* success */
137 *h_errnop = NO_DATA; /* success */
138 status = NSS_STATUS_NOTFOUND;
139 goto finish;
140 }
141 /* Alias names */
142 *((char **) buffer) = NULL;
143 result->h_aliases = (char **) buffer;
144 idx = sizeof(char*);
145
146 /* Official name */
147 strcpy (buffer + idx,
148 name);
149 result->h_name = buffer + idx;
150 idx += strlen (name) + 1;
151
152 ALIGN (idx);
153
154 result->h_addrtype = af;
155 result->h_length = address_length;
156
157 /* Check if there's enough space for the addresses */
158 if (buflen < idx + u.data_len + sizeof(char*) * (u.count + 1))
159 {
160 *errnop = ERANGE;
161 *h_errnop = NO_RECOVERY;
162 status = NSS_STATUS_TRYAGAIN;
163 goto finish;
164 }
165 /* Addresses */
166 astart = idx;
167 l = u.count * address_length;
168 if (0 != l)
169 memcpy (buffer + astart,
170 &u.data,
171 l);
172 /* address_length is a multiple of 32bits, so idx is still aligned
173 * correctly */
174 idx += l;
175
176 /* Address array address_length is always a multiple of 32bits */
177 for (i = 0; i < u.count; i++)
178 ((char **) (buffer + idx))[i] = buffer + astart + address_length * i;
179 ((char **) (buffer + idx))[i] = NULL;
180 result->h_addr_list = (char **) (buffer + idx);
181
182 status = NSS_STATUS_SUCCESS;
183
184finish:
185 return status;
186}
187
188
189/**
190 * The gethostbyname hook executed by nsswitch
191 *
192 * @param name the name to resolve
193 * @param result the result hostent
194 * @param buffer the result buffer
195 * @param buflen length of the buffer
196 * @param[out] errnop the low-level error code to return to the application
197 * @param h_errnop idk
198 * @return a nss_status code
199 */
200enum nss_status
201_nss_gns_gethostbyname_r (const char *name,
202 struct hostent *result,
203 char *buffer,
204 size_t buflen,
205 int *errnop,
206 int *h_errnop)
207{
208 return _nss_gns_gethostbyname2_r (name,
209 AF_UNSPEC,
210 result,
211 buffer,
212 buflen,
213 errnop,
214 h_errnop);
215}
216
217
218/**
219 * The gethostbyaddr hook executed by nsswitch
220 * We can't do this so we always return NSS_STATUS_UNAVAIL
221 *
222 * @param addr the address to resolve
223 * @param len the length of the address
224 * @param af the address family of the address
225 * @param result the result hostent
226 * @param buffer the result buffer
227 * @param buflen length of the buffer
228 * @param[out] errnop the low-level error code to return to the application
229 * @param h_errnop idk
230 * @return NSS_STATUS_UNAVAIL
231 */
232enum nss_status
233_nss_gns_gethostbyaddr_r (const void*addr,
234 int len,
235 int af,
236 struct hostent *result,
237 char *buffer,
238 size_t buflen,
239 int *errnop,
240 int *h_errnop)
241{
242 (void) addr;
243 (void) len;
244 (void) af;
245 (void) result;
246 (void) buffer;
247 (void) buflen;
248 *errnop = EINVAL;
249 *h_errnop = NO_RECOVERY;
250 /* NOTE we allow to leak this into DNS so no NOTFOUND */
251 return NSS_STATUS_UNAVAIL;
252}
diff --git a/src/service/gns/nss/nss_gns_query.c b/src/service/gns/nss/nss_gns_query.c
new file mode 100644
index 000000000..96e8e10da
--- /dev/null
+++ b/src/service/gns/nss/nss_gns_query.c
@@ -0,0 +1,164 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
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#include <string.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include "nss_gns_query.h"
24#include <arpa/inet.h>
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/wait.h>
28#include <netinet/in.h>
29#include <errno.h>
30#include <unistd.h>
31#include <signal.h>
32
33#define TIMEOUT "5s"
34
35static void
36kwait (pid_t chld)
37{
38 int ret;
39
40 kill (chld, SIGKILL);
41 waitpid (chld, &ret, 0);
42}
43
44
45/**
46 * Wrapper function that uses gnunet-gns cli tool to resolve
47 * an IPv4/6 address.
48 *
49 * @param af address family
50 * @param name the name to resolve
51 * @param u the userdata (result struct)
52 * @return -1 on internal error,
53 * -2 if request is not for GNS,
54 * -3 on timeout,
55 * else 0
56 */
57int
58gns_resolve_name (int af, const char *name, struct userdata *u)
59{
60 FILE *p;
61 char line[128];
62 int ret;
63 int retry = 0;
64 int out[2];
65 pid_t pid;
66
67 if (0 == getuid ())
68 return -2; /* GNS via NSS is NEVER for root */
69
70query_gns:
71 if (0 != pipe (out))
72 return -1;
73 pid = fork ();
74 if (-1 == pid)
75 return -1;
76 if (0 == pid)
77 {
78 char *argv[] = { "gnunet-gns",
79 "-r", /* Raw output for easier parsing */
80 "-d", /* DNS compatibility (allow IDNA names, no UTF-8) */
81 "-t",
82 (AF_INET6 == af) ? "AAAA" : "A",
83 "-u",
84 (char *) name,
85 "-T",
86 TIMEOUT,
87 NULL };
88
89 (void) close (STDOUT_FILENO);
90 if ((0 != close (out[0])) ||
91 (STDOUT_FILENO != dup2 (out[1], STDOUT_FILENO)))
92 _exit (1);
93 (void) execvp ("gnunet-gns", argv);
94 _exit (1);
95 }
96 (void) close (out[1]);
97 p = fdopen (out[0], "r");
98 if (NULL == p)
99 {
100 kwait (pid);
101 return -1;
102 }
103 while (NULL != fgets (line, sizeof(line), p))
104 {
105 if (u->count >= MAX_ENTRIES)
106 break;
107 if (line[strlen (line) - 1] == '\n')
108 {
109 line[strlen (line) - 1] = '\0';
110 if (AF_INET == af)
111 {
112 if (inet_pton (af, line, &u->data.ipv4[u->count]))
113 {
114 u->count++;
115 u->data_len += sizeof(ipv4_address_t);
116 }
117 else
118 {
119 (void) fclose (p);
120 kwait (pid);
121 errno = EINVAL;
122 return -1;
123 }
124 }
125 else if (AF_INET6 == af)
126 {
127 if (inet_pton (af, line, &u->data.ipv6[u->count]))
128 {
129 u->count++;
130 u->data_len += sizeof(ipv6_address_t);
131 }
132 else
133 {
134 (void) fclose (p);
135 kwait (pid);
136 errno = EINVAL;
137 return -1;
138 }
139 }
140 }
141 }
142 (void) fclose (p);
143 waitpid (pid, &ret, 0);
144 if (! WIFEXITED (ret))
145 return -1;
146 if (4 == WEXITSTATUS (ret))
147 return -2; /* not for GNS */
148 if (5 == WEXITSTATUS (ret))
149 {
150 if (1 == retry)
151 return -2; /* no go -> service unavailable */
152 retry = 1;
153 system ("gnunet-arm -s");
154 goto query_gns; /* Try again */
155 }
156 if (3 == WEXITSTATUS (ret))
157 return -2; /* timeout -> service unavailable */
158 if ((2 == WEXITSTATUS (ret)) || (1 == WEXITSTATUS (ret)))
159 return -2; /* launch failure -> service unavailable */
160 return 0;
161}
162
163
164/* end of nss_gns_query.c */
diff --git a/src/service/gns/nss/nss_gns_query.h b/src/service/gns/nss/nss_gns_query.h
new file mode 100644
index 000000000..43bf21646
--- /dev/null
+++ b/src/service/gns/nss/nss_gns_query.h
@@ -0,0 +1,73 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
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#ifndef NSS_GNS_QUERY_H
21#define NSS_GNS_QUERY_H
22
23/**
24 * Parts taken from nss-mdns
25 */
26#include <inttypes.h>
27
28/* Maximum number of entries to return */
29#define MAX_ENTRIES 16
30
31typedef struct
32{
33 uint32_t address;
34} ipv4_address_t;
35
36
37typedef struct
38{
39 uint8_t address[16];
40} ipv6_address_t;
41
42
43struct userdata
44{
45 int count;
46 int data_len; /* only valid when doing reverse lookup */
47 union
48 {
49 ipv4_address_t ipv4[MAX_ENTRIES];
50 ipv6_address_t ipv6[MAX_ENTRIES];
51 char *name[MAX_ENTRIES];
52 } data;
53};
54
55
56/**
57 * Wrapper function that uses gnunet-gns cli tool to resolve
58 * an IPv4/6 address.
59 *
60 * @param af address family
61 * @param name the name to resolve
62 * @param u the userdata (result struct)
63 * @return -1 on internal error,
64 * -2 if request is not for GNS,
65 * -3 on timeout,
66 * else 0
67 */
68int
69gns_resolve_name (int af,
70 const char *name,
71 struct userdata *userdata);
72
73#endif
diff --git a/src/service/gns/test_gns_proxy.c b/src/service/gns/test_gns_proxy.c
new file mode 100644
index 000000000..e09db5787
--- /dev/null
+++ b/src/service/gns/test_gns_proxy.c
@@ -0,0 +1,576 @@
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_gns_proxy.c
23 * @brief testcase for accessing SOCKS5 GNS proxy
24 * @author Martin Schanzenbach
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_util_lib.h"
31#include "gnutls/x509.h"
32
33/**
34 * Largest allowed size for a PEM certificate.
35 */
36#define MAX_PEM_SIZE (10 * 1024)
37
38#define TEST_DOMAIN "www.test"
39
40#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
41
42/**
43 * Return value for 'main'.
44 */
45static int global_ret;
46
47
48static struct MHD_Daemon *mhd;
49
50static struct GNUNET_SCHEDULER_Task *mhd_task_id;
51
52static struct GNUNET_SCHEDULER_Task *curl_task_id;
53
54static CURL *curl;
55
56static CURLM *multi;
57
58static char *url;
59
60static struct GNUNET_OS_Process *proxy_proc;
61
62static char*cafile_opt;
63
64static char*cafile_srv;
65
66static uint16_t port;
67
68static gnutls_x509_crt_t proxy_cert;
69
70static gnutls_x509_privkey_t proxy_key;
71
72struct CBC
73{
74 char buf[1024];
75 size_t pos;
76};
77
78static struct CBC cbc;
79
80/**
81 * Read file in filename
82 *
83 * @param filename file to read
84 * @param size pointer where filesize is stored
85 * @return NULL on error
86 */
87static void*
88load_file (const char*filename,
89 unsigned int*size)
90{
91 void *buffer;
92 uint64_t fsize;
93
94 if (GNUNET_OK !=
95 GNUNET_DISK_file_size (filename,
96 &fsize,
97 GNUNET_YES,
98 GNUNET_YES))
99 return NULL;
100 if (fsize > MAX_PEM_SIZE)
101 return NULL;
102 *size = (unsigned int) fsize;
103 buffer = GNUNET_malloc (*size);
104 if (fsize !=
105 GNUNET_DISK_fn_read (filename,
106 buffer,
107 (size_t) fsize))
108 {
109 GNUNET_free (buffer);
110 return NULL;
111 }
112 return buffer;
113}
114
115
116/**
117 * Load PEM key from file
118 *
119 * @param key where to store the data
120 * @param keyfile path to the PEM file
121 * @return #GNUNET_OK on success
122 */
123static int
124load_key_from_file (gnutls_x509_privkey_t key,
125 const char*keyfile)
126{
127 gnutls_datum_t key_data;
128 int ret;
129
130 key_data.data = load_file (keyfile,
131 &key_data.size);
132 if (NULL == key_data.data)
133 return GNUNET_SYSERR;
134 ret = gnutls_x509_privkey_import (key, &key_data,
135 GNUTLS_X509_FMT_PEM);
136 if (GNUTLS_E_SUCCESS != ret)
137 {
138 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
139 _ ("Unable to import private key from file `%s'\n"),
140 keyfile);
141 }
142 GNUNET_free (key_data.data);
143 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
144}
145
146
147/**
148 * Load cert from file
149 *
150 * @param crt struct to store data in
151 * @param certfile path to pem file
152 * @return #GNUNET_OK on success
153 */
154static int
155load_cert_from_file (gnutls_x509_crt_t crt,
156 const char*certfile)
157{
158 gnutls_datum_t cert_data;
159 int ret;
160
161 cert_data.data = load_file (certfile,
162 &cert_data.size);
163 if (NULL == cert_data.data)
164 return GNUNET_SYSERR;
165 ret = gnutls_x509_crt_import (crt,
166 &cert_data,
167 GNUTLS_X509_FMT_PEM);
168 if (GNUTLS_E_SUCCESS != ret)
169 {
170 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
171 _ ("Unable to import certificate from `%s'\n"),
172 certfile);
173 }
174 GNUNET_free (cert_data.data);
175 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
176}
177
178
179static size_t
180copy_buffer (void *ptr, size_t size, size_t nmemb, void *ctx)
181{
182 struct CBC *cbc = ctx;
183
184 if (cbc->pos + size * nmemb > sizeof(cbc->buf))
185 return 0; /* overflow */
186 GNUNET_memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
187 cbc->pos += size * nmemb;
188 return size * nmemb;
189}
190
191
192static enum MHD_Result
193mhd_ahc (void *cls,
194 struct MHD_Connection *connection,
195 const char *url,
196 const char *method,
197 const char *version,
198 const char *upload_data, size_t *upload_data_size,
199 void **unused)
200{
201 static int ptr;
202 struct MHD_Response *response;
203 int ret;
204
205 if (0 != strcmp ("GET", method))
206 return MHD_NO; /* unexpected method */
207 if (&ptr != *unused)
208 {
209 *unused = &ptr;
210 return MHD_YES;
211 }
212 *unused = NULL;
213 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
214 "MHD sends response for request to URL `%s'\n", url);
215 response = MHD_create_response_from_buffer (strlen (url),
216 (void *) url,
217 MHD_RESPMEM_MUST_COPY);
218 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
219 MHD_destroy_response (response);
220 if (ret == MHD_NO)
221 {
222 global_ret = 1;
223 abort ();
224 }
225 global_ret = 0;
226 return ret;
227}
228
229
230static void
231do_shutdown ()
232{
233 if (mhd_task_id != NULL)
234 {
235 GNUNET_SCHEDULER_cancel (mhd_task_id);
236 mhd_task_id = NULL;
237 }
238 if (curl_task_id != NULL)
239 {
240 GNUNET_SCHEDULER_cancel (curl_task_id);
241 curl_task_id = NULL;
242 }
243 if (NULL != mhd)
244 {
245 MHD_stop_daemon (mhd);
246 mhd = NULL;
247 }
248 GNUNET_free (url);
249
250 if (NULL != proxy_proc)
251 {
252 (void) GNUNET_OS_process_kill (proxy_proc, SIGKILL);
253 GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (proxy_proc));
254 GNUNET_OS_process_destroy (proxy_proc);
255 proxy_proc = NULL;
256 }
257 url = NULL;
258 GNUNET_SCHEDULER_shutdown ();
259}
260
261
262/**
263 * Function to run the HTTP client.
264 */
265static void
266curl_main (void);
267
268
269static void
270curl_task (void *cls)
271{
272 curl_task_id = NULL;
273 curl_main ();
274}
275
276
277static void
278curl_main ()
279{
280 fd_set rs;
281 fd_set ws;
282 fd_set es;
283 int max;
284 struct GNUNET_NETWORK_FDSet nrs;
285 struct GNUNET_NETWORK_FDSet nws;
286 struct GNUNET_TIME_Relative delay;
287 long timeout;
288 int running;
289 struct CURLMsg *msg;
290
291 max = 0;
292 FD_ZERO (&rs);
293 FD_ZERO (&ws);
294 FD_ZERO (&es);
295 curl_multi_perform (multi, &running);
296 if (running == 0)
297 {
298 GNUNET_assert (NULL != (msg = curl_multi_info_read (multi, &running)));
299 if (msg->msg == CURLMSG_DONE)
300 {
301 if (msg->data.result != CURLE_OK)
302 {
303 fprintf (stderr,
304 "%s failed at %s:%d: `%s'\n",
305 "curl_multi_perform",
306 __FILE__,
307 __LINE__, curl_easy_strerror (msg->data.result));
308 global_ret = 1;
309 }
310 }
311 curl_multi_remove_handle (multi, curl);
312 curl_multi_cleanup (multi);
313 curl_easy_cleanup (curl);
314 curl = NULL;
315 multi = NULL;
316 if (cbc.pos != strlen ("/hello_world"))
317 {
318 GNUNET_break (0);
319 global_ret = 2;
320 }
321 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
322 {
323 GNUNET_break (0);
324 global_ret = 3;
325 }
326 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download complete, shutting down!\n");
327 do_shutdown ();
328 return;
329 }
330 GNUNET_assert (CURLM_OK == curl_multi_fdset (multi, &rs, &ws, &es, &max));
331 if ((CURLM_OK != curl_multi_timeout (multi, &timeout)) ||
332 (-1 == timeout))
333 delay = GNUNET_TIME_UNIT_SECONDS;
334 else
335 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
336 (unsigned int) timeout);
337 GNUNET_NETWORK_fdset_copy_native (&nrs,
338 &rs,
339 max + 1);
340 GNUNET_NETWORK_fdset_copy_native (&nws,
341 &ws,
342 max + 1);
343 curl_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
344 delay,
345 &nrs,
346 &nws,
347 &curl_task,
348 NULL);
349}
350
351
352static void
353start_curl (void *cls)
354{
355 curl_task_id = NULL;
356 GNUNET_asprintf (&url,
357 "https://%s:%d/hello_world",
358 TEST_DOMAIN, port);
359 curl = curl_easy_init ();
360 curl_easy_setopt (curl, CURLOPT_URL, url);
361 // curl_easy_setopt (curl, CURLOPT_URL, "https://127.0.0.1:8443/hello_world");
362 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, &copy_buffer);
363 curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbc);
364 curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
365 curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L);
366 curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 15L);
367 curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
368 curl_easy_setopt (curl, CURLOPT_CAINFO, cafile_opt);
369 // curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L);
370 // curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L);
371 curl_easy_setopt (curl, CURLOPT_PROXY, "socks5h://127.0.0.1:7777");
372
373 multi = curl_multi_init ();
374 GNUNET_assert (multi != NULL);
375 GNUNET_assert (CURLM_OK == curl_multi_add_handle (multi, curl));
376 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
377 "Beginning HTTP download from `%s'\n",
378 url);
379 curl_main ();
380}
381
382
383/**
384 * Callback invoked from the namestore service once record is
385 * created.
386 *
387 * @param cls closure
388 * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
389 * will match 'result_af' from the request
390 * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
391 * that the VPN allocated for the redirection;
392 * traffic to this IP will now be redirected to the
393 * specified target peer; NULL on error
394 */
395static void
396commence_testing (void *cls)
397{
398 curl_task_id =
399 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
400 &start_curl,
401 NULL);
402}
403
404
405/**
406 * Function to keep the HTTP server running.
407 */
408static void
409mhd_main (void);
410
411
412static void
413mhd_task (void *cls)
414{
415 mhd_task_id = NULL;
416 MHD_run (mhd);
417 mhd_main ();
418}
419
420
421static void
422mhd_main ()
423{
424 struct GNUNET_NETWORK_FDSet nrs;
425 struct GNUNET_NETWORK_FDSet nws;
426 fd_set rs;
427 fd_set ws;
428 fd_set es;
429 int max_fd;
430 unsigned MHD_LONG_LONG timeout;
431 struct GNUNET_TIME_Relative delay;
432
433 GNUNET_assert (NULL == mhd_task_id);
434 FD_ZERO (&rs);
435 FD_ZERO (&ws);
436 FD_ZERO (&es);
437 max_fd = -1;
438 GNUNET_assert (MHD_YES ==
439 MHD_get_fdset (mhd, &rs, &ws, &es, &max_fd));
440 if (MHD_YES == MHD_get_timeout (mhd, &timeout))
441 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
442 (unsigned int) timeout);
443 else
444 delay = GNUNET_TIME_UNIT_FOREVER_REL;
445 GNUNET_NETWORK_fdset_copy_native (&nrs,
446 &rs,
447 max_fd + 1);
448 GNUNET_NETWORK_fdset_copy_native (&nws,
449 &ws,
450 max_fd + 1);
451 mhd_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
452 delay,
453 &nrs,
454 &nws,
455 &mhd_task,
456 NULL);
457}
458
459
460/**
461 * Main function that will be run
462 *
463 * @param cls closure
464 * @param args remaining command-line arguments
465 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
466 * @param c configuration
467 */
468static void
469run (void *cls,
470 char *const *args,
471 const char *cfgfile,
472 const struct GNUNET_CONFIGURATION_Handle *c)
473{
474 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
475 "Using `%s' as CA\n",
476 cafile_srv);
477 char cert[MAX_PEM_SIZE];
478 char key[MAX_PEM_SIZE];
479 size_t key_buf_size;
480 size_t cert_buf_size;
481
482 gnutls_global_init ();
483 gnutls_x509_crt_init (&proxy_cert);
484 gnutls_x509_privkey_init (&proxy_key);
485
486 if ((GNUNET_OK !=
487 load_cert_from_file (proxy_cert,
488 cafile_srv)) ||
489 (GNUNET_OK !=
490 load_key_from_file (proxy_key,
491 cafile_srv)))
492 {
493 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
494 _ ("Failed to load X.509 key and certificate from `%s'\n"),
495 cafile_srv);
496 gnutls_x509_crt_deinit (proxy_cert);
497 gnutls_x509_privkey_deinit (proxy_key);
498 gnutls_global_deinit ();
499 return;
500 }
501 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
502 NULL);
503 key_buf_size = sizeof(key);
504 cert_buf_size = sizeof(cert);
505 gnutls_x509_crt_export (proxy_cert,
506 GNUTLS_X509_FMT_PEM,
507 cert,
508 &cert_buf_size);
509 gnutls_x509_privkey_export (proxy_key,
510 GNUTLS_X509_FMT_PEM,
511 key,
512 &key_buf_size);
513 mhd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL
514 | MHD_ALLOW_SUSPEND_RESUME, port,
515 NULL, NULL,
516 &mhd_ahc, NULL,
517 MHD_OPTION_HTTPS_MEM_KEY, key,
518 MHD_OPTION_HTTPS_MEM_CERT, cert,
519 MHD_OPTION_END);
520 GNUNET_assert (NULL != mhd);
521 mhd_main ();
522
523 GNUNET_SCHEDULER_add_now (&commence_testing,
524 NULL);
525}
526
527
528int
529main (int argc, char *const *argv)
530{
531 struct GNUNET_GETOPT_CommandLineOption options[] = {
532 GNUNET_GETOPT_option_uint16 ('p',
533 "port",
534 NULL,
535 gettext_noop (
536 "listen on specified port (default: 7777)"),
537 &port),
538 GNUNET_GETOPT_option_string ('A',
539 "curlcert",
540 NULL,
541 gettext_noop ("pem file to use as CA"),
542 &cafile_opt),
543 GNUNET_GETOPT_option_string ('S',
544 "servercert",
545 NULL,
546 gettext_noop (
547 "pem file to use for the server"),
548 &cafile_srv),
549
550 GNUNET_GETOPT_OPTION_END
551 };
552
553 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
554 {
555 fprintf (stderr, "failed to initialize curl\n");
556 return 2;
557 }
558 if (GNUNET_OK !=
559 GNUNET_STRINGS_get_utf8_args (argc, argv,
560 &argc, &argv))
561 return 2;
562 GNUNET_log_setup ("gnunet-gns-proxy-test",
563 "WARNING",
564 NULL);
565 if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv,
566 "gnunet-gns-proxy-test",
567 _ ("GNUnet GNS proxy test"),
568 options,
569 &run, NULL))
570 return 1;
571 GNUNET_free_nz ((void*) argv);
572 return global_ret;
573}
574
575
576/* end of test_gns_proxy.c */
diff --git a/src/service/gns/test_gns_proxy.conf b/src/service/gns/test_gns_proxy.conf
new file mode 100644
index 000000000..6670fabf4
--- /dev/null
+++ b/src/service/gns/test_gns_proxy.conf
@@ -0,0 +1,27 @@
1@INLINE@ test_gns_defaults.conf
2
3[transport]
4PLUGINS = tcp
5
6[gns]
7# PREFIX = valgrind --leak-check=full --track-origins=yes
8START_ON_DEMAND = YES
9AUTO_IMPORT_PKEY = YES
10MAX_PARALLEL_BACKGROUND_QUERIES = 10
11DEFAULT_LOOKUP_TIMEOUT = 15 s
12RECORD_PUT_INTERVAL = 1 h
13ZONE_PUBLISH_TIME_WINDOW = 1 h
14DNS_ROOT=PD67SGHF3E0447TU9HADIVU9OM7V4QHTOG0EBU69TFRI2LG63DR0
15
16[zonemaster]
17IMMEDIATE_START = YES
18START_ON_DEMAND = YES
19
20
21
22[gns-proxy]
23PROXY_CACERT = $GNUNET_TMP/proxy_cacert.pem
24PROXY_UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-gns-proxy.sock
25
26[namestore]
27START_ON_DEMAND = YES
diff --git a/src/service/gns/test_proxy.sh b/src/service/gns/test_proxy.sh
new file mode 100755
index 000000000..aa59504ee
--- /dev/null
+++ b/src/service/gns/test_proxy.sh
@@ -0,0 +1,71 @@
1#!/bin/bash
2# This file is in the public domain.
3
4if ! which certutil > /dev/null
5then
6 echo "certutil required"
7 exit 77
8fi
9
10if ! which openssl > /dev/null
11then
12 echo "certutil required"
13 exit 77
14fi
15
16TEST_DOMAIN="www.test"
17GNUNET_TMP="$(gnunet-config -f -s PATHS -o GNUNET_TMP)"
18PROXY_CACERT="$(gnunet-config -f -c test_gns_proxy.conf -s gns-proxy -o PROXY_CACERT)"
19
20# Delete old files before starting test
21rm -rf "$GNUNET_TMP/test-gnunet-gns-testing/"
22gnunet-arm -s -c test_gns_proxy.conf
23gnunet-gns-proxy-setup-ca -c test_gns_proxy.conf
24
25openssl genrsa -des3 -passout pass:xxxx -out server.pass.key 2048
26openssl rsa -passin pass:xxxx -in server.pass.key -out local.key
27rm server.pass.key
28openssl req -new -key local.key -out server.csr \
29 -subj "/C=DE/O=GNUnet/OU=GNS/CN=test.local"
30openssl x509 -req -days 1 -in server.csr -signkey local.key -out local.crt
31openssl x509 -in local.crt -out local.der -outform DER
32HEXCERT=`xxd -p local.der | tr -d '\n'`
33#echo "This is the certificate the server does not use: $HEXCERT"
34OLDBOXVALUE="6 8443 52 3 0 0 $HEXCERT"
35
36
37openssl req -new -key local.key -out server.csr \
38 -subj "/C=DE/O=GNUnet/OU=GNS/CN=test.local"
39openssl x509 -req -days 1 -in server.csr -signkey local.key -out local.crt
40openssl x509 -in local.crt -out local.der -outform DER
41HEXCERT=`xxd -p local.der | tr -d '\n'`
42#echo "This is the certificate the server does use: $HEXCERT"
43BOXVALUE="6 8443 52 3 0 0 $HEXCERT"
44
45SERVER_CACERT="$GNUNET_TMP/server_cacert.pem"
46cat local.crt > "$SERVER_CACERT"
47cat local.key >> "$SERVER_CACERT"
48
49gnunet-identity -C test -c test_gns_proxy.conf
50gnunet-namestore -p -z "test" -a -n www -t A -V 127.0.0.1 -e never -c test_gns_proxy.conf
51gnunet-namestore -p -z "test" -a -n www -t LEHO -V "test.local" -e never -c test_gns_proxy.conf
52gnunet-namestore -p -z "test" -a -n www -t BOX -V "$OLDBOXVALUE" -e never -c test_gns_proxy.conf
53gnunet-namestore -p -z "test" -a -n www -t BOX -V "$BOXVALUE" -e never -c test_gns_proxy.conf
54
55gnunet-arm -i gns-proxy -c test_gns_proxy.conf
56
57#gnurl --socks5-hostname 127.0.0.1:7777 "https://$TEST_DOMAIN" -v --cacert "$PROXY_CACERT"
58./test_gns_proxy -A "$PROXY_CACERT" -S "$SERVER_CACERT" -p 8443 -c test_gns_proxy.conf
59
60RES=$?
61
62rm "$PROXY_CACERT"
63rm "$SERVER_CACERT"
64
65gnunet-arm -e test_gns_proxy.conf
66
67if test $RES != 0
68then
69 echo "Failed"
70 exit 1
71fi
diff --git a/src/service/gns/tlds.conf b/src/service/gns/tlds.conf
new file mode 100644
index 000000000..28f23f8e5
--- /dev/null
+++ b/src/service/gns/tlds.conf
@@ -0,0 +1,15 @@
1# WARNING:
2# This header is generated!
3# In order to add TLDs, you must register
4# them in GANA, and then use the header generation script
5# to create an update of this file. You may then replace this
6# file with the update.
7[gns]
8
9# The FCFS authority managed by GNUnet e.V.
10.pin.gns.alt = 000G0522TTKQESZ9KPRT2K0RA9ZC8YMD52D2XYVYVGPDCNPMWHH9QXXF4W
11
12
13# The authoritative zone of the GNUnet project
14.gnunet.gns.alt = 000G0522TTKQESZ9KPRT2K0RA9ZC8YMD52D2XYVYVGPDCNPMWHH9QXXF4W
15
diff --git a/src/service/gns/w32resolver.h b/src/service/gns/w32resolver.h
new file mode 100644
index 000000000..c404e37d3
--- /dev/null
+++ b/src/service/gns/w32resolver.h
@@ -0,0 +1,71 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2012 GNUnet e.V.
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 * @author Christian Grothoff
23 * @file gns/w32resolver.h
24 */
25#ifndef W32RESOLVER_H
26#define W32RESOLVER_H
27
28#include "platform.h"
29#include "gnunet_crypto_lib.h"
30#include "gnunet_common.h"
31
32/**
33 * Request DNS resolution.
34 */
35#define GNUNET_MESSAGE_TYPE_W32RESOLVER_REQUEST 4
36
37/**
38 * Response to a DNS resolution request.
39 */
40#define GNUNET_MESSAGE_TYPE_W32RESOLVER_RESPONSE 5
41
42GNUNET_NETWORK_STRUCT_BEGIN
43
44/**
45 * Request for the resolver. Followed by the 0-terminated hostname.
46 *
47 * The response will be one or more messages of type
48 * W32RESOLVER_RESPONSE, each with the message header immediately
49 * followed by the requested data (struct in[6]_addr).
50 * The last W32RESOLVER_RESPONSE will just be a header without any data
51 * (used to indicate the end of the list).
52 */
53struct GNUNET_W32RESOLVER_GetMessage
54{
55 /**
56 * Type: GNUNET_MESSAGE_TYPE_W32RESOLVER_REQUEST
57 */
58 struct GNUNET_MessageHeader header;
59
60 uint32_t af GNUNET_PACKED;
61
62 uint32_t sc_data1 GNUNET_PACKED;
63 uint16_t sc_data2 GNUNET_PACKED;
64 uint16_t sc_data3 GNUNET_PACKED;
65 uint8_t sc_data4[8];
66 /* followed by 0-terminated string for A/AAAA lookup */
67};
68
69GNUNET_NETWORK_STRUCT_END
70
71#endif