aboutsummaryrefslogtreecommitdiff
path: root/src/resolver
diff options
context:
space:
mode:
Diffstat (limited to 'src/resolver')
-rw-r--r--src/resolver/Makefile.am46
-rw-r--r--src/resolver/gnunet-service-resolver.c483
-rw-r--r--src/resolver/resolver.h63
-rw-r--r--src/resolver/resolver_api.c468
-rw-r--r--src/resolver/test_resolver_api.c229
-rw-r--r--src/resolver/test_resolver_api_data.conf5
6 files changed, 1294 insertions, 0 deletions
diff --git a/src/resolver/Makefile.am b/src/resolver/Makefile.am
new file mode 100644
index 000000000..20c07f4da
--- /dev/null
+++ b/src/resolver/Makefile.am
@@ -0,0 +1,46 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = -fprofile-arcs -ftest-coverage
9endif
10
11
12lib_LTLIBRARIES = libgnunetresolver.la
13
14libgnunetresolver_la_SOURCES = \
15 resolver_api.c resolver.h
16libgnunetresolver_la_LIBADD = \
17 $(top_builddir)/src/util/libgnunetutil.la \
18 $(GN_LIBINTL)
19libgnunetresolver_la_LDFLAGS = \
20 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
21 -version-info 0:0:0
22
23
24bin_PROGRAMS = \
25 gnunet-service-resolver
26
27gnunet_service_resolver_SOURCES = \
28 gnunet-service-resolver.c
29gnunet_service_resolver_LDADD = \
30 $(top_builddir)/src/util/libgnunetutil.la \
31 $(GN_LIBINTL)
32
33
34check_PROGRAMS = \
35 test_resolver_api
36
37TESTS = $(check_PROGRAMS)
38
39test_resolver_api_SOURCES = \
40 test_resolver_api.c
41test_resolver_api_LDADD = \
42 $(top_builddir)/src/resolver/libgnunetresolver.la \
43 $(top_builddir)/src/util/libgnunetutil.la
44
45EXTRA_DIST = \
46 test_resolver_api_data.conf
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 */
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
new file mode 100644
index 000000000..31637f01d
--- /dev/null
+++ b/src/resolver/resolver.h
@@ -0,0 +1,63 @@
1/*
2 This file is part of GNUnet.
3 (C) 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 * @author Christian Grothoff
23 * @file resolver/resolver.h
24 */
25#ifndef RESOLVER_H
26#define RESOLVER_H
27
28#include "gnunet_common.h"
29
30#define DEBUG_RESOLVER GNUNET_NO
31
32/**
33 * Request for the resolver. Followed by either
34 * the "struct sockaddr" or the 0-terminated hostname.
35 *
36 * The response will be one or more messages of type
37 * RESOLVER_RESPONSE, each with the message header
38 * immediately followed by the requested data
39 * (hostname or struct sockaddr, depending on direction).
40 * The last RESOLVER_RESPONSE will just be a header
41 * without any data (used to indicate the end of the list).
42 */
43struct GNUNET_RESOLVER_GetMessage
44{
45 /**
46 * Type: GNUNET_MESSAGE_TYPE_STATISTICS_VALUE
47 */
48 struct GNUNET_MessageHeader header;
49
50 /**
51 * GNUNET_YES to get hostname from IP,
52 * GNUNET_NO to get IP from hostname.
53 */
54 int32_t direction GNUNET_PACKED;
55
56 /**
57 * Domain to use (AF_INET, AF_INET6 or AF_UNSPEC).
58 */
59 int32_t domain GNUNET_PACKED;
60
61};
62
63#endif
diff --git a/src/resolver/resolver_api.c b/src/resolver/resolver_api.c
new file mode 100644
index 000000000..27358996a
--- /dev/null
+++ b/src/resolver/resolver_api.c
@@ -0,0 +1,468 @@
1/*
2 This file is part of GNUnet.
3 (C) 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/resolver_api.c
23 * @brief resolver for writing a tool
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_getopt_lib.h"
28#include "gnunet_client_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_resolver_service.h"
31#include "gnunet_server_lib.h"
32#include "resolver.h"
33
34
35struct GetAddressContext
36{
37 GNUNET_RESOLVER_AddressCallback callback;
38 void *cls;
39 struct GNUNET_RESOLVER_GetMessage *msg;
40 struct GNUNET_CLIENT_Connection *client;
41 struct GNUNET_TIME_Absolute timeout;
42};
43
44
45
46/**
47 * Convert IP address to string without DNS resolution.
48 */
49static char *
50no_resolve (const struct sockaddr *sa, socklen_t salen)
51{
52 char *ret;
53 char inet4[INET_ADDRSTRLEN];
54 char inet6[INET6_ADDRSTRLEN];
55
56 if (salen < sizeof (struct sockaddr))
57 return NULL;
58 switch (sa->sa_family)
59 {
60 case AF_INET:
61 if (salen != sizeof (struct sockaddr_in))
62 return NULL;
63 inet_ntop (AF_INET,
64 &((struct sockaddr_in *) sa)->sin_addr,
65 inet4, INET_ADDRSTRLEN);
66 ret = GNUNET_strdup (inet4);
67 break;
68 case AF_INET6:
69 if (salen != sizeof (struct sockaddr_in6))
70 return NULL;
71 inet_ntop (AF_INET6,
72 &((struct sockaddr_in6 *) sa)->sin6_addr,
73 inet6, INET6_ADDRSTRLEN);
74 ret = GNUNET_strdup (inet6);
75 break;
76 default:
77 ret = NULL;
78 break;
79 }
80 return ret;
81}
82
83
84static void
85handle_address_response (void *cls, const struct GNUNET_MessageHeader *msg)
86{
87 struct GetAddressContext *gac = cls;
88 uint16_t size;
89 const struct sockaddr *sa;
90 socklen_t salen;
91
92
93 if (msg == NULL)
94 {
95 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
96 _("Timeout trying to resolve hostname.\n"));
97 gac->callback (gac->cls, NULL, 0);
98 GNUNET_CLIENT_disconnect (gac->client);
99 GNUNET_free (gac);
100 return;
101 }
102 if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
103 {
104 GNUNET_break (0);
105 gac->callback (gac->cls, NULL, 0);
106 GNUNET_CLIENT_disconnect (gac->client);
107 GNUNET_free (gac);
108 return;
109 }
110
111 size = ntohs (msg->size);
112 if (size == sizeof (struct GNUNET_MessageHeader))
113 {
114#if DEBUG_RESOLVER
115 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
116 _("Received end message resolving hostname.\n"));
117#endif
118 gac->callback (gac->cls, NULL, 0);
119 GNUNET_CLIENT_disconnect (gac->client);
120 GNUNET_free (gac);
121 return;
122 }
123 sa = (const struct sockaddr *) &msg[1];
124 salen = size - sizeof (struct GNUNET_MessageHeader);
125 if (salen < sizeof (struct sockaddr))
126 {
127 GNUNET_break (0);
128 gac->callback (gac->cls, NULL, 0);
129 GNUNET_CLIENT_disconnect (gac->client);
130 GNUNET_free (gac);
131 return;
132 }
133#if DEBUG_RESOLVER
134 {
135 char *ips = no_resolve (sa, salen);
136 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s'.\n"), ips);
137 GNUNET_free (ips);
138 }
139#endif
140 gac->callback (gac->cls, sa, salen);
141 GNUNET_CLIENT_receive (gac->client,
142 &handle_address_response,
143 gac,
144 GNUNET_TIME_absolute_get_remaining (gac->timeout));
145}
146
147
148static size_t
149transmit_get_ip (void *cls, size_t size, void *buf)
150{
151 struct GetAddressContext *actx = cls;
152 uint16_t ms;
153
154 if (buf == NULL)
155 {
156 /* timeout / error */
157 GNUNET_free (actx->msg);
158 actx->callback (actx->cls, NULL, 0);
159 GNUNET_CLIENT_disconnect (actx->client);
160 GNUNET_free (actx);
161 return 0;
162 }
163 ms = ntohs (actx->msg->header.size);
164 GNUNET_assert (size >= ms);
165 memcpy (buf, actx->msg, ms);
166 GNUNET_free (actx->msg);
167 actx->msg = NULL;
168 GNUNET_CLIENT_receive (actx->client,
169 &handle_address_response,
170 actx,
171 GNUNET_TIME_absolute_get_remaining (actx->timeout));
172 return ms;
173}
174
175
176
177/**
178 * Convert a string to one or more IP addresses.
179 *
180 * @param sched scheduler to use
181 * @param cfg configuration to use
182 * @param hostname the hostname to resolve
183 * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
184 * @param callback function to call with addresses
185 * @param cls closure for callback
186 * @param timeout how long to try resolving
187 */
188void
189GNUNET_RESOLVER_ip_get (struct GNUNET_SCHEDULER_Handle *sched,
190 struct GNUNET_CONFIGURATION_Handle *cfg,
191 const char *hostname,
192 int domain,
193 struct GNUNET_TIME_Relative timeout,
194 GNUNET_RESOLVER_AddressCallback callback, void *cls)
195{
196 struct GNUNET_CLIENT_Connection *client;
197 struct GNUNET_RESOLVER_GetMessage *msg;
198 struct GetAddressContext *actx;
199 size_t slen;
200
201 slen = strlen (hostname) + 1;
202 if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
203 GNUNET_SERVER_MAX_MESSAGE_SIZE)
204 {
205 GNUNET_break (0);
206 callback (cls, NULL, 0);
207 return;
208 }
209 client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
210 if (client == NULL)
211 {
212 callback (cls, NULL, 0);
213 return;
214 }
215 msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
216 msg->header.size =
217 htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
218 msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
219 msg->direction = htonl (GNUNET_NO);
220 msg->domain = htonl (domain);
221 memcpy (&msg[1], hostname, slen);
222 actx = GNUNET_malloc (sizeof (struct GetAddressContext));
223 actx->callback = callback;
224 actx->cls = cls;
225 actx->client = client;
226 actx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
227 actx->msg = msg;
228
229#if DEBUG_RESOLVER
230 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
231 _("Resolver requests DNS resolution of hostname `%s'.\n"),
232 hostname);
233#endif
234 if (NULL ==
235 GNUNET_CLIENT_notify_transmit_ready (client,
236 slen +
237 sizeof (struct
238 GNUNET_RESOLVER_GetMessage),
239 timeout, &transmit_get_ip, actx))
240 {
241 GNUNET_free (msg);
242 GNUNET_free (actx);
243 callback (cls, NULL, 0);
244 GNUNET_CLIENT_disconnect (client);
245 return;
246 }
247}
248
249
250struct GetHostnameContext
251{
252 GNUNET_RESOLVER_HostnameCallback callback;
253 void *cls;
254 struct GNUNET_RESOLVER_GetMessage *msg;
255 struct GNUNET_CLIENT_Connection *client;
256 struct GNUNET_TIME_Absolute timeout;
257};
258
259
260static void
261handle_hostname_response (void *cls, const struct GNUNET_MessageHeader *msg)
262{
263 struct GetHostnameContext *ghc = cls;
264 uint16_t size;
265 const char *hostname;
266
267 if (msg == NULL)
268 {
269 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
270 _("Timeout trying to resolve IP address.\n"));
271 ghc->callback (ghc->cls, NULL);
272 GNUNET_CLIENT_disconnect (ghc->client);
273 GNUNET_free (ghc);
274 return;
275 }
276 size = ntohs (msg->size);
277 if (size == sizeof (struct GNUNET_MessageHeader))
278 {
279#if DEBUG_RESOLVER
280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281 _("Received end message resolving IP address.\n"));
282#endif
283 ghc->callback (ghc->cls, NULL);
284 GNUNET_CLIENT_disconnect (ghc->client);
285 GNUNET_free (ghc);
286 return;
287 }
288 hostname = (const char *) &msg[1];
289 if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
290 {
291 GNUNET_break (0);
292 ghc->callback (ghc->cls, NULL);
293 GNUNET_CLIENT_disconnect (ghc->client);
294 GNUNET_free (ghc);
295 return;
296 }
297#if DEBUG_RESOLVER
298 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
299 _("Resolver returns `%s'.\n"), hostname);
300#endif
301 ghc->callback (ghc->cls, hostname);
302 GNUNET_CLIENT_receive (ghc->client,
303 &handle_hostname_response,
304 ghc,
305 GNUNET_TIME_absolute_get_remaining (ghc->timeout));
306}
307
308
309static size_t
310transmit_get_hostname (void *cls, size_t size, void *buf)
311{
312 struct GetHostnameContext *hctx = cls;
313 uint16_t msize;
314
315 if (buf == NULL)
316 {
317 GNUNET_free (hctx->msg);
318 hctx->callback (hctx->cls, NULL);
319 GNUNET_CLIENT_disconnect (hctx->client);
320 GNUNET_free (hctx);
321 return 0;
322 }
323 msize = ntohs (hctx->msg->header.size);
324 GNUNET_assert (size >= msize);
325 memcpy (buf, hctx->msg, msize);
326 GNUNET_free (hctx->msg);
327 hctx->msg = NULL;
328 GNUNET_CLIENT_receive (hctx->client,
329 &handle_hostname_response,
330 hctx,
331 GNUNET_TIME_absolute_get_remaining (hctx->timeout));
332 return msize;
333}
334
335
336
337
338/**
339 * Get an IP address as a string.
340 *
341 * @param sched scheduler to use
342 * @param cfg configuration to use
343 * @param sa host address
344 * @param salen length of host address
345 * @param do_resolve use GNUNET_NO to return numeric hostname
346 * @param timeout how long to try resolving
347 * @param callback function to call with hostnames
348 * @param cls closure for callback
349 */
350void
351GNUNET_RESOLVER_hostname_get (struct GNUNET_SCHEDULER_Handle *sched,
352 struct GNUNET_CONFIGURATION_Handle *cfg,
353 const struct sockaddr *sa,
354 socklen_t salen,
355 int do_resolve,
356 struct GNUNET_TIME_Relative timeout,
357 GNUNET_RESOLVER_HostnameCallback callback,
358 void *cls)
359{
360 char *result;
361 struct GNUNET_CLIENT_Connection *client;
362 struct GNUNET_RESOLVER_GetMessage *msg;
363 struct GetHostnameContext *hctx;
364
365 if (GNUNET_NO == do_resolve)
366 {
367 result = no_resolve (sa, salen);
368#if DEBUG_RESOLVER
369 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
370 _("Resolver returns `%s'.\n"), result);
371#endif
372 callback (cls, result);
373 if (result != NULL)
374 {
375 GNUNET_free (result);
376 callback (cls, NULL);
377 }
378 return;
379 }
380 if (salen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
381 GNUNET_SERVER_MAX_MESSAGE_SIZE)
382 {
383 GNUNET_break (0);
384 callback (cls, NULL);
385 return;
386 }
387 client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
388 if (client == NULL)
389 {
390 callback (cls, NULL);
391 return;
392 }
393 msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
394 msg->header.size =
395 htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
396 msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
397 msg->direction = htonl (GNUNET_YES);
398 msg->domain = htonl (sa->sa_family);
399 memcpy (&msg[1], sa, salen);
400#if DEBUG_RESOLVER
401 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
402 _("Resolver requests DNS resolution of IP address.\n"));
403#endif
404 hctx = GNUNET_malloc (sizeof (struct GetHostnameContext));
405 hctx->callback = callback;
406 hctx->cls = cls;
407 hctx->client = client;
408 hctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
409 hctx->msg = msg;
410 if (NULL ==
411 GNUNET_CLIENT_notify_transmit_ready (client,
412 sizeof (struct
413 GNUNET_RESOLVER_GetMessage)
414 + salen, timeout,
415 &transmit_get_hostname, hctx))
416 {
417 GNUNET_free (msg);
418 callback (cls, NULL);
419 GNUNET_CLIENT_disconnect (client);
420 GNUNET_free (hctx);
421 }
422}
423
424/**
425 * Maximum supported length of hostname
426 */
427#define MAX_HOSTNAME 1024
428
429
430/**
431 * Resolve our hostname to an IP address.
432 *
433 * @param sched scheduler to use
434 * @param cfg configuration to use
435 * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
436 * @param callback function to call with addresses
437 * @param cls closure for callback
438 * @param timeout how long to try resolving
439 */
440void
441GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched,
442 struct GNUNET_CONFIGURATION_Handle *cfg,
443 int domain,
444 struct GNUNET_TIME_Relative timeout,
445 GNUNET_RESOLVER_AddressCallback callback,
446 void *cls)
447{
448 char hostname[MAX_HOSTNAME];
449
450 if (0 != gethostname (hostname, sizeof (hostname) - 1))
451 {
452 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR |
453 GNUNET_ERROR_TYPE_BULK, "gethostname");
454 callback (cls, NULL, 0);
455 return;
456 }
457#if DEBUG_RESOLVER
458 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
459 _("Resolving our hostname `%s'\n"), hostname);
460#endif
461 GNUNET_RESOLVER_ip_get (sched,
462 cfg, hostname, domain, timeout, callback, cls);
463}
464
465
466
467
468/* end of resolver_api.c */
diff --git a/src/resolver/test_resolver_api.c b/src/resolver/test_resolver_api.c
new file mode 100644
index 000000000..240879d73
--- /dev/null
+++ b/src/resolver/test_resolver_api.c
@@ -0,0 +1,229 @@
1/*
2 This file is part of GNUnet.
3 (C) 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 * @file resolver/test_resolver_api.c
22 * @brief testcase for resolver_api.c
23 */
24#include "platform.h"
25#include "gnunet_common.h"
26#include "gnunet_getopt_lib.h"
27#include "gnunet_os_lib.h"
28#include "gnunet_program_lib.h"
29#include "gnunet_scheduler_lib.h"
30#include "gnunet_resolver_service.h"
31#include "resolver.h"
32
33#define VERBOSE GNUNET_NO
34
35
36static void
37check_hostname (void *cls, const struct sockaddr *sa, socklen_t salen)
38{
39 char buf[INET6_ADDRSTRLEN];
40 int *ok = cls;
41
42 if (salen == 0)
43 {
44 (*ok) &= ~8;
45 return;
46 }
47 if (salen == sizeof (struct sockaddr_in))
48 {
49 struct sockaddr_in *in = (struct sockaddr_in *) sa;
50 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
51 _("Got IP address `%s' for our host.\n"),
52 inet_ntop (AF_INET, &in->sin_addr, buf, sizeof (buf)));
53 }
54 else if (salen == sizeof (struct sockaddr_in6))
55 {
56 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *) sa;
57 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
58 _("Got IP address `%s' for our host.\n"),
59 inet_ntop (AF_INET6, &in6->sin6_addr, buf, sizeof (buf)));
60 }
61 else
62 {
63 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
64 _("Got address of bogus length %u\n"), salen);
65 GNUNET_assert (0);
66 }
67}
68
69
70static void
71check_localhost_num (void *cls, const char *hostname)
72{
73 int *ok = cls;
74 if (hostname == NULL)
75 return;
76 if (0 == strcmp (hostname, "127.0.0.1"))
77 {
78#if DEBUG_RESOLVER
79 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
80 "Received correct hostname `%s'.\n", hostname);
81#endif
82 (*ok) &= ~4;
83 }
84 else
85 {
86#if DEBUG_RESOLVER
87 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
88 "Received invalid hostname `%s'.\n", hostname);
89#endif
90 GNUNET_break (0);
91 }
92}
93
94static void
95check_localhost (void *cls, const char *hostname)
96{
97 int *ok = cls;
98 if (hostname == NULL)
99 return;
100 if (0 == strcmp (hostname, "localhost"))
101 {
102#if DEBUG_RESOLVER
103 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
104 "Received correct hostname `%s'.\n", hostname);
105#endif
106 (*ok) &= ~2;
107 }
108 else
109 {
110#if DEBUG_RESOLVER
111 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
112 "Received invalid hostname `%s'.\n", hostname);
113#endif
114 GNUNET_break (0);
115 }
116}
117
118static void
119check_127 (void *cls, const struct sockaddr *sa, socklen_t salen)
120{
121 int *ok = cls;
122 const struct sockaddr_in *sai = (const struct sockaddr_in *) sa;
123
124 if (sa == NULL)
125 return;
126 GNUNET_assert (sizeof (struct sockaddr_in) == salen);
127 if (sai->sin_addr.s_addr == htonl (INADDR_LOOPBACK))
128 {
129#if DEBUG_RESOLVER
130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received correct address.\n");
131#endif
132 (*ok) &= ~1;
133 }
134 else
135 {
136#if DEBUG_RESOLVER
137 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received incorrect address.\n");
138#endif
139 GNUNET_break (0);
140 }
141}
142
143static void
144run (void *cls,
145 struct GNUNET_SCHEDULER_Handle *sched,
146 char *const *args,
147 const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg)
148{
149 struct sockaddr_in sa;
150 struct GNUNET_TIME_Relative timeout =
151 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
152 2500);
153 memset (&sa, 0, sizeof (sa));
154 sa.sin_family = AF_INET;
155 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
156 GNUNET_RESOLVER_ip_get (sched,
157 cfg,
158 "localhost", AF_INET, timeout, &check_127, cls);
159 GNUNET_RESOLVER_hostname_get (sched,
160 cfg,
161 (const struct sockaddr *) &sa,
162 sizeof (struct sockaddr),
163 GNUNET_YES, timeout, &check_localhost, cls);
164 GNUNET_RESOLVER_hostname_get (sched,
165 cfg,
166 (const struct sockaddr *) &sa,
167 sizeof (struct sockaddr),
168 GNUNET_NO,
169 timeout, &check_localhost_num, cls);
170 GNUNET_RESOLVER_hostname_resolve (sched,
171 cfg,
172 AF_UNSPEC, timeout, &check_hostname, cls);
173}
174
175static int
176check ()
177{
178 int ok = 1 + 2 + 4 + 8;
179 pid_t pid;
180 char *const argv[] = { "test-resolver-api",
181 "-c",
182 "test_resolver_api_data.conf",
183#if VERBOSE
184 "-L", "DEBUG",
185#endif
186 NULL
187 };
188 struct GNUNET_GETOPT_CommandLineOption options[] = {
189 GNUNET_GETOPT_OPTION_END
190 };
191 pid = GNUNET_OS_start_process ("gnunet-service-resolver",
192 "gnunet-service-resolver",
193#if VERBOSE
194 "-L", "DEBUG",
195#endif
196 "-c", "test_resolver_api_data.conf", NULL);
197 sleep (1);
198 GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
199 argv, "test-resolver-api", "nohelp",
200 options, &run, &ok);
201 if (0 != PLIBC_KILL (pid, SIGTERM))
202 {
203 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
204 ok = 1;
205 }
206 waitpid (pid, NULL, 0);
207 if (ok != 0)
208 fprintf (stderr, "Missed some resolutions: %u\n", ok);
209 return ok;
210}
211
212int
213main (int argc, char *argv[])
214{
215 int ret;
216
217 GNUNET_log_setup ("test-resolver-api",
218#if VERBOSE
219 "DEBUG",
220#else
221 "WARNING",
222#endif
223 NULL);
224 ret = check ();
225
226 return ret;
227}
228
229/* end of test_resolver_api.c */
diff --git a/src/resolver/test_resolver_api_data.conf b/src/resolver/test_resolver_api_data.conf
new file mode 100644
index 000000000..c31668117
--- /dev/null
+++ b/src/resolver/test_resolver_api_data.conf
@@ -0,0 +1,5 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-statistics/
3
4[resolver]
5PORT = 22354