aboutsummaryrefslogtreecommitdiff
path: root/src/dns/dnsstub.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2012-08-21 06:01:06 +0000
committerChristian Grothoff <christian@grothoff.org>2012-08-21 06:01:06 +0000
commit7dd745f0476015cbe2e80c03a7027cc259a2cef6 (patch)
tree71a692e8a19b10900dc2360173b1e8b06d1db70f /src/dns/dnsstub.c
parentde020c71e2ea9a78ad5fa2d0a6649c7bc3a86423 (diff)
downloadgnunet-7dd745f0476015cbe2e80c03a7027cc259a2cef6.tar.gz
gnunet-7dd745f0476015cbe2e80c03a7027cc259a2cef6.zip
refactoring dns service to take stub code into separate library for use in gns2dns proxy
Diffstat (limited to 'src/dns/dnsstub.c')
-rw-r--r--src/dns/dnsstub.c520
1 files changed, 520 insertions, 0 deletions
diff --git a/src/dns/dnsstub.c b/src/dns/dnsstub.c
new file mode 100644
index 000000000..383b1d699
--- /dev/null
+++ b/src/dns/dnsstub.c
@@ -0,0 +1,520 @@
1/*
2 This file is part of GNUnet.
3 (C) 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file dns/dnsstub.c
22 * @brief DNS stub resolver which sends DNS requests to an actual resolver
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_dnsstub_lib.h"
28
29/**
30 * Timeout for an external (Internet-DNS) DNS resolution
31 */
32#define REQUEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
33
34/**
35 * How many DNS sockets do we open at most at the same time?
36 * (technical socket maximum is this number x2 for IPv4+IPv6)
37 */
38#define DNS_SOCKET_MAX 128
39
40
41/**
42 * UDP socket we are using for sending DNS requests to the Internet.
43 */
44struct GNUNET_DNSSTUB_RequestSocket
45{
46
47 /**
48 * UDP socket we use for this request for IPv4
49 */
50 struct GNUNET_NETWORK_Handle *dnsout4;
51
52 /**
53 * UDP socket we use for this request for IPv6
54 */
55 struct GNUNET_NETWORK_Handle *dnsout6;
56
57 /**
58 * Function to call with result.
59 */
60 GNUNET_DNSSTUB_ResultCallback rc;
61
62 /**
63 * Closure for 'rc'.
64 */
65 void *rc_cls;
66
67 /**
68 * Task for reading from dnsout4 and dnsout6.
69 */
70 GNUNET_SCHEDULER_TaskIdentifier read_task;
71
72 /**
73 * When should this request time out?
74 */
75 struct GNUNET_TIME_Absolute timeout;
76
77 /**
78 * Address we sent the DNS request to.
79 */
80 struct sockaddr_storage addr;
81
82 /**
83 * Number of bytes in 'addr'.
84 */
85 socklen_t addrlen;
86
87};
88
89
90struct GNUNET_DNSSTUB_Context
91{
92
93 /**
94 * Array of all open sockets for DNS requests.
95 */
96 struct GNUNET_DNSSTUB_RequestSocket sockets[DNS_SOCKET_MAX];
97
98 /**
99 * IP address to use for the DNS server if we are a DNS exit service
100 * (for VPN via mesh); otherwise NULL.
101 */
102 char *dns_exit;
103};
104
105
106
107/**
108 * We're done with a GNUNET_DNSSTUB_RequestSocket, close it for now.
109 *
110 * @param rs request socket to clean up
111 */
112static void
113cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
114{
115 if (NULL != rs->dnsout4)
116 {
117 GNUNET_NETWORK_socket_close (rs->dnsout4);
118 rs->dnsout4 = NULL;
119 }
120 if (NULL != rs->dnsout6)
121 {
122 GNUNET_NETWORK_socket_close (rs->dnsout6);
123 rs->dnsout6 = NULL;
124 }
125 if (GNUNET_SCHEDULER_NO_TASK != rs->read_task)
126 {
127 GNUNET_SCHEDULER_cancel (rs->read_task);
128 rs->read_task = GNUNET_SCHEDULER_NO_TASK;
129 }
130}
131
132
133/**
134 * Open source port for sending DNS requests
135 *
136 * @param af AF_INET or AF_INET6
137 * @return GNUNET_OK on success
138 */
139static struct GNUNET_NETWORK_Handle *
140open_socket (int af)
141{
142 struct sockaddr_in a4;
143 struct sockaddr_in6 a6;
144 struct sockaddr *sa;
145 socklen_t alen;
146 struct GNUNET_NETWORK_Handle *ret;
147
148 ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
149 if (NULL == ret)
150 return NULL;
151 switch (af)
152 {
153 case AF_INET:
154 memset (&a4, 0, alen = sizeof (struct sockaddr_in));
155 sa = (struct sockaddr *) &a4;
156 break;
157 case AF_INET6:
158 memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
159 sa = (struct sockaddr *) &a6;
160 break;
161 default:
162 GNUNET_break (0);
163 GNUNET_NETWORK_socket_close (ret);
164 return NULL;
165 }
166 sa->sa_family = af;
167 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
168 sa,
169 alen))
170 {
171 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
172 _("Could not bind to any port: %s\n"),
173 STRERROR (errno));
174 GNUNET_NETWORK_socket_close (ret);
175 return NULL;
176 }
177 return ret;
178}
179
180
181/**
182 * Read a DNS response from the (unhindered) UDP-Socket
183 *
184 * @param cls socket to read from
185 * @param tc scheduler context (must be shutdown or read ready)
186 */
187static void
188read_response (void *cls,
189 const struct GNUNET_SCHEDULER_TaskContext *tc);
190
191
192/**
193 * Get a socket of the specified address family to send out a
194 * UDP DNS request to the Internet.
195 *
196 * @param af desired address family
197 * @return NULL on error (given AF not "supported")
198 */
199static struct GNUNET_DNSSTUB_RequestSocket *
200get_request_socket (struct GNUNET_DNSSTUB_Context *ctx,
201 int af)
202{
203 struct GNUNET_DNSSTUB_RequestSocket *rs;
204 struct GNUNET_NETWORK_FDSet *rset;
205
206 rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
207 DNS_SOCKET_MAX)];
208 rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
209 switch (af)
210 {
211 case AF_INET:
212 if (NULL == rs->dnsout4)
213 rs->dnsout4 = open_socket (AF_INET);
214 break;
215 case AF_INET6:
216 if (NULL == rs->dnsout6)
217 rs->dnsout6 = open_socket (AF_INET6);
218 break;
219 default:
220 return NULL;
221 }
222 if (GNUNET_SCHEDULER_NO_TASK != rs->read_task)
223 {
224 GNUNET_SCHEDULER_cancel (rs->read_task);
225 rs->read_task = GNUNET_SCHEDULER_NO_TASK;
226 }
227 if ( (NULL == rs->dnsout4) &&
228 (NULL == rs->dnsout6) )
229 return NULL;
230 rset = GNUNET_NETWORK_fdset_create ();
231 if (NULL != rs->dnsout4)
232 GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
233 if (NULL != rs->dnsout6)
234 GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
235 rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
236 REQUEST_TIMEOUT,
237 rset,
238 NULL,
239 &read_response, rs);
240 GNUNET_NETWORK_fdset_destroy (rset);
241 return rs;
242}
243
244
245/**
246 * Perform DNS resolution.
247 *
248 * @param ctx stub resolver to use
249 * @param af address family to use
250 * @param request DNS request to transmit
251 * @param request_len number of bytes in msg
252 * @param rc function to call with result
253 * @param rc_cls closure for 'rc'
254 * @return socket used for the request, NULL on error
255 */
256struct GNUNET_DNSSTUB_RequestSocket *
257GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
258 const struct sockaddr *sa,
259 socklen_t sa_len,
260 const void *request,
261 size_t request_len,
262 GNUNET_DNSSTUB_ResultCallback rc,
263 void *rc_cls)
264{
265 struct GNUNET_DNSSTUB_RequestSocket *rs;
266 struct GNUNET_NETWORK_Handle *ret;
267 int af;
268
269 af = sa->sa_family;
270 if (NULL == (rs = get_request_socket (ctx, af)))
271 return NULL;
272 if (NULL != rs->dnsout4)
273 ret = rs->dnsout4;
274 else
275 ret = rs->dnsout6;
276 GNUNET_assert (NULL != ret);
277 rs->rc = rc;
278 rs->rc_cls = rc_cls;
279 GNUNET_NETWORK_socket_sendto (ret,
280 request,
281 request_len,
282 sa,
283 sa_len);
284 return rs;
285}
286
287
288/**
289 * Perform DNS resolution using our default IP from init.
290 *
291 * @param ctx stub resolver to use
292 * @param request DNS request to transmit
293 * @param request_len number of bytes in msg
294 * @param rc function to call with result
295 * @param rc_cls closure for 'rc'
296 * @return socket used for the request, NULL on error
297 */
298struct GNUNET_DNSSTUB_RequestSocket *
299GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx,
300 const void *request,
301 size_t request_len,
302 GNUNET_DNSSTUB_ResultCallback rc,
303 void *rc_cls)
304{
305 int af;
306 struct sockaddr_in v4;
307 struct sockaddr_in6 v6;
308 struct sockaddr *so;
309 socklen_t salen;
310 struct GNUNET_NETWORK_Handle *dnsout;
311 struct GNUNET_DNSSTUB_RequestSocket *rs;
312
313 memset (&v4, 0, sizeof (v4));
314 memset (&v6, 0, sizeof (v6));
315 if (1 == inet_pton (AF_INET, ctx->dns_exit, &v4.sin_addr))
316 {
317 salen = sizeof (v4);
318 v4.sin_family = AF_INET;
319 v4.sin_port = htons (53);
320#if HAVE_SOCKADDR_IN_SIN_LEN
321 v4.sin_len = (u_char) salen;
322#endif
323 so = (struct sockaddr *) &v4;
324 af = AF_INET;
325 }
326 else if (1 == inet_pton (AF_INET6, ctx->dns_exit, &v6.sin6_addr))
327 {
328 salen = sizeof (v6);
329 v6.sin6_family = AF_INET6;
330 v6.sin6_port = htons (53);
331#if HAVE_SOCKADDR_IN_SIN_LEN
332 v6.sin6_len = (u_char) salen;
333#endif
334 so = (struct sockaddr *) &v6;
335 af = AF_INET6;
336 }
337 else
338 {
339 GNUNET_break (0);
340 return NULL;
341 }
342 if (NULL == (rs = get_request_socket (ctx, af)))
343 return NULL;
344 if (NULL != rs->dnsout4)
345 dnsout = rs->dnsout4;
346 else
347 dnsout = rs->dnsout6;
348 if (NULL == dnsout)
349 {
350 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
351 _("Configured DNS exit `%s' is not working / valid.\n"),
352 ctx->dns_exit);
353 return NULL;
354 }
355 memcpy (&rs->addr,
356 so,
357 salen);
358 rs->addrlen = salen;
359 GNUNET_NETWORK_socket_sendto (dnsout,
360 request,
361 request_len, so, salen);
362 rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
363
364 return rs;
365
366}
367
368
369/**
370 * Actually do the reading of a DNS packet from our UDP socket and see
371 * if we have a valid, matching, pending request.
372 *
373 * @param rs request socket with callback details
374 * @param dnsout socket to read from
375 * @return GNUNET_OK on success, GNUNET_NO on drop, GNUNET_SYSERR on IO-errors (closed socket)
376 */
377static int
378do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
379 struct GNUNET_NETWORK_Handle *dnsout)
380{
381 struct sockaddr_storage addr;
382 socklen_t addrlen;
383 struct GNUNET_TUN_DnsHeader *dns;
384 ssize_t r;
385 int len;
386
387#ifndef MINGW
388 if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
389 {
390 /* conservative choice: */
391 len = UINT16_MAX;
392 }
393#else
394 /* port the code above? */
395 len = UINT16_MAX;
396#endif
397
398 {
399 unsigned char buf[len] GNUNET_ALIGN;
400
401 addrlen = sizeof (addr);
402 memset (&addr, 0, sizeof (addr));
403 r = GNUNET_NETWORK_socket_recvfrom (dnsout,
404 buf, sizeof (buf),
405 (struct sockaddr*) &addr, &addrlen);
406 if (-1 == r)
407 {
408 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
409 GNUNET_NETWORK_socket_close (dnsout);
410 return GNUNET_SYSERR;
411 }
412 if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
413 {
414 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
415 _("Received DNS response that is too small (%u bytes)"),
416 r);
417 return GNUNET_NO;
418 }
419 dns = (struct GNUNET_TUN_DnsHeader *) buf;
420 if ( (addrlen != rs->addrlen) ||
421 (0 != memcmp (&rs->addr,
422 &addr,
423 addrlen)) ||
424 (0 == GNUNET_TIME_absolute_get_remaining (rs->timeout).rel_value) )
425 return GNUNET_NO;
426 rs->rc (rs->rc_cls,
427 rs,
428 dns,
429 r);
430 }
431 return GNUNET_OK;
432}
433
434
435/**
436 * Read a DNS response from the (unhindered) UDP-Socket
437 *
438 * @param cls socket to read from
439 * @param tc scheduler context (must be shutdown or read ready)
440 */
441static void
442read_response (void *cls,
443 const struct GNUNET_SCHEDULER_TaskContext *tc)
444{
445 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
446 struct GNUNET_NETWORK_FDSet *rset;
447
448 rs->read_task = GNUNET_SCHEDULER_NO_TASK;
449 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
450 {
451 /* timeout or shutdown */
452 cleanup_rs (rs);
453 return;
454 }
455 /* read and process ready sockets */
456 if ((NULL != rs->dnsout4) &&
457 (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout4)) &&
458 (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout4)))
459 rs->dnsout4 = NULL;
460 if ((NULL != rs->dnsout6) &&
461 (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) &&
462 (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout6)))
463 rs->dnsout6 = NULL;
464
465 /* re-schedule read task */
466 rset = GNUNET_NETWORK_fdset_create ();
467 if (NULL != rs->dnsout4)
468 GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
469 if (NULL != rs->dnsout6)
470 GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
471 rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
472 GNUNET_TIME_absolute_get_remaining (rs->timeout),
473 rset,
474 NULL,
475 &read_response, rs);
476 GNUNET_NETWORK_fdset_destroy (rset);
477}
478
479
480
481/**
482 * Start a DNS stub resolver.
483 *
484 * @param dns_ip target IP address to use
485 * @return NULL on error
486 */
487struct GNUNET_DNSSTUB_Context *
488GNUNET_DNSSTUB_start (const char *dns_ip)
489{
490 struct GNUNET_DNSSTUB_Context *ctx;
491
492 ctx = GNUNET_malloc (sizeof (struct GNUNET_DNSSTUB_Context));
493 if (NULL != dns_ip)
494 ctx->dns_exit = GNUNET_strdup (dns_ip);
495 return ctx;
496}
497
498
499/**
500 * Cleanup DNSSTUB resolver.
501 *
502 * @param ctx stub resolver to clean up
503 */
504void
505GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
506{
507 unsigned int i;
508
509 for (i=0;i<=UINT16_MAX;i++)
510 cleanup_rs (&ctx->sockets[i]);
511 if (NULL != ctx->dns_exit)
512 {
513 GNUNET_free (ctx->dns_exit);
514 ctx->dns_exit = NULL;
515 }
516 GNUNET_free (ctx);
517}
518
519
520/* end of dnsstub.c */