aboutsummaryrefslogtreecommitdiff
path: root/src/service/dns
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/dns')
-rw-r--r--src/service/dns/.gitignore7
-rw-r--r--src/service/dns/Makefile.am83
-rw-r--r--src/service/dns/dns.conf.in34
-rw-r--r--src/service/dns/dns.h99
-rw-r--r--src/service/dns/dns_api.c381
-rw-r--r--src/service/dns/gnunet-dns-monitor.c411
-rw-r--r--src/service/dns/gnunet-dns-redirector.c267
-rw-r--r--src/service/dns/gnunet-helper-dns.c1195
-rw-r--r--src/service/dns/gnunet-service-dns.c1272
-rw-r--r--src/service/dns/gnunet-zonewalk.c616
-rw-r--r--src/service/dns/meson.build63
-rwxr-xr-xsrc/service/dns/test_gnunet_dns.sh68
12 files changed, 4496 insertions, 0 deletions
diff --git a/src/service/dns/.gitignore b/src/service/dns/.gitignore
new file mode 100644
index 000000000..d3a62470e
--- /dev/null
+++ b/src/service/dns/.gitignore
@@ -0,0 +1,7 @@
1gnunet-service-dns
2gnunet-dns-monitor
3gnunet-dns-redirector
4gnunet-helper-dns
5test_hexcoder
6gnunet-zoneimport
7gnunet-zonewalk
diff --git a/src/service/dns/Makefile.am b/src/service/dns/Makefile.am
new file mode 100644
index 000000000..0d07b9e5e
--- /dev/null
+++ b/src/service/dns/Makefile.am
@@ -0,0 +1,83 @@
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
32if LINUX
33check_SCRIPTS = \
34 test_gnunet_dns.sh
35endif
36
37gnunet_helper_dns_SOURCES = \
38 gnunet-helper-dns.c
39
40
41gnunet_dns_monitor_SOURCES = \
42 gnunet-dns-monitor.c
43gnunet_dns_monitor_LDADD = \
44 libgnunetdns.la \
45 $(top_builddir)/src/lib/util/libgnunetutil.la \
46 $(GN_LIBINTL)
47
48gnunet_zonewalk_SOURCES = \
49 gnunet-zonewalk.c
50gnunet_zonewalk_LDADD = \
51 $(top_builddir)/src/lib/util/libgnunetutil.la \
52 $(GN_LIBINTL)
53
54gnunet_dns_redirector_SOURCES = \
55 gnunet-dns-redirector.c
56gnunet_dns_redirector_LDADD = \
57 libgnunetdns.la \
58 $(top_builddir)/src/lib/util/libgnunetutil.la \
59 $(GN_LIBINTL)
60
61gnunet_service_dns_SOURCES = \
62 gnunet-service-dns.c
63gnunet_service_dns_LDADD = \
64 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
65 $(top_builddir)/src/lib/util/libgnunetutil.la \
66 $(GN_LIBINTL)
67
68libgnunetdns_la_SOURCES = \
69 dns_api.c dns.h
70libgnunetdns_la_LIBADD = \
71 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIB)
72libgnunetdns_la_LDFLAGS = \
73 $(GN_LIBINTL) \
74 $(GN_LIB_LDFLAGS) \
75 -version-info 0:0:0
76
77if ENABLE_TEST_RUN
78AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
79TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
80endif
81
82EXTRA_DIST = \
83 $(check_SCRIPTS)
diff --git a/src/service/dns/dns.conf.in b/src/service/dns/dns.conf.in
new file mode 100644
index 000000000..39f260813
--- /dev/null
+++ b/src/service/dns/dns.conf.in
@@ -0,0 +1,34 @@
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/service/dns/dns.h b/src/service/dns/dns.h
new file mode 100644
index 000000000..515012079
--- /dev/null
+++ b/src/service/dns/dns.h
@@ -0,0 +1,99 @@
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/service/dns/dns_api.c b/src/service/dns/dns_api.c
new file mode 100644
index 000000000..b0bbb894f
--- /dev/null
+++ b/src/service/dns/dns_api.c
@@ -0,0 +1,381 @@
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 */
106static void
107reconnect (void *cls);
108
109
110/**
111 * Drop the existing connection and reconnect to the DNS service.
112 *
113 * @param dh handle with the connection
114 */
115static void
116force_reconnect (struct GNUNET_DNS_Handle *dh)
117{
118 if (NULL != dh->mq)
119 {
120 GNUNET_MQ_destroy (dh->mq);
121 dh->mq = NULL;
122 }
123 dh->reconnect_task =
124 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
125 &reconnect,
126 dh);
127}
128
129
130/**
131 * Generic error handler, called with the appropriate error code and
132 * the same closure specified at the creation of the message queue.
133 * Not every message queue implementation supports an error handler.
134 *
135 * @param cls closure with the `struct GNUNET_DNS_Handle *`
136 * @param error error code
137 */
138static void
139mq_error_handler (void *cls,
140 enum GNUNET_MQ_Error error)
141{
142 struct GNUNET_DNS_Handle *dh = cls;
143
144 force_reconnect (dh);
145}
146
147
148/**
149 * This receives packets from the DNS service and calls the application to
150 * check that the request is well-formed
151 *
152 * @param cls the struct GNUNET_DNS_Handle
153 * @param req message from the service (request)
154 */
155static int
156check_request (void *cls,
157 const struct GNUNET_DNS_Request *req)
158{
159 if (0 != ntohl (req->reserved))
160 {
161 GNUNET_break (0);
162 return GNUNET_SYSERR;
163 }
164 return GNUNET_OK;
165}
166
167
168/**
169 * This receives packets from the DNS service and calls the application to
170 * handle it.
171 *
172 * @param cls the `struct GNUNET_DNS_Handle *`
173 * @param req message from the service (request)
174 */
175static void
176handle_request (void *cls,
177 const struct GNUNET_DNS_Request *req)
178{
179 struct GNUNET_DNS_Handle *dh = cls;
180 size_t payload_length = ntohs (req->header.size) - sizeof(*req);
181 struct GNUNET_DNS_RequestHandle *rh;
182
183 rh = GNUNET_new (struct GNUNET_DNS_RequestHandle);
184 rh->dh = dh;
185 rh->request_id = req->request_id;
186 rh->generation = dh->generation;
187 dh->pending_requests++;
188 dh->rh (dh->rh_cls,
189 rh,
190 payload_length,
191 (const char *) &req[1]);
192}
193
194
195static void
196reconnect (void *cls)
197{
198 struct GNUNET_DNS_Handle *dh = cls;
199 struct GNUNET_MQ_MessageHandler handlers[] = {
200 GNUNET_MQ_hd_var_size (request,
201 GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST,
202 struct GNUNET_DNS_Request,
203 dh),
204 GNUNET_MQ_handler_end ()
205 };
206 struct GNUNET_MQ_Envelope *env;
207 struct GNUNET_DNS_Register *msg;
208
209 dh->reconnect_task = NULL;
210 dh->mq = GNUNET_CLIENT_connect (dh->cfg,
211 "dns",
212 handlers,
213 &mq_error_handler,
214 dh);
215 if (NULL == dh->mq)
216 return;
217 dh->generation++;
218 env = GNUNET_MQ_msg (msg,
219 GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT);
220 msg->flags = htonl (dh->flags);
221 GNUNET_MQ_send (dh->mq,
222 env);
223}
224
225
226/**
227 * If a GNUNET_DNS_RequestHandler calls this function, the request is
228 * given to other clients or the global DNS for resolution. Once a
229 * global response has been obtained, the request handler is AGAIN
230 * called to give it a chance to observe and modify the response after
231 * the "normal" resolution. It is not legal for the request handler
232 * to call this function if a response is already present.
233 *
234 * @param rh request that should now be forwarded
235 */
236void
237GNUNET_DNS_request_forward (struct GNUNET_DNS_RequestHandle *rh)
238{
239 struct GNUNET_MQ_Envelope *env;
240 struct GNUNET_DNS_Response *resp;
241
242 GNUNET_assert (0 < rh->dh->pending_requests--);
243 if (rh->generation != rh->dh->generation)
244 {
245 GNUNET_free (rh);
246 return;
247 }
248 env = GNUNET_MQ_msg (resp,
249 GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
250 resp->drop_flag = htonl (1);
251 resp->request_id = rh->request_id;
252 GNUNET_MQ_send (rh->dh->mq,
253 env);
254 GNUNET_free (rh);
255}
256
257
258/**
259 * If a GNUNET_DNS_RequestHandler calls this function, the request is
260 * to be dropped and no response should be generated.
261 *
262 * @param rh request that should now be dropped
263 */
264void
265GNUNET_DNS_request_drop (struct GNUNET_DNS_RequestHandle *rh)
266{
267 struct GNUNET_MQ_Envelope *env;
268 struct GNUNET_DNS_Response *resp;
269
270 GNUNET_assert (0 < rh->dh->pending_requests--);
271 if (rh->generation != rh->dh->generation)
272 {
273 GNUNET_free (rh);
274 return;
275 }
276 env = GNUNET_MQ_msg (resp,
277 GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
278 resp->request_id = rh->request_id;
279 resp->drop_flag = htonl (0);
280 GNUNET_MQ_send (rh->dh->mq,
281 env);
282 GNUNET_free (rh);
283}
284
285
286/**
287 * If a GNUNET_DNS_RequestHandler calls this function, the request is
288 * supposed to be answered with the data provided to this call (with
289 * the modifications the function might have made).
290 *
291 * @param rh request that should now be answered
292 * @param reply_length size of @a reply (uint16_t to force sane size)
293 * @param reply reply data
294 */
295void
296GNUNET_DNS_request_answer (struct GNUNET_DNS_RequestHandle *rh,
297 uint16_t reply_length,
298 const char *reply)
299{
300 struct GNUNET_MQ_Envelope *env;
301 struct GNUNET_DNS_Response *resp;
302
303 GNUNET_assert (0 < rh->dh->pending_requests--);
304 if (rh->generation != rh->dh->generation)
305 {
306 GNUNET_free (rh);
307 return;
308 }
309 if (reply_length + sizeof(struct GNUNET_DNS_Response)
310 >= GNUNET_MAX_MESSAGE_SIZE)
311 {
312 GNUNET_break (0);
313 GNUNET_free (rh);
314 return;
315 }
316 env = GNUNET_MQ_msg_extra (resp,
317 reply_length,
318 GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
319 resp->drop_flag = htonl (2);
320 resp->request_id = rh->request_id;
321 GNUNET_memcpy (&resp[1],
322 reply,
323 reply_length);
324 GNUNET_MQ_send (rh->dh->mq,
325 env);
326 GNUNET_free (rh);
327}
328
329
330/**
331 * Connect to the service-dns
332 *
333 * @param cfg configuration to use
334 * @param flags when to call @a rh
335 * @param rh function to call with DNS requests
336 * @param rh_cls closure to pass to @a rh
337 * @return DNS handle
338 */
339struct GNUNET_DNS_Handle *
340GNUNET_DNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
341 enum GNUNET_DNS_Flags flags,
342 GNUNET_DNS_RequestHandler rh,
343 void *rh_cls)
344{
345 struct GNUNET_DNS_Handle *dh;
346
347 dh = GNUNET_new (struct GNUNET_DNS_Handle);
348 dh->cfg = cfg;
349 dh->flags = flags;
350 dh->rh = rh;
351 dh->rh_cls = rh_cls;
352 dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
353 return dh;
354}
355
356
357/**
358 * Disconnect from the DNS service.
359 *
360 * @param dh DNS handle
361 */
362void
363GNUNET_DNS_disconnect (struct GNUNET_DNS_Handle *dh)
364{
365 if (NULL != dh->mq)
366 {
367 GNUNET_MQ_destroy (dh->mq);
368 dh->mq = NULL;
369 }
370 if (NULL != dh->reconnect_task)
371 {
372 GNUNET_SCHEDULER_cancel (dh->reconnect_task);
373 dh->reconnect_task = NULL;
374 }
375 /* make sure client has no pending requests left over! */
376 GNUNET_break (0 == dh->pending_requests);
377 GNUNET_free (dh);
378}
379
380
381/* end of dns_api.c */
diff --git a/src/service/dns/gnunet-dns-monitor.c b/src/service/dns/gnunet-dns-monitor.c
new file mode 100644
index 000000000..afdb336f0
--- /dev/null
+++ b/src/service/dns/gnunet-dns-monitor.c
@@ -0,0 +1,411 @@
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
31/**
32 * Handle to transport service.
33 */
34static struct GNUNET_DNS_Handle *handle;
35
36/**
37 * Option -i.
38 */
39static int inbound_only;
40
41/**
42 * Option -o.
43 */
44static int outbound_only;
45
46/**
47 * Global return value (0 success).
48 */
49static int ret;
50
51/**
52 * Selected level of verbosity.
53 */
54static unsigned int verbosity;
55
56
57/**
58 * Convert numeric DNS record type to a string.
59 *
60 * @param type type to convert
61 * @return type as string, only valid until the next call to this function
62 */
63static const char *
64get_type (uint16_t type)
65{
66 static char buf[6];
67
68 switch (type)
69 {
70 case GNUNET_DNSPARSER_TYPE_A: return "A";
71
72 case GNUNET_DNSPARSER_TYPE_NS: return "NS";
73
74 case GNUNET_DNSPARSER_TYPE_CNAME: return "CNAME";
75
76 case GNUNET_DNSPARSER_TYPE_SOA: return "SOA";
77
78 case GNUNET_DNSPARSER_TYPE_PTR: return "PTR";
79
80 case GNUNET_DNSPARSER_TYPE_MX: return "MX";
81
82 case GNUNET_DNSPARSER_TYPE_TXT: return "TXT";
83
84 case GNUNET_DNSPARSER_TYPE_AAAA: return "AAAA";
85
86 case GNUNET_DNSPARSER_TYPE_SRV: return "SRV";
87
88 case GNUNET_DNSPARSER_TYPE_URI: return "URI";
89 }
90 GNUNET_snprintf (buf, sizeof(buf), "%u", (unsigned int) type);
91 return buf;
92}
93
94
95/**
96 * Convert numeric DNS record class to a string.
97 *
98 * @param class class to convert
99 * @return class as string, only valid until the next call to this function
100 */
101static const char *
102get_class (uint16_t class)
103{
104 static char buf[6];
105
106 switch (class)
107 {
108 case GNUNET_TUN_DNS_CLASS_INTERNET: return "IN";
109
110 case GNUNET_TUN_DNS_CLASS_CHAOS: return "CHAOS";
111
112 case GNUNET_TUN_DNS_CLASS_HESIOD: return "HESIOD";
113 }
114 GNUNET_snprintf (buf, sizeof(buf), "%u", (unsigned int) class);
115 return buf;
116}
117
118
119/**
120 * Output the given DNS query to stdout.
121 *
122 * @param query query to display.
123 */
124static void
125display_query (const struct GNUNET_DNSPARSER_Query *query)
126{
127 fprintf (stdout,
128 "\t\t%s %s: %s\n",
129 get_class (query->dns_traffic_class),
130 get_type (query->type),
131 query->name);
132}
133
134
135/**
136 * Output the given DNS record to stdout.
137 *
138 * @param record record to display.
139 */
140static void
141display_record (const struct GNUNET_DNSPARSER_Record *record)
142{
143 const char *format;
144 char buf[INET6_ADDRSTRLEN];
145 char *tmp;
146
147 tmp = NULL;
148 switch (record->type)
149 {
150 case GNUNET_DNSPARSER_TYPE_A:
151 if (record->data.raw.data_len != sizeof(struct in_addr))
152 format = "<invalid>";
153 else
154 format = inet_ntop (AF_INET, record->data.raw.data, buf, sizeof(buf));
155 break;
156
157 case GNUNET_DNSPARSER_TYPE_AAAA:
158 if (record->data.raw.data_len != sizeof(struct in6_addr))
159 format = "<invalid>";
160 else
161 format = inet_ntop (AF_INET6, record->data.raw.data, buf, sizeof(buf));
162 break;
163
164 case GNUNET_DNSPARSER_TYPE_NS:
165 case GNUNET_DNSPARSER_TYPE_CNAME:
166 case GNUNET_DNSPARSER_TYPE_PTR:
167 format = record->data.hostname;
168 break;
169
170 case GNUNET_DNSPARSER_TYPE_SOA:
171 if (NULL == record->data.soa)
172 format = "<invalid>";
173 else
174 {
175 GNUNET_asprintf (&tmp,
176 "origin: %s, mail: %s, serial = %u, refresh = %u s, retry = %u s, expire = %u s, minimum = %u s",
177 record->data.soa->mname,
178 record->data.soa->rname,
179 (unsigned int) record->data.soa->serial,
180 (unsigned int) record->data.soa->refresh,
181 (unsigned int) record->data.soa->retry,
182 (unsigned int) record->data.soa->expire,
183 (unsigned int) record->data.soa->minimum_ttl);
184 format = tmp;
185 }
186 break;
187
188 case GNUNET_DNSPARSER_TYPE_MX:
189 if (record->data.mx == NULL)
190 format = "<invalid>";
191 else
192 {
193 GNUNET_asprintf (&tmp,
194 "%u: %s",
195 record->data.mx->preference,
196 record->data.mx->mxhost);
197 format = tmp;
198 }
199 break;
200
201 case GNUNET_DNSPARSER_TYPE_SRV:
202 if (NULL == record->data.srv)
203 format = "<invalid>";
204 else
205 {
206 GNUNET_asprintf (&tmp,
207 "priority %u, weight = %u, port = %u, target = %s",
208 (unsigned int) record->data.srv->priority,
209 (unsigned int) record->data.srv->weight,
210 (unsigned int) record->data.srv->port,
211 record->data.srv->target);
212 format = tmp;
213 }
214 break;
215
216 case GNUNET_DNSPARSER_TYPE_URI:
217 if (NULL == record->data.uri)
218 format = "<invalid>";
219 else
220 {
221 GNUNET_asprintf (&tmp,
222 "priority %u, weight = %u, target = \"%s\"",
223 (unsigned int) record->data.uri->priority,
224 (unsigned int) record->data.uri->weight,
225 record->data.uri->target);
226 format = tmp;
227 }
228 break;
229
230
231 case GNUNET_DNSPARSER_TYPE_TXT:
232 GNUNET_asprintf (&tmp,
233 "%.*s",
234 (unsigned int) record->data.raw.data_len,
235 (char*) record->data.raw.data);
236 format = tmp;
237 break;
238
239 default:
240 format = "<payload>";
241 break;
242 }
243 fprintf (stdout,
244 "\t\t%s %s: %s = %s (%u s)\n",
245 get_class (record->dns_traffic_class),
246 get_type (record->type),
247 record->name,
248 format,
249 (unsigned int) (GNUNET_TIME_absolute_get_remaining (
250 record->expiration_time).rel_value_us / 1000LL
251 / 1000LL));
252 GNUNET_free (tmp);
253}
254
255
256/**
257 * Signature of a function that is called whenever the DNS service
258 * encounters a DNS request and needs to do something with it. The
259 * function has then the chance to generate or modify the response by
260 * calling one of the three "GNUNET_DNS_request_*" continuations.
261 *
262 * When a request is intercepted, this function is called first to
263 * give the client a chance to do the complete address resolution;
264 * "rdata" will be NULL for this first call for a DNS request, unless
265 * some other client has already filled in a response.
266 *
267 * If multiple clients exist, all of them are called before the global
268 * DNS. The global DNS is only called if all of the clients'
269 * functions call GNUNET_DNS_request_forward. Functions that call
270 * GNUNET_DNS_request_forward will be called again before a final
271 * response is returned to the application. If any of the clients'
272 * functions call GNUNET_DNS_request_drop, the response is dropped.
273 *
274 * @param cls closure
275 * @param rh request handle to user for reply
276 * @param request_length number of bytes in request
277 * @param request udp payload of the DNS request
278 */
279static void
280display_request (void *cls,
281 struct GNUNET_DNS_RequestHandle *rh,
282 size_t request_length,
283 const char *request)
284{
285 static const char *return_codes[] = {
286 "No error", "Format error", "Server failure", "Name error",
287 "Not implemented", "Refused", "YXDomain", "YXRRset",
288 "NXRRset", "NOT AUTH", "NOT ZONE", "<invalid>",
289 "<invalid>", "<invalid>", "<invalid>", "<invalid>"
290 };
291 static const char *op_codes[] = {
292 "Query", "Inverse query", "Status", "<invalid>",
293 "<invalid>", "<invalid>", "<invalid>", "<invalid>",
294 "<invalid>", "<invalid>", "<invalid>", "<invalid>",
295 "<invalid>", "<invalid>", "<invalid>", "<invalid>"
296 };
297 struct GNUNET_DNSPARSER_Packet *p;
298 unsigned int i;
299
300 p = GNUNET_DNSPARSER_parse (request, request_length);
301 if (NULL == p)
302 {
303 fprintf (stderr, "Received malformed DNS packet!\n");
304 // FIXME: drop instead?
305 GNUNET_DNS_request_forward (rh);
306 return;
307 }
308 fprintf (stdout,
309 "%s with ID: %5u Flags: %s%s%s%s%s%s, Return Code: %s, Opcode: %s\n",
310 p->flags.query_or_response ? "Response" : "Query",
311 p->id,
312 p->flags.recursion_desired ? "RD " : "",
313 p->flags.message_truncated ? "MT " : "",
314 p->flags.authoritative_answer ? "AA " : "",
315 p->flags.checking_disabled ? "CD " : "",
316 p->flags.authenticated_data ? "AD " : "",
317 p->flags.recursion_available ? "RA " : "",
318 return_codes[p->flags.return_code & 15],
319 op_codes[p->flags.opcode & 15]);
320 if (p->num_queries > 0)
321 fprintf (stdout,
322 "\tQueries:\n");
323 for (i = 0; i < p->num_queries; i++)
324 display_query (&p->queries[i]);
325
326 if (p->num_answers > 0)
327 fprintf (stdout,
328 "\tAnswers:\n");
329 for (i = 0; i < p->num_answers; i++)
330 display_record (&p->answers[i]);
331 fprintf (stdout, "\n");
332 GNUNET_DNSPARSER_free_packet (p);
333 GNUNET_DNS_request_forward (rh);
334}
335
336
337/**
338 * Shutdown.
339 */
340static void
341do_disconnect (void *cls)
342{
343 if (NULL != handle)
344 {
345 GNUNET_DNS_disconnect (handle);
346 handle = NULL;
347 }
348}
349
350
351/**
352 * Main function that will be run by the scheduler.
353 *
354 * @param cls closure
355 * @param args remaining command-line arguments
356 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
357 * @param cfg configuration
358 */
359static void
360run (void *cls, char *const *args, const char *cfgfile,
361 const struct GNUNET_CONFIGURATION_Handle *cfg)
362{
363 enum GNUNET_DNS_Flags flags;
364
365 flags = GNUNET_DNS_FLAG_REQUEST_MONITOR | GNUNET_DNS_FLAG_RESPONSE_MONITOR;
366 if (inbound_only | outbound_only)
367 flags = 0;
368 if (inbound_only)
369 flags |= GNUNET_DNS_FLAG_REQUEST_MONITOR;
370 if (outbound_only)
371 flags |= GNUNET_DNS_FLAG_RESPONSE_MONITOR;
372 handle =
373 GNUNET_DNS_connect (cfg,
374 flags,
375 &display_request,
376 NULL);
377 GNUNET_SCHEDULER_add_shutdown (&do_disconnect, NULL);
378}
379
380
381int
382main (int argc, char *const *argv)
383{
384 struct GNUNET_GETOPT_CommandLineOption options[] = {
385 GNUNET_GETOPT_option_flag ('i',
386 "inbound-only",
387 gettext_noop ("only monitor DNS queries"),
388 &inbound_only),
389
390 GNUNET_GETOPT_option_flag ('o',
391 "outbound-only",
392 gettext_noop ("only monitor DNS queries"),
393 &outbound_only),
394
395 GNUNET_GETOPT_option_verbose (&verbosity),
396 GNUNET_GETOPT_OPTION_END
397 };
398
399 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
400 return 2;
401 ret = (GNUNET_OK ==
402 GNUNET_PROGRAM_run (argc, argv, "gnunet-dns-monitor",
403 gettext_noop
404 ("Monitor DNS queries."), options,
405 &run, NULL)) ? ret : 1;
406 GNUNET_free_nz ((void *) argv);
407 return ret;
408}
409
410
411/* end of gnunet-dns-monitor.c */
diff --git a/src/service/dns/gnunet-dns-redirector.c b/src/service/dns/gnunet-dns-redirector.c
new file mode 100644
index 000000000..045207f8c
--- /dev/null
+++ b/src/service/dns/gnunet-dns-redirector.c
@@ -0,0 +1,267 @@
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
31/**
32 * Handle to DNS service.
33 */
34static struct GNUNET_DNS_Handle *handle;
35
36/**
37 * New target for A records.
38 */
39static char *n4;
40
41/**
42 * New target for AAAA records.
43 */
44static char *n6;
45
46/**
47 * Global return value (0 success).
48 */
49static int ret;
50
51/**
52 * Selected level of verbosity.
53 */
54static unsigned int verbosity;
55
56
57/**
58 * Modify the given DNS record.
59 *
60 * @param record record to modify
61 */
62static void
63modify_record (const struct GNUNET_DNSPARSER_Record *record)
64{
65 char buf[INET6_ADDRSTRLEN];
66
67 switch (record->type)
68 {
69 case GNUNET_DNSPARSER_TYPE_A:
70 if (record->data.raw.data_len != sizeof(struct in_addr))
71 return;
72 if (NULL != n4)
73 {
74 if (verbosity > 1)
75 fprintf (stderr,
76 "Changing A record from `%s' to `%s'\n",
77 inet_ntop (AF_INET, record->data.raw.data, buf, sizeof(buf)),
78 n4);
79 GNUNET_assert (1 == inet_pton (AF_INET, n4, record->data.raw.data));
80 }
81 break;
82
83 case GNUNET_DNSPARSER_TYPE_AAAA:
84 if (record->data.raw.data_len != sizeof(struct in6_addr))
85 return;
86 if (NULL != n6)
87 {
88 if (verbosity > 1)
89 fprintf (stderr,
90 "Changing AAAA record from `%s' to `%s'\n",
91 inet_ntop (AF_INET6, record->data.raw.data, buf, sizeof(buf)),
92 n6);
93 GNUNET_assert (1 == inet_pton (AF_INET6, n6, record->data.raw.data));
94 }
95 break;
96
97 case GNUNET_DNSPARSER_TYPE_NS:
98 case GNUNET_DNSPARSER_TYPE_CNAME:
99 case GNUNET_DNSPARSER_TYPE_PTR:
100 case GNUNET_DNSPARSER_TYPE_SOA:
101 case GNUNET_DNSPARSER_TYPE_MX:
102 case GNUNET_DNSPARSER_TYPE_TXT:
103 break;
104
105 default:
106 break;
107 }
108}
109
110
111/**
112 * Signature of a function that is called whenever the DNS service
113 * encounters a DNS request and needs to do something with it. The
114 * function has then the chance to generate or modify the response by
115 * calling one of the three "GNUNET_DNS_request_*" continuations.
116 *
117 * When a request is intercepted, this function is called first to
118 * give the client a chance to do the complete address resolution;
119 * "rdata" will be NULL for this first call for a DNS request, unless
120 * some other client has already filled in a response.
121 *
122 * If multiple clients exist, all of them are called before the global
123 * DNS. The global DNS is only called if all of the clients'
124 * functions call GNUNET_DNS_request_forward. Functions that call
125 * GNUNET_DNS_request_forward will be called again before a final
126 * response is returned to the application. If any of the clients'
127 * functions call GNUNET_DNS_request_drop, the response is dropped.
128 *
129 * @param cls closure
130 * @param rh request handle to user for reply
131 * @param request_length number of bytes in request
132 * @param request udp payload of the DNS request
133 */
134static void
135modify_request (void *cls,
136 struct GNUNET_DNS_RequestHandle *rh,
137 size_t request_length,
138 const char *request)
139{
140 struct GNUNET_DNSPARSER_Packet *p;
141 unsigned int i;
142 char *buf;
143 size_t len;
144 int ret;
145
146 p = GNUNET_DNSPARSER_parse (request, request_length);
147 if (NULL == p)
148 {
149 fprintf (stderr, "Received malformed DNS packet, leaving it untouched\n");
150 GNUNET_DNS_request_forward (rh);
151 return;
152 }
153 for (i = 0; i < p->num_answers; i++)
154 modify_record (&p->answers[i]);
155 buf = NULL;
156 ret = GNUNET_DNSPARSER_pack (p, 1024, &buf, &len);
157 GNUNET_DNSPARSER_free_packet (p);
158 if (GNUNET_OK != ret)
159 {
160 if (GNUNET_NO == ret)
161 fprintf (stderr,
162 "Modified DNS response did not fit, keeping old response\n");
163 else
164 GNUNET_break (0); /* our modifications should have been sane! */
165 GNUNET_DNS_request_forward (rh);
166 }
167 else
168 {
169 if (verbosity > 0)
170 fprintf (stdout,
171 "Injecting modified DNS response\n");
172 GNUNET_DNS_request_answer (rh, len, buf);
173 }
174 GNUNET_free (buf);
175}
176
177
178/**
179 * Shutdown.
180 */
181static void
182do_disconnect (void *cls)
183{
184 if (NULL != handle)
185 {
186 GNUNET_DNS_disconnect (handle);
187 handle = NULL;
188 }
189}
190
191
192/**
193 * Main function that will be run by the scheduler.
194 *
195 * @param cls closure
196 * @param args remaining command-line arguments
197 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
198 * @param cfg configuration
199 */
200static void
201run (void *cls, char *const *args, const char *cfgfile,
202 const struct GNUNET_CONFIGURATION_Handle *cfg)
203{
204 struct in_addr i4;
205 struct in6_addr i6;
206
207 if ((n4 != NULL) &&
208 (1 != inet_pton (AF_INET, n4, &i4)))
209 {
210 fprintf (stderr,
211 "`%s' is nto a valid IPv4 address!\n",
212 n4);
213 return;
214 }
215 if ((n6 != NULL) &&
216 (1 != inet_pton (AF_INET6, n6, &i6)))
217 {
218 fprintf (stderr,
219 "`%s' is nto a valid IPv6 address!\n",
220 n6);
221 return;
222 }
223
224 handle =
225 GNUNET_DNS_connect (cfg,
226 GNUNET_DNS_FLAG_POST_RESOLUTION,
227 &modify_request,
228 NULL);
229 GNUNET_SCHEDULER_add_shutdown (&do_disconnect, NULL);
230}
231
232
233int
234main (int argc, char *const *argv)
235{
236 struct GNUNET_GETOPT_CommandLineOption options[] = {
237 GNUNET_GETOPT_option_string ('4',
238 "ipv4",
239 "IPV4",
240 gettext_noop ("set A records"),
241 &n4),
242
243 GNUNET_GETOPT_option_string ('6',
244 "ipv4",
245 "IPV6",
246 gettext_noop ("set AAAA records"),
247 &n6),
248
249 GNUNET_GETOPT_option_verbose (&verbosity),
250 GNUNET_GETOPT_OPTION_END
251 };
252
253 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
254 return 2;
255
256 ret = (GNUNET_OK ==
257 GNUNET_PROGRAM_run (argc, argv, "gnunet-dns-redirector",
258 gettext_noop
259 ("Change DNS replies to point elsewhere."),
260 options,
261 &run, NULL)) ? ret : 1;
262 GNUNET_free_nz ((void *) argv);
263 return ret;
264}
265
266
267/* end of gnunet-dns-redirector.c */
diff --git a/src/service/dns/gnunet-helper-dns.c b/src/service/dns/gnunet-helper-dns.c
new file mode 100644
index 000000000..54443cd7a
--- /dev/null
+++ b/src/service/dns/gnunet-helper-dns.c
@@ -0,0 +1,1195 @@
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_util_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/service/dns/gnunet-service-dns.c b/src/service/dns/gnunet-service-dns.c
new file mode 100644
index 000000000..24566ae21
--- /dev/null
+++ b/src/service/dns/gnunet-service-dns.c
@@ -0,0 +1,1272 @@
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_statistics_service.h"
48
49/**
50 * Port number for DNS
51 */
52#define DNS_PORT 53
53
54
55/**
56 * Generic logging shorthand
57 */
58#define LOG(kind, ...) \
59 GNUNET_log_from (kind, "dns", __VA_ARGS__);
60
61
62/**
63 * Phases each request goes through.
64 */
65enum RequestPhase
66{
67 /**
68 * Request has just been received.
69 */
70 RP_INIT,
71
72 /**
73 * Showing the request to all monitor clients. If
74 * client list is empty, will enter QUERY phase.
75 */
76 RP_REQUEST_MONITOR,
77
78 /**
79 * Showing the request to PRE-RESOLUTION clients to find an answer.
80 * If client list is empty, will trigger global DNS request.
81 */
82 RP_QUERY,
83
84 /**
85 * Global Internet query is now pending.
86 */
87 RP_INTERNET_DNS,
88
89 /**
90 * Client (or global DNS request) has resulted in a response.
91 * Forward to all POST-RESOLUTION clients. If client list is empty,
92 * will enter RESPONSE_MONITOR phase.
93 */
94 RP_MODIFY,
95
96 /**
97 * Showing the request to all monitor clients. If
98 * client list is empty, give the result to the hijacker (and be done).
99 */
100 RP_RESPONSE_MONITOR,
101
102 /**
103 * Some client has told us to drop the request.
104 */
105 RP_DROP
106};
107
108
109/**
110 * Entry we keep for each client.
111 */
112struct ClientRecord
113{
114 /**
115 * Kept in doubly-linked list.
116 */
117 struct ClientRecord *next;
118
119 /**
120 * Kept in doubly-linked list.
121 */
122 struct ClientRecord *prev;
123
124 /**
125 * Handle to the client.
126 */
127 struct GNUNET_SERVICE_Client *client;
128
129 /**
130 * Message queue to talk to @a client.
131 */
132 struct GNUNET_MQ_Handle *mq;
133
134 /**
135 * Flags for the client.
136 */
137 enum GNUNET_DNS_Flags flags;
138};
139
140
141/**
142 * Entry we keep for each active request.
143 */
144struct RequestRecord
145{
146 /**
147 * List of clients that still need to see this request (each entry
148 * is set to NULL when the client is done).
149 */
150 struct ClientRecord **client_wait_list;
151
152 /**
153 * Payload of the UDP packet (the UDP payload), can be either query
154 * or already the response.
155 */
156 char *payload;
157
158 /**
159 * Socket we are using to transmit this request (must match if we receive
160 * a response).
161 */
162 struct GNUNET_DNSSTUB_RequestSocket *rs;
163
164 /**
165 * Source address of the original request (for sending response).
166 */
167 struct sockaddr_storage src_addr;
168
169 /**
170 * Destination address of the original request (for potential use as exit).
171 */
172 struct sockaddr_storage dst_addr;
173
174 /**
175 * ID of this request, also basis for hashing. Lowest 16 bit will
176 * be our message ID when doing a global DNS request and our index
177 * into the 'requests' array.
178 */
179 uint64_t request_id;
180
181 /**
182 * Number of bytes in payload.
183 */
184 size_t payload_length;
185
186 /**
187 * Length of the @e client_wait_list.
188 */
189 unsigned int client_wait_list_length;
190
191 /**
192 * In which phase this this request?
193 */
194 enum RequestPhase phase;
195};
196
197
198/**
199 * Global return value from 'main'.
200 */
201static int global_ret;
202
203/**
204 * The configuration to use
205 */
206static const struct GNUNET_CONFIGURATION_Handle *cfg;
207
208/**
209 * Statistics.
210 */
211static struct GNUNET_STATISTICS_Handle *stats;
212
213/**
214 * Handle to DNS hijacker helper process ("gnunet-helper-dns").
215 */
216static struct GNUNET_HELPER_Handle *hijacker;
217
218/**
219 * Command-line arguments we are giving to the hijacker process.
220 */
221static char *helper_argv[8];
222
223/**
224 * Head of DLL of clients we consult.
225 */
226static struct ClientRecord *clients_head;
227
228/**
229 * Tail of DLL of clients we consult.
230 */
231static struct ClientRecord *clients_tail;
232
233/**
234 * Array of all open requests.
235 */
236static struct RequestRecord requests[UINT16_MAX + 1];
237
238/**
239 * Generator for unique request IDs.
240 */
241static uint64_t request_id_gen;
242
243/**
244 * Handle to the DNS Stub resolver.
245 */
246static struct GNUNET_DNSSTUB_Context *dnsstub;
247
248
249/**
250 * We're done processing a DNS request, free associated memory.
251 *
252 * @param rr request to clean up
253 */
254static void
255cleanup_rr (struct RequestRecord *rr)
256{
257 GNUNET_free (rr->payload);
258 rr->payload = NULL;
259 rr->payload_length = 0;
260 GNUNET_array_grow (rr->client_wait_list,
261 rr->client_wait_list_length,
262 0);
263}
264
265
266/**
267 * Task run during shutdown.
268 *
269 * @param cls unused
270 */
271static void
272cleanup_task (void *cls GNUNET_UNUSED)
273{
274 if (NULL != hijacker)
275 {
276 GNUNET_HELPER_stop (hijacker, GNUNET_NO);
277 hijacker = NULL;
278 }
279 for (unsigned int i = 0; i < 8; i++)
280 GNUNET_free (helper_argv[i]);
281 for (unsigned int i = 0; i <= UINT16_MAX; i++)
282 cleanup_rr (&requests[i]);
283 if (NULL != stats)
284 {
285 GNUNET_STATISTICS_destroy (stats,
286 GNUNET_NO);
287 stats = NULL;
288 }
289 if (NULL != dnsstub)
290 {
291 GNUNET_DNSSTUB_stop (dnsstub);
292 dnsstub = NULL;
293 }
294}
295
296
297/**
298 * We're done with some request, finish processing.
299 *
300 * @param rr request send to the network or just clean up.
301 */
302static void
303request_done (struct RequestRecord *rr)
304{
305 struct GNUNET_MessageHeader *hdr;
306 size_t reply_len;
307 uint16_t source_port;
308 uint16_t destination_port;
309
310 GNUNET_array_grow (rr->client_wait_list,
311 rr->client_wait_list_length,
312 0);
313 if (RP_RESPONSE_MONITOR != rr->phase)
314 {
315 /* no response, drop */
316 LOG (GNUNET_ERROR_TYPE_DEBUG,
317 "Got no response for request %llu, dropping\n",
318 (unsigned long long) rr->request_id);
319 cleanup_rr (rr);
320 return;
321 }
322
323 LOG (GNUNET_ERROR_TYPE_DEBUG,
324 "Transmitting response for request %llu\n",
325 (unsigned long long) rr->request_id);
326 /* send response via hijacker */
327 reply_len = sizeof(struct GNUNET_MessageHeader);
328 reply_len += sizeof(struct GNUNET_TUN_Layer2PacketHeader);
329 switch (rr->src_addr.ss_family)
330 {
331 case AF_INET:
332 reply_len += sizeof(struct GNUNET_TUN_IPv4Header);
333 break;
334
335 case AF_INET6:
336 reply_len += sizeof(struct GNUNET_TUN_IPv6Header);
337 break;
338
339 default:
340 GNUNET_break (0);
341 cleanup_rr (rr);
342 return;
343 }
344 reply_len += sizeof(struct GNUNET_TUN_UdpHeader);
345 reply_len += rr->payload_length;
346 if (reply_len >= GNUNET_MAX_MESSAGE_SIZE)
347 {
348 /* response too big, drop */
349 GNUNET_break (0); /* how can this be? */
350 cleanup_rr (rr);
351 return;
352 }
353 {
354 char buf[reply_len] GNUNET_ALIGN;
355 size_t off;
356 struct GNUNET_TUN_IPv4Header ip4;
357 struct GNUNET_TUN_IPv6Header ip6;
358
359 /* first, GNUnet message header */
360 hdr = (struct GNUNET_MessageHeader*) buf;
361 hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
362 hdr->size = htons ((uint16_t) reply_len);
363 off = sizeof(struct GNUNET_MessageHeader);
364
365 /* first, TUN header */
366 {
367 struct GNUNET_TUN_Layer2PacketHeader tun;
368
369 tun.flags = htons (0);
370 if (rr->src_addr.ss_family == AF_INET)
371 tun.proto = htons (ETH_P_IPV4);
372 else
373 tun.proto = htons (ETH_P_IPV6);
374 GNUNET_memcpy (&buf[off],
375 &tun,
376 sizeof(struct GNUNET_TUN_Layer2PacketHeader));
377 off += sizeof(struct GNUNET_TUN_Layer2PacketHeader);
378 }
379
380 /* now IP header */
381 switch (rr->src_addr.ss_family)
382 {
383 case AF_INET:
384 {
385 struct sockaddr_in *src = (struct sockaddr_in *) &rr->src_addr;
386 struct sockaddr_in *dst = (struct sockaddr_in *) &rr->dst_addr;
387
388 source_port = dst->sin_port;
389 destination_port = src->sin_port;
390 GNUNET_TUN_initialize_ipv4_header (&ip4,
391 IPPROTO_UDP,
392 reply_len - off - sizeof(struct
393 GNUNET_TUN_IPv4Header),
394 &dst->sin_addr,
395 &src->sin_addr);
396 GNUNET_memcpy (&buf[off],
397 &ip4,
398 sizeof(ip4));
399 off += sizeof(ip4);
400 }
401 break;
402
403 case AF_INET6:
404 {
405 struct sockaddr_in6 *src = (struct sockaddr_in6 *) &rr->src_addr;
406 struct sockaddr_in6 *dst = (struct sockaddr_in6 *) &rr->dst_addr;
407
408 source_port = dst->sin6_port;
409 destination_port = src->sin6_port;
410 GNUNET_TUN_initialize_ipv6_header (&ip6,
411 IPPROTO_UDP,
412 reply_len - off - sizeof(struct
413 GNUNET_TUN_IPv6Header),
414 &dst->sin6_addr,
415 &src->sin6_addr);
416 GNUNET_memcpy (&buf[off],
417 &ip6,
418 sizeof(ip6));
419 off += sizeof(ip6);
420 }
421 break;
422
423 default:
424 GNUNET_assert (0);
425 }
426
427 /* now UDP header */
428 {
429 struct GNUNET_TUN_UdpHeader udp;
430
431 udp.source_port = source_port;
432 udp.destination_port = destination_port;
433 udp.len = htons (reply_len - off);
434 if (AF_INET == rr->src_addr.ss_family)
435 GNUNET_TUN_calculate_udp4_checksum (&ip4,
436 &udp,
437 rr->payload,
438 rr->payload_length);
439 else
440 GNUNET_TUN_calculate_udp6_checksum (&ip6,
441 &udp,
442 rr->payload,
443 rr->payload_length);
444 GNUNET_memcpy (&buf[off],
445 &udp,
446 sizeof(udp));
447 off += sizeof(udp);
448 }
449
450 /* now DNS payload */
451 {
452 GNUNET_memcpy (&buf[off], rr->payload, rr->payload_length);
453 off += rr->payload_length;
454 }
455 /* final checks & sending */
456 GNUNET_assert (off == reply_len);
457 (void) GNUNET_HELPER_send (hijacker,
458 hdr,
459 GNUNET_YES,
460 NULL, NULL);
461 GNUNET_STATISTICS_update (stats,
462 gettext_noop (
463 "# DNS requests answered via TUN interface"),
464 1, GNUNET_NO);
465 }
466 /* clean up, we're done */
467 cleanup_rr (rr);
468}
469
470
471/**
472 * Show the payload of the given request record to the client
473 * (and wait for a response).
474 *
475 * @param rr request to send to client
476 * @param cr client to send the response to
477 */
478static void
479send_request_to_client (struct RequestRecord *rr,
480 struct ClientRecord *cr)
481{
482 struct GNUNET_MQ_Envelope *env;
483 struct GNUNET_DNS_Request *req;
484
485 if (sizeof(struct GNUNET_DNS_Request) + rr->payload_length >=
486 GNUNET_MAX_MESSAGE_SIZE)
487 {
488 GNUNET_break (0);
489 cleanup_rr (rr);
490 return;
491 }
492 LOG (GNUNET_ERROR_TYPE_DEBUG,
493 "Sending information about request %llu to local client\n",
494 (unsigned long long) rr->request_id);
495 env = GNUNET_MQ_msg_extra (req,
496 rr->payload_length,
497 GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST);
498 req->reserved = htonl (0);
499 req->request_id = rr->request_id;
500 GNUNET_memcpy (&req[1],
501 rr->payload,
502 rr->payload_length);
503 GNUNET_MQ_send (cr->mq,
504 env);
505}
506
507
508/**
509 * Callback called from DNSSTUB resolver when a resolution
510 * succeeded.
511 *
512 * @param cls NULL
513 * @param dns the response itself
514 * @param r number of bytes in dns
515 */
516static void
517process_dns_result (void *cls,
518 const struct GNUNET_TUN_DnsHeader *dns,
519 size_t r);
520
521
522/**
523 * A client has completed its processing for this
524 * request. Move on.
525 *
526 * @param rr request to process further
527 */
528static void
529next_phase (struct RequestRecord *rr)
530{
531 struct ClientRecord *cr;
532 int nz;
533
534 if (rr->phase == RP_DROP)
535 {
536 cleanup_rr (rr);
537 return;
538 }
539 nz = -1;
540 for (unsigned int j = 0; j < rr->client_wait_list_length; j++)
541 {
542 if (NULL != rr->client_wait_list[j])
543 {
544 nz = (int) j;
545 break;
546 }
547 }
548 if (-1 != nz)
549 {
550 send_request_to_client (rr,
551 rr->client_wait_list[nz]);
552 return;
553 }
554 /* done with current phase, advance! */
555 LOG (GNUNET_ERROR_TYPE_DEBUG,
556 "Request %llu now in phase %d\n",
557 (unsigned long long) rr->request_id,
558 rr->phase);
559 switch (rr->phase)
560 {
561 case RP_INIT:
562 rr->phase = RP_REQUEST_MONITOR;
563 for (cr = clients_head; NULL != cr; cr = cr->next)
564 {
565 if (0 != (cr->flags & GNUNET_DNS_FLAG_REQUEST_MONITOR))
566 GNUNET_array_append (rr->client_wait_list,
567 rr->client_wait_list_length,
568 cr);
569 }
570 next_phase (rr);
571 return;
572
573 case RP_REQUEST_MONITOR:
574 rr->phase = RP_QUERY;
575 for (cr = clients_head; NULL != cr; cr = cr->next)
576 {
577 if (0 != (cr->flags & GNUNET_DNS_FLAG_PRE_RESOLUTION))
578 GNUNET_array_append (rr->client_wait_list,
579 rr->client_wait_list_length,
580 cr);
581 }
582 next_phase (rr);
583 return;
584
585 case RP_QUERY:
586#if 0
587 /* TODO: optionally, use this to forward DNS requests to the
588 * original* DNS server instead of the one we have configured...
589 (but then we need to create a fresh dnsstub for each request
590 * and* manage the timeout) */
591 switch (rr->dst_addr.ss_family)
592 {
593 case AF_INET:
594 salen = sizeof(struct sockaddr_in);
595 sa = (const struct sockaddr *) &rr->dst_addr;
596 break;
597
598 case AF_INET6:
599 salen = sizeof(struct sockaddr_in6);
600 sa = (const struct sockaddr *) &rr->dst_addr;
601 break;
602
603 default:
604 GNUNET_assert (0);
605 }
606#endif
607 rr->phase = RP_INTERNET_DNS;
608 rr->rs = GNUNET_DNSSTUB_resolve (dnsstub,
609 rr->payload,
610 rr->payload_length,
611 &process_dns_result,
612 NULL);
613 if (NULL == rr->rs)
614 {
615 GNUNET_STATISTICS_update (stats,
616 gettext_noop (
617 "# DNS exit failed (failed to open socket)"),
618 1,
619 GNUNET_NO);
620 cleanup_rr (rr);
621 return;
622 }
623 return;
624
625 case RP_INTERNET_DNS:
626 rr->phase = RP_MODIFY;
627 for (cr = clients_head; NULL != cr; cr = cr->next)
628 {
629 if (0 != (cr->flags & GNUNET_DNS_FLAG_POST_RESOLUTION))
630 GNUNET_array_append (rr->client_wait_list,
631 rr->client_wait_list_length,
632 cr);
633 }
634 next_phase (rr);
635 return;
636
637 case RP_MODIFY:
638 rr->phase = RP_RESPONSE_MONITOR;
639 for (cr = clients_head; NULL != cr; cr = cr->next)
640 {
641 if (0 != (cr->flags & GNUNET_DNS_FLAG_RESPONSE_MONITOR))
642 GNUNET_array_append (rr->client_wait_list,
643 rr->client_wait_list_length,
644 cr);
645 }
646 next_phase (rr);
647 return;
648
649 case RP_RESPONSE_MONITOR:
650 request_done (rr);
651 break;
652
653 case RP_DROP:
654 cleanup_rr (rr);
655 break;
656
657 default:
658 GNUNET_break (0);
659 cleanup_rr (rr);
660 break;
661 }
662}
663
664
665/**
666 * A client connected, setup our data structures.
667 *
668 * @param cls unused
669 * @param client handle of client that connected
670 * @param mq message queue to talk to @a client
671 * @return our `struct ClientRecord`
672 */
673static void *
674client_connect_cb (void *cls,
675 struct GNUNET_SERVICE_Client *client,
676 struct GNUNET_MQ_Handle *mq)
677{
678 struct ClientRecord *cr = cls;
679
680 cr = GNUNET_new (struct ClientRecord);
681 cr->client = client;
682 cr->mq = mq;
683 GNUNET_CONTAINER_DLL_insert (clients_head,
684 clients_tail,
685 cr);
686 return cr;
687}
688
689
690/**
691 * A client disconnected, clean up after it.
692 *
693 * @param cls unused
694 * @param client handle of client that disconnected
695 * @param app_ctx our `struct ClientRecord`
696 */
697static void
698client_disconnect_cb (void *cls,
699 struct GNUNET_SERVICE_Client *client,
700 void *app_ctx)
701{
702 struct ClientRecord *cr = app_ctx;
703 struct RequestRecord *rr;
704
705 GNUNET_CONTAINER_DLL_remove (clients_head,
706 clients_tail,
707 cr);
708 for (unsigned int i = 0; i < UINT16_MAX; i++)
709 {
710 rr = &requests[i];
711 if (0 == rr->client_wait_list_length)
712 continue; /* not in use */
713 for (unsigned int j = 0; j < rr->client_wait_list_length; j++)
714 {
715 if (rr->client_wait_list[j] == cr)
716 {
717 rr->client_wait_list[j] = NULL;
718 next_phase (rr);
719 }
720 }
721 }
722 GNUNET_free (cr);
723}
724
725
726/**
727 * Callback called from DNSSTUB resolver when a resolution
728 * succeeded.
729 *
730 * @param cls NULL
731 * @param dns the response itself
732 * @param r number of bytes in dns
733 */
734static void
735process_dns_result (void *cls,
736 const struct GNUNET_TUN_DnsHeader *dns,
737 size_t r)
738{
739 struct RequestRecord *rr;
740
741 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
742 "Processing DNS result from stub resolver\n");
743 GNUNET_assert (NULL == cls);
744 if (NULL == dns)
745 return; /* ignore */
746
747 rr = &requests[dns->id];
748 if (rr->phase != RP_INTERNET_DNS)
749 {
750 /* unexpected / bogus reply */
751 GNUNET_STATISTICS_update (stats,
752 gettext_noop (
753 "# External DNS response discarded (no matching request)"),
754 1, GNUNET_NO);
755 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
756 "Received DNS reply that does not match any pending request. Dropping.\n");
757 return;
758 }
759 LOG (GNUNET_ERROR_TYPE_DEBUG,
760 "Got a response from the stub resolver for DNS request %llu intercepted locally!\n",
761 (unsigned long long) rr->request_id);
762 GNUNET_free (rr->payload);
763 rr->payload = GNUNET_malloc (r);
764 GNUNET_memcpy (rr->payload,
765 dns,
766 r);
767 rr->payload_length = r;
768 next_phase (rr);
769}
770
771
772/**
773 * We got a new client. Make sure all new DNS requests pass by its desk.
774 *
775 * @param cls the client
776 * @param reg the init message
777 */
778static void
779handle_client_init (void *cls,
780 const struct GNUNET_DNS_Register *reg)
781{
782 struct ClientRecord *cr = cls;
783
784 cr->flags = (enum GNUNET_DNS_Flags) ntohl (reg->flags);
785 GNUNET_SERVICE_client_continue (cr->client);
786}
787
788
789/**
790 * Check a response from a client.
791 *
792 * @param cls the client
793 * @param resp the response
794 * @return #GNUNET_OK (always fine)
795 */
796static int
797check_client_response (void *cls,
798 const struct GNUNET_DNS_Response *resp)
799{
800 return GNUNET_OK; /* any payload is acceptable */
801}
802
803
804/**
805 * Handle a response from a client.
806 *
807 * @param cls the client
808 * @param resp the response
809 */
810static void
811handle_client_response (void *cls,
812 const struct GNUNET_DNS_Response *resp)
813{
814 struct ClientRecord *cr = cls;
815 struct RequestRecord *rr;
816 uint16_t msize;
817 uint16_t off;
818
819 msize = ntohs (resp->header.size);
820 off = (uint16_t) resp->request_id;
821 rr = &requests[off];
822 LOG (GNUNET_ERROR_TYPE_DEBUG,
823 "Received DNS response with ID %llu from local client!\n",
824 (unsigned long long) resp->request_id);
825 if (rr->request_id != resp->request_id)
826 {
827 GNUNET_STATISTICS_update (stats,
828 gettext_noop (
829 "# Client response discarded (no matching request)"),
830 1,
831 GNUNET_NO);
832 GNUNET_SERVICE_client_continue (cr->client);
833 return;
834 }
835 for (unsigned int i = 0; i < rr->client_wait_list_length; i++)
836 {
837 if (NULL == rr->client_wait_list[i])
838 continue;
839 if (rr->client_wait_list[i] != cr)
840 continue;
841 rr->client_wait_list[i] = NULL;
842 switch (ntohl (resp->drop_flag))
843 {
844 case 0: /* drop */
845 rr->phase = RP_DROP;
846 break;
847
848 case 1: /* no change */
849 break;
850
851 case 2: /* update */
852 msize -= sizeof(struct GNUNET_DNS_Response);
853 if ((sizeof(struct GNUNET_TUN_DnsHeader) > msize) ||
854 (RP_REQUEST_MONITOR == rr->phase) ||
855 (RP_RESPONSE_MONITOR == rr->phase))
856 {
857 GNUNET_break (0);
858 GNUNET_SERVICE_client_drop (cr->client);
859 next_phase (rr);
860 return;
861 }
862 GNUNET_free (rr->payload);
863 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864 "Changing DNS reply according to client specifications\n");
865 rr->payload = GNUNET_malloc (msize);
866 rr->payload_length = msize;
867 GNUNET_memcpy (rr->payload, &resp[1], msize);
868 if (rr->phase == RP_QUERY)
869 {
870 /* clear wait list, we're moving to MODIFY phase next */
871 GNUNET_array_grow (rr->client_wait_list,
872 rr->client_wait_list_length,
873 0);
874 }
875 /* if query changed to answer, move past DNS resolution phase... */
876 if ((RP_QUERY == rr->phase) &&
877 (rr->payload_length > sizeof(struct GNUNET_TUN_DnsHeader)) &&
878 ( ((struct GNUNET_TUN_DnsFlags*) &(((struct
879 GNUNET_TUN_DnsHeader*) rr->
880 payload)->flags))->
881 query_or_response == 1) )
882 {
883 rr->phase = RP_INTERNET_DNS;
884 GNUNET_array_grow (rr->client_wait_list,
885 rr->client_wait_list_length,
886 0);
887 }
888 break;
889 }
890 next_phase (rr);
891 GNUNET_SERVICE_client_continue (cr->client);
892 return;
893 }
894 /* odd, client was not on our list for the request, that ought
895 to be an error */
896 GNUNET_break (0);
897 GNUNET_SERVICE_client_drop (cr->client);
898}
899
900
901/**
902 * Functions with this signature are called whenever a complete
903 * message is received by the tokenizer from the DNS hijack process.
904 *
905 * @param cls closure
906 * @param message the actual message, a DNS request we should handle
907 */
908static int
909process_helper_messages (void *cls,
910 const struct GNUNET_MessageHeader *message)
911{
912 uint16_t msize;
913 const struct GNUNET_TUN_Layer2PacketHeader *tun;
914 const struct GNUNET_TUN_IPv4Header *ip4;
915 const struct GNUNET_TUN_IPv6Header *ip6;
916 const struct GNUNET_TUN_UdpHeader *udp;
917 const struct GNUNET_TUN_DnsHeader *dns;
918 struct RequestRecord *rr;
919 struct sockaddr_in *srca4;
920 struct sockaddr_in6 *srca6;
921 struct sockaddr_in *dsta4;
922 struct sockaddr_in6 *dsta6;
923
924 LOG (GNUNET_ERROR_TYPE_DEBUG,
925 "Intercepted message via DNS hijacker\n");
926 msize = ntohs (message->size);
927 if (msize < sizeof(struct GNUNET_MessageHeader) + sizeof(struct
928 GNUNET_TUN_Layer2PacketHeader)
929 + sizeof(struct GNUNET_TUN_IPv4Header))
930 {
931 /* non-IP packet received on TUN!? */
932 GNUNET_break (0);
933 return GNUNET_OK;
934 }
935 msize -= sizeof(struct GNUNET_MessageHeader);
936 tun = (const struct GNUNET_TUN_Layer2PacketHeader *) &message[1];
937 msize -= sizeof(struct GNUNET_TUN_Layer2PacketHeader);
938 switch (ntohs (tun->proto))
939 {
940 case ETH_P_IPV4:
941 ip4 = (const struct GNUNET_TUN_IPv4Header *) &tun[1];
942 ip6 = NULL; /* make compiler happy */
943 if ((msize < sizeof(struct GNUNET_TUN_IPv4Header)) ||
944 (ip4->version != 4) ||
945 (ip4->header_length != sizeof(struct GNUNET_TUN_IPv4Header) / 4) ||
946 (ntohs (ip4->total_length) != msize) ||
947 (ip4->protocol != IPPROTO_UDP))
948 {
949 /* non-IP/UDP packet received on TUN (or with options) */
950 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
951 _ ("Received malformed IPv4-UDP packet on TUN interface.\n"));
952 return GNUNET_OK;
953 }
954 udp = (const struct GNUNET_TUN_UdpHeader*) &ip4[1];
955 msize -= sizeof(struct GNUNET_TUN_IPv4Header);
956 break;
957
958 case ETH_P_IPV6:
959 ip4 = NULL; /* make compiler happy */
960 ip6 = (const struct GNUNET_TUN_IPv6Header *) &tun[1];
961 if ((msize < sizeof(struct GNUNET_TUN_IPv6Header)) ||
962 (ip6->version != 6) ||
963 (ntohs (ip6->payload_length) != msize - sizeof(struct
964 GNUNET_TUN_IPv6Header))
965 ||
966 (ip6->next_header != IPPROTO_UDP))
967 {
968 /* non-IP/UDP packet received on TUN (or with extensions) */
969 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
970 _ ("Received malformed IPv6-UDP packet on TUN interface.\n"));
971 return GNUNET_OK;
972 }
973 udp = (const struct GNUNET_TUN_UdpHeader *) &ip6[1];
974 msize -= sizeof(struct GNUNET_TUN_IPv6Header);
975 break;
976
977 default:
978 /* non-IP packet received on TUN!? */
979 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
980 _ (
981 "Got non-IP packet with %u bytes and protocol %u from TUN\n"),
982 (unsigned int) msize,
983 ntohs (tun->proto));
984 return GNUNET_OK;
985 }
986 if ((msize <= sizeof(struct GNUNET_TUN_UdpHeader) + sizeof(struct
987 GNUNET_TUN_DnsHeader))
988 ||
989 (DNS_PORT != ntohs (udp->destination_port)))
990 {
991 /* non-DNS packet received on TUN, ignore */
992 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
993 _ ("DNS interceptor got non-DNS packet (dropped)\n"));
994 GNUNET_STATISTICS_update (stats,
995 gettext_noop (
996 "# Non-DNS UDP packet received via TUN interface"),
997 1, GNUNET_NO);
998 return GNUNET_OK;
999 }
1000 msize -= sizeof(struct GNUNET_TUN_UdpHeader);
1001 dns = (const struct GNUNET_TUN_DnsHeader*) &udp[1];
1002 rr = &requests[dns->id];
1003
1004 /* clean up from previous request */
1005 GNUNET_free (rr->payload);
1006 rr->payload = NULL;
1007 GNUNET_array_grow (rr->client_wait_list,
1008 rr->client_wait_list_length,
1009 0);
1010
1011 /* setup new request */
1012 rr->phase = RP_INIT;
1013 switch (ntohs (tun->proto))
1014 {
1015 case ETH_P_IPV4:
1016 {
1017 srca4 = (struct sockaddr_in*) &rr->src_addr;
1018 dsta4 = (struct sockaddr_in*) &rr->dst_addr;
1019 memset (srca4, 0, sizeof(struct sockaddr_in));
1020 memset (dsta4, 0, sizeof(struct sockaddr_in));
1021 srca4->sin_family = AF_INET;
1022 dsta4->sin_family = AF_INET;
1023 srca4->sin_addr = ip4->source_address;
1024 dsta4->sin_addr = ip4->destination_address;
1025 srca4->sin_port = udp->source_port;
1026 dsta4->sin_port = udp->destination_port;
1027#if HAVE_SOCKADDR_IN_SIN_LEN
1028 srca4->sin_len = sizeof(struct sockaddr_in);
1029 dsta4->sin_len = sizeof(struct sockaddr_in);
1030#endif
1031 }
1032 break;
1033
1034 case ETH_P_IPV6:
1035 {
1036 srca6 = (struct sockaddr_in6*) &rr->src_addr;
1037 dsta6 = (struct sockaddr_in6*) &rr->dst_addr;
1038 memset (srca6, 0, sizeof(struct sockaddr_in6));
1039 memset (dsta6, 0, sizeof(struct sockaddr_in6));
1040 srca6->sin6_family = AF_INET6;
1041 dsta6->sin6_family = AF_INET6;
1042 srca6->sin6_addr = ip6->source_address;
1043 dsta6->sin6_addr = ip6->destination_address;
1044 srca6->sin6_port = udp->source_port;
1045 dsta6->sin6_port = udp->destination_port;
1046#if HAVE_SOCKADDR_IN_SIN_LEN
1047 srca6->sin6_len = sizeof(struct sockaddr_in6);
1048 dsta6->sin6_len = sizeof(struct sockaddr_in6);
1049#endif
1050 }
1051 break;
1052
1053 default:
1054 GNUNET_assert (0);
1055 }
1056 rr->payload = GNUNET_malloc (msize);
1057 rr->payload_length = msize;
1058 GNUNET_memcpy (rr->payload, dns, msize);
1059 rr->request_id = dns->id | (request_id_gen << 16);
1060 request_id_gen++;
1061 LOG (GNUNET_ERROR_TYPE_DEBUG,
1062 "Creating new DNS request %llu\n",
1063 (unsigned long long) rr->request_id);
1064 GNUNET_STATISTICS_update (stats,
1065 gettext_noop (
1066 "# DNS requests received via TUN interface"),
1067 1, GNUNET_NO);
1068 /* start request processing state machine */
1069 next_phase (rr);
1070 return GNUNET_OK;
1071}
1072
1073
1074/**
1075 * @param cls closure
1076 * @param cfg_ configuration to use
1077 * @param service the initialized service
1078 */
1079static void
1080run (void *cls,
1081 const struct GNUNET_CONFIGURATION_Handle *cfg_,
1082 struct GNUNET_SERVICE_Handle *service)
1083{
1084 char *ifc_name;
1085 char *ipv4addr;
1086 char *ipv4mask;
1087 char *ipv6addr;
1088 char *ipv6prefix;
1089 char *dns_exit;
1090 char *binary;
1091 int nortsetup;
1092
1093 cfg = cfg_;
1094 stats = GNUNET_STATISTICS_create ("dns", cfg);
1095 GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
1096 cls);
1097 dnsstub = GNUNET_DNSSTUB_start (128);
1098 /* TODO: support multiple DNS_EXIT servers being configured */
1099 /* TODO: see above TODO on using DNS server from original packet.
1100 Not sure which is best... */
1101 dns_exit = NULL;
1102 if ((GNUNET_OK !=
1103 GNUNET_CONFIGURATION_get_value_string (cfg,
1104 "dns",
1105 "DNS_EXIT",
1106 &dns_exit)) ||
1107 (GNUNET_OK !=
1108 GNUNET_DNSSTUB_add_dns_ip (dnsstub,
1109 dns_exit)))
1110 {
1111 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1112 "dns",
1113 "DNS_EXIT",
1114 _ ("need a valid IPv4 or IPv6 address\n"));
1115 GNUNET_free (dns_exit);
1116 }
1117 binary = GNUNET_OS_get_suid_binary_path (cfg, "gnunet-helper-dns");
1118
1119 if (GNUNET_YES !=
1120 GNUNET_OS_check_helper_binary (binary,
1121 GNUNET_YES,
1122 NULL)) // TODO: once we have a windows-testcase, add test parameters here
1123 {
1124 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1125 _ ("`%s' is not SUID or the path is invalid, "
1126 "will not run DNS interceptor\n"),
1127 binary);
1128 global_ret = 1;
1129 GNUNET_free (binary);
1130 return;
1131 }
1132 GNUNET_free (binary);
1133
1134 helper_argv[0] = GNUNET_strdup ("gnunet-dns");
1135 if (GNUNET_SYSERR ==
1136 GNUNET_CONFIGURATION_get_value_string (cfg,
1137 "dns",
1138 "IFNAME",
1139 &ifc_name))
1140 {
1141 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1142 "No entry 'IFNAME' in configuration!\n");
1143 GNUNET_free (binary);
1144 GNUNET_SCHEDULER_shutdown ();
1145 return;
1146 }
1147 helper_argv[1] = ifc_name;
1148 if ((GNUNET_SYSERR ==
1149 GNUNET_CONFIGURATION_get_value_string (cfg,
1150 "dns",
1151 "IPV6ADDR",
1152 &ipv6addr)))
1153 {
1154 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1155 "No entry 'IPV6ADDR' in configuration!\n");
1156 GNUNET_free (binary);
1157 GNUNET_SCHEDULER_shutdown ();
1158 return;
1159 }
1160 helper_argv[2] = ipv6addr;
1161 if (GNUNET_SYSERR ==
1162 GNUNET_CONFIGURATION_get_value_string (cfg,
1163 "dns",
1164 "IPV6PREFIX",
1165 &ipv6prefix))
1166 {
1167 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1168 "No entry 'IPV6PREFIX' in configuration!\n");
1169 GNUNET_free (binary);
1170 GNUNET_SCHEDULER_shutdown ();
1171 return;
1172 }
1173 helper_argv[3] = ipv6prefix;
1174
1175 if (GNUNET_SYSERR ==
1176 GNUNET_CONFIGURATION_get_value_string (cfg,
1177 "dns",
1178 "IPV4ADDR",
1179 &ipv4addr))
1180 {
1181 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1182 "No entry 'IPV4ADDR' in configuration!\n");
1183 GNUNET_free (binary);
1184 GNUNET_SCHEDULER_shutdown ();
1185 return;
1186 }
1187 helper_argv[4] = ipv4addr;
1188 if (GNUNET_SYSERR ==
1189 GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "IPV4MASK",
1190 &ipv4mask))
1191 {
1192 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1193 "No entry 'IPV4MASK' in configuration!\n");
1194 GNUNET_free (binary);
1195 GNUNET_SCHEDULER_shutdown ();
1196 return;
1197 }
1198 helper_argv[5] = ipv4mask;
1199
1200 nortsetup = GNUNET_CONFIGURATION_get_value_yesno (cfg, "dns",
1201 "SKIP_ROUTING_SETUP");
1202 if (GNUNET_YES == nortsetup)
1203 helper_argv[6] = GNUNET_strdup ("1");
1204 else
1205 helper_argv[6] = GNUNET_strdup ("0");
1206
1207 helper_argv[7] = NULL;
1208 hijacker = GNUNET_HELPER_start (GNUNET_NO,
1209 binary,
1210 helper_argv,
1211 &process_helper_messages,
1212 NULL, NULL);
1213 GNUNET_free (binary);
1214}
1215
1216
1217/**
1218 * Define "main" method using service macro.
1219 */
1220GNUNET_SERVICE_MAIN
1221 ("dns",
1222 GNUNET_SERVICE_OPTION_NONE,
1223 &run,
1224 &client_connect_cb,
1225 &client_disconnect_cb,
1226 NULL,
1227 GNUNET_MQ_hd_fixed_size (client_init,
1228 GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT,
1229 struct GNUNET_DNS_Register,
1230 NULL),
1231 GNUNET_MQ_hd_var_size (client_response,
1232 GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE,
1233 struct GNUNET_DNS_Response,
1234 NULL),
1235 GNUNET_MQ_handler_end ());
1236
1237
1238/* FIXME: this might need a port on systems without 'getresgid' */
1239#if HAVE_GETRESGID
1240/**
1241 * Enable use of SGID capabilities on POSIX
1242 */
1243void __attribute__ ((constructor))
1244GNUNET_DNS_init ()
1245{
1246 gid_t rgid;
1247 gid_t egid;
1248 gid_t sgid;
1249
1250 if (-1 == getresgid (&rgid,
1251 &egid,
1252 &sgid))
1253 {
1254 fprintf (stderr,
1255 "getresgid failed: %s\n",
1256 strerror (errno));
1257 }
1258 else if (sgid != rgid)
1259 {
1260 if (-1 == setregid (sgid,
1261 sgid))
1262 fprintf (stderr,
1263 "setregid failed: %s\n",
1264 strerror (errno));
1265 }
1266}
1267
1268
1269#endif
1270
1271
1272/* end of gnunet-service-dns.c */
diff --git a/src/service/dns/gnunet-zonewalk.c b/src/service/dns/gnunet-zonewalk.c
new file mode 100644
index 000000000..f4b676d6c
--- /dev/null
+++ b/src/service/dns/gnunet-zonewalk.c
@@ -0,0 +1,616 @@
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
29/**
30 * Request we should make.
31 */
32struct Request
33{
34 /**
35 * Requests are kept in a DLL.
36 */
37 struct Request *next;
38
39 /**
40 * Requests are kept in a DLL.
41 */
42 struct Request *prev;
43
44 /**
45 * Socket used to make the request, NULL if not active.
46 */
47 struct GNUNET_DNSSTUB_RequestSocket *rs;
48
49 /**
50 * Raw DNS query.
51 */
52 void *raw;
53
54 /**
55 * Number of bytes in @e raw.
56 */
57 size_t raw_len;
58
59 /**
60 * Hostname we are resolving.
61 */
62 char *hostname;
63
64 /**
65 * When did we last issue this request?
66 */
67 time_t time;
68
69 /**
70 * How often did we issue this query?
71 */
72 int issue_num;
73
74 /**
75 * random 16-bit DNS query identifier.
76 */
77 uint16_t id;
78};
79
80
81/**
82 * Context for DNS resolution.
83 */
84static struct GNUNET_DNSSTUB_Context *ctx;
85
86/**
87 * The number of queries that are outstanding
88 */
89static unsigned int pending;
90
91/**
92 * Number of lookups we performed overall.
93 */
94static unsigned int lookups;
95
96/**
97 * Number of lookups that failed.
98 */
99static unsigned int failures;
100
101/**
102 * Number of records we found.
103 */
104static unsigned int records;
105
106/**
107 * Head of DLL of all requests to perform.
108 */
109static struct Request *req_head;
110
111/**
112 * Tail of DLL of all requests to perform.
113 */
114static struct Request *req_tail;
115
116/**
117 * Main task.
118 */
119static struct GNUNET_SCHEDULER_Task *t;
120
121/**
122 * Maximum number of queries pending at the same time.
123 */
124#define THRESH 20
125
126/**
127 * TIME_THRESH is in usecs. How quickly do we submit fresh queries.
128 * Used as an additional throttle.
129 */
130#define TIME_THRESH 10
131
132/**
133 * How often do we retry a query before giving up for good?
134 */
135#define MAX_RETRIES 5
136
137
138/**
139 * We received @a rec for @a req. Remember the answer.
140 *
141 * @param req request
142 * @param rec response
143 */
144static void
145process_record (struct Request *req,
146 struct GNUNET_DNSPARSER_Record *rec)
147{
148 char buf[INET6_ADDRSTRLEN];
149
150 records++;
151 switch (rec->type)
152 {
153 case GNUNET_DNSPARSER_TYPE_A:
154 fprintf (stdout,
155 "%s A %s\n",
156 req->hostname,
157 inet_ntop (AF_INET,
158 rec->data.raw.data,
159 buf,
160 sizeof(buf)));
161 break;
162
163 case GNUNET_DNSPARSER_TYPE_AAAA:
164 fprintf (stdout,
165 "%s AAAA %s\n",
166 req->hostname,
167 inet_ntop (AF_INET6,
168 rec->data.raw.data,
169 buf,
170 sizeof(buf)));
171 break;
172
173 case GNUNET_DNSPARSER_TYPE_NS:
174 fprintf (stdout,
175 "%s NS %s\n",
176 req->hostname,
177 rec->data.hostname);
178 break;
179
180 case GNUNET_DNSPARSER_TYPE_CNAME:
181 fprintf (stdout,
182 "%s CNAME %s\n",
183 req->hostname,
184 rec->data.hostname);
185 break;
186
187 case GNUNET_DNSPARSER_TYPE_MX:
188 fprintf (stdout,
189 "%s MX %u %s\n",
190 req->hostname,
191 (unsigned int) rec->data.mx->preference,
192 rec->data.mx->mxhost);
193 break;
194
195 case GNUNET_DNSPARSER_TYPE_SOA:
196 fprintf (stdout,
197 "%s SOA %s %s %u %u %u %u %u\n",
198 req->hostname,
199 rec->data.soa->mname,
200 rec->data.soa->rname,
201 (unsigned int) rec->data.soa->serial,
202 (unsigned int) rec->data.soa->refresh,
203 (unsigned int) rec->data.soa->retry,
204 (unsigned int) rec->data.soa->expire,
205 (unsigned int) rec->data.soa->minimum_ttl);
206 break;
207
208 case GNUNET_DNSPARSER_TYPE_SRV:
209 fprintf (stdout,
210 "%s SRV %s %u %u %u\n",
211 req->hostname,
212 rec->data.srv->target,
213 rec->data.srv->priority,
214 rec->data.srv->weight,
215 rec->data.srv->port);
216 break;
217
218 case GNUNET_DNSPARSER_TYPE_URI:
219 fprintf (stdout,
220 "%s URI \"%s\" %u %u\n",
221 req->hostname,
222 rec->data.uri->target,
223 rec->data.uri->priority,
224 rec->data.uri->weight);
225 break;
226
227 case GNUNET_DNSPARSER_TYPE_PTR:
228 fprintf (stdout,
229 "%s PTR %s\n",
230 req->hostname,
231 rec->data.hostname);
232 break;
233
234 case GNUNET_DNSPARSER_TYPE_TXT:
235 fprintf (stdout,
236 "%s TXT %.*s\n",
237 req->hostname,
238 (int) rec->data.raw.data_len,
239 (char *) rec->data.raw.data);
240 break;
241
242 case GNUNET_DNSPARSER_TYPE_DNAME:
243 fprintf (stdout,
244 "%s DNAME %s\n",
245 req->hostname,
246 rec->data.hostname);
247 break;
248
249 /* obscure records */
250 case GNUNET_DNSPARSER_TYPE_AFSDB:
251 case GNUNET_DNSPARSER_TYPE_NAPTR:
252 case GNUNET_DNSPARSER_TYPE_APL:
253 case GNUNET_DNSPARSER_TYPE_DHCID:
254 case GNUNET_DNSPARSER_TYPE_HIP:
255 case GNUNET_DNSPARSER_TYPE_LOC:
256 case GNUNET_DNSPARSER_TYPE_RP:
257 case GNUNET_DNSPARSER_TYPE_TKEY:
258 case GNUNET_DNSPARSER_TYPE_TSIG:
259 case GNUNET_DNSPARSER_TYPE_TA:
260
261 /* DNSSEC */
262 case GNUNET_DNSPARSER_TYPE_DS:
263 case GNUNET_DNSPARSER_TYPE_RRSIG:
264 case GNUNET_DNSPARSER_TYPE_NSEC:
265 case GNUNET_DNSPARSER_TYPE_DNSKEY:
266 case GNUNET_DNSPARSER_TYPE_NSEC3:
267 case GNUNET_DNSPARSER_TYPE_NSEC3PARAM:
268 case GNUNET_DNSPARSER_TYPE_CDS:
269 case GNUNET_DNSPARSER_TYPE_CDNSKEY:
270
271 /* DNSSEC payload */
272 case GNUNET_DNSPARSER_TYPE_CERT:
273 case GNUNET_DNSPARSER_TYPE_SSHFP:
274 case GNUNET_DNSPARSER_TYPE_IPSECKEY:
275 case GNUNET_DNSPARSER_TYPE_TLSA:
276 case GNUNET_DNSPARSER_TYPE_SMIMEA:
277 case GNUNET_DNSPARSER_TYPE_OPENPGPKEY:
278
279 /* obsolete records */
280 case GNUNET_DNSPARSER_TYPE_SIG:
281 case GNUNET_DNSPARSER_TYPE_KEY:
282 case GNUNET_DNSPARSER_TYPE_KX:
283 {
284 char *base32;
285
286 base32 = GNUNET_STRINGS_data_to_string_alloc (rec->data.raw.data,
287 rec->data.raw.data_len);
288 fprintf (stdout,
289 "%s (%u) %s\n",
290 req->hostname,
291 rec->type,
292 base32);
293 GNUNET_free (base32);
294 }
295 break;
296
297 default:
298 fprintf (stderr,
299 "Unsupported type %u\n",
300 (unsigned int) rec->type);
301 break;
302 }
303}
304
305
306/**
307 * Function called with the result of a DNS resolution.
308 *
309 * @param cls closure with the `struct Request`
310 * @param dns dns response, never NULL
311 * @param dns_len number of bytes in @a dns
312 */
313static void
314process_result (void *cls,
315 const struct GNUNET_TUN_DnsHeader *dns,
316 size_t dns_len)
317{
318 struct Request *req = cls;
319 struct GNUNET_DNSPARSER_Packet *p;
320
321 if (NULL == dns)
322 {
323 /* stub gave up */
324 pending--;
325 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
326 "Stub gave up on DNS reply for `%s'\n",
327 req->hostname);
328 GNUNET_CONTAINER_DLL_remove (req_head,
329 req_tail,
330 req);
331 if (req->issue_num > MAX_RETRIES)
332 {
333 failures++;
334 GNUNET_free (req->hostname);
335 GNUNET_free (req->raw);
336 GNUNET_free (req);
337 return;
338 }
339 GNUNET_CONTAINER_DLL_insert_tail (req_head,
340 req_tail,
341 req);
342 req->rs = NULL;
343 return;
344 }
345 if (req->id != dns->id)
346 return;
347 pending--;
348 GNUNET_DNSSTUB_resolve_cancel (req->rs);
349 req->rs = NULL;
350 GNUNET_CONTAINER_DLL_remove (req_head,
351 req_tail,
352 req);
353 p = GNUNET_DNSPARSER_parse ((const char *) dns,
354 dns_len);
355 if (NULL == p)
356 {
357 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
358 "Failed to parse DNS reply for `%s'\n",
359 req->hostname);
360 if (req->issue_num > MAX_RETRIES)
361 {
362 failures++;
363 GNUNET_free (req->hostname);
364 GNUNET_free (req->raw);
365 GNUNET_free (req);
366 return;
367 }
368 GNUNET_CONTAINER_DLL_insert_tail (req_head,
369 req_tail,
370 req);
371 return;
372 }
373 for (unsigned int i = 0; i < p->num_answers; i++)
374 {
375 struct GNUNET_DNSPARSER_Record *rs = &p->answers[i];
376
377 process_record (req,
378 rs);
379 }
380 for (unsigned int i = 0; i < p->num_authority_records; i++)
381 {
382 struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i];
383
384 process_record (req,
385 rs);
386 }
387 for (unsigned int i = 0; i < p->num_additional_records; i++)
388 {
389 struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i];
390
391 process_record (req,
392 rs);
393 }
394 GNUNET_DNSPARSER_free_packet (p);
395 GNUNET_free (req->hostname);
396 GNUNET_free (req->raw);
397 GNUNET_free (req);
398}
399
400
401/**
402 * Submit a request to DNS unless we need to slow down because
403 * we are at the rate limit.
404 *
405 * @param req request to submit
406 * @return #GNUNET_OK if request was submitted
407 * #GNUNET_NO if request was already submitted
408 * #GNUNET_SYSERR if we are at the rate limit
409 */
410static int
411submit_req (struct Request *req)
412{
413 static struct timeval last_request;
414 struct timeval now;
415
416 if (NULL != req->rs)
417 return GNUNET_NO; /* already submitted */
418 gettimeofday (&now,
419 NULL);
420 if ((((now.tv_sec - last_request.tv_sec) == 0) &&
421 ((now.tv_usec - last_request.tv_usec) < TIME_THRESH)) ||
422 (pending >= THRESH))
423 return GNUNET_SYSERR;
424 GNUNET_assert (NULL == req->rs);
425 req->rs = GNUNET_DNSSTUB_resolve (ctx,
426 req->raw,
427 req->raw_len,
428 &process_result,
429 req);
430 GNUNET_assert (NULL != req->rs);
431 req->issue_num++;
432 last_request = now;
433 lookups++;
434 pending++;
435 req->time = time (NULL);
436 return GNUNET_OK;
437}
438
439
440/**
441 * Process as many requests as possible from the queue.
442 *
443 * @param cls NULL
444 */
445static void
446process_queue (void *cls)
447{
448 (void) cls;
449 t = NULL;
450 for (struct Request *req = req_head;
451 NULL != req;
452 req = req->next)
453 {
454 if (GNUNET_SYSERR == submit_req (req))
455 break;
456 }
457 if (NULL != req_head)
458 t = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
459 &process_queue,
460 NULL);
461 else
462 GNUNET_SCHEDULER_shutdown ();
463}
464
465
466/**
467 * Clean up and terminate the process.
468 *
469 * @param cls NULL
470 */
471static void
472do_shutdown (void *cls)
473{
474 (void) cls;
475 if (NULL != t)
476 {
477 GNUNET_SCHEDULER_cancel (t);
478 t = NULL;
479 }
480 GNUNET_DNSSTUB_stop (ctx);
481 ctx = NULL;
482}
483
484
485/**
486 * Process requests from the queue, then if the queue is
487 * not empty, try again.
488 *
489 * @param cls NULL
490 */
491static void
492run (void *cls)
493{
494 (void) cls;
495
496 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
497 NULL);
498 t = GNUNET_SCHEDULER_add_now (&process_queue,
499 NULL);
500}
501
502
503/**
504 * Add @a hostname to the list of requests to be made.
505 *
506 * @param hostname name to resolve
507 */
508static void
509queue (const char *hostname)
510{
511 struct GNUNET_DNSPARSER_Packet p;
512 struct GNUNET_DNSPARSER_Query q;
513 struct Request *req;
514 char *raw;
515 size_t raw_size;
516 int ret;
517
518 if (GNUNET_OK !=
519 GNUNET_DNSPARSER_check_name (hostname))
520 {
521 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
522 "Refusing invalid hostname `%s'\n",
523 hostname);
524 return;
525 }
526 q.name = (char *) hostname;
527 q.type = GNUNET_DNSPARSER_TYPE_NS;
528 q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
529
530 memset (&p,
531 0,
532 sizeof(p));
533 p.num_queries = 1;
534 p.queries = &q;
535 p.id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
536 UINT16_MAX);
537 ret = GNUNET_DNSPARSER_pack (&p,
538 UINT16_MAX,
539 &raw,
540 &raw_size);
541 if (GNUNET_OK != ret)
542 {
543 if (GNUNET_NO == ret)
544 GNUNET_free (raw);
545 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
546 "Failed to pack query for hostname `%s'\n",
547 hostname);
548 return;
549 }
550
551 req = GNUNET_new (struct Request);
552 req->hostname = strdup (hostname);
553 req->raw = raw;
554 req->raw_len = raw_size;
555 req->id = p.id;
556 GNUNET_CONTAINER_DLL_insert_tail (req_head,
557 req_tail,
558 req);
559}
560
561
562/**
563 * Call with IP address of resolver to query.
564 *
565 * @param argc should be 2
566 * @param argv[1] should contain IP address
567 * @return 0 on success
568 */
569int
570main (int argc,
571 char **argv)
572{
573 char hn[256];
574
575 if (2 != argc)
576 {
577 fprintf (stderr,
578 "Missing required configuration argument\n");
579 return -1;
580 }
581 ctx = GNUNET_DNSSTUB_start (256);
582 if (NULL == ctx)
583 {
584 fprintf (stderr,
585 "Failed to initialize GNUnet DNS STUB\n");
586 return 1;
587 }
588 if (GNUNET_OK !=
589 GNUNET_DNSSTUB_add_dns_ip (ctx,
590 argv[1]))
591 {
592 fprintf (stderr,
593 "Failed to use `%s' for DNS resolver\n",
594 argv[1]);
595 return 1;
596 }
597
598 while (NULL !=
599 fgets (hn,
600 sizeof(hn),
601 stdin))
602 {
603 if (strlen (hn) > 0)
604 hn[strlen (hn) - 1] = '\0'; /* eat newline */
605 queue (hn);
606 }
607 GNUNET_SCHEDULER_run (&run,
608 NULL);
609 fprintf (stderr,
610 "Did %u lookups, found %u records, %u lookups failed, %u pending on shutdown\n",
611 lookups,
612 records,
613 failures,
614 pending);
615 return 0;
616}
diff --git a/src/service/dns/meson.build b/src/service/dns/meson.build
new file mode 100644
index 000000000..81fecd32a
--- /dev/null
+++ b/src/service/dns/meson.build
@@ -0,0 +1,63 @@
1libgnunetdns_src = ['dns_api.c']
2
3gnunetservicedns_src = ['gnunet-service-dns.c']
4gnunetdnsredirector_src = ['gnunet-dns-monitor.c']
5gnunetdnsmonitor_src = ['gnunet-dns-redirector.c']
6
7configure_file(input : 'dns.conf.in',
8 output : 'dns.conf',
9 configuration : cdata,
10 install: true,
11 install_dir: pkgcfgdir)
12
13if get_option('monolith')
14 foreach p : libgnunetdns_src + gnunetservicedns_src
15 gnunet_src += 'dns/' + p
16 endforeach
17endif
18
19libgnunetdns = library('gnunetdns',
20 libgnunetdns_src,
21 soversion: '0',
22 version: '0.0.0',
23 dependencies: libgnunetutil_dep,
24 include_directories: [incdir, configuration_inc],
25 install: true,
26 install_dir: get_option('libdir'))
27libgnunetdns_dep = declare_dependency(link_with : libgnunetdns)
28pkg.generate(libgnunetdns, url: 'https://www.gnunet.org',
29 description : 'Provides API to access GNUnet\'s DNS service (to intercept and manipulate DNS queries)')
30
31executable ('gnunet-service-dns',
32 gnunetservicedns_src,
33 dependencies: [libgnunetdns_dep, libgnunetutil_dep,
34 libgnunetstatistics_dep],
35 include_directories: [incdir, configuration_inc],
36 install: true,
37 install_dir: get_option('libdir')/'gnunet'/'libexec')
38executable ('gnunet-dns-monitor',
39 gnunetdnsmonitor_src,
40 dependencies: [libgnunetdns_dep, libgnunetutil_dep, libgnunetdns_dep],
41 include_directories: [incdir, configuration_inc],
42 install: false)
43
44executable ('gnunet-dns-redirector',
45 gnunetdnsredirector_src,
46 dependencies: [libgnunetdns_dep, libgnunetutil_dep, libgnunetdns_dep],
47 include_directories: [incdir, configuration_inc],
48 install: false)
49
50if host_machine.system() == 'linux'
51 executable ('gnunet-helper-dns',
52 ['gnunet-helper-dns.c'],
53 dependencies: [libgnunetdns_dep, libgnunetutil_dep, libgnunetdns_dep],
54 include_directories: [incdir, configuration_inc],
55 install: true,
56 install_dir: get_option('libdir')/'gnunet'/'libexec')
57
58 test_dns = configure_file(input : 'test_gnunet_dns.sh',
59 output : 'test_gnunet_dns.sh',
60 copy: true)
61
62 test('test_gnunet_gns', test_dns, suite: 'dns', workdir: meson.current_build_dir())
63endif
diff --git a/src/service/dns/test_gnunet_dns.sh b/src/service/dns/test_gnunet_dns.sh
new file mode 100755
index 000000000..e0fcb711d
--- /dev/null
+++ b/src/service/dns/test_gnunet_dns.sh
@@ -0,0 +1,68 @@
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