aboutsummaryrefslogtreecommitdiff
path: root/src/util/gnunet-service-resolver.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/gnunet-service-resolver.c')
-rw-r--r--src/util/gnunet-service-resolver.c1161
1 files changed, 512 insertions, 649 deletions
diff --git a/src/util/gnunet-service-resolver.c b/src/util/gnunet-service-resolver.c
index d90d8ec10..d5d608b2f 100644
--- a/src/util/gnunet-service-resolver.c
+++ b/src/util/gnunet-service-resolver.c
@@ -27,721 +27,547 @@
27#include "gnunet_statistics_service.h" 27#include "gnunet_statistics_service.h"
28#include "resolver.h" 28#include "resolver.h"
29 29
30
31struct Record
32{
33 struct Record *next;
34
35 struct Record *prev;
36
37 struct GNUNET_DNSPARSER_Record *record;
38};
39
30/** 40/**
31 * A cached DNS lookup result (for reverse lookup). 41 * A cached DNS lookup result.
32 */ 42 */
33struct IPCache 43struct ResolveCache
34{ 44{
35 /** 45 /**
36 * This is a doubly linked list. 46 * This is a doubly linked list.
37 */ 47 */
38 struct IPCache *next; 48 struct ResolveCache *next;
39 49
40 /** 50 /**
41 * This is a doubly linked list. 51 * This is a doubly linked list.
42 */ 52 */
43 struct IPCache *prev; 53 struct ResolveCache *prev;
44 54
45 /** 55 /**
46 * Hostname in human-readable form. 56 * type of queried DNS record
47 */ 57 */
48 char *addr; 58 uint16_t record_type;
49 59
50 /** 60 /**
51 * Binary IP address, allocated at the end of this struct. 61 * a pointer to the request_id if a query for this hostname/record_type
62 * is currently pending, NULL otherwise.
52 */ 63 */
53 const void *ip; 64 int16_t *request_id;
65
66 struct GNUNET_SERVICE_Client *client;
54 67
55 /** 68 /**
56 * Last time this entry was updated. 69 * head of a double linked list containing the lookup results
57 */ 70 */
58 struct GNUNET_TIME_Absolute last_refresh; 71 struct Record *records_head;
59 72
60 /** 73 /**
61 * Last time this entry was requested. 74 * tail of a double linked list containing the lookup results
62 */ 75 */
63 struct GNUNET_TIME_Absolute last_request; 76 struct Record *records_tail;
64 77
65 /** 78 /**
66 * Number of bytes in ip. 79 * handle for cancelling a request
67 */ 80 */
68 size_t ip_len; 81 struct GNUNET_DNSSTUB_RequestSocket *resolve_handle;
69 82
70 /** 83 /**
71 * Address family of the IP. 84 * handle for the resolution timeout task
72 */ 85 */
73 int af; 86 struct GNUNET_SCHEDULER_Task *timeout_task;
87
74}; 88};
75 89
76 90
77/** 91/**
78 * Start of the linked list of cached DNS lookup results. 92 * Start of the linked list of cached DNS lookup results.
79 */ 93 */
80static struct IPCache *cache_head; 94static struct ResolveCache *cache_head;
81 95
82/** 96/**
83 * Tail of the linked list of cached DNS lookup results. 97 * Tail of the linked list of cached DNS lookup results.
84 */ 98 */
85static struct IPCache *cache_tail; 99static struct ResolveCache *cache_tail;
86 100
87/** 101/**
88 * Pipe for asynchronously notifying about resolve result 102 * context of dnsstub library
89 */ 103 */
90static struct GNUNET_DISK_PipeHandle *resolve_result_pipe; 104static struct GNUNET_DNSSTUB_Context *dnsstub_ctx;
91
92/**
93 * Task for reading from resolve_result_pipe
94 */
95static struct GNUNET_SCHEDULER_Task *resolve_result_pipe_task;
96 105
97 106
98#if HAVE_GETNAMEINFO 107void free_cache_entry (struct ResolveCache *entry)
99/**
100 * Resolve the given request using getnameinfo
101 *
102 * @param cache the request to resolve (and where to store the result)
103 */
104static void
105getnameinfo_resolve (struct IPCache *cache)
106{ 108{
107 char hostname[256]; 109 struct Record *pos;
108 const struct sockaddr *sa; 110 struct Record *next;
109 struct sockaddr_in v4; 111
110 struct sockaddr_in6 v6; 112 next = entry->records_head;
111 size_t salen; 113 while (NULL != (pos = next))
112 int ret;
113
114 switch (cache->af)
115 { 114 {
116 case AF_INET: 115 next = pos->next;
117 GNUNET_assert (cache->ip_len == sizeof (struct in_addr)); 116 GNUNET_CONTAINER_DLL_remove (entry->records_head,
118 sa = (const struct sockaddr*) &v4; 117 entry->records_tail,
119 memset (&v4, 0, sizeof (v4)); 118 pos);
120 v4.sin_addr = * (const struct in_addr*) cache->ip; 119 if (NULL != pos->record)
121 v4.sin_family = AF_INET; 120 {
122#if HAVE_SOCKADDR_IN_SIN_LEN 121 GNUNET_DNSPARSER_free_record (pos->record);
123 v4.sin_len = sizeof (v4); 122 GNUNET_free (pos->record);
124#endif 123 }
125 salen = sizeof (v4); 124 GNUNET_free (pos);
126 break;
127 case AF_INET6:
128 GNUNET_assert (cache->ip_len == sizeof (struct in6_addr));
129 sa = (const struct sockaddr*) &v6;
130 memset (&v6, 0, sizeof (v6));
131 v6.sin6_addr = * (const struct in6_addr*) cache->ip;
132 v6.sin6_family = AF_INET6;
133#if HAVE_SOCKADDR_IN_SIN_LEN
134 v6.sin6_len = sizeof (v6);
135#endif
136 salen = sizeof (v6);
137 break;
138 default:
139 GNUNET_assert (0);
140 } 125 }
141 126 if (NULL != entry->resolve_handle)
142 if (0 ==
143 (ret = getnameinfo (sa, salen,
144 hostname, sizeof (hostname),
145 NULL,
146 0, 0)))
147 { 127 {
148 cache->addr = GNUNET_strdup (hostname); 128 GNUNET_DNSSTUB_resolve_cancel (entry->resolve_handle);
129 entry->resolve_handle = NULL;
149 } 130 }
150 else 131 if (NULL != entry->timeout_task)
151 { 132 {
152 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 133 GNUNET_SCHEDULER_cancel (entry->timeout_task);
153 "getnameinfo failed: %s\n", 134 entry->timeout_task = NULL;
154 gai_strerror (ret));
155 } 135 }
136 GNUNET_free_non_null (entry->request_id);
137 GNUNET_free (entry);
156} 138}
157#endif
158 139
159 140
160#if HAVE_GETHOSTBYADDR 141static char*
142extract_dns_server (const char* line, size_t line_len)
143{
144 if (0 == strncmp (line, "nameserver ", 11))
145 return GNUNET_strndup (line + 11, line_len - 11);
146 return NULL;
147}
148
149
161/** 150/**
162 * Resolve the given request using gethostbyaddr 151 * reads the list of nameservers from /etc/resolve.conf
163 * 152 *
164 * @param cache the request to resolve (and where to store the result) 153 * @param server_addrs[out] a list of null-terminated server address strings
154 * @return the number of server addresses in @server_addrs, -1 on error
165 */ 155 */
166static void 156static ssize_t
167gethostbyaddr_resolve (struct IPCache *cache) 157lookup_dns_servers (char ***server_addrs)
168{ 158{
169 struct hostent *ent; 159 struct GNUNET_DISK_FileHandle *fh;
170 160 char buf[2048];
171 ent = gethostbyaddr (cache->ip, 161 ssize_t bytes_read;
172 cache->ip_len, 162 size_t read_offset = 0;
173 cache->af); 163 unsigned int num_dns_servers = 0;
174 if (NULL != ent) 164
165 fh = GNUNET_DISK_file_open ("/etc/resolv.conf",
166 GNUNET_DISK_OPEN_READ,
167 GNUNET_DISK_PERM_NONE);
168 if (NULL == fh)
175 { 169 {
176 cache->addr = GNUNET_strdup (ent->h_name); 170 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
171 "Could not open /etc/resolv.conf. "
172 "DNS resolution will not be possible.\n");
173 return -1;
177 } 174 }
178 else 175 bytes_read = GNUNET_DISK_file_read (fh,
176 buf,
177 sizeof (buf));
178 *server_addrs = NULL;
179 while (read_offset < bytes_read)
179 { 180 {
180 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 181 char *newline;
181 "gethostbyaddr failed: %s\n", 182 size_t line_len;
182 hstrerror (h_errno)); 183 char *dns_server;
184
185 newline = strchr (buf + read_offset, '\n');
186 if (NULL == newline)
187 {
188 break;
189 }
190 line_len = newline - buf - read_offset;
191 dns_server = extract_dns_server (buf + read_offset, line_len);
192 if (NULL != dns_server)
193 {
194 GNUNET_array_append (*server_addrs,
195 num_dns_servers,
196 dns_server);
197 }
198 read_offset += line_len + 1;
183 } 199 }
200 GNUNET_DISK_file_close (fh);
201 return num_dns_servers;
184} 202}
185#endif
186 203
187 204
188/** 205static char *
189 * Resolve the given request using the available methods. 206make_reverse_hostname (const void *ip, int af)
190 *
191 * @param cache the request to resolve (and where to store the result)
192 */
193static void
194cache_resolve (struct IPCache *cache)
195{ 207{
196#if HAVE_GETNAMEINFO 208 char *buf = GNUNET_new_array (80, char);
197 if (NULL == cache->addr) 209 int pos = 0;
198 getnameinfo_resolve (cache); 210 if (AF_INET == af)
199#endif 211 {
200#if HAVE_GETHOSTBYADDR 212 struct in_addr *addr = (struct in_addr *)ip;
201 if (NULL == cache->addr) 213 uint32_t ip_int = addr->s_addr;
202 gethostbyaddr_resolve (cache); 214 for (int i = 3; i >= 0; i--)
203#endif 215 {
216 int n = GNUNET_snprintf (buf + pos,
217 80 - pos,
218 "%u.",
219 ((uint8_t *)&ip_int)[i]);
220 if (n < 0)
221 {
222 GNUNET_free (buf);
223 return NULL;
224 }
225 pos += n;
226 }
227 pos += GNUNET_snprintf (buf + pos, 80 - pos, "in-addr.arpa");
228 }
229 else if (AF_INET6 == af)
230 {
231 struct in6_addr *addr = (struct in6_addr *)ip;
232 for (int i = 15; i >= 0; i--)
233 {
234 int n = GNUNET_snprintf (buf + pos, 80 - pos, "%x.", addr->s6_addr[i] & 0xf);
235 if (n < 0)
236 {
237 GNUNET_free (buf);
238 return NULL;
239 }
240 pos += n;
241 n = GNUNET_snprintf (buf + pos, 80 - pos, "%x.", addr->s6_addr[i] >> 4);
242 if (n < 0)
243 {
244 GNUNET_free (buf);
245 return NULL;
246 }
247 pos += n;
248 }
249 pos += GNUNET_snprintf (buf + pos, 80 - pos, "ip6.arpa");
250 }
251 buf[pos] = '\0';
252 return buf;
204} 253}
205 254
206 255
207/**
208 * Function called after the replies for the request have all
209 * been transmitted to the client, and we can now read the next
210 * request from the client.
211 *
212 * @param cls the `struct GNUNET_SERVICE_Client` to continue with
213 */
214static void 256static void
215notify_service_client_done (void *cls) 257send_reply (struct GNUNET_DNSPARSER_Record *record,
258 uint16_t request_id,
259 struct GNUNET_SERVICE_Client *client)
216{ 260{
217 struct GNUNET_SERVICE_Client *client = cls;
218
219 GNUNET_SERVICE_client_continue (client);
220}
221
222
223/**
224 * Get an IP address as a string (works for both IPv4 and IPv6). Note
225 * that the resolution happens asynchronously and that the first call
226 * may not immediately result in the FQN (but instead in a
227 * human-readable IP address).
228 *
229 * @param client handle to the client making the request (for sending the reply)
230 * @param af AF_INET or AF_INET6
231 * @param ip `struct in_addr` or `struct in6_addr`
232 */
233static void
234get_ip_as_string (struct GNUNET_SERVICE_Client *client,
235 int af,
236 const void *ip,
237 uint32_t request_id)
238{
239 struct IPCache *pos;
240 struct IPCache *next;
241 struct GNUNET_TIME_Absolute now;
242 struct GNUNET_MQ_Envelope *env;
243 struct GNUNET_MQ_Handle *mq;
244 struct GNUNET_RESOLVER_ResponseMessage *msg; 261 struct GNUNET_RESOLVER_ResponseMessage *msg;
245 size_t ip_len; 262 struct GNUNET_MQ_Envelope *env;
246 struct in6_addr ix; 263 void *payload;
247 size_t alen; 264 size_t payload_len;
248 265
249 switch (af) 266 switch (record->type)
250 {
251 case AF_INET:
252 ip_len = sizeof (struct in_addr);
253 break;
254 case AF_INET6:
255 ip_len = sizeof (struct in6_addr);
256 break;
257 default:
258 GNUNET_assert (0);
259 }
260 now = GNUNET_TIME_absolute_get ();
261 next = cache_head;
262 while ( (NULL != (pos = next)) &&
263 ( (pos->af != af) ||
264 (pos->ip_len != ip_len) ||
265 (0 != memcmp (pos->ip, ip, ip_len))) )
266 { 267 {
267 next = pos->next; 268 case GNUNET_DNSPARSER_TYPE_PTR:
268 if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us <
269 60 * 60 * 1000 * 1000LL)
270 { 269 {
271 GNUNET_CONTAINER_DLL_remove (cache_head, 270 char *hostname = record->data.hostname;
272 cache_tail, 271 payload = hostname;
273 pos); 272 payload_len = strlen (hostname) + 1;
274 GNUNET_free_non_null (pos->addr); 273 break;
275 GNUNET_free (pos);
276 continue;
277 } 274 }
278 } 275 case GNUNET_DNSPARSER_TYPE_A:
279 if (NULL != pos) 276 case GNUNET_DNSPARSER_TYPE_AAAA:
280 {
281 if ( (1 == inet_pton (af,
282 pos->ip,
283 &ix)) &&
284 (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us >
285 120 * 1000 * 1000LL) )
286 { 277 {
287 /* try again if still numeric AND 2 minutes have expired */ 278 payload = record->data.raw.data;
288 GNUNET_free_non_null (pos->addr); 279 payload_len = record->data.raw.data_len;
289 pos->addr = NULL; 280 break;
290 cache_resolve (pos); 281 }
291 pos->last_request = now; 282 default:
283 {
284 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
285 "Cannot handle DNS response type: unimplemented\n");
286 return;
292 } 287 }
293 } 288 }
294 else
295 {
296 pos = GNUNET_malloc (sizeof (struct IPCache) + ip_len);
297 pos->ip = &pos[1];
298 GNUNET_memcpy (&pos[1],
299 ip,
300 ip_len);
301 pos->last_request = now;
302 pos->last_refresh = now;
303 pos->ip_len = ip_len;
304 pos->af = af;
305 GNUNET_CONTAINER_DLL_insert (cache_head,
306 cache_tail,
307 pos);
308 cache_resolve (pos);
309 }
310 if (NULL != pos->addr)
311 alen = strlen (pos->addr) + 1;
312 else
313 alen = 0;
314 mq = GNUNET_SERVICE_client_get_mq (client);
315 env = GNUNET_MQ_msg_extra (msg, 289 env = GNUNET_MQ_msg_extra (msg,
316 alen, 290 payload_len,
317 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); 291 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
318 msg->id = request_id; 292 msg->id = request_id;
319 GNUNET_memcpy (&msg[1], 293 GNUNET_memcpy (&msg[1],
320 pos->addr, 294 payload,
321 alen); 295 payload_len);
322 GNUNET_MQ_send (mq, 296 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
323 env); 297 env);
324 // send end message
325 env = GNUNET_MQ_msg (msg,
326 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
327 msg->id = request_id;
328 GNUNET_MQ_notify_sent (env,
329 &notify_service_client_done,
330 client);
331 GNUNET_MQ_send (mq,
332 env);
333} 298}
334 299
335 300
336#if HAVE_GETADDRINFO_A
337struct AsyncCls
338{
339 struct gaicb *host;
340 struct sigevent *sig;
341 struct GNUNET_MQ_Handle *mq;
342 uint32_t request_id;
343};
344
345
346static void 301static void
347resolve_result_pipe_cb (void *cls) 302send_end_msg (uint16_t request_id,
303 struct GNUNET_SERVICE_Client *client)
348{ 304{
349 struct AsyncCls *async_cls;
350 struct gaicb *host;
351 struct GNUNET_RESOLVER_ResponseMessage *msg; 305 struct GNUNET_RESOLVER_ResponseMessage *msg;
352 struct GNUNET_MQ_Envelope *env; 306 struct GNUNET_MQ_Envelope *env;
353 307
354 GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle (resolve_result_pipe, 308 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
355 GNUNET_DISK_PIPE_END_READ), 309 "Sending end message\n");
356 &async_cls, 310 env = GNUNET_MQ_msg (msg,
357 sizeof (struct AsyncCls *)); 311 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
358 resolve_result_pipe_task = 312 msg->id = request_id;
359 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, 313 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
360 GNUNET_DISK_pipe_handle (resolve_result_pipe, 314 env);
361 GNUNET_DISK_PIPE_END_READ), 315}
362 &resolve_result_pipe_cb, 316
363 NULL); 317
364 host = async_cls->host; 318static void
365 for (struct addrinfo *pos = host->ar_result; pos != NULL; pos = pos->ai_next) 319handle_resolve_result (void *cls,
320 const struct GNUNET_TUN_DnsHeader *dns,
321 size_t dns_len)
322{
323 struct ResolveCache *cache = cls;
324 struct GNUNET_DNSPARSER_Packet *parsed;
325 uint16_t request_id = *cache->request_id;
326 struct GNUNET_SERVICE_Client *client = cache->client;
327
328 parsed = GNUNET_DNSPARSER_parse ((const char *)dns,
329 dns_len);
330 if (NULL == parsed)
331 {
332 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
333 "Failed to parse DNS reply (request ID %u\n",
334 request_id);
335 return;
336 }
337 if (request_id != ntohs (parsed->id))
338 {
339 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
340 "Request ID in DNS reply does not match\n");
341 return;
342 }
343 else if (0 == parsed->num_answers)
366 { 344 {
367 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 345 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368 "Lookup result for hostname %s: %s (request ID %u)\n", 346 "DNS reply (request ID %u) contains no answers\n",
369 host->ar_name, 347 request_id);
370 GNUNET_a2s (pos->ai_addr, pos->ai_addrlen), 348 GNUNET_CONTAINER_DLL_remove (cache_head,
371 async_cls->request_id); 349 cache_tail,
372 switch (pos->ai_family) 350 cache);
351 free_cache_entry (cache);
352 cache = NULL;
353 }
354 else
355 {
356 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
357 "Got reply for request ID %u\n",
358 request_id);
359 for (unsigned int i = 0; i != parsed->num_answers; i++)
373 { 360 {
374 case AF_INET: 361 struct Record *cache_entry = GNUNET_new (struct Record);
375 env = GNUNET_MQ_msg_extra (msg, 362 struct GNUNET_DNSPARSER_Record *record = &parsed->answers[i];
376 sizeof (struct in_addr), 363 cache_entry->record = GNUNET_DNSPARSER_duplicate_record (record);
377 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); 364 GNUNET_CONTAINER_DLL_insert (cache->records_head,
378 msg->id = async_cls->request_id; 365 cache->records_tail,
379 GNUNET_memcpy (&msg[1], 366 cache_entry);
380 &((struct sockaddr_in*) pos->ai_addr)->sin_addr, 367 send_reply (cache_entry->record,
381 sizeof (struct in_addr)); 368 request_id,
382 GNUNET_MQ_send (async_cls->mq, 369 cache->client);
383 env);
384 break;
385 case AF_INET6:
386 env = GNUNET_MQ_msg_extra (msg,
387 sizeof (struct in6_addr),
388 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
389 msg->id = async_cls->request_id;
390 GNUNET_memcpy (&msg[1],
391 &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
392 sizeof (struct in6_addr));
393 GNUNET_MQ_send (async_cls->mq,
394 env);
395 break;
396 default:
397 /* unsupported, skip */
398 break;
399 } 370 }
371 GNUNET_free_non_null (cache->request_id);
372 cache->request_id = NULL;
400 } 373 }
401 // send end message 374 send_end_msg (request_id,
402 env = GNUNET_MQ_msg (msg, 375 client);
403 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); 376 if (NULL != cache)
404 msg->id = async_cls->request_id; 377 cache->client = NULL;
405 GNUNET_MQ_send (async_cls->mq, 378 GNUNET_SCHEDULER_cancel (cache->timeout_task);
406 env); 379 GNUNET_DNSSTUB_resolve_cancel (cache->resolve_handle);
407 freeaddrinfo (host->ar_result); 380 cache->timeout_task = NULL;
408 GNUNET_free ((struct gaicb *)host->ar_request); // free hints 381 cache->resolve_handle = NULL;
409 GNUNET_free (host); 382 GNUNET_DNSPARSER_free_packet (parsed);
410 GNUNET_free (async_cls->sig);
411 GNUNET_free (async_cls);
412} 383}
413 384
414 385
415static void 386static void
416handle_async_result (union sigval val) 387handle_resolve_timeout (void *cls)
417{ 388{
418 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (resolve_result_pipe, 389 struct ResolveCache *cache = cls;
419 GNUNET_DISK_PIPE_END_WRITE), 390
420 &val.sival_ptr, 391 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421 sizeof (val.sival_ptr)); 392 "timeout!\n");
393 if (NULL != cache->resolve_handle)
394 {
395 GNUNET_DNSSTUB_resolve_cancel (cache->resolve_handle);
396 cache->resolve_handle = NULL;
397 }
398 GNUNET_CONTAINER_DLL_remove (cache_head,
399 cache_tail,
400 cache);
401 free_cache_entry (cache);
422} 402}
423 403
424 404
425static int 405static int
426getaddrinfo_a_resolve (struct GNUNET_MQ_Handle *mq, 406resolve_and_cache (const char* hostname,
427 const char *hostname, 407 uint16_t record_type,
428 int af, 408 uint16_t request_id,
429 uint32_t request_id) 409 struct GNUNET_SERVICE_Client *client)
430{ 410{
431 int ret; 411 char *packet_buf;
432 struct gaicb *host; 412 size_t packet_size;
433 struct addrinfo *hints; 413 struct GNUNET_DNSPARSER_Query query;
434 struct sigevent *sig; 414 struct GNUNET_DNSPARSER_Packet packet;
435 struct AsyncCls *async_cls; 415 struct ResolveCache *cache;
436 416 struct GNUNET_TIME_Relative timeout =
437 host = GNUNET_new (struct gaicb); 417 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5);
438 hints = GNUNET_new (struct addrinfo); 418
439 sig = GNUNET_new (struct sigevent); 419 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
440 async_cls = GNUNET_new (struct AsyncCls); 420 "resolve_and_cache\n");
441 memset (hints, 421 query.name = (char *)hostname;
422 query.type = record_type;
423 query.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
424 memset (&packet,
442 0, 425 0,
443 sizeof (struct addrinfo)); 426 sizeof (packet));
444 memset (sig, 427 packet.num_queries = 1;
445 0, 428 packet.queries = &query;
446 sizeof (struct sigevent)); 429 packet.id = htons (request_id);
447 hints->ai_family = af; 430 packet.flags.recursion_desired = 1;
448 hints->ai_socktype = SOCK_STREAM; /* go for TCP */ 431 if (GNUNET_OK !=
449 host->ar_name = hostname; 432 GNUNET_DNSPARSER_pack (&packet,
450 host->ar_service = NULL; 433 UINT16_MAX,
451 host->ar_request = hints; 434 &packet_buf,
452 host->ar_result = NULL; 435 &packet_size))
453 sig->sigev_notify = SIGEV_THREAD; 436 {
454 sig->sigev_value.sival_ptr = async_cls; 437 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
455 sig->sigev_notify_function = &handle_async_result; 438 "Failed to pack query for hostname `%s'\n",
456 async_cls->host = host; 439 hostname);
457 async_cls->sig = sig;
458 async_cls->mq = mq;
459 async_cls->request_id = request_id;
460 ret = getaddrinfo_a (GAI_NOWAIT,
461 &host,
462 1,
463 sig);
464 if (0 != ret)
465 return GNUNET_SYSERR; 440 return GNUNET_SYSERR;
441
442 }
443 cache = GNUNET_malloc (sizeof (struct ResolveCache));
444 cache->record_type = record_type;
445 cache->request_id = GNUNET_memdup (&request_id, sizeof (request_id));
446 cache->client = client;
447 cache->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout,
448 &handle_resolve_timeout,
449 cache);
450 cache->resolve_handle =
451 GNUNET_DNSSTUB_resolve (dnsstub_ctx,
452 packet_buf,
453 packet_size,
454 &handle_resolve_result,
455 cache);
456 GNUNET_CONTAINER_DLL_insert (cache_head,
457 cache_tail,
458 cache);
459 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
460 "resolve %s, request_id = %u\n",
461 hostname,
462 request_id);
463 GNUNET_free (packet_buf);
466 return GNUNET_OK; 464 return GNUNET_OK;
467} 465}
468 466
469 467
470#elif HAVE_GETADDRINFO 468static const char *
471static int 469get_hostname (struct ResolveCache *cache_entry)
472getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq,
473 const char *hostname,
474 int af,
475 uint32_t request_id)
476{ 470{
477 int s; 471 if (NULL != cache_entry->records_head)
478 struct addrinfo hints;
479 struct addrinfo *result;
480 struct addrinfo *pos;
481 struct GNUNET_RESOLVER_ResponseMessage *msg;
482 struct GNUNET_MQ_Envelope *env;
483
484#ifdef WINDOWS
485 /* Due to a bug, getaddrinfo will not return a mix of different families */
486 if (AF_UNSPEC == af)
487 {
488 int ret1;
489 int ret2;
490 ret1 = getaddrinfo_resolve (mq,
491 hostname,
492 AF_INET,
493 request_id);
494 ret2 = getaddrinfo_resolve (mq,
495 hostname,
496 AF_INET6,
497 request_id);
498 if ( (ret1 == GNUNET_OK) ||
499 (ret2 == GNUNET_OK) )
500 return GNUNET_OK;
501 if ( (ret1 == GNUNET_SYSERR) ||
502 (ret2 == GNUNET_SYSERR) )
503 return GNUNET_SYSERR;
504 return GNUNET_NO;
505 }
506#endif
507
508 memset (&hints,
509 0,
510 sizeof (struct addrinfo));
511 hints.ai_family = af;
512 hints.ai_socktype = SOCK_STREAM; /* go for TCP */
513
514 if (0 != (s = getaddrinfo (hostname,
515 NULL,
516 &hints,
517 &result)))
518 {
519 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
520 _("Could not resolve `%s' (%s): %s\n"),
521 hostname,
522 (af ==
523 AF_INET) ? "IPv4" : ((af == AF_INET6) ? "IPv6" : "any"),
524 gai_strerror (s));
525 if ( (s == EAI_BADFLAGS) ||
526#ifndef WINDOWS
527 (s == EAI_SYSTEM) ||
528#endif
529 (s == EAI_MEMORY) )
530 return GNUNET_NO; /* other function may still succeed */
531 return GNUNET_SYSERR;
532 }
533 if (NULL == result)
534 return GNUNET_SYSERR;
535 for (pos = result; pos != NULL; pos = pos->ai_next)
536 { 472 {
537 switch (pos->ai_family) 473 GNUNET_assert (NULL != cache_entry->records_head);
538 { 474 GNUNET_assert (NULL != cache_entry->records_head->record);
539 case AF_INET: 475 GNUNET_assert (NULL != cache_entry->records_head->record->name);
540 env = GNUNET_MQ_msg_extra (msg, 476 return cache_entry->records_head->record->name;
541 sizeof (struct in_addr),
542 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
543 msg->id = request_id;
544 GNUNET_memcpy (&msg[1],
545 &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
546 sizeof (struct in_addr));
547 GNUNET_MQ_send (mq,
548 env);
549 break;
550 case AF_INET6:
551 env = GNUNET_MQ_msg_extra (msg,
552 sizeof (struct in6_addr),
553 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
554 msg->id = request_id;
555 GNUNET_memcpy (&msg[1],
556 &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
557 sizeof (struct in6_addr));
558 GNUNET_MQ_send (mq,
559 env);
560 break;
561 default:
562 /* unsupported, skip */
563 break;
564 }
565 } 477 }
566 freeaddrinfo (result); 478 return NULL;
567 return GNUNET_OK;
568} 479}
569 480
570 481
571#elif HAVE_GETHOSTBYNAME2 482static const uint16_t *
572 483get_record_type (struct ResolveCache *cache_entry)
573
574static int
575gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq,
576 const char *hostname,
577 int af,
578 uint32_t request_id)
579{ 484{
580 struct hostent *hp; 485 if (NULL != cache_entry->records_head)
581 int ret1; 486 return &cache_entry->record_type;
582 int ret2; 487 return NULL;
583 struct GNUNET_MQ_Envelope *env; 488}
584 struct GNUNET_RESOLVER_ResponseMessage *msg;
585 489
586#ifdef WINDOWS
587 /* gethostbyname2() in plibc is a compat dummy that calls gethostbyname(). */
588 return GNUNET_NO;
589#endif
590 490
591 if (af == AF_UNSPEC) 491static const struct GNUNET_TIME_Absolute *
592 { 492get_expiration_time (struct ResolveCache *cache_entry)
593 ret1 = gethostbyname2_resolve (mq, 493{
594 hostname, 494 if (NULL != cache_entry->records_head)
595 AF_INET, 495 return &cache_entry->records_head->record->expiration_time;
596 request_id); 496 return NULL;
597 ret2 = gethostbyname2_resolve (mq,
598 hostname,
599 AF_INET6,
600 request_id);
601 if ( (ret1 == GNUNET_OK) ||
602 (ret2 == GNUNET_OK) )
603 return GNUNET_OK;
604 if ( (ret1 == GNUNET_SYSERR) ||
605 (ret2 == GNUNET_SYSERR) )
606 return GNUNET_SYSERR;
607 return GNUNET_NO;
608 }
609 hp = gethostbyname2 (hostname,
610 af);
611 if (hp == NULL)
612 {
613 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
614 _("Could not find IP of host `%s': %s\n"),
615 hostname,
616 hstrerror (h_errno));
617 return GNUNET_SYSERR;
618 }
619 GNUNET_assert (hp->h_addrtype == af);
620 switch (af)
621 {
622 case AF_INET:
623 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
624 env = GNUNET_MQ_msg_extra (msg,
625 hp->h_length,
626 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
627 msg->id = request_id;
628 GNUNET_memcpy (&msg[1],
629 hp->h_addr_list[0],
630 hp->h_length);
631 GNUNET_MQ_send (mq,
632 env);
633 break;
634 case AF_INET6:
635 GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
636 env = GNUNET_MQ_msg_extra (msg,
637 hp->h_length,
638 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
639 msg->id = request_id;
640 GNUNET_memcpy (&msg[1],
641 hp->h_addr_list[0],
642 hp->h_length);
643 GNUNET_MQ_send (mq,
644 env);
645 break;
646 default:
647 GNUNET_break (0);
648 return GNUNET_SYSERR;
649 }
650 return GNUNET_OK;
651} 497}
652 498
653#elif HAVE_GETHOSTBYNAME
654
655 499
656static int 500static int
657gethostbyname_resolve (struct GNUNET_MQ_Handle *mq, 501remove_if_expired (struct ResolveCache *cache_entry)
658 const char *hostname,
659 uint32_t request_id)
660{ 502{
661 struct hostent *hp; 503 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
662 struct GNUNET_RESOLVER_ResponseMessage *msg;
663 struct GNUNET_MQ_Envelope *env;
664 504
665 hp = GETHOSTBYNAME (hostname); 505 if ( (NULL != cache_entry->records_head) &&
666 if (NULL == hp) 506 (now.abs_value_us > get_expiration_time (cache_entry)->abs_value_us) )
667 { 507 {
668 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 508 GNUNET_CONTAINER_DLL_remove (cache_head,
669 _("Could not find IP of host `%s': %s\n"), 509 cache_tail,
670 hostname, 510 cache_entry);
671 hstrerror (h_errno)); 511 free_cache_entry (cache_entry);
672 return GNUNET_SYSERR; 512 return GNUNET_YES;
673 }
674 if (hp->h_addrtype != AF_INET)
675 {
676 GNUNET_break (0);
677 return GNUNET_SYSERR;
678 } 513 }
679 GNUNET_assert (hp->h_length == sizeof (struct in_addr)); 514 return GNUNET_NO;
680 env = GNUNET_MQ_msg_extra (msg,
681 hp->h_length,
682 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
683 msg->id = request_id;
684 GNUNET_memcpy (&msg[1],
685 hp->h_addr_list[0],
686 hp->h_length);
687 GNUNET_MQ_send (mq,
688 env);
689 return GNUNET_OK;
690} 515}
691#endif
692 516
693 517
694/** 518/**
695 * Convert a string to an IP address. 519 * Get an IP address as a string (works for both IPv4 and IPv6). Note
520 * that the resolution happens asynchronously and that the first call
521 * may not immediately result in the FQN (but instead in a
522 * human-readable IP address).
696 * 523 *
697 * @param client where to send the IP address 524 * @param client handle to the client making the request (for sending the reply)
698 * @param hostname the hostname to resolve 525 * @param af AF_INET or AF_INET6
699 * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any" 526 * @param ip `struct in_addr` or `struct in6_addr`
700 */ 527 */
701static void 528static int
702get_ip_from_hostname (struct GNUNET_SERVICE_Client *client, 529try_cache (const char *hostname,
703 const char *hostname, 530 uint16_t record_type,
704 int af, 531 uint16_t request_id,
705 uint32_t request_id) 532 struct GNUNET_SERVICE_Client *client)
706{ 533{
707 struct GNUNET_MQ_Envelope *env; 534 struct ResolveCache *pos;
708 struct GNUNET_RESOLVER_ResponseMessage *msg; 535 struct ResolveCache *next;
709 struct GNUNET_MQ_Handle *mq; 536
710 537 next = cache_head;
711 mq = GNUNET_SERVICE_client_get_mq (client); 538 while ( (NULL != (pos = next)) &&
712#if HAVE_GETADDRINFO_A 539 ( (NULL == pos->records_head) ||
713 getaddrinfo_a_resolve (mq, 540 (0 != strcmp (get_hostname (pos), hostname)) ||
714 hostname, 541 (*get_record_type (pos) != record_type) ) )
715 af, 542 {
716 request_id); 543 next = pos->next;
717 GNUNET_SERVICE_client_continue (client); 544 remove_if_expired (pos);
718 return; 545 }
719#elif HAVE_GETADDRINFO 546 if (NULL != pos)
720 getaddrinfo_resolve (mq, 547 {
721 hostname, 548 if (GNUNET_NO == remove_if_expired (pos))
722 af, 549 {
723 request_id); 550 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
724#elif HAVE_GETHOSTBYNAME2 551 "found cache entry for '%s', record type '%u'\n",
725 gethostbyname2_resolve (mq, 552 hostname,
726 hostname, 553 record_type);
727 af, 554 struct Record *cache_pos = pos->records_head;
728 request_id); 555 while (NULL != cache_pos)
729#elif HAVE_GETHOSTBYNAME 556 {
730 if ( ( (af == AF_UNSPEC) || 557 send_reply (cache_pos->record,
731 (af == PF_INET) ) ) 558 request_id,
732 gethostbyname_resolve (mq, 559 client);
733 hostname, 560 cache_pos = cache_pos->next;
734 request_id); 561 }
735#endif 562 send_end_msg (request_id,
736 // send end message 563 client);
737 env = GNUNET_MQ_msg (msg, 564 return GNUNET_YES;
738 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); 565 }
739 msg->id = request_id; 566 }
740 GNUNET_MQ_notify_sent (env, 567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
741 &notify_service_client_done, 568 "no cache entry for '%s'\n",
742 client); 569 hostname);
743 GNUNET_MQ_send (mq, 570 return GNUNET_NO;
744 env);
745} 571}
746 572
747 573
@@ -801,6 +627,23 @@ check_get (void *cls,
801} 627}
802 628
803 629
630static void
631process_get (const char *hostname,
632 uint16_t record_type,
633 uint16_t request_id,
634 struct GNUNET_SERVICE_Client *client)
635{
636 if (GNUNET_NO == try_cache (hostname, record_type, request_id, client))
637 {
638 int result = resolve_and_cache (hostname,
639 record_type,
640 request_id,
641 client);
642 GNUNET_assert (GNUNET_OK == result);
643 }
644}
645
646
804/** 647/**
805 * Handle GET-message. 648 * Handle GET-message.
806 * 649 *
@@ -812,45 +655,100 @@ handle_get (void *cls,
812 const struct GNUNET_RESOLVER_GetMessage *msg) 655 const struct GNUNET_RESOLVER_GetMessage *msg)
813{ 656{
814 struct GNUNET_SERVICE_Client *client = cls; 657 struct GNUNET_SERVICE_Client *client = cls;
815 const void *ip;
816 int direction; 658 int direction;
817 int af; 659 int af;
818 uint32_t id; 660 uint16_t request_id;
661 const char *hostname;
819 662
820 direction = ntohl (msg->direction); 663 direction = ntohl (msg->direction);
821 af = ntohl (msg->af); 664 af = ntohl (msg->af);
822 id = ntohl (msg->id); 665 request_id = ntohs (msg->id);
823 if (GNUNET_NO == direction) 666 if (GNUNET_NO == direction)
824 { 667 {
825 /* IP from hostname */ 668 /* IP from hostname */
826 const char *hostname; 669 hostname = GNUNET_strdup ((const char *) &msg[1]);
827 670 switch (af)
828 hostname = (const char *) &msg[1]; 671 {
829 get_ip_from_hostname (client, 672 case AF_UNSPEC:
830 hostname, 673 {
831 af, 674 process_get (hostname, GNUNET_DNSPARSER_TYPE_ALL, request_id, client);
832 id); 675 break;
833 return; 676 }
677 case AF_INET:
678 {
679 process_get (hostname, GNUNET_DNSPARSER_TYPE_A, request_id, client);
680 break;
681 }
682 case AF_INET6:
683 {
684 process_get (hostname, GNUNET_DNSPARSER_TYPE_AAAA, request_id, client);
685 break;
686 }
687 default:
688 {
689 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
690 "got invalid af: %d\n",
691 af);
692 GNUNET_assert (0);
693 }
694 }
834 } 695 }
835 ip = &msg[1]; 696 else
697 {
698 /* hostname from IP */
699 hostname = make_reverse_hostname (&msg[1], af);
700 process_get (hostname, GNUNET_DNSPARSER_TYPE_PTR, request_id, client);
701 }
702 GNUNET_free_non_null ((char *)hostname);
703 GNUNET_SERVICE_client_continue (client);
704}
705
836 706
837#if !defined(GNUNET_CULL_LOGGING) 707static void
708shutdown_task (void *cls)
709{
710 (void) cls;
711 struct ResolveCache *pos;
712
713 while (NULL != (pos = cache_head))
838 { 714 {
839 char buf[INET6_ADDRSTRLEN]; 715 GNUNET_CONTAINER_DLL_remove (cache_head,
716 cache_tail,
717 pos);
718 free_cache_entry (pos);
719 }
720 GNUNET_DNSSTUB_stop (dnsstub_ctx);
721}
840 722
723
724static void
725init_cb (void *cls,
726 const struct GNUNET_CONFIGURATION_Handle *cfg,
727 struct GNUNET_SERVICE_Handle *sh)
728{
729 (void) cfg;
730 (void) sh;
731
732 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
733 cls);
734 dnsstub_ctx = GNUNET_DNSSTUB_start (128);
735 char **dns_servers;
736 ssize_t num_dns_servers = lookup_dns_servers (&dns_servers);
737 if (0 == num_dns_servers)
738 {
739 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
740 "no DNS server available. DNS resolution will not be possible.\n");
741 }
742 for (int i = 0; i != num_dns_servers; i++)
743 {
744 int result = GNUNET_DNSSTUB_add_dns_ip (dnsstub_ctx, dns_servers[i]);
841 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 745 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
842 "Resolver asked to look up IP address `%s (request ID %u)'.\n", 746 "Adding DNS server '%s': %s\n",
843 inet_ntop (af, 747 dns_servers[i],
844 ip, 748 GNUNET_OK == result ? "success" : "failure");
845 buf, 749 GNUNET_free (dns_servers[i]);
846 sizeof (buf)),
847 id);
848 } 750 }
849#endif 751 GNUNET_free_non_null (dns_servers);
850 get_ip_as_string (client,
851 af,
852 ip,
853 id);
854} 752}
855 753
856 754
@@ -870,19 +768,6 @@ connect_cb (void *cls,
870 (void) cls; 768 (void) cls;
871 (void) mq; 769 (void) mq;
872 770
873#if HAVE_GETADDRINFO_A
874 resolve_result_pipe = GNUNET_DISK_pipe (GNUNET_NO,
875 GNUNET_NO,
876 GNUNET_NO,
877 GNUNET_NO);
878 GNUNET_assert (NULL != resolve_result_pipe);
879 resolve_result_pipe_task =
880 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
881 GNUNET_DISK_pipe_handle (resolve_result_pipe,
882 GNUNET_DISK_PIPE_END_READ),
883 &resolve_result_pipe_cb,
884 NULL);
885#endif
886 return c; 771 return c;
887} 772}
888 773
@@ -900,19 +785,16 @@ disconnect_cb (void *cls,
900 void *internal_cls) 785 void *internal_cls)
901{ 786{
902 (void) cls; 787 (void) cls;
788 struct ResolveCache *pos = cache_head;
903 789
904#if HAVE_GETADDRINFO_A 790 while (NULL != pos)
905 if (NULL != resolve_result_pipe_task)
906 {
907 GNUNET_SCHEDULER_cancel (resolve_result_pipe_task);
908 resolve_result_pipe_task = NULL;
909 }
910 if (NULL != resolve_result_pipe)
911 { 791 {
912 GNUNET_DISK_pipe_close (resolve_result_pipe); 792 if (pos->client == c)
913 resolve_result_pipe = NULL; 793 {
794 pos->client = NULL;
795 }
796 pos = pos->next;
914 } 797 }
915#endif
916 GNUNET_assert (c == internal_cls); 798 GNUNET_assert (c == internal_cls);
917} 799}
918 800
@@ -923,7 +805,7 @@ disconnect_cb (void *cls,
923GNUNET_SERVICE_MAIN 805GNUNET_SERVICE_MAIN
924("resolver", 806("resolver",
925 GNUNET_SERVICE_OPTION_NONE, 807 GNUNET_SERVICE_OPTION_NONE,
926 NULL, 808 &init_cb,
927 &connect_cb, 809 &connect_cb,
928 &disconnect_cb, 810 &disconnect_cb,
929 NULL, 811 NULL,
@@ -950,23 +832,4 @@ GNUNET_RESOLVER_memory_init ()
950#endif 832#endif
951 833
952 834
953/**
954 * Free globals on exit.
955 */
956void __attribute__ ((destructor))
957GNUNET_RESOLVER_memory_done ()
958{
959 struct IPCache *pos;
960
961 while (NULL != (pos = cache_head))
962 {
963 GNUNET_CONTAINER_DLL_remove (cache_head,
964 cache_tail,
965 pos);
966 GNUNET_free_non_null (pos->addr);
967 GNUNET_free (pos);
968 }
969}
970
971
972/* end of gnunet-service-resolver.c */ 835/* end of gnunet-service-resolver.c */