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