aboutsummaryrefslogtreecommitdiff
path: root/src/resolver/gnunet-service-resolver.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/resolver/gnunet-service-resolver.c')
-rw-r--r--src/resolver/gnunet-service-resolver.c483
1 files changed, 483 insertions, 0 deletions
diff --git a/src/resolver/gnunet-service-resolver.c b/src/resolver/gnunet-service-resolver.c
new file mode 100644
index 000000000..dbdecdfe5
--- /dev/null
+++ b/src/resolver/gnunet-service-resolver.c
@@ -0,0 +1,483 @@
1/*
2 This file is part of GNUnet.
3 (C) 2007, 2008, 2009 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 2, 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/**
22 * @file resolver/gnunet-service-resolver.c
23 * @brief code to do DNS resolution
24 * @author Christian Grothoff
25 */
26
27#include <stdlib.h>
28#include "platform.h"
29#include "gnunet_disk_lib.h"
30#include "gnunet_getopt_lib.h"
31#include "gnunet_protocols.h"
32#include "gnunet_service_lib.h"
33#include "gnunet_statistics_service.h"
34#include "gnunet_strings_lib.h"
35#include "gnunet_time_lib.h"
36#include "resolver.h"
37
38
39struct IPCache
40{
41 struct IPCache *next;
42 char *addr;
43 struct sockaddr *sa;
44 struct GNUNET_TIME_Absolute last_refresh;
45 struct GNUNET_TIME_Absolute last_request;
46 unsigned int salen;
47};
48
49
50static struct IPCache *head;
51
52
53
54
55#if HAVE_GETNAMEINFO
56static void
57getnameinfo_resolve (struct IPCache *cache)
58{
59 char hostname[256];
60
61 if (0 == getnameinfo (cache->sa, cache->salen, hostname, 255, NULL, 0, 0))
62 cache->addr = GNUNET_strdup (hostname);
63}
64#endif
65
66
67#if HAVE_GETHOSTBYADDR
68static void
69gethostbyaddr_resolve (struct IPCache *cache)
70{
71 struct hostent *ent;
72
73 switch (cache->sa->sa_family)
74 {
75 case AF_INET:
76 ent = gethostbyaddr (&((struct sockaddr_in *) cache->sa)->sin_addr,
77 sizeof (struct in_addr), AF_INET);
78 break;
79 case AF_INET6:
80 ent = gethostbyaddr (&((struct sockaddr_in6 *) cache->sa)->sin6_addr,
81 sizeof (struct in6_addr), AF_INET6);
82 break;
83 default:
84 ent = NULL;
85 }
86 if (ent != NULL)
87 cache->addr = GNUNET_strdup (ent->h_name);
88}
89#endif
90
91
92static void
93cache_resolve (struct IPCache *cache)
94{
95#if HAVE_GETNAMEINFO
96 if (cache->addr == NULL)
97 getnameinfo_resolve (cache);
98#endif
99#if HAVE_GETHOSTBYADDR
100 if (cache->addr == NULL)
101 gethostbyaddr_resolve (cache);
102#endif
103}
104
105
106
107/**
108 * Get an IP address as a string (works for both IPv4 and IPv6). Note
109 * that the resolution happens asynchronously and that the first call
110 * may not immediately result in the FQN (but instead in a
111 * human-readable IP address).
112 *
113 * @param sa should be of type "struct sockaddr*"
114 */
115static void
116get_ip_as_string (struct GNUNET_SERVER_Client *client,
117 const struct sockaddr *sav, socklen_t salen)
118{
119 struct IPCache *cache;
120 struct IPCache *prev;
121 struct GNUNET_TIME_Absolute now;
122 struct GNUNET_SERVER_TransmitContext *tc;
123
124 if (salen < sizeof (struct sockaddr))
125 {
126 GNUNET_break (0);
127 return;
128 }
129 now = GNUNET_TIME_absolute_get ();
130 cache = head;
131 prev = NULL;
132 while ((cache != NULL) &&
133 ((cache->salen != salen) || (0 != memcmp (cache->sa, sav, salen))))
134 {
135 if (GNUNET_TIME_absolute_get_duration (cache->last_request).value <
136 60 * 60 * 1000)
137 {
138 if (prev != NULL)
139 {
140 prev->next = cache->next;
141 GNUNET_free_non_null (cache->addr);
142 GNUNET_free (cache->sa);
143 GNUNET_free (cache);
144 cache = prev->next;
145 }
146 else
147 {
148 head = cache->next;
149 GNUNET_free_non_null (cache->addr);
150 GNUNET_free (cache->sa);
151 GNUNET_free (cache);
152 cache = head;
153 }
154 continue;
155 }
156 prev = cache;
157 cache = cache->next;
158 }
159 if (cache != NULL)
160 {
161 cache->last_request = now;
162 if (GNUNET_TIME_absolute_get_duration (cache->last_request).value <
163 60 * 60 * 1000)
164 {
165 GNUNET_free_non_null (cache->addr);
166 cache->addr = NULL;
167 cache->salen = 0;
168 cache_resolve (cache);
169 }
170 }
171 else
172 {
173 cache = GNUNET_malloc (sizeof (struct IPCache));
174 cache->next = head;
175 cache->salen = salen;
176 cache->sa = GNUNET_malloc (salen);
177 memcpy (cache->sa, sav, salen);
178 cache->last_request = GNUNET_TIME_absolute_get ();
179 cache->last_refresh = GNUNET_TIME_absolute_get ();
180 cache->addr = NULL;
181 cache_resolve (cache);
182 head = cache;
183 }
184 tc = GNUNET_SERVER_transmit_context_create (client);
185 if (cache->addr != NULL)
186 GNUNET_SERVER_transmit_context_append (tc,
187 cache->addr,
188 strlen (cache->addr) + 1,
189 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
190 GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
191 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
192 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
193}
194
195
196#if HAVE_GETADDRINFO
197static int
198getaddrinfo_resolve (struct GNUNET_SERVER_TransmitContext *tc,
199 const char *hostname, int domain)
200{
201 int s;
202 struct addrinfo hints;
203 struct addrinfo *result;
204 struct addrinfo *pos;
205
206 memset (&hints, 0, sizeof (struct addrinfo));
207// FIXME in PlibC
208#ifndef MINGW
209 hints.ai_family = domain;
210#else
211 hints.ai_family = AF_INET;
212#endif
213 hints.ai_socktype = SOCK_STREAM; /* go for TCP */
214
215 if (0 != (s = getaddrinfo (hostname, NULL, &hints, &result)))
216 {
217 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
218 _("Could not resolve `%s' (%s): %s\n"), hostname,
219 (domain ==
220 AF_INET) ? "IPv4" : ((domain ==
221 AF_INET6) ? "IPv6" : "any"),
222 gai_strerror (s));
223 if ((s == EAI_BADFLAGS) || (s == EAI_MEMORY) || (s == EAI_SYSTEM))
224 return GNUNET_NO; /* other function may still succeed */
225 return GNUNET_SYSERR;
226 }
227 if (result == NULL)
228 return GNUNET_SYSERR;
229 pos = result;
230 while (pos != NULL)
231 {
232 GNUNET_SERVER_transmit_context_append (tc,
233 result->ai_addr,
234 result->ai_addrlen,
235 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
236 pos = pos->ai_next;
237 }
238 freeaddrinfo (result);
239 return GNUNET_OK;
240}
241#endif
242
243#if HAVE_GETHOSTBYNAME2
244static int
245gethostbyname2_resolve (struct GNUNET_SERVER_TransmitContext *tc,
246 const char *hostname, int domain)
247{
248 struct hostent *hp;
249 struct sockaddr_in a4;
250 struct sockaddr_in6 a6;
251 int ret1;
252 int ret2;
253
254 if (domain == AF_UNSPEC)
255 {
256 ret1 = gethostbyname2_resolve (tc, hostname, AF_INET);
257 ret2 = gethostbyname2_resolve (tc, hostname, AF_INET6);
258 if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK))
259 return GNUNET_OK;
260 if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR))
261 return GNUNET_SYSERR;
262 return GNUNET_NO;
263 }
264 hp = gethostbyname2 (hostname, domain);
265 if (hp == NULL)
266 {
267 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
268 _("Could not find IP of host `%s': %s\n"),
269 hostname, hstrerror (h_errno));
270 return GNUNET_SYSERR;
271 }
272 GNUNET_assert (hp->h_addrtype == domain);
273 if (domain == AF_INET)
274 {
275 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
276 memset (&a4, 0, sizeof (a4));
277 a4.sin_family = AF_INET;
278 memcpy (&a4.sin_addr, hp->h_addr_list[0], hp->h_length);
279 GNUNET_SERVER_transmit_context_append (tc,
280 &a4,
281 sizeof (a4),
282 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
283 }
284 else
285 {
286 GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
287 memset (&a6, 0, sizeof (a6));
288 a6.sin6_family = AF_INET6;
289 memcpy (&a6.sin6_addr, hp->h_addr_list[0], hp->h_length);
290 GNUNET_SERVER_transmit_context_append (tc,
291 &a6,
292 sizeof (a6),
293 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
294 }
295 return GNUNET_OK;
296}
297#endif
298
299#if HAVE_GETHOSTBYNAME
300static int
301gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc,
302 const char *hostname)
303{
304 struct hostent *hp;
305 struct sockaddr_in addr;
306
307 hp = GETHOSTBYNAME (hostname);
308 if (hp == NULL)
309 {
310 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
311 _("Could not find IP of host `%s': %s\n"),
312 hostname, hstrerror (h_errno));
313 return GNUNET_SYSERR;
314 }
315 if (hp->h_addrtype != AF_INET)
316 {
317 GNUNET_break (0);
318 return GNUNET_SYSERR;
319 }
320 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
321 memset (&addr, 0, sizeof (addr));
322 addr.sin_family = AF_INET;
323 memcpy (&addr.sin_addr, hp->h_addr_list[0], hp->h_length);
324 GNUNET_SERVER_transmit_context_append (tc,
325 &addr,
326 sizeof (addr),
327 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
328 return GNUNET_OK;
329}
330#endif
331
332
333/**
334 * Convert a string to an IP address.
335 *
336 * @param client where to send the IP address
337 * @param hostname the hostname to resolve
338 * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
339 */
340static void
341get_ip_from_hostname (struct GNUNET_SERVER_Client *client,
342 const char *hostname, int domain)
343{
344 int ret;
345 struct GNUNET_SERVER_TransmitContext *tc;
346
347 tc = GNUNET_SERVER_transmit_context_create (client);
348 ret = GNUNET_NO;
349#if HAVE_GETADDRINFO
350 if (ret == GNUNET_NO)
351 ret = getaddrinfo_resolve (tc, hostname, domain);
352#endif
353#if HAVE_GETHOSTBYNAME2
354 if (ret == GNUNET_NO)
355 ret = gethostbyname2_resolve (tc, hostname, domain);
356#endif
357#if HAVE_GETHOSTBYNAME
358 if ((ret == GNUNET_NO) && ((domain == AF_UNSPEC) || (domain == PF_INET)))
359 gethostbyname_resolve (tc, hostname);
360#endif
361 GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
362 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
363 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
364}
365
366
367/**
368 * Handle GET-message.
369 *
370 * @param cls closure
371 * @param server the server handling the message
372 * @param client identification of the client
373 * @param message the actual message
374 */
375static void
376handle_get (void *cls,
377 struct GNUNET_SERVER_Handle *server,
378 struct GNUNET_SERVER_Client *client,
379 const struct GNUNET_MessageHeader *message)
380{
381 uint16_t msize;
382 const struct GNUNET_RESOLVER_GetMessage *msg;
383 const char *hostname;
384 uint16_t size;
385 int direction;
386 int domain;
387
388 msize = ntohs (message->size);
389 if (msize < sizeof (struct GNUNET_RESOLVER_GetMessage))
390 {
391 GNUNET_break (0);
392 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
393 return;
394 }
395 msg = (const struct GNUNET_RESOLVER_GetMessage *) message;
396 size = msize - sizeof (struct GNUNET_RESOLVER_GetMessage);
397 direction = ntohl (msg->direction);
398 domain = ntohl (msg->domain);
399 if (direction == GNUNET_NO)
400 {
401 /* IP from hostname */
402 hostname = (const char *) &msg[1];
403 if (hostname[size - 1] != '\0')
404 {
405 GNUNET_break (0);
406 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
407 return;
408 }
409#if DEBUG_RESOLVER
410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411 _("Resolver asked to look up `%s'.\n"), hostname);
412#endif
413 get_ip_from_hostname (client, hostname, domain);
414 }
415 else
416 {
417#if DEBUG_RESOLVER
418 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
419 _("Resolver asked to look up IP address.\n"));
420#endif
421 get_ip_as_string (client, (const struct sockaddr *) &msg[1], size);
422 }
423}
424
425
426/**
427 * List of handlers for the messages understood by this
428 * service.
429 */
430static struct GNUNET_SERVER_MessageHandler handlers[] = {
431 {&handle_get, NULL, GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST, 0},
432 {NULL, NULL, 0, 0}
433};
434
435
436/**
437 * Process statistics requests.
438 *
439 * @param cls closure
440 * @param sched scheduler to use
441 * @param server the initialized server
442 * @param cfg configuration to use
443 */
444static void
445run (void *cls,
446 struct GNUNET_SCHEDULER_Handle *sched,
447 struct GNUNET_SERVER_Handle *server,
448 struct GNUNET_CONFIGURATION_Handle *cfg)
449{
450 GNUNET_SERVER_add_handlers (server, handlers);
451}
452
453
454/**
455 * The main function for the resolver service.
456 *
457 * @param argc number of arguments from the command line
458 * @param argv command line arguments
459 * @return 0 ok, 1 on error
460 */
461int
462main (int argc, char *const *argv)
463{
464 int ret;
465 struct IPCache *pos;
466
467 ret = (GNUNET_OK ==
468 GNUNET_SERVICE_run (argc,
469 argv,
470 "resolver", &run, NULL, NULL, NULL)) ? 0 : 1;
471
472 while (head != NULL)
473 {
474 pos = head->next;
475 GNUNET_free_non_null (head->addr);
476 GNUNET_free (head->sa);
477 GNUNET_free (head);
478 head = pos;
479 }
480 return ret;
481}
482
483/* end of gnunet-service-resolver.c */