aboutsummaryrefslogtreecommitdiff
path: root/src/dns
diff options
context:
space:
mode:
Diffstat (limited to 'src/dns')
-rw-r--r--src/dns/.gitignore7
-rw-r--r--src/dns/Makefile.am97
-rw-r--r--src/dns/dns.conf.in34
-rw-r--r--src/dns/dns.h99
-rw-r--r--src/dns/dns_api.c387
-rw-r--r--src/dns/gnunet-dns-monitor.c395
-rw-r--r--src/dns/gnunet-dns-redirector.c268
-rw-r--r--src/dns/gnunet-helper-dns.c1195
-rw-r--r--src/dns/gnunet-service-dns.c1275
-rw-r--r--src/dns/gnunet-zonewalk.c609
-rw-r--r--src/dns/plugin_block_dns.c236
-rwxr-xr-xsrc/dns/test_gnunet_dns.sh68
12 files changed, 0 insertions, 4670 deletions
diff --git a/src/dns/.gitignore b/src/dns/.gitignore
deleted file mode 100644
index d3a62470e..000000000
--- a/src/dns/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
1gnunet-service-dns
2gnunet-dns-monitor
3gnunet-dns-redirector
4gnunet-helper-dns
5test_hexcoder
6gnunet-zoneimport
7gnunet-zonewalk
diff --git a/src/dns/Makefile.am b/src/dns/Makefile.am
deleted file mode 100644
index f8672d55e..000000000
--- a/src/dns/Makefile.am
+++ /dev/null
@@ -1,97 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6endif
7
8pkgcfgdir= $(pkgdatadir)/config.d/
9
10libexecdir= $(pkglibdir)/libexec/
11
12plugindir = $(libdir)/gnunet
13
14pkgcfg_DATA = \
15 dns.conf
16
17if LINUX
18HIJACKBIN = gnunet-helper-dns
19endif
20
21lib_LTLIBRARIES = \
22 libgnunetdns.la
23
24libexec_PROGRAMS = \
25 gnunet-service-dns $(HIJACKBIN)
26
27noinst_PROGRAMS = \
28 gnunet-dns-monitor \
29 gnunet-dns-redirector \
30 gnunet-zonewalk
31
32plugin_LTLIBRARIES = \
33 libgnunet_plugin_block_dns.la
34
35if LINUX
36check_SCRIPTS = \
37 test_gnunet_dns.sh
38endif
39
40gnunet_helper_dns_SOURCES = \
41 gnunet-helper-dns.c
42
43
44gnunet_dns_monitor_SOURCES = \
45 gnunet-dns-monitor.c
46gnunet_dns_monitor_LDADD = \
47 libgnunetdns.la \
48 $(top_builddir)/src/util/libgnunetutil.la \
49 $(GN_LIBINTL)
50
51gnunet_zonewalk_SOURCES = \
52 gnunet-zonewalk.c
53gnunet_zonewalk_LDADD = \
54 $(top_builddir)/src/util/libgnunetutil.la \
55 $(GN_LIBINTL)
56
57gnunet_dns_redirector_SOURCES = \
58 gnunet-dns-redirector.c
59gnunet_dns_redirector_LDADD = \
60 libgnunetdns.la \
61 $(top_builddir)/src/util/libgnunetutil.la \
62 $(GN_LIBINTL)
63
64gnunet_service_dns_SOURCES = \
65 gnunet-service-dns.c
66gnunet_service_dns_LDADD = \
67 $(top_builddir)/src/statistics/libgnunetstatistics.la \
68 $(top_builddir)/src/util/libgnunetutil.la \
69 $(GN_LIBINTL)
70
71libgnunetdns_la_SOURCES = \
72 dns_api.c dns.h
73libgnunetdns_la_LIBADD = \
74 $(top_builddir)/src/util/libgnunetutil.la $(XLIB)
75libgnunetdns_la_LDFLAGS = \
76 $(GN_LIBINTL) \
77 $(GN_LIB_LDFLAGS) \
78 -version-info 0:0:0
79
80libgnunet_plugin_block_dns_la_SOURCES = \
81 plugin_block_dns.c
82libgnunet_plugin_block_dns_la_LIBADD = \
83 $(top_builddir)/src/block/libgnunetblockgroup.la \
84 $(top_builddir)/src/block/libgnunetblock.la \
85 $(top_builddir)/src/util/libgnunetutil.la
86libgnunet_plugin_block_dns_la_LDFLAGS = \
87 $(GN_LIBINTL) \
88 $(top_builddir)/src/block/$(GN_PLUGIN_LDFLAGS)
89
90
91if ENABLE_TEST_RUN
92AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
93TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
94endif
95
96EXTRA_DIST = \
97 $(check_SCRIPTS)
diff --git a/src/dns/dns.conf.in b/src/dns/dns.conf.in
deleted file mode 100644
index 39f260813..000000000
--- a/src/dns/dns.conf.in
+++ /dev/null
@@ -1,34 +0,0 @@
1[dns]
2START_ON_DEMAND = @START_ON_DEMAND@
3HOSTNAME = localhost
4BINARY = gnunet-service-dns
5UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-dns.sock
6@UNIXONLY@ PORT = 2122
7# Access to this service can compromise all DNS queries in this
8# system. Thus access should be restricted to the same UID.
9# (see https://gnunet.org/gnunet-access-control-model)
10UNIX_MATCH_UID = YES
11UNIX_MATCH_GID = YES
12
13# As there is no sufficiently restrictive access control for TCP,
14# we never use it, even if @UNIXONLY@ is not set (just to be safe)
15@UNIXONLY@ PORT = 0
16
17# Name of the virtual interface we use to intercept DNS traffic.
18IFNAME = gnunet-dns
19
20# Use RFC 3849-style documentation IPv6 address (RFC 4773 might provide an alternative in the future)
21# FIXME: or just default to a site-local address scope as we do for VPN!?
22IPV6ADDR = 2001:DB8::1
23IPV6PREFIX = 126
24
25# Use RFC 3927-style link-local address
26IPV4ADDR = 169.254.1.1
27IPV4MASK = 255.255.0.0
28
29# Enable GNUnet-wide DNS-EXIT service by setting this value to the IP address (IPv4 or IPv6)
30# of a DNS resolver to use. Only works if "PROVIDE_EXIT" is also set to YES. Must absolutely
31# NOT be an address of any of GNUnet's virtual tunnel interfaces. Use a well-known
32# public DNS resolver or your ISP's resolver from /etc/resolv.conf.
33DNS_EXIT = 8.8.8.8
34
diff --git a/src/dns/dns.h b/src/dns/dns.h
deleted file mode 100644
index 515012079..000000000
--- a/src/dns/dns.h
+++ /dev/null
@@ -1,99 +0,0 @@
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
21/**
22 * @file dns/dns.h
23 * @brief IPC messages between DNS API and DNS service
24 * @author Christian Grothoff
25 */
26#ifndef DNS_H
27#define DNS_H
28
29GNUNET_NETWORK_STRUCT_BEGIN
30
31
32/**
33 * Message from client to DNS service to register itself.
34 */
35struct GNUNET_DNS_Register
36{
37 /**
38 * Header of type #GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT
39 */
40 struct GNUNET_MessageHeader header;
41
42 /**
43 * NBO encoding of `enum GNUNET_DNS_Flags` for the client.
44 */
45 uint32_t flags GNUNET_PACKED;
46};
47
48
49/**
50 * Message from DNS service to client: please handle a request.
51 */
52struct GNUNET_DNS_Request
53{
54 /**
55 * Header of type #GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST
56 */
57 struct GNUNET_MessageHeader header;
58
59 /**
60 * Always zero.
61 */
62 uint32_t reserved GNUNET_PACKED;
63
64 /**
65 * Unique request ID.
66 */
67 uint64_t request_id GNUNET_PACKED;
68
69 /* followed by original DNS request (without UDP header) */
70};
71
72
73/**
74 * Message from client to DNS service: here is my reply.
75 */
76struct GNUNET_DNS_Response
77{
78 /**
79 * Header of type #GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE
80 */
81 struct GNUNET_MessageHeader header;
82
83 /**
84 * Zero to drop, 1 for no change (no payload), 2 for update (message has payload).
85 */
86 uint32_t drop_flag GNUNET_PACKED;
87
88 /**
89 * Unique request ID.
90 */
91 uint64_t request_id GNUNET_PACKED;
92
93 /* followed by original DNS request (without UDP header) */
94};
95
96
97GNUNET_NETWORK_STRUCT_END
98
99#endif
diff --git a/src/dns/dns_api.c b/src/dns/dns_api.c
deleted file mode 100644
index 448d86a17..000000000
--- a/src/dns/dns_api.c
+++ /dev/null
@@ -1,387 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012, 2016 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 dns/dns_api.c
23 * @brief API to access the DNS service.
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_dns_service.h"
28#include "dns.h"
29
30
31/**
32 * Handle to identify an individual DNS request.
33 */
34struct GNUNET_DNS_RequestHandle
35{
36 /**
37 * Handle to DNS API.
38 */
39 struct GNUNET_DNS_Handle *dh;
40
41 /**
42 * Stored in network byte order (as for us, it is just a random number).
43 */
44 uint64_t request_id;
45
46 /**
47 * Re-connect counter, to make sure we did not reconnect in the meantime.
48 */
49 uint32_t generation;
50};
51
52
53/**
54 * DNS handle
55 */
56struct GNUNET_DNS_Handle
57{
58 /**
59 * Connection to DNS service, or NULL.
60 */
61 struct GNUNET_MQ_Handle *mq;
62
63 /**
64 * Configuration to use.
65 */
66 const struct GNUNET_CONFIGURATION_Handle *cfg;
67
68 /**
69 * Function to call to get replies.
70 */
71 GNUNET_DNS_RequestHandler rh;
72
73 /**
74 * Closure for @e rh.
75 */
76 void *rh_cls;
77
78 /**
79 * Task to reconnect to the service.
80 */
81 struct GNUNET_SCHEDULER_Task *reconnect_task;
82
83 /**
84 * Re-connect counter, to make sure we did not reconnect in the meantime.
85 */
86 uint32_t generation;
87
88 /**
89 * Flags for events we care about.
90 */
91 enum GNUNET_DNS_Flags flags;
92
93 /**
94 * Number of GNUNET_DNS_RequestHandles we have outstanding. Must be 0 before
95 * we can be disconnected.
96 */
97 unsigned int pending_requests;
98};
99
100
101/**
102 * Reconnect to the DNS service.
103 *
104 * @param cls handle with the connection to connect
105 * @param tc scheduler context (unused)
106 */
107static void
108reconnect (void *cls);
109
110
111/**
112 * Drop the existing connection and reconnect to the DNS service.
113 *
114 * @param dh handle with the connection
115 */
116static void
117force_reconnect (struct GNUNET_DNS_Handle *dh)
118{
119 if (NULL != dh->mq)
120 {
121 GNUNET_MQ_destroy (dh->mq);
122 dh->mq = NULL;
123 }
124 dh->reconnect_task =
125 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
126 &reconnect,
127 dh);
128}
129
130
131/**
132 * Generic error handler, called with the appropriate error code and
133 * the same closure specified at the creation of the message queue.
134 * Not every message queue implementation supports an error handler.
135 *
136 * @param cls closure with the `struct GNUNET_DNS_Handle *`
137 * @param error error code
138 */
139static void
140mq_error_handler (void *cls,
141 enum GNUNET_MQ_Error error)
142{
143 struct GNUNET_DNS_Handle *dh = cls;
144
145 force_reconnect (dh);
146}
147
148
149/**
150 * This receives packets from the DNS service and calls the application to
151 * check that the request is well-formed
152 *
153 * @param cls the struct GNUNET_DNS_Handle
154 * @param req message from the service (request)
155 */
156static int
157check_request (void *cls,
158 const struct GNUNET_DNS_Request *req)
159{
160 if (0 != ntohl (req->reserved))
161 {
162 GNUNET_break (0);
163 return GNUNET_SYSERR;
164 }
165 return GNUNET_OK;
166}
167
168
169/**
170 * This receives packets from the DNS service and calls the application to
171 * handle it.
172 *
173 * @param cls the `struct GNUNET_DNS_Handle *`
174 * @param msg message from the service (request)
175 */
176static void
177handle_request (void *cls,
178 const struct GNUNET_DNS_Request *req)
179{
180 struct GNUNET_DNS_Handle *dh = cls;
181 size_t payload_length = ntohs (req->header.size) - sizeof(*req);
182 struct GNUNET_DNS_RequestHandle *rh;
183
184 rh = GNUNET_new (struct GNUNET_DNS_RequestHandle);
185 rh->dh = dh;
186 rh->request_id = req->request_id;
187 rh->generation = dh->generation;
188 dh->pending_requests++;
189 dh->rh (dh->rh_cls,
190 rh,
191 payload_length,
192 (const char *) &req[1]);
193}
194
195
196/**
197 * Reconnect to the DNS service.
198 *
199 * @param cls handle with the connection to connect
200 */
201static void
202reconnect (void *cls)
203{
204 struct GNUNET_DNS_Handle *dh = cls;
205 struct GNUNET_MQ_MessageHandler handlers[] = {
206 GNUNET_MQ_hd_var_size (request,
207 GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST,
208 struct GNUNET_DNS_Request,
209 dh),
210 GNUNET_MQ_handler_end ()
211 };
212 struct GNUNET_MQ_Envelope *env;
213 struct GNUNET_DNS_Register *msg;
214
215 dh->reconnect_task = NULL;
216 dh->mq = GNUNET_CLIENT_connect (dh->cfg,
217 "dns",
218 handlers,
219 &mq_error_handler,
220 dh);
221 if (NULL == dh->mq)
222 return;
223 dh->generation++;
224 env = GNUNET_MQ_msg (msg,
225 GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT);
226 msg->flags = htonl (dh->flags);
227 GNUNET_MQ_send (dh->mq,
228 env);
229}
230
231
232/**
233 * If a GNUNET_DNS_RequestHandler calls this function, the request is
234 * given to other clients or the global DNS for resolution. Once a
235 * global response has been obtained, the request handler is AGAIN
236 * called to give it a chance to observe and modify the response after
237 * the "normal" resolution. It is not legal for the request handler
238 * to call this function if a response is already present.
239 *
240 * @param rh request that should now be forwarded
241 */
242void
243GNUNET_DNS_request_forward (struct GNUNET_DNS_RequestHandle *rh)
244{
245 struct GNUNET_MQ_Envelope *env;
246 struct GNUNET_DNS_Response *resp;
247
248 GNUNET_assert (0 < rh->dh->pending_requests--);
249 if (rh->generation != rh->dh->generation)
250 {
251 GNUNET_free (rh);
252 return;
253 }
254 env = GNUNET_MQ_msg (resp,
255 GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
256 resp->drop_flag = htonl (1);
257 resp->request_id = rh->request_id;
258 GNUNET_MQ_send (rh->dh->mq,
259 env);
260 GNUNET_free (rh);
261}
262
263
264/**
265 * If a GNUNET_DNS_RequestHandler calls this function, the request is
266 * to be dropped and no response should be generated.
267 *
268 * @param rh request that should now be dropped
269 */
270void
271GNUNET_DNS_request_drop (struct GNUNET_DNS_RequestHandle *rh)
272{
273 struct GNUNET_MQ_Envelope *env;
274 struct GNUNET_DNS_Response *resp;
275
276 GNUNET_assert (0 < rh->dh->pending_requests--);
277 if (rh->generation != rh->dh->generation)
278 {
279 GNUNET_free (rh);
280 return;
281 }
282 env = GNUNET_MQ_msg (resp,
283 GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
284 resp->request_id = rh->request_id;
285 resp->drop_flag = htonl (0);
286 GNUNET_MQ_send (rh->dh->mq,
287 env);
288 GNUNET_free (rh);
289}
290
291
292/**
293 * If a GNUNET_DNS_RequestHandler calls this function, the request is
294 * supposed to be answered with the data provided to this call (with
295 * the modifications the function might have made).
296 *
297 * @param rh request that should now be answered
298 * @param reply_length size of @a reply (uint16_t to force sane size)
299 * @param reply reply data
300 */
301void
302GNUNET_DNS_request_answer (struct GNUNET_DNS_RequestHandle *rh,
303 uint16_t reply_length,
304 const char *reply)
305{
306 struct GNUNET_MQ_Envelope *env;
307 struct GNUNET_DNS_Response *resp;
308
309 GNUNET_assert (0 < rh->dh->pending_requests--);
310 if (rh->generation != rh->dh->generation)
311 {
312 GNUNET_free (rh);
313 return;
314 }
315 if (reply_length + sizeof(struct GNUNET_DNS_Response)
316 >= GNUNET_MAX_MESSAGE_SIZE)
317 {
318 GNUNET_break (0);
319 GNUNET_free (rh);
320 return;
321 }
322 env = GNUNET_MQ_msg_extra (resp,
323 reply_length,
324 GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
325 resp->drop_flag = htonl (2);
326 resp->request_id = rh->request_id;
327 GNUNET_memcpy (&resp[1],
328 reply,
329 reply_length);
330 GNUNET_MQ_send (rh->dh->mq,
331 env);
332 GNUNET_free (rh);
333}
334
335
336/**
337 * Connect to the service-dns
338 *
339 * @param cfg configuration to use
340 * @param flags when to call @a rh
341 * @param rh function to call with DNS requests
342 * @param rh_cls closure to pass to @a rh
343 * @return DNS handle
344 */
345struct GNUNET_DNS_Handle *
346GNUNET_DNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
347 enum GNUNET_DNS_Flags flags,
348 GNUNET_DNS_RequestHandler rh,
349 void *rh_cls)
350{
351 struct GNUNET_DNS_Handle *dh;
352
353 dh = GNUNET_new (struct GNUNET_DNS_Handle);
354 dh->cfg = cfg;
355 dh->flags = flags;
356 dh->rh = rh;
357 dh->rh_cls = rh_cls;
358 dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
359 return dh;
360}
361
362
363/**
364 * Disconnect from the DNS service.
365 *
366 * @param dh DNS handle
367 */
368void
369GNUNET_DNS_disconnect (struct GNUNET_DNS_Handle *dh)
370{
371 if (NULL != dh->mq)
372 {
373 GNUNET_MQ_destroy (dh->mq);
374 dh->mq = NULL;
375 }
376 if (NULL != dh->reconnect_task)
377 {
378 GNUNET_SCHEDULER_cancel (dh->reconnect_task);
379 dh->reconnect_task = NULL;
380 }
381 /* make sure client has no pending requests left over! */
382 GNUNET_break (0 == dh->pending_requests);
383 GNUNET_free (dh);
384}
385
386
387/* end of dns_api.c */
diff --git a/src/dns/gnunet-dns-monitor.c b/src/dns/gnunet-dns-monitor.c
deleted file mode 100644
index 48923b613..000000000
--- a/src/dns/gnunet-dns-monitor.c
+++ /dev/null
@@ -1,395 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 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 src/dns/gnunet-dns-monitor.c
23 * @brief Tool to monitor DNS queries
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_dns_service.h"
30#include "gnunet_dnsparser_lib.h"
31
32/**
33 * Handle to transport service.
34 */
35static struct GNUNET_DNS_Handle *handle;
36
37/**
38 * Option -i.
39 */
40static int inbound_only;
41
42/**
43 * Option -o.
44 */
45static int outbound_only;
46
47/**
48 * Global return value (0 success).
49 */
50static int ret;
51
52/**
53 * Selected level of verbosity.
54 */
55static unsigned int verbosity;
56
57
58/**
59 * Convert numeric DNS record type to a string.
60 *
61 * @param type type to convert
62 * @return type as string, only valid until the next call to this function
63 */
64static const char *
65get_type (uint16_t type)
66{
67 static char buf[6];
68
69 switch (type)
70 {
71 case GNUNET_DNSPARSER_TYPE_A: return "A";
72
73 case GNUNET_DNSPARSER_TYPE_NS: return "NS";
74
75 case GNUNET_DNSPARSER_TYPE_CNAME: return "CNAME";
76
77 case GNUNET_DNSPARSER_TYPE_SOA: return "SOA";
78
79 case GNUNET_DNSPARSER_TYPE_PTR: return "PTR";
80
81 case GNUNET_DNSPARSER_TYPE_MX: return "MX";
82
83 case GNUNET_DNSPARSER_TYPE_TXT: return "TXT";
84
85 case GNUNET_DNSPARSER_TYPE_AAAA: return "AAAA";
86
87 case GNUNET_DNSPARSER_TYPE_SRV: return "SRV";
88 }
89 GNUNET_snprintf (buf, sizeof(buf), "%u", (unsigned int) type);
90 return buf;
91}
92
93
94/**
95 * Convert numeric DNS record class to a string.
96 *
97 * @param class class to convert
98 * @return class as string, only valid until the next call to this function
99 */
100static const char *
101get_class (uint16_t class)
102{
103 static char buf[6];
104
105 switch (class)
106 {
107 case GNUNET_TUN_DNS_CLASS_INTERNET: return "IN";
108
109 case GNUNET_TUN_DNS_CLASS_CHAOS: return "CHAOS";
110
111 case GNUNET_TUN_DNS_CLASS_HESIOD: return "HESIOD";
112 }
113 GNUNET_snprintf (buf, sizeof(buf), "%u", (unsigned int) class);
114 return buf;
115}
116
117
118/**
119 * Output the given DNS query to stdout.
120 *
121 * @param query query to display.
122 */
123static void
124display_query (const struct GNUNET_DNSPARSER_Query *query)
125{
126 fprintf (stdout,
127 "\t\t%s %s: %s\n",
128 get_class (query->dns_traffic_class),
129 get_type (query->type),
130 query->name);
131}
132
133
134/**
135 * Output the given DNS record to stdout.
136 *
137 * @param record record to display.
138 */
139static void
140display_record (const struct GNUNET_DNSPARSER_Record *record)
141{
142 const char *format;
143 char buf[INET6_ADDRSTRLEN];
144 char *tmp;
145
146 tmp = NULL;
147 switch (record->type)
148 {
149 case GNUNET_DNSPARSER_TYPE_A:
150 if (record->data.raw.data_len != sizeof(struct in_addr))
151 format = "<invalid>";
152 else
153 format = inet_ntop (AF_INET, record->data.raw.data, buf, sizeof(buf));
154 break;
155
156 case GNUNET_DNSPARSER_TYPE_AAAA:
157 if (record->data.raw.data_len != sizeof(struct in6_addr))
158 format = "<invalid>";
159 else
160 format = inet_ntop (AF_INET6, record->data.raw.data, buf, sizeof(buf));
161 break;
162
163 case GNUNET_DNSPARSER_TYPE_NS:
164 case GNUNET_DNSPARSER_TYPE_CNAME:
165 case GNUNET_DNSPARSER_TYPE_PTR:
166 format = record->data.hostname;
167 break;
168
169 case GNUNET_DNSPARSER_TYPE_SOA:
170 if (NULL == record->data.soa)
171 format = "<invalid>";
172 else
173 {
174 GNUNET_asprintf (&tmp,
175 "origin: %s, mail: %s, serial = %u, refresh = %u s, retry = %u s, expire = %u s, minimum = %u s",
176 record->data.soa->mname,
177 record->data.soa->rname,
178 (unsigned int) record->data.soa->serial,
179 (unsigned int) record->data.soa->refresh,
180 (unsigned int) record->data.soa->retry,
181 (unsigned int) record->data.soa->expire,
182 (unsigned int) record->data.soa->minimum_ttl);
183 format = tmp;
184 }
185 break;
186
187 case GNUNET_DNSPARSER_TYPE_MX:
188 if (record->data.mx == NULL)
189 format = "<invalid>";
190 else
191 {
192 GNUNET_asprintf (&tmp,
193 "%u: %s",
194 record->data.mx->preference,
195 record->data.mx->mxhost);
196 format = tmp;
197 }
198 break;
199
200 case GNUNET_DNSPARSER_TYPE_SRV:
201 if (NULL == record->data.srv)
202 format = "<invalid>";
203 else
204 {
205 GNUNET_asprintf (&tmp,
206 "priority %u, weight = %u, port = %u, target = %s",
207 (unsigned int) record->data.srv->priority,
208 (unsigned int) record->data.srv->weight,
209 (unsigned int) record->data.srv->port,
210 record->data.srv->target);
211 format = tmp;
212 }
213 break;
214
215 case GNUNET_DNSPARSER_TYPE_TXT:
216 GNUNET_asprintf (&tmp,
217 "%.*s",
218 (unsigned int) record->data.raw.data_len,
219 (char*) record->data.raw.data);
220 format = tmp;
221 break;
222
223 default:
224 format = "<payload>";
225 break;
226 }
227 fprintf (stdout,
228 "\t\t%s %s: %s = %s (%u s)\n",
229 get_class (record->dns_traffic_class),
230 get_type (record->type),
231 record->name,
232 format,
233 (unsigned int) (GNUNET_TIME_absolute_get_remaining (
234 record->expiration_time).rel_value_us / 1000LL
235 / 1000LL));
236 GNUNET_free (tmp);
237}
238
239
240/**
241 * Signature of a function that is called whenever the DNS service
242 * encounters a DNS request and needs to do something with it. The
243 * function has then the chance to generate or modify the response by
244 * calling one of the three "GNUNET_DNS_request_*" continuations.
245 *
246 * When a request is intercepted, this function is called first to
247 * give the client a chance to do the complete address resolution;
248 * "rdata" will be NULL for this first call for a DNS request, unless
249 * some other client has already filled in a response.
250 *
251 * If multiple clients exist, all of them are called before the global
252 * DNS. The global DNS is only called if all of the clients'
253 * functions call GNUNET_DNS_request_forward. Functions that call
254 * GNUNET_DNS_request_forward will be called again before a final
255 * response is returned to the application. If any of the clients'
256 * functions call GNUNET_DNS_request_drop, the response is dropped.
257 *
258 * @param cls closure
259 * @param rh request handle to user for reply
260 * @param request_length number of bytes in request
261 * @param request udp payload of the DNS request
262 */
263static void
264display_request (void *cls,
265 struct GNUNET_DNS_RequestHandle *rh,
266 size_t request_length,
267 const char *request)
268{
269 static const char *return_codes[] = {
270 "No error", "Format error", "Server failure", "Name error",
271 "Not implemented", "Refused", "YXDomain", "YXRRset",
272 "NXRRset", "NOT AUTH", "NOT ZONE", "<invalid>",
273 "<invalid>", "<invalid>", "<invalid>", "<invalid>"
274 };
275 static const char *op_codes[] = {
276 "Query", "Inverse query", "Status", "<invalid>",
277 "<invalid>", "<invalid>", "<invalid>", "<invalid>",
278 "<invalid>", "<invalid>", "<invalid>", "<invalid>",
279 "<invalid>", "<invalid>", "<invalid>", "<invalid>"
280 };
281 struct GNUNET_DNSPARSER_Packet *p;
282 unsigned int i;
283
284 p = GNUNET_DNSPARSER_parse (request, request_length);
285 if (NULL == p)
286 {
287 fprintf (stderr, "Received malformed DNS packet!\n");
288 // FIXME: drop instead?
289 GNUNET_DNS_request_forward (rh);
290 return;
291 }
292 fprintf (stdout,
293 "%s with ID: %5u Flags: %s%s%s%s%s%s, Return Code: %s, Opcode: %s\n",
294 p->flags.query_or_response ? "Response" : "Query",
295 p->id,
296 p->flags.recursion_desired ? "RD " : "",
297 p->flags.message_truncated ? "MT " : "",
298 p->flags.authoritative_answer ? "AA " : "",
299 p->flags.checking_disabled ? "CD " : "",
300 p->flags.authenticated_data ? "AD " : "",
301 p->flags.recursion_available ? "RA " : "",
302 return_codes[p->flags.return_code & 15],
303 op_codes[p->flags.opcode & 15]);
304 if (p->num_queries > 0)
305 fprintf (stdout,
306 "\tQueries:\n");
307 for (i = 0; i < p->num_queries; i++)
308 display_query (&p->queries[i]);
309
310 if (p->num_answers > 0)
311 fprintf (stdout,
312 "\tAnswers:\n");
313 for (i = 0; i < p->num_answers; i++)
314 display_record (&p->answers[i]);
315 fprintf (stdout, "\n");
316 GNUNET_DNSPARSER_free_packet (p);
317 GNUNET_DNS_request_forward (rh);
318}
319
320
321/**
322 * Shutdown.
323 */
324static void
325do_disconnect (void *cls)
326{
327 if (NULL != handle)
328 {
329 GNUNET_DNS_disconnect (handle);
330 handle = NULL;
331 }
332}
333
334
335/**
336 * Main function that will be run by the scheduler.
337 *
338 * @param cls closure
339 * @param args remaining command-line arguments
340 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
341 * @param cfg configuration
342 */
343static void
344run (void *cls, char *const *args, const char *cfgfile,
345 const struct GNUNET_CONFIGURATION_Handle *cfg)
346{
347 enum GNUNET_DNS_Flags flags;
348
349 flags = GNUNET_DNS_FLAG_REQUEST_MONITOR | GNUNET_DNS_FLAG_RESPONSE_MONITOR;
350 if (inbound_only | outbound_only)
351 flags = 0;
352 if (inbound_only)
353 flags |= GNUNET_DNS_FLAG_REQUEST_MONITOR;
354 if (outbound_only)
355 flags |= GNUNET_DNS_FLAG_RESPONSE_MONITOR;
356 handle =
357 GNUNET_DNS_connect (cfg,
358 flags,
359 &display_request,
360 NULL);
361 GNUNET_SCHEDULER_add_shutdown (&do_disconnect, NULL);
362}
363
364
365int
366main (int argc, char *const *argv)
367{
368 struct GNUNET_GETOPT_CommandLineOption options[] = {
369 GNUNET_GETOPT_option_flag ('i',
370 "inbound-only",
371 gettext_noop ("only monitor DNS queries"),
372 &inbound_only),
373
374 GNUNET_GETOPT_option_flag ('o',
375 "outbound-only",
376 gettext_noop ("only monitor DNS queries"),
377 &outbound_only),
378
379 GNUNET_GETOPT_option_verbose (&verbosity),
380 GNUNET_GETOPT_OPTION_END
381 };
382
383 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
384 return 2;
385 ret = (GNUNET_OK ==
386 GNUNET_PROGRAM_run (argc, argv, "gnunet-dns-monitor",
387 gettext_noop
388 ("Monitor DNS queries."), options,
389 &run, NULL)) ? ret : 1;
390 GNUNET_free_nz ((void *) argv);
391 return ret;
392}
393
394
395/* end of gnunet-dns-monitor.c */
diff --git a/src/dns/gnunet-dns-redirector.c b/src/dns/gnunet-dns-redirector.c
deleted file mode 100644
index 835497dba..000000000
--- a/src/dns/gnunet-dns-redirector.c
+++ /dev/null
@@ -1,268 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 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 src/dns/gnunet-dns-redirector.c
23 * @brief Tool to change DNS replies (for testing)
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_dns_service.h"
30#include "gnunet_dnsparser_lib.h"
31
32/**
33 * Handle to DNS service.
34 */
35static struct GNUNET_DNS_Handle *handle;
36
37/**
38 * New target for A records.
39 */
40static char *n4;
41
42/**
43 * New target for AAAA records.
44 */
45static char *n6;
46
47/**
48 * Global return value (0 success).
49 */
50static int ret;
51
52/**
53 * Selected level of verbosity.
54 */
55static unsigned int verbosity;
56
57
58/**
59 * Modify the given DNS record.
60 *
61 * @param record record to modify
62 */
63static void
64modify_record (const struct GNUNET_DNSPARSER_Record *record)
65{
66 char buf[INET6_ADDRSTRLEN];
67
68 switch (record->type)
69 {
70 case GNUNET_DNSPARSER_TYPE_A:
71 if (record->data.raw.data_len != sizeof(struct in_addr))
72 return;
73 if (NULL != n4)
74 {
75 if (verbosity > 1)
76 fprintf (stderr,
77 "Changing A record from `%s' to `%s'\n",
78 inet_ntop (AF_INET, record->data.raw.data, buf, sizeof(buf)),
79 n4);
80 GNUNET_assert (1 == inet_pton (AF_INET, n4, record->data.raw.data));
81 }
82 break;
83
84 case GNUNET_DNSPARSER_TYPE_AAAA:
85 if (record->data.raw.data_len != sizeof(struct in6_addr))
86 return;
87 if (NULL != n6)
88 {
89 if (verbosity > 1)
90 fprintf (stderr,
91 "Changing AAAA record from `%s' to `%s'\n",
92 inet_ntop (AF_INET6, record->data.raw.data, buf, sizeof(buf)),
93 n6);
94 GNUNET_assert (1 == inet_pton (AF_INET6, n6, record->data.raw.data));
95 }
96 break;
97
98 case GNUNET_DNSPARSER_TYPE_NS:
99 case GNUNET_DNSPARSER_TYPE_CNAME:
100 case GNUNET_DNSPARSER_TYPE_PTR:
101 case GNUNET_DNSPARSER_TYPE_SOA:
102 case GNUNET_DNSPARSER_TYPE_MX:
103 case GNUNET_DNSPARSER_TYPE_TXT:
104 break;
105
106 default:
107 break;
108 }
109}
110
111
112/**
113 * Signature of a function that is called whenever the DNS service
114 * encounters a DNS request and needs to do something with it. The
115 * function has then the chance to generate or modify the response by
116 * calling one of the three "GNUNET_DNS_request_*" continuations.
117 *
118 * When a request is intercepted, this function is called first to
119 * give the client a chance to do the complete address resolution;
120 * "rdata" will be NULL for this first call for a DNS request, unless
121 * some other client has already filled in a response.
122 *
123 * If multiple clients exist, all of them are called before the global
124 * DNS. The global DNS is only called if all of the clients'
125 * functions call GNUNET_DNS_request_forward. Functions that call
126 * GNUNET_DNS_request_forward will be called again before a final
127 * response is returned to the application. If any of the clients'
128 * functions call GNUNET_DNS_request_drop, the response is dropped.
129 *
130 * @param cls closure
131 * @param rh request handle to user for reply
132 * @param request_length number of bytes in request
133 * @param request udp payload of the DNS request
134 */
135static void
136modify_request (void *cls,
137 struct GNUNET_DNS_RequestHandle *rh,
138 size_t request_length,
139 const char *request)
140{
141 struct GNUNET_DNSPARSER_Packet *p;
142 unsigned int i;
143 char *buf;
144 size_t len;
145 int ret;
146
147 p = GNUNET_DNSPARSER_parse (request, request_length);
148 if (NULL == p)
149 {
150 fprintf (stderr, "Received malformed DNS packet, leaving it untouched\n");
151 GNUNET_DNS_request_forward (rh);
152 return;
153 }
154 for (i = 0; i < p->num_answers; i++)
155 modify_record (&p->answers[i]);
156 buf = NULL;
157 ret = GNUNET_DNSPARSER_pack (p, 1024, &buf, &len);
158 GNUNET_DNSPARSER_free_packet (p);
159 if (GNUNET_OK != ret)
160 {
161 if (GNUNET_NO == ret)
162 fprintf (stderr,
163 "Modified DNS response did not fit, keeping old response\n");
164 else
165 GNUNET_break (0); /* our modifications should have been sane! */
166 GNUNET_DNS_request_forward (rh);
167 }
168 else
169 {
170 if (verbosity > 0)
171 fprintf (stdout,
172 "Injecting modified DNS response\n");
173 GNUNET_DNS_request_answer (rh, len, buf);
174 }
175 GNUNET_free (buf);
176}
177
178
179/**
180 * Shutdown.
181 */
182static void
183do_disconnect (void *cls)
184{
185 if (NULL != handle)
186 {
187 GNUNET_DNS_disconnect (handle);
188 handle = NULL;
189 }
190}
191
192
193/**
194 * Main function that will be run by the scheduler.
195 *
196 * @param cls closure
197 * @param args remaining command-line arguments
198 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
199 * @param cfg configuration
200 */
201static void
202run (void *cls, char *const *args, const char *cfgfile,
203 const struct GNUNET_CONFIGURATION_Handle *cfg)
204{
205 struct in_addr i4;
206 struct in6_addr i6;
207
208 if ((n4 != NULL) &&
209 (1 != inet_pton (AF_INET, n4, &i4)))
210 {
211 fprintf (stderr,
212 "`%s' is nto a valid IPv4 address!\n",
213 n4);
214 return;
215 }
216 if ((n6 != NULL) &&
217 (1 != inet_pton (AF_INET6, n6, &i6)))
218 {
219 fprintf (stderr,
220 "`%s' is nto a valid IPv6 address!\n",
221 n6);
222 return;
223 }
224
225 handle =
226 GNUNET_DNS_connect (cfg,
227 GNUNET_DNS_FLAG_POST_RESOLUTION,
228 &modify_request,
229 NULL);
230 GNUNET_SCHEDULER_add_shutdown (&do_disconnect, NULL);
231}
232
233
234int
235main (int argc, char *const *argv)
236{
237 struct GNUNET_GETOPT_CommandLineOption options[] = {
238 GNUNET_GETOPT_option_string ('4',
239 "ipv4",
240 "IPV4",
241 gettext_noop ("set A records"),
242 &n4),
243
244 GNUNET_GETOPT_option_string ('6',
245 "ipv4",
246 "IPV6",
247 gettext_noop ("set AAAA records"),
248 &n6),
249
250 GNUNET_GETOPT_option_verbose (&verbosity),
251 GNUNET_GETOPT_OPTION_END
252 };
253
254 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
255 return 2;
256
257 ret = (GNUNET_OK ==
258 GNUNET_PROGRAM_run (argc, argv, "gnunet-dns-redirector",
259 gettext_noop
260 ("Change DNS replies to point elsewhere."),
261 options,
262 &run, NULL)) ? ret : 1;
263 GNUNET_free_nz ((void *) argv);
264 return ret;
265}
266
267
268/* end of gnunet-dns-redirector.c */
diff --git a/src/dns/gnunet-helper-dns.c b/src/dns/gnunet-helper-dns.c
deleted file mode 100644
index f0e39464d..000000000
--- a/src/dns/gnunet-helper-dns.c
+++ /dev/null
@@ -1,1195 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 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 dns/gnunet-helper-dns.c
23 * @brief helper to install firewall rules to hijack all DNS traffic
24 * and send it to our virtual interface (except for DNS traffic
25 * that originates on the specified port). We then
26 * allow interacting with our virtual interface via stdin/stdout.
27 * @author Philipp Tölke
28 * @author Christian Grothoff
29 *
30 * This program alters the Linux firewall rules so that DNS traffic
31 * that ordinarily exits the system can be intercepted and managed by
32 * a virtual interface. In order to achieve this, DNS traffic is
33 * marked with the DNS_MARK given in below and re-routed to a custom
34 * table with the DNS_TABLE ID given below. Systems and
35 * administrators must take care to not cause conflicts with these
36 * values (it was deemed safest to hardcode them as passing these
37 * values as arguments might permit messing with arbitrary firewall
38 * rules, which would be dangerous). Traffic coming from the same
39 * group ID as the effective group ID that this process is running
40 * as is not intercepted.
41 *
42 * The code first sets up the virtual interface, then begins to
43 * redirect the DNS traffic to it, and then on errors or SIGTERM shuts
44 * down the virtual interface and removes the rules for the traffic
45 * redirection.
46 *
47 *
48 * Note that having this binary SUID is only partially safe: it will
49 * allow redirecting (and intercepting / mangling) of all DNS traffic
50 * originating from this system by any user who is able to run it.
51 * Furthermore, this code will make it trivial to DoS all DNS traffic
52 * originating from the current system, simply by sending it to
53 * nowhere (redirect stdout to /dev/null).
54 *
55 * Naturally, neither of these problems can be helped as this is the
56 * fundamental purpose of the binary. Certifying that this code is
57 * "safe" thus only means that it doesn't allow anything else (such
58 * as local priv. escalation, etc.).
59 *
60 * The following list of people have reviewed this code and considered
61 * it safe (within specifications) since the last modification (if you
62 * reviewed it, please have your name added to the list):
63 *
64 * - Christian Grothoff
65 */
66#include "platform.h"
67
68#ifdef IF_TUN_HDR
69#include IF_TUN_HDR
70#endif
71
72/**
73 * Need 'struct GNUNET_MessageHeader'.
74 */
75#include "gnunet_crypto_lib.h"
76#include "gnunet_common.h"
77
78/**
79 * Need DNS message types.
80 */
81#include "gnunet_protocols.h"
82
83/**
84 * Maximum size of a GNUnet message (GNUNET_MAX_MESSAGE_SIZE)
85 */
86#define MAX_SIZE 65536
87
88#if ! HAVE_DECL_STRUCT_IN6_IFREQ
89/**
90 * This is in linux/include/net/ipv6.h, but not always exported...
91 */
92struct in6_ifreq
93{
94 struct in6_addr ifr6_addr;
95 uint32_t ifr6_prefixlen;
96 unsigned int ifr6_ifindex;
97};
98#endif
99
100/**
101 * Name and full path of IPTABLES binary.
102 */
103static const char *sbin_iptables;
104
105/**
106 * Name and full path of IPTABLES binary.
107 */
108static const char *sbin_ip6tables;
109
110/**
111 * Name and full path of sysctl binary
112 */
113static const char *sbin_sysctl;
114
115/**
116 * Name and full path of IPTABLES binary.
117 */
118static const char *sbin_ip;
119
120/**
121 * Port for DNS traffic.
122 */
123#define DNS_PORT "53"
124
125/**
126 * Marker we set for our hijacked DNS traffic. We use GNUnet's
127 * port (2086) plus the DNS port (53) in HEX to make a 32-bit mark
128 * (which is hopefully long enough to not collide); so
129 * 0x08260035 = 136708149 (hopefully unique enough...).
130 */
131#define DNS_MARK "136708149"
132
133/**
134 * Table we use for our DNS rules. 0-255 is the range and
135 * 0, 253, 254 and 255 are already reserved. As this is about
136 * DNS and as "53" is likely (fingers crossed!) high enough to
137 * not usually conflict with a normal user's setup, we use 53
138 * to give a hint that this has something to do with DNS.
139 */
140#define DNS_TABLE "53"
141
142
143/**
144 * Control pipe for shutdown via signal. [0] is the read end,
145 * [1] is the write end.
146 */
147static int cpipe[2];
148
149
150/**
151 * Signal handler called to initiate "nice" shutdown. Signals select
152 * loop via non-bocking pipe 'cpipe'.
153 *
154 * @param signal signal number of the signal (not used)
155 */
156static void
157signal_handler (int signal)
158{
159 /* ignore return value, as the signal handler could theoretically
160 be called many times before the shutdown can actually happen */
161 (void) write (cpipe[1], "K", 1);
162}
163
164
165/**
166 * Open '/dev/null' and make the result the given
167 * file descriptor.
168 *
169 * @param target_fd desired FD to point to /dev/null
170 * @param flags open flags (O_RDONLY, O_WRONLY)
171 */
172static void
173open_dev_null (int target_fd,
174 int flags)
175{
176 int fd;
177
178 fd = open ("/dev/null", flags);
179 if (-1 == fd)
180 abort ();
181 if (fd == target_fd)
182 return;
183 if (-1 == dup2 (fd, target_fd))
184 {
185 (void) close (fd);
186 abort ();
187 }
188 (void) close (fd);
189}
190
191
192/**
193 * Run the given command and wait for it to complete.
194 *
195 * @param file name of the binary to run
196 * @param cmd command line arguments (as given to 'execv')
197 * @return 0 on success, 1 on any error
198 */
199static int
200fork_and_exec (const char *file,
201 char *const cmd[])
202{
203 int status;
204 pid_t pid;
205 pid_t ret;
206
207 pid = fork ();
208 if (-1 == pid)
209 {
210 fprintf (stderr,
211 "fork failed: %s\n",
212 strerror (errno));
213 return 1;
214 }
215 if (0 == pid)
216 {
217 /* we are the child process */
218 /* close stdin/stdout to not cause interference
219 with the helper's main protocol! */
220 (void) close (0);
221 open_dev_null (0, O_RDONLY);
222 (void) close (1);
223 open_dev_null (1, O_WRONLY);
224 (void) execv (file, cmd);
225 /* can only get here on error */
226 fprintf (stderr,
227 "exec `%s' failed: %s\n",
228 file,
229 strerror (errno));
230 _exit (1);
231 }
232 /* keep running waitpid as long as the only error we get is 'EINTR' */
233 while ((-1 == (ret = waitpid (pid, &status, 0))) &&
234 (errno == EINTR))
235 ;
236 if (-1 == ret)
237 {
238 fprintf (stderr,
239 "waitpid failed: %s\n",
240 strerror (errno));
241 return 1;
242 }
243 if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
244 return 1;
245 /* child process completed and returned success, we're happy */
246 return 0;
247}
248
249
250/**
251 * Creates a tun-interface called @a dev;
252 *
253 * @param dev is assumed to point to a char[IFNAMSIZ]
254 * if *dev == '\\0', uses the name supplied by the kernel;
255 * @return the fd to the tun or -1 on error
256 */
257static int
258init_tun (char *dev)
259{
260 struct ifreq ifr;
261 int fd;
262
263 if (NULL == dev)
264 {
265 errno = EINVAL;
266 return -1;
267 }
268
269 if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
270 {
271 fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun",
272 strerror (errno));
273 return -1;
274 }
275
276 if (fd >= FD_SETSIZE)
277 {
278 fprintf (stderr, "File descriptor to large: %d", fd);
279 (void) close (fd);
280 return -1;
281 }
282
283 memset (&ifr, 0, sizeof(ifr));
284 ifr.ifr_flags = IFF_TUN;
285
286 if ('\0' != *dev)
287 strncpy (ifr.ifr_name, dev, IFNAMSIZ);
288
289 if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
290 {
291 fprintf (stderr, "Error with ioctl on `%s': %s\n", "/dev/net/tun",
292 strerror (errno));
293 (void) close (fd);
294 return -1;
295 }
296 strcpy (dev, ifr.ifr_name);
297 return fd;
298}
299
300
301/**
302 * @brief Sets the IPv6-Address given in @a address on the interface @a dev
303 *
304 * @param dev the interface to configure
305 * @param address the IPv6-Address
306 * @param prefix_len the length of the network-prefix
307 */
308static void
309set_address6 (const char *dev, const char *address, unsigned long prefix_len)
310{
311 struct ifreq ifr;
312 struct in6_ifreq ifr6;
313 struct sockaddr_in6 sa6;
314 int fd;
315
316 /*
317 * parse the new address
318 */
319 memset (&sa6, 0, sizeof(struct sockaddr_in6));
320 sa6.sin6_family = AF_INET6;
321 if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
322 {
323 fprintf (stderr,
324 "Failed to parse IPv6 address `%s': %s\n",
325 address,
326 strerror (errno));
327 exit (1);
328 }
329
330 if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
331 {
332 fprintf (stderr,
333 "Error creating IPv6 socket: %s (ignored)\n",
334 strerror (errno));
335 /* ignore error, maybe only IPv4 works on this system! */
336 return;
337 }
338
339 memset (&ifr, 0, sizeof(struct ifreq));
340 /*
341 * Get the index of the if
342 */
343 strncpy (ifr.ifr_name, dev, IFNAMSIZ);
344 if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
345 {
346 fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
347 (void) close (fd);
348 exit (1);
349 }
350
351 memset (&ifr6, 0, sizeof(struct in6_ifreq));
352 ifr6.ifr6_addr = sa6.sin6_addr;
353 ifr6.ifr6_ifindex = ifr.ifr_ifindex;
354 ifr6.ifr6_prefixlen = prefix_len;
355
356 /*
357 * Set the address
358 */
359 if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
360 {
361 fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
362 strerror (errno));
363 (void) close (fd);
364 exit (1);
365 }
366
367 /*
368 * Get the flags
369 */
370 if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
371 {
372 fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
373 strerror (errno));
374 (void) close (fd);
375 exit (1);
376 }
377
378 /*
379 * Add the UP and RUNNING flags
380 */
381 ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
382 if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
383 {
384 fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
385 strerror (errno));
386 (void) close (fd);
387 exit (1);
388 }
389
390 if (0 != close (fd))
391 {
392 fprintf (stderr, "close failed: %s\n", strerror (errno));
393 exit (1);
394 }
395}
396
397
398/**
399 * @brief Sets the IPv4-Address given in @a address on the interface @a dev
400 *
401 * @param dev the interface to configure
402 * @param address the IPv4-Address
403 * @param mask the netmask
404 */
405static void
406set_address4 (const char *dev, const char *address, const char *mask)
407{
408 int fd;
409 struct sockaddr_in *addr;
410 struct ifreq ifr;
411
412 memset (&ifr, 0, sizeof(struct ifreq));
413 addr = (struct sockaddr_in *) &(ifr.ifr_addr);
414 addr->sin_family = AF_INET;
415
416 /*
417 * Parse the address
418 */
419 if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
420 {
421 fprintf (stderr,
422 "Failed to parse IPv4 address `%s': %s\n",
423 address,
424 strerror (errno));
425 exit (1);
426 }
427
428 if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
429 {
430 fprintf (stderr,
431 "Error creating IPv4 socket: %s\n",
432 strerror (errno));
433 exit (1);
434 }
435
436 strncpy (ifr.ifr_name, dev, IFNAMSIZ);
437
438 /*
439 * Set the address
440 */
441 if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
442 {
443 fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
444 (void) close (fd);
445 exit (1);
446 }
447
448 /*
449 * Parse the netmask
450 */
451 addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
452 if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
453 {
454 fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
455 strerror (errno));
456 (void) close (fd);
457 exit (1);
458 }
459
460 /*
461 * Set the netmask
462 */
463 if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
464 {
465 fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
466 strerror (errno));
467 (void) close (fd);
468 exit (1);
469 }
470
471 /*
472 * Get the flags
473 */
474 if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
475 {
476 fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
477 strerror (errno));
478 (void) close (fd);
479 exit (1);
480 }
481
482 /*
483 * Add the UP and RUNNING flags
484 */
485 ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
486 if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
487 {
488 fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
489 strerror (errno));
490 (void) close (fd);
491 exit (1);
492 }
493
494 if (0 != close (fd))
495 {
496 fprintf (stderr, "close failed: %s\n", strerror (errno));
497 (void) close (fd);
498 exit (1);
499 }
500}
501
502
503/**
504 * Start forwarding to and from the tunnel. This function runs with
505 * "reduced" privileges (saved UID is still 0, but effective UID is
506 * the real user ID).
507 *
508 * @param fd_tun tunnel FD
509 */
510static void
511run (int fd_tun)
512{
513 /*
514 * The buffer filled by reading from fd_tun
515 */
516 unsigned char buftun[MAX_SIZE];
517 ssize_t buftun_size = 0;
518 unsigned char *buftun_read = NULL;
519
520 /*
521 * The buffer filled by reading from stdin
522 */
523 unsigned char bufin[MAX_SIZE];
524 ssize_t bufin_size = 0;
525 size_t bufin_rpos = 0;
526 unsigned char *bufin_read = NULL;
527 fd_set fds_w;
528 fd_set fds_r;
529 int max;
530
531 while (1)
532 {
533 FD_ZERO (&fds_w);
534 FD_ZERO (&fds_r);
535
536 /*
537 * We are supposed to read and the buffer is empty
538 * -> select on read from tun
539 */
540 if (0 == buftun_size)
541 FD_SET (fd_tun, &fds_r);
542
543 /*
544 * We are supposed to read and the buffer is not empty
545 * -> select on write to stdout
546 */
547 if (0 < buftun_size)
548 FD_SET (1, &fds_w);
549
550 /*
551 * We are supposed to write and the buffer is empty
552 * -> select on read from stdin
553 */
554 if (NULL == bufin_read)
555 FD_SET (0, &fds_r);
556
557 /*
558 * We are supposed to write and the buffer is not empty
559 * -> select on write to tun
560 */
561 if (NULL != bufin_read)
562 FD_SET (fd_tun, &fds_w);
563
564 FD_SET (cpipe[0], &fds_r);
565 max = (fd_tun > cpipe[0]) ? fd_tun : cpipe[0];
566
567 int r = select (max + 1, &fds_r, &fds_w, NULL, NULL);
568
569 if (-1 == r)
570 {
571 if (EINTR == errno)
572 continue;
573 fprintf (stderr, "select failed: %s\n", strerror (errno));
574 return;
575 }
576
577 if (r > 0)
578 {
579 if (FD_ISSET (cpipe[0], &fds_r))
580 return; /* aborted by signal */
581
582 if (FD_ISSET (fd_tun, &fds_r))
583 {
584 buftun_size =
585 read (fd_tun, buftun + sizeof(struct GNUNET_MessageHeader),
586 MAX_SIZE - sizeof(struct GNUNET_MessageHeader));
587 if (-1 == buftun_size)
588 {
589 if ((errno == EINTR) ||
590 (errno == EAGAIN))
591 {
592 buftun_size = 0;
593 continue;
594 }
595 fprintf (stderr, "read-error: %s\n", strerror (errno));
596 return;
597 }
598 if (0 == buftun_size)
599 {
600 fprintf (stderr, "EOF on tun\n");
601 return;
602 }
603 buftun_read = buftun;
604 {
605 struct GNUNET_MessageHeader *hdr =
606 (struct GNUNET_MessageHeader *) buftun;
607 buftun_size += sizeof(struct GNUNET_MessageHeader);
608 hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
609 hdr->size = htons (buftun_size);
610 }
611 }
612 else if (FD_ISSET (1, &fds_w))
613 {
614 ssize_t written = write (1, buftun_read, buftun_size);
615
616 if (-1 == written)
617 {
618 if ((errno == EINTR) ||
619 (errno == EAGAIN))
620 continue;
621 fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
622 return;
623 }
624 if (0 == written)
625 {
626 fprintf (stderr, "write returned 0\n");
627 return;
628 }
629 buftun_size -= written;
630 buftun_read += written;
631 }
632
633 if (FD_ISSET (0, &fds_r))
634 {
635 bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
636 if (-1 == bufin_size)
637 {
638 bufin_read = NULL;
639 if ((errno == EINTR) ||
640 (errno == EAGAIN))
641 continue;
642 fprintf (stderr, "read-error: %s\n", strerror (errno));
643 return;
644 }
645 if (0 == bufin_size)
646 {
647 bufin_read = NULL;
648 fprintf (stderr, "EOF on stdin\n");
649 return;
650 }
651 {
652 struct GNUNET_MessageHeader *hdr;
653
654PROCESS_BUFFER:
655 bufin_rpos += bufin_size;
656 if (bufin_rpos < sizeof(struct GNUNET_MessageHeader))
657 continue;
658 hdr = (struct GNUNET_MessageHeader *) bufin;
659 if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_DNS_HELPER)
660 {
661 fprintf (stderr, "protocol violation!\n");
662 return;
663 }
664 if (ntohs (hdr->size) > bufin_rpos)
665 continue;
666 bufin_read = bufin + sizeof(struct GNUNET_MessageHeader);
667 bufin_size = ntohs (hdr->size) - sizeof(struct GNUNET_MessageHeader);
668 bufin_rpos -= bufin_size + sizeof(struct GNUNET_MessageHeader);
669 }
670 }
671 else if (FD_ISSET (fd_tun, &fds_w))
672 {
673 ssize_t written = write (fd_tun, bufin_read, bufin_size);
674
675 if (-1 == written)
676 {
677 if ((errno == EINTR) ||
678 (errno == EAGAIN))
679 continue;
680 fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
681 return;
682 }
683 if (0 == written)
684 {
685 fprintf (stderr, "write returned 0\n");
686 return;
687 }
688 {
689 bufin_size -= written;
690 bufin_read += written;
691 if (0 == bufin_size)
692 {
693 memmove (bufin, bufin_read, bufin_rpos);
694 bufin_read = NULL; /* start reading again */
695 bufin_size = 0;
696 goto PROCESS_BUFFER;
697 }
698 }
699 }
700 }
701 }
702}
703
704
705/**
706 * Main function of "gnunet-helper-dns", which opens a VPN tunnel interface,
707 * redirects all outgoing DNS traffic (except from the specified port) to that
708 * interface and then passes traffic from and to the interface via stdin/stdout.
709 *
710 * Once stdin/stdout close or have other errors, the tunnel is closed and the
711 * DNS traffic redirection is stopped.
712 *
713 * @param argc number of arguments
714 * @param argv 0: binary name (should be "gnunet-helper-vpn")
715 * 1: tunnel interface name (typically "gnunet-dns")
716 * 2: IPv6 address for the tunnel ("FE80::1")
717 * 3: IPv6 netmask length in bits ("64")
718 * 4: IPv4 address for the tunnel ("1.2.3.4")
719 * 5: IPv4 netmask ("255.255.0.0")
720 * 6: skip sysctl, routing and iptables setup ("0")
721 * @return 0 on success, otherwise code indicating type of error:
722 * 1 wrong number of arguments
723 * 2 invalid arguments (i.e. port number / prefix length wrong)
724 * 3 iptables not executable
725 * 4 ip not executable
726 * 5 failed to initialize tunnel interface
727 * 6 failed to initialize control pipe
728 * 8 failed to change routing table, cleanup successful
729 * 9-23 failed to change routing table and failed to undo some changes to routing table
730 * 24 failed to drop privs
731 * 25-39 failed to drop privs and then failed to undo some changes to routing table
732 * 40 failed to regain privs
733 * 41-55 failed to regain prisv and then failed to undo some changes to routing table
734 * 254 insufficient privileges
735 * 255 failed to handle kill signal properly
736 */
737int
738main (int argc, char *const*argv)
739{
740 int r;
741 char dev[IFNAMSIZ];
742 char mygid[32];
743 int fd_tun;
744 uid_t uid;
745 int nortsetup = 0;
746
747 if (7 != argc)
748 {
749 fprintf (stderr, "Fatal: must supply 6 arguments!\n");
750 return 1;
751 }
752
753 /* assert privs so we can modify the firewall rules! */
754 uid = getuid ();
755#ifdef HAVE_SETRESUID
756 if (0 != setresuid (uid, 0, 0))
757 {
758 fprintf (stderr, "Failed to setresuid to root: %s\n", strerror (errno));
759 return 254;
760 }
761#else
762 if (0 != seteuid (0))
763 {
764 fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
765 return 254;
766 }
767#endif
768 if (0 == strncmp (argv[6], "1", 2))
769 nortsetup = 1;
770
771 if (0 == nortsetup)
772 {
773 /* verify that the binaries we care about are executable */
774#ifdef IPTABLES
775 if (0 == access (IPTABLES, X_OK))
776 sbin_iptables = IPTABLES;
777 else
778#endif
779 if (0 == access ("/sbin/iptables", X_OK))
780 sbin_iptables = "/sbin/iptables";
781 else if (0 == access ("/usr/sbin/iptables", X_OK))
782 sbin_iptables = "/usr/sbin/iptables";
783 else
784 {
785 fprintf (stderr,
786 "Fatal: executable iptables not found in approved directories: %s\n",
787 strerror (errno));
788 return 3;
789 }
790#ifdef IP6TABLES
791 if (0 == access (IP6TABLES, X_OK))
792 sbin_ip6tables = IP6TABLES;
793 else
794#endif
795 if (0 == access ("/sbin/ip6tables", X_OK))
796 sbin_ip6tables = "/sbin/ip6tables";
797 else if (0 == access ("/usr/sbin/ip6tables", X_OK))
798 sbin_ip6tables = "/usr/sbin/ip6tables";
799 else
800 {
801 fprintf (stderr,
802 "Fatal: executable ip6tables not found in approved directories: %s\n",
803 strerror (errno));
804 return 3;
805 }
806#ifdef PATH_TO_IP
807 if (0 == access (PATH_TO_IP, X_OK))
808 sbin_ip = PATH_TO_IP;
809 else
810#endif
811 if (0 == access ("/sbin/ip", X_OK))
812 sbin_ip = "/sbin/ip";
813 else if (0 == access ("/usr/sbin/ip", X_OK))
814 sbin_ip = "/usr/sbin/ip";
815 else if (0 == access ("/bin/ip", X_OK)) /* gentoo has it there */
816 sbin_ip = "/bin/ip";
817 else
818 {
819 fprintf (stderr,
820 "Fatal: executable ip not found in approved directories: %s\n",
821 strerror (errno));
822 return 4;
823 }
824#ifdef SYSCTL
825 if (0 == access (SYSCTL, X_OK))
826 sbin_sysctl = SYSCTL;
827 else
828#endif
829 if (0 == access ("/sbin/sysctl", X_OK))
830 sbin_sysctl = "/sbin/sysctl";
831 else if (0 == access ("/usr/sbin/sysctl", X_OK))
832 sbin_sysctl = "/usr/sbin/sysctl";
833 else
834 {
835 fprintf (stderr,
836 "Fatal: executable sysctl not found in approved directories: %s\n",
837 strerror (errno));
838 return 5;
839 }
840 }
841
842 /* setup 'mygid' string */
843 snprintf (mygid, sizeof(mygid), "%d", (int) getegid ());
844
845 /* do not die on SIGPIPE */
846 if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
847 {
848 fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
849 strerror (errno));
850 return 7;
851 }
852
853 /* setup pipe to shutdown nicely on SIGINT */
854 if (0 != pipe (cpipe))
855 {
856 fprintf (stderr,
857 "Fatal: could not setup control pipe: %s\n",
858 strerror (errno));
859 return 6;
860 }
861 if (cpipe[0] >= FD_SETSIZE)
862 {
863 fprintf (stderr, "Pipe file descriptor to large: %d", cpipe[0]);
864 (void) close (cpipe[0]);
865 (void) close (cpipe[1]);
866 return 6;
867 }
868 {
869 /* make pipe non-blocking, as we theoretically could otherwise block
870 in the signal handler */
871 int flags = fcntl (cpipe[1], F_GETFL);
872 if (-1 == flags)
873 {
874 fprintf (stderr, "Failed to read flags for pipe: %s", strerror (errno));
875 (void) close (cpipe[0]);
876 (void) close (cpipe[1]);
877 return 6;
878 }
879 flags |= O_NONBLOCK;
880 if (0 != fcntl (cpipe[1], F_SETFL, flags))
881 {
882 fprintf (stderr, "Failed to make pipe non-blocking: %s", strerror (
883 errno));
884 (void) close (cpipe[0]);
885 (void) close (cpipe[1]);
886 return 6;
887 }
888 }
889 if ((SIG_ERR == signal (SIGTERM, &signal_handler)) ||
890#if (SIGTERM != GNUNET_TERM_SIG)
891 (SIG_ERR == signal (GNUNET_TERM_SIG, &signal_handler)) ||
892#endif
893 (SIG_ERR == signal (SIGINT, &signal_handler)) ||
894 (SIG_ERR == signal (SIGHUP, &signal_handler)))
895 {
896 fprintf (stderr,
897 "Fatal: could not initialize signal handler: %s\n",
898 strerror (errno));
899 (void) close (cpipe[0]);
900 (void) close (cpipe[1]);
901 return 7;
902 }
903
904
905 /* get interface name */
906 strncpy (dev, argv[1], IFNAMSIZ);
907 dev[IFNAMSIZ - 1] = '\0';
908
909 /* Disable rp filtering */
910 if (0 == nortsetup)
911 {
912 char *const sysctl_args[] = { "sysctl", "-w",
913 "net.ipv4.conf.all.rp_filter=0", NULL };
914 char *const sysctl_args2[] = { "sysctl", "-w",
915 "net.ipv4.conf.default.rp_filter=0", NULL };
916 if ((0 != fork_and_exec (sbin_sysctl, sysctl_args)) ||
917 (0 != fork_and_exec (sbin_sysctl, sysctl_args2)))
918 {
919 fprintf (stderr,
920 "Failed to disable rp filtering.\n");
921 return 5;
922 }
923 }
924
925
926 /* now open virtual interface (first part that requires root) */
927 if (-1 == (fd_tun = init_tun (dev)))
928 {
929 fprintf (stderr, "Fatal: could not initialize tun-interface\n");
930 (void) signal (SIGTERM, SIG_IGN);
931#if (SIGTERM != GNUNET_TERM_SIG)
932 (void) signal (GNUNET_TERM_SIG, SIG_IGN);
933#endif
934 (void) signal (SIGINT, SIG_IGN);
935 (void) signal (SIGHUP, SIG_IGN);
936 (void) close (cpipe[0]);
937 (void) close (cpipe[1]);
938 return 5;
939 }
940
941 /* now set interface addresses */
942 {
943 const char *address = argv[2];
944 long prefix_len = atol (argv[3]);
945
946 if ((prefix_len < 1) || (prefix_len > 127))
947 {
948 fprintf (stderr, "Fatal: prefix_len out of range\n");
949 (void) signal (SIGTERM, SIG_IGN);
950#if (SIGTERM != GNUNET_TERM_SIG)
951 (void) signal (GNUNET_TERM_SIG, SIG_IGN);
952#endif
953 (void) signal (SIGINT, SIG_IGN);
954 (void) signal (SIGHUP, SIG_IGN);
955 (void) close (cpipe[0]);
956 (void) close (cpipe[1]);
957 return 2;
958 }
959 set_address6 (dev, address, prefix_len);
960 }
961
962 {
963 const char *address = argv[4];
964 const char *mask = argv[5];
965
966 set_address4 (dev, address, mask);
967 }
968
969
970 /* update routing tables -- next part why we need SUID! */
971 /* Forward everything from our EGID (which should only be held
972 by the 'gnunet-service-dns') and with destination
973 to port 53 on UDP, without hijacking */
974 if (0 == nortsetup)
975 {
976 r = 8; /* failed to fully setup routing table */
977 {
978 char *const mangle_args[] = {
979 "iptables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
980 "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
981 "ACCEPT", NULL
982 };
983 if (0 != fork_and_exec (sbin_iptables, mangle_args))
984 goto cleanup_rest;
985 }
986 {
987 char *const mangle_args[] = {
988 "ip6tables", "-m", "owner", "-t", "mangle", "-I", "OUTPUT", "1", "-p",
989 "udp", "--gid-owner", mygid, "--dport", DNS_PORT, "-j",
990 "ACCEPT", NULL
991 };
992 if (0 != fork_and_exec (sbin_ip6tables, mangle_args))
993 goto cleanup_mangle_1b;
994 }
995 /* Mark all of the other DNS traffic using our mark DNS_MARK,
996 unless it is on a link-local IPv6 address, which we cannot support. */
997 {
998 char *const mark_args[] = {
999 "iptables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
1000 "udp", "--dport", DNS_PORT,
1001 "-j", "MARK", "--set-mark", DNS_MARK,
1002 NULL
1003 };
1004 if (0 != fork_and_exec (sbin_iptables, mark_args))
1005 goto cleanup_mangle_1;
1006 }
1007 {
1008 char *const mark_args[] = {
1009 "ip6tables", "-t", "mangle", "-I", "OUTPUT", "2", "-p",
1010 "udp", "--dport", DNS_PORT,
1011 "!", "-s", "fe80::/10", /* this line excludes link-local traffic */
1012 "-j", "MARK", "--set-mark", DNS_MARK,
1013 NULL
1014 };
1015 if (0 != fork_and_exec (sbin_ip6tables, mark_args))
1016 goto cleanup_mark_2b;
1017 }
1018 /* Forward all marked DNS traffic to our DNS_TABLE */
1019 {
1020 char *const forward_args[] = {
1021 "ip", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1022 };
1023 if (0 != fork_and_exec (sbin_ip, forward_args))
1024 goto cleanup_mark_2;
1025 }
1026 {
1027 char *const forward_args[] = {
1028 "ip", "-6", "rule", "add", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1029 };
1030 if (0 != fork_and_exec (sbin_ip, forward_args))
1031 goto cleanup_forward_3b;
1032 }
1033 /* Finally, add rule in our forwarding table to pass to our virtual interface */
1034 {
1035 char *const route_args[] = {
1036 "ip", "route", "add", "default", "dev", dev,
1037 "table", DNS_TABLE, NULL
1038 };
1039 if (0 != fork_and_exec (sbin_ip, route_args))
1040 goto cleanup_forward_3;
1041 }
1042 {
1043 char *const route_args[] = {
1044 "ip", "-6", "route", "add", "default", "dev", dev,
1045 "table", DNS_TABLE, NULL
1046 };
1047 if (0 != fork_and_exec (sbin_ip, route_args))
1048 goto cleanup_route_4b;
1049 }
1050 }
1051
1052 /* drop privs *except* for the saved UID; this is not perfect, but better
1053 than doing nothing */
1054#ifdef HAVE_SETRESUID
1055 if (0 != setresuid (uid, uid, 0))
1056 {
1057 fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
1058 r = 24;
1059 goto cleanup_route_4;
1060 }
1061#else
1062 /* Note: no 'setuid' here as we must keep our saved UID as root */
1063 if (0 != seteuid (uid))
1064 {
1065 fprintf (stderr, "Failed to seteuid: %s\n", strerror (errno));
1066 r = 24;
1067 goto cleanup_route_4;
1068 }
1069#endif
1070
1071 r = 0; /* did fully setup routing table (if nothing else happens, we were successful!) */
1072
1073 /* now forward until we hit a problem */
1074 run (fd_tun);
1075
1076 /* now need to regain privs so we can remove the firewall rules we added! */
1077#ifdef HAVE_SETRESUID
1078 if (0 != setresuid (uid, 0, 0))
1079 {
1080 fprintf (stderr, "Failed to setresuid back to root: %s\n", strerror (
1081 errno));
1082 r = 40;
1083 goto cleanup_route_4;
1084 }
1085#else
1086 if (0 != seteuid (0))
1087 {
1088 fprintf (stderr, "Failed to seteuid back to root: %s\n", strerror (errno));
1089 r = 40;
1090 goto cleanup_route_4;
1091 }
1092#endif
1093
1094 /* update routing tables again -- this is why we could not fully drop privs */
1095 /* now undo updating of routing tables; normal exit or clean-up-on-error case */
1096cleanup_route_4:
1097 if (0 == nortsetup)
1098 {
1099 char *const route_clean_args[] = {
1100 "ip", "-6", "route", "del", "default", "dev", dev,
1101 "table", DNS_TABLE, NULL
1102 };
1103 if (0 != fork_and_exec (sbin_ip, route_clean_args))
1104 r += 1;
1105 }
1106cleanup_route_4b:
1107 if (0 == nortsetup)
1108 {
1109 char *const route_clean_args[] = {
1110 "ip", "route", "del", "default", "dev", dev,
1111 "table", DNS_TABLE, NULL
1112 };
1113 if (0 != fork_and_exec (sbin_ip, route_clean_args))
1114 r += 1;
1115 }
1116cleanup_forward_3:
1117 if (0 == nortsetup)
1118 {
1119 char *const forward_clean_args[] = {
1120 "ip", "-6", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1121 };
1122 if (0 != fork_and_exec (sbin_ip, forward_clean_args))
1123 r += 2;
1124 }
1125cleanup_forward_3b:
1126 if (0 == nortsetup)
1127 {
1128 char *const forward_clean_args[] = {
1129 "ip", "rule", "del", "fwmark", DNS_MARK, "table", DNS_TABLE, NULL
1130 };
1131 if (0 != fork_and_exec (sbin_ip, forward_clean_args))
1132 r += 2;
1133 }
1134cleanup_mark_2:
1135 if (0 == nortsetup)
1136 {
1137 char *const mark_clean_args[] = {
1138 "ip6tables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1139 "--dport", DNS_PORT,
1140 "!", "-s", "fe80::/10", /* this line excludes link-local traffic */
1141 "-j", "MARK", "--set-mark", DNS_MARK, NULL
1142 };
1143 if (0 != fork_and_exec (sbin_ip6tables, mark_clean_args))
1144 r += 4;
1145 }
1146cleanup_mark_2b:
1147 if (0 == nortsetup)
1148 {
1149 char *const mark_clean_args[] = {
1150 "iptables", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1151 "--dport", DNS_PORT, "-j", "MARK", "--set-mark", DNS_MARK, NULL
1152 };
1153 if (0 != fork_and_exec (sbin_iptables, mark_clean_args))
1154 r += 4;
1155 }
1156cleanup_mangle_1:
1157 if (0 == nortsetup)
1158 {
1159 char *const mangle_clean_args[] = {
1160 "ip6tables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1161 "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
1162 NULL
1163 };
1164 if (0 != fork_and_exec (sbin_ip6tables, mangle_clean_args))
1165 r += 8;
1166 }
1167cleanup_mangle_1b:
1168 if (0 == nortsetup)
1169 {
1170 char *const mangle_clean_args[] = {
1171 "iptables", "-m", "owner", "-t", "mangle", "-D", "OUTPUT", "-p", "udp",
1172 "--gid-owner", mygid, "--dport", DNS_PORT, "-j", "ACCEPT",
1173 NULL
1174 };
1175 if (0 != fork_and_exec (sbin_iptables, mangle_clean_args))
1176 r += 8;
1177 }
1178
1179cleanup_rest:
1180 /* close virtual interface */
1181 (void) close (fd_tun);
1182 /* remove signal handler so we can close the pipes */
1183 (void) signal (SIGTERM, SIG_IGN);
1184#if (SIGTERM != GNUNET_TERM_SIG)
1185 (void) signal (GNUNET_TERM_SIG, SIG_IGN);
1186#endif
1187 (void) signal (SIGINT, SIG_IGN);
1188 (void) signal (SIGHUP, SIG_IGN);
1189 (void) close (cpipe[0]);
1190 (void) close (cpipe[1]);
1191 return r;
1192}
1193
1194
1195/* end of gnunet-helper-dns.c */
diff --git a/src/dns/gnunet-service-dns.c b/src/dns/gnunet-service-dns.c
deleted file mode 100644
index 4840c0c95..000000000
--- a/src/dns/gnunet-service-dns.c
+++ /dev/null
@@ -1,1275 +0,0 @@
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
21/**
22 * @file dns/gnunet-service-dns.c
23 * @brief service to intercept and modify DNS queries (and replies) of this system
24 * @author Christian Grothoff
25 *
26 * For "secure" interaction with the legacy DNS system, we permit
27 * replies only to arrive within a 5s window (and they must match
28 * ports, IPs and request IDs). Furthermore, we let the OS pick a
29 * source port, opening up to 128 sockets per address family (IPv4 or
30 * IPv6). Those sockets are closed if they are not in use for 5s
31 * (which means they will be freshly randomized afterwards). For new
32 * requests, we pick a random slot in the array with 128 socket slots
33 * (and re-use an existing socket if the slot is still in use). Thus
34 * each request will be given one of 128 random source ports, and the
35 * 128 random source ports will also change "often" (less often if the
36 * system is very busy, each time if we are mostly idle). At the same
37 * time, the system will never use more than 256 UDP sockets.
38 */
39#include "platform.h"
40#include "gnunet_util_lib.h"
41#include "gnunet_applications.h"
42#include "gnunet_constants.h"
43#include "gnunet_protocols.h"
44#include "gnunet_signatures.h"
45#include "dns.h"
46#include "gnunet_dns_service.h"
47#include "gnunet_dnsparser_lib.h"
48#include "gnunet_dnsstub_lib.h"
49#include "gnunet_statistics_service.h"
50#include "gnunet_tun_lib.h"
51
52/**
53 * Port number for DNS
54 */
55#define DNS_PORT 53
56
57
58/**
59 * Generic logging shorthand
60 */
61#define LOG(kind, ...) \
62 GNUNET_log_from (kind, "dns", __VA_ARGS__);
63
64
65/**
66 * Phases each request goes through.
67 */
68enum RequestPhase
69{
70 /**
71 * Request has just been received.
72 */
73 RP_INIT,
74
75 /**
76 * Showing the request to all monitor clients. If
77 * client list is empty, will enter QUERY phase.
78 */
79 RP_REQUEST_MONITOR,
80
81 /**
82 * Showing the request to PRE-RESOLUTION clients to find an answer.
83 * If client list is empty, will trigger global DNS request.
84 */
85 RP_QUERY,
86
87 /**
88 * Global Internet query is now pending.
89 */
90 RP_INTERNET_DNS,
91
92 /**
93 * Client (or global DNS request) has resulted in a response.
94 * Forward to all POST-RESOLUTION clients. If client list is empty,
95 * will enter RESPONSE_MONITOR phase.
96 */
97 RP_MODIFY,
98
99 /**
100 * Showing the request to all monitor clients. If
101 * client list is empty, give the result to the hijacker (and be done).
102 */
103 RP_RESPONSE_MONITOR,
104
105 /**
106 * Some client has told us to drop the request.
107 */
108 RP_DROP
109};
110
111
112/**
113 * Entry we keep for each client.
114 */
115struct ClientRecord
116{
117 /**
118 * Kept in doubly-linked list.
119 */
120 struct ClientRecord *next;
121
122 /**
123 * Kept in doubly-linked list.
124 */
125 struct ClientRecord *prev;
126
127 /**
128 * Handle to the client.
129 */
130 struct GNUNET_SERVICE_Client *client;
131
132 /**
133 * Message queue to talk to @a client.
134 */
135 struct GNUNET_MQ_Handle *mq;
136
137 /**
138 * Flags for the client.
139 */
140 enum GNUNET_DNS_Flags flags;
141};
142
143
144/**
145 * Entry we keep for each active request.
146 */
147struct RequestRecord
148{
149 /**
150 * List of clients that still need to see this request (each entry
151 * is set to NULL when the client is done).
152 */
153 struct ClientRecord **client_wait_list;
154
155 /**
156 * Payload of the UDP packet (the UDP payload), can be either query
157 * or already the response.
158 */
159 char *payload;
160
161 /**
162 * Socket we are using to transmit this request (must match if we receive
163 * a response).
164 */
165 struct GNUNET_DNSSTUB_RequestSocket *rs;
166
167 /**
168 * Source address of the original request (for sending response).
169 */
170 struct sockaddr_storage src_addr;
171
172 /**
173 * Destination address of the original request (for potential use as exit).
174 */
175 struct sockaddr_storage dst_addr;
176
177 /**
178 * ID of this request, also basis for hashing. Lowest 16 bit will
179 * be our message ID when doing a global DNS request and our index
180 * into the 'requests' array.
181 */
182 uint64_t request_id;
183
184 /**
185 * Number of bytes in payload.
186 */
187 size_t payload_length;
188
189 /**
190 * Length of the @e client_wait_list.
191 */
192 unsigned int client_wait_list_length;
193
194 /**
195 * In which phase this this request?
196 */
197 enum RequestPhase phase;
198};
199
200
201/**
202 * Global return value from 'main'.
203 */
204static int global_ret;
205
206/**
207 * The configuration to use
208 */
209static const struct GNUNET_CONFIGURATION_Handle *cfg;
210
211/**
212 * Statistics.
213 */
214static struct GNUNET_STATISTICS_Handle *stats;
215
216/**
217 * Handle to DNS hijacker helper process ("gnunet-helper-dns").
218 */
219static struct GNUNET_HELPER_Handle *hijacker;
220
221/**
222 * Command-line arguments we are giving to the hijacker process.
223 */
224static char *helper_argv[8];
225
226/**
227 * Head of DLL of clients we consult.
228 */
229static struct ClientRecord *clients_head;
230
231/**
232 * Tail of DLL of clients we consult.
233 */
234static struct ClientRecord *clients_tail;
235
236/**
237 * Array of all open requests.
238 */
239static struct RequestRecord requests[UINT16_MAX + 1];
240
241/**
242 * Generator for unique request IDs.
243 */
244static uint64_t request_id_gen;
245
246/**
247 * Handle to the DNS Stub resolver.
248 */
249static struct GNUNET_DNSSTUB_Context *dnsstub;
250
251
252/**
253 * We're done processing a DNS request, free associated memory.
254 *
255 * @param rr request to clean up
256 */
257static void
258cleanup_rr (struct RequestRecord *rr)
259{
260 GNUNET_free (rr->payload);
261 rr->payload = NULL;
262 rr->payload_length = 0;
263 GNUNET_array_grow (rr->client_wait_list,
264 rr->client_wait_list_length,
265 0);
266}
267
268
269/**
270 * Task run during shutdown.
271 *
272 * @param cls unused
273 */
274static void
275cleanup_task (void *cls GNUNET_UNUSED)
276{
277 if (NULL != hijacker)
278 {
279 GNUNET_HELPER_stop (hijacker, GNUNET_NO);
280 hijacker = NULL;
281 }
282 for (unsigned int i = 0; i < 8; i++)
283 GNUNET_free (helper_argv[i]);
284 for (unsigned int i = 0; i <= UINT16_MAX; i++)
285 cleanup_rr (&requests[i]);
286 if (NULL != stats)
287 {
288 GNUNET_STATISTICS_destroy (stats,
289 GNUNET_NO);
290 stats = NULL;
291 }
292 if (NULL != dnsstub)
293 {
294 GNUNET_DNSSTUB_stop (dnsstub);
295 dnsstub = NULL;
296 }
297}
298
299
300/**
301 * We're done with some request, finish processing.
302 *
303 * @param rr request send to the network or just clean up.
304 */
305static void
306request_done (struct RequestRecord *rr)
307{
308 struct GNUNET_MessageHeader *hdr;
309 size_t reply_len;
310 uint16_t source_port;
311 uint16_t destination_port;
312
313 GNUNET_array_grow (rr->client_wait_list,
314 rr->client_wait_list_length,
315 0);
316 if (RP_RESPONSE_MONITOR != rr->phase)
317 {
318 /* no response, drop */
319 LOG (GNUNET_ERROR_TYPE_DEBUG,
320 "Got no response for request %llu, dropping\n",
321 (unsigned long long) rr->request_id);
322 cleanup_rr (rr);
323 return;
324 }
325
326 LOG (GNUNET_ERROR_TYPE_DEBUG,
327 "Transmitting response for request %llu\n",
328 (unsigned long long) rr->request_id);
329 /* send response via hijacker */
330 reply_len = sizeof(struct GNUNET_MessageHeader);
331 reply_len += sizeof(struct GNUNET_TUN_Layer2PacketHeader);
332 switch (rr->src_addr.ss_family)
333 {
334 case AF_INET:
335 reply_len += sizeof(struct GNUNET_TUN_IPv4Header);
336 break;
337
338 case AF_INET6:
339 reply_len += sizeof(struct GNUNET_TUN_IPv6Header);
340 break;
341
342 default:
343 GNUNET_break (0);
344 cleanup_rr (rr);
345 return;
346 }
347 reply_len += sizeof(struct GNUNET_TUN_UdpHeader);
348 reply_len += rr->payload_length;
349 if (reply_len >= GNUNET_MAX_MESSAGE_SIZE)
350 {
351 /* response too big, drop */
352 GNUNET_break (0); /* how can this be? */
353 cleanup_rr (rr);
354 return;
355 }
356 {
357 char buf[reply_len] GNUNET_ALIGN;
358 size_t off;
359 struct GNUNET_TUN_IPv4Header ip4;
360 struct GNUNET_TUN_IPv6Header ip6;
361
362 /* first, GNUnet message header */
363 hdr = (struct GNUNET_MessageHeader*) buf;
364 hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
365 hdr->size = htons ((uint16_t) reply_len);
366 off = sizeof(struct GNUNET_MessageHeader);
367
368 /* first, TUN header */
369 {
370 struct GNUNET_TUN_Layer2PacketHeader tun;
371
372 tun.flags = htons (0);
373 if (rr->src_addr.ss_family == AF_INET)
374 tun.proto = htons (ETH_P_IPV4);
375 else
376 tun.proto = htons (ETH_P_IPV6);
377 GNUNET_memcpy (&buf[off],
378 &tun,
379 sizeof(struct GNUNET_TUN_Layer2PacketHeader));
380 off += sizeof(struct GNUNET_TUN_Layer2PacketHeader);
381 }
382
383 /* now IP header */
384 switch (rr->src_addr.ss_family)
385 {
386 case AF_INET:
387 {
388 struct sockaddr_in *src = (struct sockaddr_in *) &rr->src_addr;
389 struct sockaddr_in *dst = (struct sockaddr_in *) &rr->dst_addr;
390
391 source_port = dst->sin_port;
392 destination_port = src->sin_port;
393 GNUNET_TUN_initialize_ipv4_header (&ip4,
394 IPPROTO_UDP,
395 reply_len - off - sizeof(struct
396 GNUNET_TUN_IPv4Header),
397 &dst->sin_addr,
398 &src->sin_addr);
399 GNUNET_memcpy (&buf[off],
400 &ip4,
401 sizeof(ip4));
402 off += sizeof(ip4);
403 }
404 break;
405
406 case AF_INET6:
407 {
408 struct sockaddr_in6 *src = (struct sockaddr_in6 *) &rr->src_addr;
409 struct sockaddr_in6 *dst = (struct sockaddr_in6 *) &rr->dst_addr;
410
411 source_port = dst->sin6_port;
412 destination_port = src->sin6_port;
413 GNUNET_TUN_initialize_ipv6_header (&ip6,
414 IPPROTO_UDP,
415 reply_len - off - sizeof(struct
416 GNUNET_TUN_IPv6Header),
417 &dst->sin6_addr,
418 &src->sin6_addr);
419 GNUNET_memcpy (&buf[off],
420 &ip6,
421 sizeof(ip6));
422 off += sizeof(ip6);
423 }
424 break;
425
426 default:
427 GNUNET_assert (0);
428 }
429
430 /* now UDP header */
431 {
432 struct GNUNET_TUN_UdpHeader udp;
433
434 udp.source_port = source_port;
435 udp.destination_port = destination_port;
436 udp.len = htons (reply_len - off);
437 if (AF_INET == rr->src_addr.ss_family)
438 GNUNET_TUN_calculate_udp4_checksum (&ip4,
439 &udp,
440 rr->payload,
441 rr->payload_length);
442 else
443 GNUNET_TUN_calculate_udp6_checksum (&ip6,
444 &udp,
445 rr->payload,
446 rr->payload_length);
447 GNUNET_memcpy (&buf[off],
448 &udp,
449 sizeof(udp));
450 off += sizeof(udp);
451 }
452
453 /* now DNS payload */
454 {
455 GNUNET_memcpy (&buf[off], rr->payload, rr->payload_length);
456 off += rr->payload_length;
457 }
458 /* final checks & sending */
459 GNUNET_assert (off == reply_len);
460 (void) GNUNET_HELPER_send (hijacker,
461 hdr,
462 GNUNET_YES,
463 NULL, NULL);
464 GNUNET_STATISTICS_update (stats,
465 gettext_noop (
466 "# DNS requests answered via TUN interface"),
467 1, GNUNET_NO);
468 }
469 /* clean up, we're done */
470 cleanup_rr (rr);
471}
472
473
474/**
475 * Show the payload of the given request record to the client
476 * (and wait for a response).
477 *
478 * @param rr request to send to client
479 * @param cr client to send the response to
480 */
481static void
482send_request_to_client (struct RequestRecord *rr,
483 struct ClientRecord *cr)
484{
485 struct GNUNET_MQ_Envelope *env;
486 struct GNUNET_DNS_Request *req;
487
488 if (sizeof(struct GNUNET_DNS_Request) + rr->payload_length >=
489 GNUNET_MAX_MESSAGE_SIZE)
490 {
491 GNUNET_break (0);
492 cleanup_rr (rr);
493 return;
494 }
495 LOG (GNUNET_ERROR_TYPE_DEBUG,
496 "Sending information about request %llu to local client\n",
497 (unsigned long long) rr->request_id);
498 env = GNUNET_MQ_msg_extra (req,
499 rr->payload_length,
500 GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST);
501 req->reserved = htonl (0);
502 req->request_id = rr->request_id;
503 GNUNET_memcpy (&req[1],
504 rr->payload,
505 rr->payload_length);
506 GNUNET_MQ_send (cr->mq,
507 env);
508}
509
510
511/**
512 * Callback called from DNSSTUB resolver when a resolution
513 * succeeded.
514 *
515 * @param cls NULL
516 * @param dns the response itself
517 * @param r number of bytes in dns
518 */
519static void
520process_dns_result (void *cls,
521 const struct GNUNET_TUN_DnsHeader *dns,
522 size_t r);
523
524
525/**
526 * A client has completed its processing for this
527 * request. Move on.
528 *
529 * @param rr request to process further
530 */
531static void
532next_phase (struct RequestRecord *rr)
533{
534 struct ClientRecord *cr;
535 int nz;
536
537 if (rr->phase == RP_DROP)
538 {
539 cleanup_rr (rr);
540 return;
541 }
542 nz = -1;
543 for (unsigned int j = 0; j < rr->client_wait_list_length; j++)
544 {
545 if (NULL != rr->client_wait_list[j])
546 {
547 nz = (int) j;
548 break;
549 }
550 }
551 if (-1 != nz)
552 {
553 send_request_to_client (rr,
554 rr->client_wait_list[nz]);
555 return;
556 }
557 /* done with current phase, advance! */
558 LOG (GNUNET_ERROR_TYPE_DEBUG,
559 "Request %llu now in phase %d\n",
560 (unsigned long long) rr->request_id,
561 rr->phase);
562 switch (rr->phase)
563 {
564 case RP_INIT:
565 rr->phase = RP_REQUEST_MONITOR;
566 for (cr = clients_head; NULL != cr; cr = cr->next)
567 {
568 if (0 != (cr->flags & GNUNET_DNS_FLAG_REQUEST_MONITOR))
569 GNUNET_array_append (rr->client_wait_list,
570 rr->client_wait_list_length,
571 cr);
572 }
573 next_phase (rr);
574 return;
575
576 case RP_REQUEST_MONITOR:
577 rr->phase = RP_QUERY;
578 for (cr = clients_head; NULL != cr; cr = cr->next)
579 {
580 if (0 != (cr->flags & GNUNET_DNS_FLAG_PRE_RESOLUTION))
581 GNUNET_array_append (rr->client_wait_list,
582 rr->client_wait_list_length,
583 cr);
584 }
585 next_phase (rr);
586 return;
587
588 case RP_QUERY:
589#if 0
590 /* TODO: optionally, use this to forward DNS requests to the
591 * original* DNS server instead of the one we have configured...
592 (but then we need to create a fresh dnsstub for each request
593 * and* manage the timeout) */
594 switch (rr->dst_addr.ss_family)
595 {
596 case AF_INET:
597 salen = sizeof(struct sockaddr_in);
598 sa = (const struct sockaddr *) &rr->dst_addr;
599 break;
600
601 case AF_INET6:
602 salen = sizeof(struct sockaddr_in6);
603 sa = (const struct sockaddr *) &rr->dst_addr;
604 break;
605
606 default:
607 GNUNET_assert (0);
608 }
609#endif
610 rr->phase = RP_INTERNET_DNS;
611 rr->rs = GNUNET_DNSSTUB_resolve (dnsstub,
612 rr->payload,
613 rr->payload_length,
614 &process_dns_result,
615 NULL);
616 if (NULL == rr->rs)
617 {
618 GNUNET_STATISTICS_update (stats,
619 gettext_noop (
620 "# DNS exit failed (failed to open socket)"),
621 1,
622 GNUNET_NO);
623 cleanup_rr (rr);
624 return;
625 }
626 return;
627
628 case RP_INTERNET_DNS:
629 rr->phase = RP_MODIFY;
630 for (cr = clients_head; NULL != cr; cr = cr->next)
631 {
632 if (0 != (cr->flags & GNUNET_DNS_FLAG_POST_RESOLUTION))
633 GNUNET_array_append (rr->client_wait_list,
634 rr->client_wait_list_length,
635 cr);
636 }
637 next_phase (rr);
638 return;
639
640 case RP_MODIFY:
641 rr->phase = RP_RESPONSE_MONITOR;
642 for (cr = clients_head; NULL != cr; cr = cr->next)
643 {
644 if (0 != (cr->flags & GNUNET_DNS_FLAG_RESPONSE_MONITOR))
645 GNUNET_array_append (rr->client_wait_list,
646 rr->client_wait_list_length,
647 cr);
648 }
649 next_phase (rr);
650 return;
651
652 case RP_RESPONSE_MONITOR:
653 request_done (rr);
654 break;
655
656 case RP_DROP:
657 cleanup_rr (rr);
658 break;
659
660 default:
661 GNUNET_break (0);
662 cleanup_rr (rr);
663 break;
664 }
665}
666
667
668/**
669 * A client connected, setup our data structures.
670 *
671 * @param cls unused
672 * @param client handle of client that connected
673 * @param mq message queue to talk to @a client
674 * @return our `struct ClientRecord`
675 */
676static void *
677client_connect_cb (void *cls,
678 struct GNUNET_SERVICE_Client *client,
679 struct GNUNET_MQ_Handle *mq)
680{
681 struct ClientRecord *cr = cls;
682
683 cr = GNUNET_new (struct ClientRecord);
684 cr->client = client;
685 cr->mq = mq;
686 GNUNET_CONTAINER_DLL_insert (clients_head,
687 clients_tail,
688 cr);
689 return cr;
690}
691
692
693/**
694 * A client disconnected, clean up after it.
695 *
696 * @param cls unused
697 * @param client handle of client that disconnected
698 * @param app_ctx our `struct ClientRecord`
699 */
700static void
701client_disconnect_cb (void *cls,
702 struct GNUNET_SERVICE_Client *client,
703 void *app_ctx)
704{
705 struct ClientRecord *cr = app_ctx;
706 struct RequestRecord *rr;
707
708 GNUNET_CONTAINER_DLL_remove (clients_head,
709 clients_tail,
710 cr);
711 for (unsigned int i = 0; i < UINT16_MAX; i++)
712 {
713 rr = &requests[i];
714 if (0 == rr->client_wait_list_length)
715 continue; /* not in use */
716 for (unsigned int j = 0; j < rr->client_wait_list_length; j++)
717 {
718 if (rr->client_wait_list[j] == cr)
719 {
720 rr->client_wait_list[j] = NULL;
721 next_phase (rr);
722 }
723 }
724 }
725 GNUNET_free (cr);
726}
727
728
729/**
730 * Callback called from DNSSTUB resolver when a resolution
731 * succeeded.
732 *
733 * @param cls NULL
734 * @param dns the response itself
735 * @param r number of bytes in dns
736 */
737static void
738process_dns_result (void *cls,
739 const struct GNUNET_TUN_DnsHeader *dns,
740 size_t r)
741{
742 struct RequestRecord *rr;
743
744 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745 "Processing DNS result from stub resolver\n");
746 GNUNET_assert (NULL == cls);
747 if (NULL == dns)
748 return; /* ignore */
749
750 rr = &requests[dns->id];
751 if (rr->phase != RP_INTERNET_DNS)
752 {
753 /* unexpected / bogus reply */
754 GNUNET_STATISTICS_update (stats,
755 gettext_noop (
756 "# External DNS response discarded (no matching request)"),
757 1, GNUNET_NO);
758 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
759 "Received DNS reply that does not match any pending request. Dropping.\n");
760 return;
761 }
762 LOG (GNUNET_ERROR_TYPE_DEBUG,
763 "Got a response from the stub resolver for DNS request %llu intercepted locally!\n",
764 (unsigned long long) rr->request_id);
765 GNUNET_free (rr->payload);
766 rr->payload = GNUNET_malloc (r);
767 GNUNET_memcpy (rr->payload,
768 dns,
769 r);
770 rr->payload_length = r;
771 next_phase (rr);
772}
773
774
775/**
776 * We got a new client. Make sure all new DNS requests pass by its desk.
777 *
778 * @param cls the client
779 * @param reg the init message
780 */
781static void
782handle_client_init (void *cls,
783 const struct GNUNET_DNS_Register *reg)
784{
785 struct ClientRecord *cr = cls;
786
787 cr->flags = (enum GNUNET_DNS_Flags) ntohl (reg->flags);
788 GNUNET_SERVICE_client_continue (cr->client);
789}
790
791
792/**
793 * Check a response from a client.
794 *
795 * @param cls the client
796 * @param resp the response
797 * @return #GNUNET_OK (always fine)
798 */
799static int
800check_client_response (void *cls,
801 const struct GNUNET_DNS_Response *resp)
802{
803 return GNUNET_OK; /* any payload is acceptable */
804}
805
806
807/**
808 * Handle a response from a client.
809 *
810 * @param cls the client
811 * @param resp the response
812 */
813static void
814handle_client_response (void *cls,
815 const struct GNUNET_DNS_Response *resp)
816{
817 struct ClientRecord *cr = cls;
818 struct RequestRecord *rr;
819 uint16_t msize;
820 uint16_t off;
821
822 msize = ntohs (resp->header.size);
823 off = (uint16_t) resp->request_id;
824 rr = &requests[off];
825 LOG (GNUNET_ERROR_TYPE_DEBUG,
826 "Received DNS response with ID %llu from local client!\n",
827 (unsigned long long) resp->request_id);
828 if (rr->request_id != resp->request_id)
829 {
830 GNUNET_STATISTICS_update (stats,
831 gettext_noop (
832 "# Client response discarded (no matching request)"),
833 1,
834 GNUNET_NO);
835 GNUNET_SERVICE_client_continue (cr->client);
836 return;
837 }
838 for (unsigned int i = 0; i < rr->client_wait_list_length; i++)
839 {
840 if (NULL == rr->client_wait_list[i])
841 continue;
842 if (rr->client_wait_list[i] != cr)
843 continue;
844 rr->client_wait_list[i] = NULL;
845 switch (ntohl (resp->drop_flag))
846 {
847 case 0: /* drop */
848 rr->phase = RP_DROP;
849 break;
850
851 case 1: /* no change */
852 break;
853
854 case 2: /* update */
855 msize -= sizeof(struct GNUNET_DNS_Response);
856 if ((sizeof(struct GNUNET_TUN_DnsHeader) > msize) ||
857 (RP_REQUEST_MONITOR == rr->phase) ||
858 (RP_RESPONSE_MONITOR == rr->phase))
859 {
860 GNUNET_break (0);
861 GNUNET_SERVICE_client_drop (cr->client);
862 next_phase (rr);
863 return;
864 }
865 GNUNET_free (rr->payload);
866 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
867 "Changing DNS reply according to client specifications\n");
868 rr->payload = GNUNET_malloc (msize);
869 rr->payload_length = msize;
870 GNUNET_memcpy (rr->payload, &resp[1], msize);
871 if (rr->phase == RP_QUERY)
872 {
873 /* clear wait list, we're moving to MODIFY phase next */
874 GNUNET_array_grow (rr->client_wait_list,
875 rr->client_wait_list_length,
876 0);
877 }
878 /* if query changed to answer, move past DNS resolution phase... */
879 if ((RP_QUERY == rr->phase) &&
880 (rr->payload_length > sizeof(struct GNUNET_TUN_DnsHeader)) &&
881 ( ((struct GNUNET_TUN_DnsFlags*) &(((struct
882 GNUNET_TUN_DnsHeader*) rr->
883 payload)->flags))->
884 query_or_response == 1) )
885 {
886 rr->phase = RP_INTERNET_DNS;
887 GNUNET_array_grow (rr->client_wait_list,
888 rr->client_wait_list_length,
889 0);
890 }
891 break;
892 }
893 next_phase (rr);
894 GNUNET_SERVICE_client_continue (cr->client);
895 return;
896 }
897 /* odd, client was not on our list for the request, that ought
898 to be an error */
899 GNUNET_break (0);
900 GNUNET_SERVICE_client_drop (cr->client);
901}
902
903
904/**
905 * Functions with this signature are called whenever a complete
906 * message is received by the tokenizer from the DNS hijack process.
907 *
908 * @param cls closure
909 * @param message the actual message, a DNS request we should handle
910 */
911static int
912process_helper_messages (void *cls,
913 const struct GNUNET_MessageHeader *message)
914{
915 uint16_t msize;
916 const struct GNUNET_TUN_Layer2PacketHeader *tun;
917 const struct GNUNET_TUN_IPv4Header *ip4;
918 const struct GNUNET_TUN_IPv6Header *ip6;
919 const struct GNUNET_TUN_UdpHeader *udp;
920 const struct GNUNET_TUN_DnsHeader *dns;
921 struct RequestRecord *rr;
922 struct sockaddr_in *srca4;
923 struct sockaddr_in6 *srca6;
924 struct sockaddr_in *dsta4;
925 struct sockaddr_in6 *dsta6;
926
927 LOG (GNUNET_ERROR_TYPE_DEBUG,
928 "Intercepted message via DNS hijacker\n");
929 msize = ntohs (message->size);
930 if (msize < sizeof(struct GNUNET_MessageHeader) + sizeof(struct
931 GNUNET_TUN_Layer2PacketHeader)
932 + sizeof(struct GNUNET_TUN_IPv4Header))
933 {
934 /* non-IP packet received on TUN!? */
935 GNUNET_break (0);
936 return GNUNET_OK;
937 }
938 msize -= sizeof(struct GNUNET_MessageHeader);
939 tun = (const struct GNUNET_TUN_Layer2PacketHeader *) &message[1];
940 msize -= sizeof(struct GNUNET_TUN_Layer2PacketHeader);
941 switch (ntohs (tun->proto))
942 {
943 case ETH_P_IPV4:
944 ip4 = (const struct GNUNET_TUN_IPv4Header *) &tun[1];
945 ip6 = NULL; /* make compiler happy */
946 if ((msize < sizeof(struct GNUNET_TUN_IPv4Header)) ||
947 (ip4->version != 4) ||
948 (ip4->header_length != sizeof(struct GNUNET_TUN_IPv4Header) / 4) ||
949 (ntohs (ip4->total_length) != msize) ||
950 (ip4->protocol != IPPROTO_UDP))
951 {
952 /* non-IP/UDP packet received on TUN (or with options) */
953 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
954 _ ("Received malformed IPv4-UDP packet on TUN interface.\n"));
955 return GNUNET_OK;
956 }
957 udp = (const struct GNUNET_TUN_UdpHeader*) &ip4[1];
958 msize -= sizeof(struct GNUNET_TUN_IPv4Header);
959 break;
960
961 case ETH_P_IPV6:
962 ip4 = NULL; /* make compiler happy */
963 ip6 = (const struct GNUNET_TUN_IPv6Header *) &tun[1];
964 if ((msize < sizeof(struct GNUNET_TUN_IPv6Header)) ||
965 (ip6->version != 6) ||
966 (ntohs (ip6->payload_length) != msize - sizeof(struct
967 GNUNET_TUN_IPv6Header))
968 ||
969 (ip6->next_header != IPPROTO_UDP))
970 {
971 /* non-IP/UDP packet received on TUN (or with extensions) */
972 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
973 _ ("Received malformed IPv6-UDP packet on TUN interface.\n"));
974 return GNUNET_OK;
975 }
976 udp = (const struct GNUNET_TUN_UdpHeader *) &ip6[1];
977 msize -= sizeof(struct GNUNET_TUN_IPv6Header);
978 break;
979
980 default:
981 /* non-IP packet received on TUN!? */
982 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
983 _ (
984 "Got non-IP packet with %u bytes and protocol %u from TUN\n"),
985 (unsigned int) msize,
986 ntohs (tun->proto));
987 return GNUNET_OK;
988 }
989 if ((msize <= sizeof(struct GNUNET_TUN_UdpHeader) + sizeof(struct
990 GNUNET_TUN_DnsHeader))
991 ||
992 (DNS_PORT != ntohs (udp->destination_port)))
993 {
994 /* non-DNS packet received on TUN, ignore */
995 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
996 _ ("DNS interceptor got non-DNS packet (dropped)\n"));
997 GNUNET_STATISTICS_update (stats,
998 gettext_noop (
999 "# Non-DNS UDP packet received via TUN interface"),
1000 1, GNUNET_NO);
1001 return GNUNET_OK;
1002 }
1003 msize -= sizeof(struct GNUNET_TUN_UdpHeader);
1004 dns = (const struct GNUNET_TUN_DnsHeader*) &udp[1];
1005 rr = &requests[dns->id];
1006
1007 /* clean up from previous request */
1008 GNUNET_free (rr->payload);
1009 rr->payload = NULL;
1010 GNUNET_array_grow (rr->client_wait_list,
1011 rr->client_wait_list_length,
1012 0);
1013
1014 /* setup new request */
1015 rr->phase = RP_INIT;
1016 switch (ntohs (tun->proto))
1017 {
1018 case ETH_P_IPV4:
1019 {
1020 srca4 = (struct sockaddr_in*) &rr->src_addr;
1021 dsta4 = (struct sockaddr_in*) &rr->dst_addr;
1022 memset (srca4, 0, sizeof(struct sockaddr_in));
1023 memset (dsta4, 0, sizeof(struct sockaddr_in));
1024 srca4->sin_family = AF_INET;
1025 dsta4->sin_family = AF_INET;
1026 srca4->sin_addr = ip4->source_address;
1027 dsta4->sin_addr = ip4->destination_address;
1028 srca4->sin_port = udp->source_port;
1029 dsta4->sin_port = udp->destination_port;
1030#if HAVE_SOCKADDR_IN_SIN_LEN
1031 srca4->sin_len = sizeof(struct sockaddr_in);
1032 dsta4->sin_len = sizeof(struct sockaddr_in);
1033#endif
1034 }
1035 break;
1036
1037 case ETH_P_IPV6:
1038 {
1039 srca6 = (struct sockaddr_in6*) &rr->src_addr;
1040 dsta6 = (struct sockaddr_in6*) &rr->dst_addr;
1041 memset (srca6, 0, sizeof(struct sockaddr_in6));
1042 memset (dsta6, 0, sizeof(struct sockaddr_in6));
1043 srca6->sin6_family = AF_INET6;
1044 dsta6->sin6_family = AF_INET6;
1045 srca6->sin6_addr = ip6->source_address;
1046 dsta6->sin6_addr = ip6->destination_address;
1047 srca6->sin6_port = udp->source_port;
1048 dsta6->sin6_port = udp->destination_port;
1049#if HAVE_SOCKADDR_IN_SIN_LEN
1050 srca6->sin6_len = sizeof(struct sockaddr_in6);
1051 dsta6->sin6_len = sizeof(struct sockaddr_in6);
1052#endif
1053 }
1054 break;
1055
1056 default:
1057 GNUNET_assert (0);
1058 }
1059 rr->payload = GNUNET_malloc (msize);
1060 rr->payload_length = msize;
1061 GNUNET_memcpy (rr->payload, dns, msize);
1062 rr->request_id = dns->id | (request_id_gen << 16);
1063 request_id_gen++;
1064 LOG (GNUNET_ERROR_TYPE_DEBUG,
1065 "Creating new DNS request %llu\n",
1066 (unsigned long long) rr->request_id);
1067 GNUNET_STATISTICS_update (stats,
1068 gettext_noop (
1069 "# DNS requests received via TUN interface"),
1070 1, GNUNET_NO);
1071 /* start request processing state machine */
1072 next_phase (rr);
1073 return GNUNET_OK;
1074}
1075
1076
1077/**
1078 * @param cls closure
1079 * @param cfg_ configuration to use
1080 * @param service the initialized service
1081 */
1082static void
1083run (void *cls,
1084 const struct GNUNET_CONFIGURATION_Handle *cfg_,
1085 struct GNUNET_SERVICE_Handle *service)
1086{
1087 char *ifc_name;
1088 char *ipv4addr;
1089 char *ipv4mask;
1090 char *ipv6addr;
1091 char *ipv6prefix;
1092 char *dns_exit;
1093 char *binary;
1094 int nortsetup;
1095
1096 cfg = cfg_;
1097 stats = GNUNET_STATISTICS_create ("dns", cfg);
1098 GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
1099 cls);
1100 dnsstub = GNUNET_DNSSTUB_start (128);
1101 /* TODO: support multiple DNS_EXIT servers being configured */
1102 /* TODO: see above TODO on using DNS server from original packet.
1103 Not sure which is best... */
1104 dns_exit = NULL;
1105 if ((GNUNET_OK !=
1106 GNUNET_CONFIGURATION_get_value_string (cfg,
1107 "dns",
1108 "DNS_EXIT",
1109 &dns_exit)) ||
1110 (GNUNET_OK !=
1111 GNUNET_DNSSTUB_add_dns_ip (dnsstub,
1112 dns_exit)))
1113 {
1114 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1115 "dns",
1116 "DNS_EXIT",
1117 _ ("need a valid IPv4 or IPv6 address\n"));
1118 GNUNET_free (dns_exit);
1119 }
1120 binary = GNUNET_OS_get_suid_binary_path (cfg, "gnunet-helper-dns");
1121
1122 if (GNUNET_YES !=
1123 GNUNET_OS_check_helper_binary (binary,
1124 GNUNET_YES,
1125 NULL)) // TODO: once we have a windows-testcase, add test parameters here
1126 {
1127 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1128 _ ("`%s' is not SUID or the path is invalid, "
1129 "will not run DNS interceptor\n"),
1130 binary);
1131 global_ret = 1;
1132 GNUNET_free (binary);
1133 return;
1134 }
1135 GNUNET_free (binary);
1136
1137 helper_argv[0] = GNUNET_strdup ("gnunet-dns");
1138 if (GNUNET_SYSERR ==
1139 GNUNET_CONFIGURATION_get_value_string (cfg,
1140 "dns",
1141 "IFNAME",
1142 &ifc_name))
1143 {
1144 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1145 "No entry 'IFNAME' in configuration!\n");
1146 GNUNET_free (binary);
1147 GNUNET_SCHEDULER_shutdown ();
1148 return;
1149 }
1150 helper_argv[1] = ifc_name;
1151 if ((GNUNET_SYSERR ==
1152 GNUNET_CONFIGURATION_get_value_string (cfg,
1153 "dns",
1154 "IPV6ADDR",
1155 &ipv6addr)))
1156 {
1157 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1158 "No entry 'IPV6ADDR' in configuration!\n");
1159 GNUNET_free (binary);
1160 GNUNET_SCHEDULER_shutdown ();
1161 return;
1162 }
1163 helper_argv[2] = ipv6addr;
1164 if (GNUNET_SYSERR ==
1165 GNUNET_CONFIGURATION_get_value_string (cfg,
1166 "dns",
1167 "IPV6PREFIX",
1168 &ipv6prefix))
1169 {
1170 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1171 "No entry 'IPV6PREFIX' in configuration!\n");
1172 GNUNET_free (binary);
1173 GNUNET_SCHEDULER_shutdown ();
1174 return;
1175 }
1176 helper_argv[3] = ipv6prefix;
1177
1178 if (GNUNET_SYSERR ==
1179 GNUNET_CONFIGURATION_get_value_string (cfg,
1180 "dns",
1181 "IPV4ADDR",
1182 &ipv4addr))
1183 {
1184 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1185 "No entry 'IPV4ADDR' in configuration!\n");
1186 GNUNET_free (binary);
1187 GNUNET_SCHEDULER_shutdown ();
1188 return;
1189 }
1190 helper_argv[4] = ipv4addr;
1191 if (GNUNET_SYSERR ==
1192 GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "IPV4MASK",
1193 &ipv4mask))
1194 {
1195 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1196 "No entry 'IPV4MASK' in configuration!\n");
1197 GNUNET_free (binary);
1198 GNUNET_SCHEDULER_shutdown ();
1199 return;
1200 }
1201 helper_argv[5] = ipv4mask;
1202
1203 nortsetup = GNUNET_CONFIGURATION_get_value_yesno (cfg, "dns",
1204 "SKIP_ROUTING_SETUP");
1205 if (GNUNET_YES == nortsetup)
1206 helper_argv[6] = GNUNET_strdup ("1");
1207 else
1208 helper_argv[6] = GNUNET_strdup ("0");
1209
1210 helper_argv[7] = NULL;
1211 hijacker = GNUNET_HELPER_start (GNUNET_NO,
1212 binary,
1213 helper_argv,
1214 &process_helper_messages,
1215 NULL, NULL);
1216 GNUNET_free (binary);
1217}
1218
1219
1220/**
1221 * Define "main" method using service macro.
1222 */
1223GNUNET_SERVICE_MAIN
1224 ("dns",
1225 GNUNET_SERVICE_OPTION_NONE,
1226 &run,
1227 &client_connect_cb,
1228 &client_disconnect_cb,
1229 NULL,
1230 GNUNET_MQ_hd_fixed_size (client_init,
1231 GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT,
1232 struct GNUNET_DNS_Register,
1233 NULL),
1234 GNUNET_MQ_hd_var_size (client_response,
1235 GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE,
1236 struct GNUNET_DNS_Response,
1237 NULL),
1238 GNUNET_MQ_handler_end ());
1239
1240
1241/* FIXME: this might need a port on systems without 'getresgid' */
1242#if HAVE_GETRESGID
1243/**
1244 * Enable use of SGID capabilities on POSIX
1245 */
1246void __attribute__ ((constructor))
1247GNUNET_DNS_init ()
1248{
1249 gid_t rgid;
1250 gid_t egid;
1251 gid_t sgid;
1252
1253 if (-1 == getresgid (&rgid,
1254 &egid,
1255 &sgid))
1256 {
1257 fprintf (stderr,
1258 "getresgid failed: %s\n",
1259 strerror (errno));
1260 }
1261 else if (sgid != rgid)
1262 {
1263 if (-1 == setregid (sgid,
1264 sgid))
1265 fprintf (stderr,
1266 "setregid failed: %s\n",
1267 strerror (errno));
1268 }
1269}
1270
1271
1272#endif
1273
1274
1275/* end of gnunet-service-dns.c */
diff --git a/src/dns/gnunet-zonewalk.c b/src/dns/gnunet-zonewalk.c
deleted file mode 100644
index 91f8456df..000000000
--- a/src/dns/gnunet-zonewalk.c
+++ /dev/null
@@ -1,609 +0,0 @@
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/**
22 * @file src/dns/gnunet-zoneimport.c
23 * @brief import a DNS zone for analysis, brute force
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <gnunet_util_lib.h>
28#include <gnunet_dnsstub_lib.h>
29#include <gnunet_dnsparser_lib.h>
30
31/**
32 * Request we should make.
33 */
34struct Request
35{
36 /**
37 * Requests are kept in a DLL.
38 */
39 struct Request *next;
40
41 /**
42 * Requests are kept in a DLL.
43 */
44 struct Request *prev;
45
46 /**
47 * Socket used to make the request, NULL if not active.
48 */
49 struct GNUNET_DNSSTUB_RequestSocket *rs;
50
51 /**
52 * Raw DNS query.
53 */
54 void *raw;
55
56 /**
57 * Number of bytes in @e raw.
58 */
59 size_t raw_len;
60
61 /**
62 * Hostname we are resolving.
63 */
64 char *hostname;
65
66 /**
67 * When did we last issue this request?
68 */
69 time_t time;
70
71 /**
72 * How often did we issue this query?
73 */
74 int issue_num;
75
76 /**
77 * random 16-bit DNS query identifier.
78 */
79 uint16_t id;
80};
81
82
83/**
84 * Context for DNS resolution.
85 */
86static struct GNUNET_DNSSTUB_Context *ctx;
87
88/**
89 * The number of queries that are outstanding
90 */
91static unsigned int pending;
92
93/**
94 * Number of lookups we performed overall.
95 */
96static unsigned int lookups;
97
98/**
99 * Number of lookups that failed.
100 */
101static unsigned int failures;
102
103/**
104 * Number of records we found.
105 */
106static unsigned int records;
107
108/**
109 * Head of DLL of all requests to perform.
110 */
111static struct Request *req_head;
112
113/**
114 * Tail of DLL of all requests to perform.
115 */
116static struct Request *req_tail;
117
118/**
119 * Main task.
120 */
121static struct GNUNET_SCHEDULER_Task *t;
122
123/**
124 * Maximum number of queries pending at the same time.
125 */
126#define THRESH 20
127
128/**
129 * TIME_THRESH is in usecs. How quickly do we submit fresh queries.
130 * Used as an additional throttle.
131 */
132#define TIME_THRESH 10
133
134/**
135 * How often do we retry a query before giving up for good?
136 */
137#define MAX_RETRIES 5
138
139
140/**
141 * We received @a rec for @a req. Remember the answer.
142 *
143 * @param req request
144 * @param rec response
145 */
146static void
147process_record (struct Request *req,
148 struct GNUNET_DNSPARSER_Record *rec)
149{
150 char buf[INET6_ADDRSTRLEN];
151
152 records++;
153 switch (rec->type)
154 {
155 case GNUNET_DNSPARSER_TYPE_A:
156 fprintf (stdout,
157 "%s A %s\n",
158 req->hostname,
159 inet_ntop (AF_INET,
160 rec->data.raw.data,
161 buf,
162 sizeof(buf)));
163 break;
164
165 case GNUNET_DNSPARSER_TYPE_AAAA:
166 fprintf (stdout,
167 "%s AAAA %s\n",
168 req->hostname,
169 inet_ntop (AF_INET6,
170 rec->data.raw.data,
171 buf,
172 sizeof(buf)));
173 break;
174
175 case GNUNET_DNSPARSER_TYPE_NS:
176 fprintf (stdout,
177 "%s NS %s\n",
178 req->hostname,
179 rec->data.hostname);
180 break;
181
182 case GNUNET_DNSPARSER_TYPE_CNAME:
183 fprintf (stdout,
184 "%s CNAME %s\n",
185 req->hostname,
186 rec->data.hostname);
187 break;
188
189 case GNUNET_DNSPARSER_TYPE_MX:
190 fprintf (stdout,
191 "%s MX %u %s\n",
192 req->hostname,
193 (unsigned int) rec->data.mx->preference,
194 rec->data.mx->mxhost);
195 break;
196
197 case GNUNET_DNSPARSER_TYPE_SOA:
198 fprintf (stdout,
199 "%s SOA %s %s %u %u %u %u %u\n",
200 req->hostname,
201 rec->data.soa->mname,
202 rec->data.soa->rname,
203 (unsigned int) rec->data.soa->serial,
204 (unsigned int) rec->data.soa->refresh,
205 (unsigned int) rec->data.soa->retry,
206 (unsigned int) rec->data.soa->expire,
207 (unsigned int) rec->data.soa->minimum_ttl);
208 break;
209
210 case GNUNET_DNSPARSER_TYPE_SRV:
211 fprintf (stdout,
212 "%s SRV %s %u %u %u\n",
213 req->hostname,
214 rec->data.srv->target,
215 rec->data.srv->priority,
216 rec->data.srv->weight,
217 rec->data.srv->port);
218 break;
219
220 case GNUNET_DNSPARSER_TYPE_PTR:
221 fprintf (stdout,
222 "%s PTR %s\n",
223 req->hostname,
224 rec->data.hostname);
225 break;
226
227 case GNUNET_DNSPARSER_TYPE_TXT:
228 fprintf (stdout,
229 "%s TXT %.*s\n",
230 req->hostname,
231 (int) rec->data.raw.data_len,
232 (char *) rec->data.raw.data);
233 break;
234
235 case GNUNET_DNSPARSER_TYPE_DNAME:
236 fprintf (stdout,
237 "%s DNAME %s\n",
238 req->hostname,
239 rec->data.hostname);
240 break;
241
242 /* obscure records */
243 case GNUNET_DNSPARSER_TYPE_AFSDB:
244 case GNUNET_DNSPARSER_TYPE_NAPTR:
245 case GNUNET_DNSPARSER_TYPE_APL:
246 case GNUNET_DNSPARSER_TYPE_DHCID:
247 case GNUNET_DNSPARSER_TYPE_HIP:
248 case GNUNET_DNSPARSER_TYPE_LOC:
249 case GNUNET_DNSPARSER_TYPE_RP:
250 case GNUNET_DNSPARSER_TYPE_TKEY:
251 case GNUNET_DNSPARSER_TYPE_TSIG:
252 case GNUNET_DNSPARSER_TYPE_URI:
253 case GNUNET_DNSPARSER_TYPE_TA:
254
255 /* DNSSEC */
256 case GNUNET_DNSPARSER_TYPE_DS:
257 case GNUNET_DNSPARSER_TYPE_RRSIG:
258 case GNUNET_DNSPARSER_TYPE_NSEC:
259 case GNUNET_DNSPARSER_TYPE_DNSKEY:
260 case GNUNET_DNSPARSER_TYPE_NSEC3:
261 case GNUNET_DNSPARSER_TYPE_NSEC3PARAM:
262 case GNUNET_DNSPARSER_TYPE_CDS:
263 case GNUNET_DNSPARSER_TYPE_CDNSKEY:
264
265 /* DNSSEC payload */
266 case GNUNET_DNSPARSER_TYPE_CERT:
267 case GNUNET_DNSPARSER_TYPE_SSHFP:
268 case GNUNET_DNSPARSER_TYPE_IPSECKEY:
269 case GNUNET_DNSPARSER_TYPE_TLSA:
270 case GNUNET_DNSPARSER_TYPE_OPENPGPKEY:
271
272 /* obsolete records */
273 case GNUNET_DNSPARSER_TYPE_SIG:
274 case GNUNET_DNSPARSER_TYPE_KEY:
275 case GNUNET_DNSPARSER_TYPE_KX:
276 {
277 char *base32;
278
279 base32 = GNUNET_STRINGS_data_to_string_alloc (rec->data.raw.data,
280 rec->data.raw.data_len);
281 fprintf (stdout,
282 "%s (%u) %s\n",
283 req->hostname,
284 rec->type,
285 base32);
286 GNUNET_free (base32);
287 }
288 break;
289
290 default:
291 fprintf (stderr,
292 "Unsupported type %u\n",
293 (unsigned int) rec->type);
294 break;
295 }
296}
297
298
299/**
300 * Function called with the result of a DNS resolution.
301 *
302 * @param cls closure with the `struct Request`
303 * @param dns dns response, never NULL
304 * @param dns_len number of bytes in @a dns
305 */
306static void
307process_result (void *cls,
308 const struct GNUNET_TUN_DnsHeader *dns,
309 size_t dns_len)
310{
311 struct Request *req = cls;
312 struct GNUNET_DNSPARSER_Packet *p;
313
314 if (NULL == dns)
315 {
316 /* stub gave up */
317 pending--;
318 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
319 "Stub gave up on DNS reply for `%s'\n",
320 req->hostname);
321 GNUNET_CONTAINER_DLL_remove (req_head,
322 req_tail,
323 req);
324 if (req->issue_num > MAX_RETRIES)
325 {
326 failures++;
327 GNUNET_free (req->hostname);
328 GNUNET_free (req->raw);
329 GNUNET_free (req);
330 return;
331 }
332 GNUNET_CONTAINER_DLL_insert_tail (req_head,
333 req_tail,
334 req);
335 req->rs = NULL;
336 return;
337 }
338 if (req->id != dns->id)
339 return;
340 pending--;
341 GNUNET_DNSSTUB_resolve_cancel (req->rs);
342 req->rs = NULL;
343 GNUNET_CONTAINER_DLL_remove (req_head,
344 req_tail,
345 req);
346 p = GNUNET_DNSPARSER_parse ((const char *) dns,
347 dns_len);
348 if (NULL == p)
349 {
350 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
351 "Failed to parse DNS reply for `%s'\n",
352 req->hostname);
353 if (req->issue_num > MAX_RETRIES)
354 {
355 failures++;
356 GNUNET_free (req->hostname);
357 GNUNET_free (req->raw);
358 GNUNET_free (req);
359 return;
360 }
361 GNUNET_CONTAINER_DLL_insert_tail (req_head,
362 req_tail,
363 req);
364 return;
365 }
366 for (unsigned int i = 0; i < p->num_answers; i++)
367 {
368 struct GNUNET_DNSPARSER_Record *rs = &p->answers[i];
369
370 process_record (req,
371 rs);
372 }
373 for (unsigned int i = 0; i < p->num_authority_records; i++)
374 {
375 struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i];
376
377 process_record (req,
378 rs);
379 }
380 for (unsigned int i = 0; i < p->num_additional_records; i++)
381 {
382 struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i];
383
384 process_record (req,
385 rs);
386 }
387 GNUNET_DNSPARSER_free_packet (p);
388 GNUNET_free (req->hostname);
389 GNUNET_free (req->raw);
390 GNUNET_free (req);
391}
392
393
394/**
395 * Submit a request to DNS unless we need to slow down because
396 * we are at the rate limit.
397 *
398 * @param req request to submit
399 * @return #GNUNET_OK if request was submitted
400 * #GNUNET_NO if request was already submitted
401 * #GNUNET_SYSERR if we are at the rate limit
402 */
403static int
404submit_req (struct Request *req)
405{
406 static struct timeval last_request;
407 struct timeval now;
408
409 if (NULL != req->rs)
410 return GNUNET_NO; /* already submitted */
411 gettimeofday (&now,
412 NULL);
413 if ((((now.tv_sec - last_request.tv_sec) == 0) &&
414 ((now.tv_usec - last_request.tv_usec) < TIME_THRESH)) ||
415 (pending >= THRESH))
416 return GNUNET_SYSERR;
417 GNUNET_assert (NULL == req->rs);
418 req->rs = GNUNET_DNSSTUB_resolve (ctx,
419 req->raw,
420 req->raw_len,
421 &process_result,
422 req);
423 GNUNET_assert (NULL != req->rs);
424 req->issue_num++;
425 last_request = now;
426 lookups++;
427 pending++;
428 req->time = time (NULL);
429 return GNUNET_OK;
430}
431
432
433/**
434 * Process as many requests as possible from the queue.
435 *
436 * @param cls NULL
437 */
438static void
439process_queue (void *cls)
440{
441 (void) cls;
442 t = NULL;
443 for (struct Request *req = req_head;
444 NULL != req;
445 req = req->next)
446 {
447 if (GNUNET_SYSERR == submit_req (req))
448 break;
449 }
450 if (NULL != req_head)
451 t = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
452 &process_queue,
453 NULL);
454 else
455 GNUNET_SCHEDULER_shutdown ();
456}
457
458
459/**
460 * Clean up and terminate the process.
461 *
462 * @param cls NULL
463 */
464static void
465do_shutdown (void *cls)
466{
467 (void) cls;
468 if (NULL != t)
469 {
470 GNUNET_SCHEDULER_cancel (t);
471 t = NULL;
472 }
473 GNUNET_DNSSTUB_stop (ctx);
474 ctx = NULL;
475}
476
477
478/**
479 * Process requests from the queue, then if the queue is
480 * not empty, try again.
481 *
482 * @param cls NULL
483 */
484static void
485run (void *cls)
486{
487 (void) cls;
488
489 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
490 NULL);
491 t = GNUNET_SCHEDULER_add_now (&process_queue,
492 NULL);
493}
494
495
496/**
497 * Add @a hostname to the list of requests to be made.
498 *
499 * @param hostname name to resolve
500 */
501static void
502queue (const char *hostname)
503{
504 struct GNUNET_DNSPARSER_Packet p;
505 struct GNUNET_DNSPARSER_Query q;
506 struct Request *req;
507 char *raw;
508 size_t raw_size;
509 int ret;
510
511 if (GNUNET_OK !=
512 GNUNET_DNSPARSER_check_name (hostname))
513 {
514 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
515 "Refusing invalid hostname `%s'\n",
516 hostname);
517 return;
518 }
519 q.name = (char *) hostname;
520 q.type = GNUNET_DNSPARSER_TYPE_NS;
521 q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
522
523 memset (&p,
524 0,
525 sizeof(p));
526 p.num_queries = 1;
527 p.queries = &q;
528 p.id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
529 UINT16_MAX);
530 ret = GNUNET_DNSPARSER_pack (&p,
531 UINT16_MAX,
532 &raw,
533 &raw_size);
534 if (GNUNET_OK != ret)
535 {
536 if (GNUNET_NO == ret)
537 GNUNET_free (raw);
538 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
539 "Failed to pack query for hostname `%s'\n",
540 hostname);
541 return;
542 }
543
544 req = GNUNET_new (struct Request);
545 req->hostname = strdup (hostname);
546 req->raw = raw;
547 req->raw_len = raw_size;
548 req->id = p.id;
549 GNUNET_CONTAINER_DLL_insert_tail (req_head,
550 req_tail,
551 req);
552}
553
554
555/**
556 * Call with IP address of resolver to query.
557 *
558 * @param argc should be 2
559 * @param argv[1] should contain IP address
560 * @return 0 on success
561 */
562int
563main (int argc,
564 char **argv)
565{
566 char hn[256];
567
568 if (2 != argc)
569 {
570 fprintf (stderr,
571 "Missing required configuration argument\n");
572 return -1;
573 }
574 ctx = GNUNET_DNSSTUB_start (256);
575 if (NULL == ctx)
576 {
577 fprintf (stderr,
578 "Failed to initialize GNUnet DNS STUB\n");
579 return 1;
580 }
581 if (GNUNET_OK !=
582 GNUNET_DNSSTUB_add_dns_ip (ctx,
583 argv[1]))
584 {
585 fprintf (stderr,
586 "Failed to use `%s' for DNS resolver\n",
587 argv[1]);
588 return 1;
589 }
590
591 while (NULL !=
592 fgets (hn,
593 sizeof(hn),
594 stdin))
595 {
596 if (strlen (hn) > 0)
597 hn[strlen (hn) - 1] = '\0'; /* eat newline */
598 queue (hn);
599 }
600 GNUNET_SCHEDULER_run (&run,
601 NULL);
602 fprintf (stderr,
603 "Did %u lookups, found %u records, %u lookups failed, %u pending on shutdown\n",
604 lookups,
605 records,
606 failures,
607 pending);
608 return 0;
609}
diff --git a/src/dns/plugin_block_dns.c b/src/dns/plugin_block_dns.c
deleted file mode 100644
index e0beccb52..000000000
--- a/src/dns/plugin_block_dns.c
+++ /dev/null
@@ -1,236 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013, 2017 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 dns/plugin_block_dns.c
23 * @brief block plugin for advertising a DNS exit service
24 * @author Christian Grothoff
25 *
26 * Note that this plugin might more belong with EXIT and PT
27 * as those two are using this type of block. Still, this
28 * might be a natural enough place for people to find the code...
29 */
30#include "platform.h"
31#include "gnunet_block_plugin.h"
32#include "block_dns.h"
33#include "gnunet_signatures.h"
34#include "gnunet_block_group_lib.h"
35
36
37/**
38 * Number of bits we set per entry in the bloomfilter.
39 * Do not change!
40 */
41#define BLOOMFILTER_K 16
42
43
44/**
45 * Create a new block group.
46 *
47 * @param ctx block context in which the block group is created
48 * @param type type of the block for which we are creating the group
49 * @param nonce random value used to seed the group creation
50 * @param raw_data optional serialized prior state of the group, NULL if unavailable/fresh
51 * @param raw_data_size number of bytes in @a raw_data, 0 if unavailable/fresh
52 * @param va variable arguments specific to @a type
53 * @return block group handle, NULL if block groups are not supported
54 * by this @a type of block (this is not an error)
55 */
56static struct GNUNET_BLOCK_Group *
57block_plugin_dns_create_group (void *cls,
58 enum GNUNET_BLOCK_Type type,
59 uint32_t nonce,
60 const void *raw_data,
61 size_t raw_data_size,
62 va_list va)
63{
64 unsigned int bf_size;
65 const char *guard;
66
67 guard = va_arg (va, const char *);
68 if (0 == strcmp (guard,
69 "seen-set-size"))
70 bf_size = GNUNET_BLOCK_GROUP_compute_bloomfilter_size (va_arg (va, unsigned
71 int),
72 BLOOMFILTER_K);
73 else if (0 == strcmp (guard,
74 "filter-size"))
75 bf_size = va_arg (va, unsigned int);
76 else
77 {
78 GNUNET_break (0);
79 bf_size = 8;
80 }
81 GNUNET_break (NULL == va_arg (va, const char *));
82 return GNUNET_BLOCK_GROUP_bf_create (cls,
83 bf_size,
84 BLOOMFILTER_K,
85 type,
86 nonce,
87 raw_data,
88 raw_data_size);
89}
90
91
92/**
93 * Function called to validate a reply or a request. For
94 * request evaluation, simply pass "NULL" for the reply_block.
95 *
96 * @param cls closure
97 * @param ctx block context
98 * @param type block type
99 * @param bg group to evaluate against
100 * @param eo control flags
101 * @param query original query (hash)
102 * @param xquery extended query data (can be NULL, depending on type)
103 * @param xquery_size number of bytes in @a xquery
104 * @param reply_block response to validate
105 * @param reply_block_size number of bytes in @a reply_block
106 * @return characterization of result
107 */
108static enum GNUNET_BLOCK_EvaluationResult
109block_plugin_dns_evaluate (void *cls,
110 struct GNUNET_BLOCK_Context *ctx,
111 enum GNUNET_BLOCK_Type type,
112 struct GNUNET_BLOCK_Group *bg,
113 enum GNUNET_BLOCK_EvaluationOptions eo,
114 const struct GNUNET_HashCode *query,
115 const void *xquery,
116 size_t xquery_size,
117 const void *reply_block,
118 size_t reply_block_size)
119{
120 const struct GNUNET_DNS_Advertisement *ad;
121 struct GNUNET_HashCode phash;
122
123 switch (type)
124 {
125 case GNUNET_BLOCK_TYPE_DNS:
126 if (0 != xquery_size)
127 return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
128
129 if (NULL == reply_block)
130 return GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
131
132 if (sizeof(struct GNUNET_DNS_Advertisement) != reply_block_size)
133 {
134 GNUNET_break_op (0);
135 return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
136 }
137 ad = reply_block;
138
139 if (ntohl (ad->purpose.size) !=
140 sizeof(struct GNUNET_DNS_Advertisement)
141 - sizeof(struct GNUNET_CRYPTO_EddsaSignature))
142 {
143 GNUNET_break_op (0);
144 return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
145 }
146 if (0 ==
147 GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh
148 (ad->expiration_time)).
149 rel_value_us)
150 {
151 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
152 "DNS advertisement has expired\n");
153 return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
154 }
155 if (GNUNET_OK !=
156 GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_DNS_RECORD,
157 &ad->purpose,
158 &ad->signature,
159 &ad->peer.public_key))
160 {
161 GNUNET_break_op (0);
162 return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
163 }
164 GNUNET_CRYPTO_hash (reply_block,
165 reply_block_size,
166 &phash);
167 if (GNUNET_YES ==
168 GNUNET_BLOCK_GROUP_bf_test_and_set (bg,
169 &phash))
170 return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE;
171 return GNUNET_BLOCK_EVALUATION_OK_MORE;
172
173 default:
174 return GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED;
175 }
176}
177
178
179/**
180 * Function called to obtain the key for a block.
181 *
182 * @param cls closure
183 * @param type block type
184 * @param block block to get the key for
185 * @param block_size number of bytes in @a block
186 * @param key set to the key (query) for the given block
187 * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
188 * (or if extracting a key from a block of this type does not work)
189 */
190static int
191block_plugin_dns_get_key (void *cls,
192 enum GNUNET_BLOCK_Type type,
193 const void *block,
194 size_t block_size,
195 struct GNUNET_HashCode *key)
196{
197 /* we cannot extract a key from a block of this type */
198 return GNUNET_SYSERR;
199}
200
201
202/**
203 * Entry point for the plugin.
204 */
205void *
206libgnunet_plugin_block_dns_init (void *cls)
207{
208 static enum GNUNET_BLOCK_Type types[] = {
209 GNUNET_BLOCK_TYPE_DNS,
210 GNUNET_BLOCK_TYPE_ANY /* end of list */
211 };
212 struct GNUNET_BLOCK_PluginFunctions *api;
213
214 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
215 api->evaluate = &block_plugin_dns_evaluate;
216 api->get_key = &block_plugin_dns_get_key;
217 api->create_group = &block_plugin_dns_create_group;
218 api->types = types;
219 return api;
220}
221
222
223/**
224 * Exit point from the plugin.
225 */
226void *
227libgnunet_plugin_block_dns_done (void *cls)
228{
229 struct GNUNET_BLOCK_PluginFunctions *api = cls;
230
231 GNUNET_free (api);
232 return NULL;
233}
234
235
236/* end of plugin_block_dns.c */
diff --git a/src/dns/test_gnunet_dns.sh b/src/dns/test_gnunet_dns.sh
deleted file mode 100755
index e0fcb711d..000000000
--- a/src/dns/test_gnunet_dns.sh
+++ /dev/null
@@ -1,68 +0,0 @@
1#!/bin/sh
2
3ME=`whoami`
4if [ "$ME" != "root" ]
5then
6 echo "This test only works if run as root. Skipping."
7 exit 77
8fi
9if ! which sudo > /dev/null
10then
11 echo "This test requires sudo. Skipping."
12 exit 77
13fi
14if [ ! -x `which sudo` ]
15then
16 echo "This test requires sudo. Skipping."
17 exit 77
18fi
19if ! which nslookup > /dev/null
20then
21 echo "This test requires nslookup. Skipping."
22 exit 77
23fi
24if [ ! -x `which nslookup` ]
25then
26 echo "This test requires nslookup. Skipping."
27 exit 77
28fi
29if [ ! -x `which iptables` ]
30then
31 echo "This test requires iptables. Skipping."
32 exit 77
33fi
34if ! iptables -t mangle --list > /dev/null 2>&1
35then
36 echo "This test requires iptables with 'mangle' support. Skipping."
37 exit 77
38fi
39if grep % /etc/resolv.conf > /dev/null 2>&1
40then
41 echo "This system seems to use a DNS server on an IPv6 link-local address, which is not supported. Skipping."
42 exit 77
43fi
44
45if test ! `id nobody`;
46then
47 echo "This tests requires a user account 'nobody'. Skipping."
48 exit 77
49fi
50
51export PATH=".:$PATH"
52gnunet-service-dns -c dns.conf &
53gnunet-dns-redirector -c dns.conf -4 127.0.0.1 &
54sleep 1
55# need to run 'nslookup' as 'nobody', as gnunet-service-dns runs as root
56# and thus 'root' is excepted from DNS interception!
57LO=`sudo -u nobody nslookup -type=A gnunet.org | grep Address | tail -n1`
58if [ "$LO" != "Address: 127.0.0.1" ]
59then
60 echo "Fail: got address $LO, wanted 127.0.0.1"
61 ret=1
62else
63 echo "Test run, with success."
64 ret=0
65fi
66# TODO: jobs is a possible bashism. Fix.
67kill `jobs -p`
68exit $ret