aboutsummaryrefslogtreecommitdiff
path: root/src/util/gnunet-service-resolver.c
diff options
context:
space:
mode:
authorlurchi <lurchi@strangeplace.net>2018-06-29 00:10:08 +0200
committerlurchi <lurchi@strangeplace.net>2018-06-29 00:10:08 +0200
commite7e14740d619777613734cec9400c33cfd30fc3d (patch)
tree0a5ba01886c59709ffcc0052227ef7caa3f68aa0 /src/util/gnunet-service-resolver.c
parent7c04b23c8c80e06257706facf74a92bfec7a9914 (diff)
downloadgnunet-e7e14740d619777613734cec9400c33cfd30fc3d.tar.gz
gnunet-e7e14740d619777613734cec9400c33cfd30fc3d.zip
Use the DNSPARSER and DNSSTUB libraries in the resolver service
We are not using the libc functions anymore for forward and backup DNS resolutions and the DNSPARSER and DNSSTUB libraries instead. This has the advantage that the APIs are asynchronous now and thus multiple DNS resolutions can be done in parallel. This breaks support for Windows and other operating systems that don't use /etc/resolv.conf for defining DNS servers. For fixing this the function lookup_dns_servers can be extended with different lookup mechanisms.
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 */