diff options
author | lurchi <lurchi@strangeplace.net> | 2018-06-29 00:10:08 +0200 |
---|---|---|
committer | lurchi <lurchi@strangeplace.net> | 2018-06-29 00:10:08 +0200 |
commit | e7e14740d619777613734cec9400c33cfd30fc3d (patch) | |
tree | 0a5ba01886c59709ffcc0052227ef7caa3f68aa0 /src/util | |
parent | 7c04b23c8c80e06257706facf74a92bfec7a9914 (diff) | |
download | gnunet-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')
-rw-r--r-- | src/util/gnunet-service-resolver.c | 1161 | ||||
-rw-r--r-- | src/util/resolver.h | 4 | ||||
-rw-r--r-- | src/util/resolver_api.c | 26 |
3 files changed, 532 insertions, 659 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 | |||
31 | struct 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 | */ |
33 | struct IPCache | 43 | struct 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 | */ |
80 | static struct IPCache *cache_head; | 94 | static 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 | */ |
85 | static struct IPCache *cache_tail; | 99 | static struct ResolveCache *cache_tail; |
86 | 100 | ||
87 | /** | 101 | /** |
88 | * Pipe for asynchronously notifying about resolve result | 102 | * context of dnsstub library |
89 | */ | 103 | */ |
90 | static struct GNUNET_DISK_PipeHandle *resolve_result_pipe; | 104 | static struct GNUNET_DNSSTUB_Context *dnsstub_ctx; |
91 | |||
92 | /** | ||
93 | * Task for reading from resolve_result_pipe | ||
94 | */ | ||
95 | static struct GNUNET_SCHEDULER_Task *resolve_result_pipe_task; | ||
96 | 105 | ||
97 | 106 | ||
98 | #if HAVE_GETNAMEINFO | 107 | void 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 | */ | ||
104 | static void | ||
105 | getnameinfo_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 | 141 | static char* |
142 | extract_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 | */ |
166 | static void | 156 | static ssize_t |
167 | gethostbyaddr_resolve (struct IPCache *cache) | 157 | lookup_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 | /** | 205 | static char * |
189 | * Resolve the given request using the available methods. | 206 | make_reverse_hostname (const void *ip, int af) |
190 | * | ||
191 | * @param cache the request to resolve (and where to store the result) | ||
192 | */ | ||
193 | static void | ||
194 | cache_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 | */ | ||
214 | static void | 256 | static void |
215 | notify_service_client_done (void *cls) | 257 | send_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 | */ | ||
233 | static void | ||
234 | get_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 | ¬ify_service_client_done, | ||
330 | client); | ||
331 | GNUNET_MQ_send (mq, | ||
332 | env); | ||
333 | } | 298 | } |
334 | 299 | ||
335 | 300 | ||
336 | #if HAVE_GETADDRINFO_A | ||
337 | struct AsyncCls | ||
338 | { | ||
339 | struct gaicb *host; | ||
340 | struct sigevent *sig; | ||
341 | struct GNUNET_MQ_Handle *mq; | ||
342 | uint32_t request_id; | ||
343 | }; | ||
344 | |||
345 | |||
346 | static void | 301 | static void |
347 | resolve_result_pipe_cb (void *cls) | 302 | send_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; | 318 | static void |
365 | for (struct addrinfo *pos = host->ar_result; pos != NULL; pos = pos->ai_next) | 319 | handle_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 | ||
415 | static void | 386 | static void |
416 | handle_async_result (union sigval val) | 387 | handle_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 | ||
425 | static int | 405 | static int |
426 | getaddrinfo_a_resolve (struct GNUNET_MQ_Handle *mq, | 406 | resolve_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 | 468 | static const char * |
471 | static int | 469 | get_hostname (struct ResolveCache *cache_entry) |
472 | getaddrinfo_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 | 482 | static const uint16_t * |
572 | 483 | get_record_type (struct ResolveCache *cache_entry) | |
573 | |||
574 | static int | ||
575 | gethostbyname2_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) | 491 | static const struct GNUNET_TIME_Absolute * |
592 | { | 492 | get_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 | ||
656 | static int | 500 | static int |
657 | gethostbyname_resolve (struct GNUNET_MQ_Handle *mq, | 501 | remove_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 | */ |
701 | static void | 528 | static int |
702 | get_ip_from_hostname (struct GNUNET_SERVICE_Client *client, | 529 | try_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 | ¬ify_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 | ||
630 | static void | ||
631 | process_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) | 707 | static void |
708 | shutdown_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 | |||
724 | static void | ||
725 | init_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, | |||
923 | GNUNET_SERVICE_MAIN | 805 | GNUNET_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 | */ | ||
956 | void __attribute__ ((destructor)) | ||
957 | GNUNET_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 */ |
diff --git a/src/util/resolver.h b/src/util/resolver.h index a0f105afa..07851d052 100644 --- a/src/util/resolver.h +++ b/src/util/resolver.h | |||
@@ -60,7 +60,7 @@ struct GNUNET_RESOLVER_GetMessage | |||
60 | * identifies the request and is contained in the response message. The | 60 | * identifies the request and is contained in the response message. The |
61 | * client has to match response to request by this identifier. | 61 | * client has to match response to request by this identifier. |
62 | */ | 62 | */ |
63 | uint32_t id GNUNET_PACKED; | 63 | uint16_t id GNUNET_PACKED; |
64 | 64 | ||
65 | /* followed by 0-terminated string for A/AAAA-lookup or | 65 | /* followed by 0-terminated string for A/AAAA-lookup or |
66 | by 'struct in_addr' / 'struct in6_addr' for reverse lookup */ | 66 | by 'struct in_addr' / 'struct in6_addr' for reverse lookup */ |
@@ -79,7 +79,7 @@ struct GNUNET_RESOLVER_ResponseMessage | |||
79 | * identifies the request this message responds to. The client | 79 | * identifies the request this message responds to. The client |
80 | * has to match response to request by this identifier. | 80 | * has to match response to request by this identifier. |
81 | */ | 81 | */ |
82 | uint32_t id GNUNET_PACKED; | 82 | uint16_t id GNUNET_PACKED; |
83 | 83 | ||
84 | /* followed by 0-terminated string for response to a reverse lookup | 84 | /* followed by 0-terminated string for response to a reverse lookup |
85 | * or by 'struct in_addr' / 'struct in6_addr' for response to | 85 | * or by 'struct in_addr' / 'struct in6_addr' for response to |
diff --git a/src/util/resolver_api.c b/src/util/resolver_api.c index b94819f06..8a054327b 100644 --- a/src/util/resolver_api.c +++ b/src/util/resolver_api.c | |||
@@ -68,10 +68,10 @@ static struct GNUNET_RESOLVER_RequestHandle *req_head; | |||
68 | */ | 68 | */ |
69 | static struct GNUNET_RESOLVER_RequestHandle *req_tail; | 69 | static struct GNUNET_RESOLVER_RequestHandle *req_tail; |
70 | 70 | ||
71 | /** | 71 | ///** |
72 | * ID of the last request we sent to the service | 72 | // * ID of the last request we sent to the service |
73 | */ | 73 | // */ |
74 | static uint32_t last_request_id; | 74 | //static uint16_t last_request_id; |
75 | 75 | ||
76 | /** | 76 | /** |
77 | * How long should we wait to reconnect? | 77 | * How long should we wait to reconnect? |
@@ -445,7 +445,7 @@ process_requests () | |||
445 | GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST); | 445 | GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST); |
446 | msg->direction = htonl (rh->direction); | 446 | msg->direction = htonl (rh->direction); |
447 | msg->af = htonl (rh->af); | 447 | msg->af = htonl (rh->af); |
448 | msg->id = htonl (rh->id); | 448 | msg->id = htons (rh->id); |
449 | GNUNET_memcpy (&msg[1], | 449 | GNUNET_memcpy (&msg[1], |
450 | &rh[1], | 450 | &rh[1], |
451 | rh->data_len); | 451 | rh->data_len); |
@@ -491,7 +491,7 @@ handle_response (void *cls, | |||
491 | struct GNUNET_RESOLVER_RequestHandle *rh = req_head; | 491 | struct GNUNET_RESOLVER_RequestHandle *rh = req_head; |
492 | uint16_t size; | 492 | uint16_t size; |
493 | char *nret; | 493 | char *nret; |
494 | uint32_t request_id = msg->id; | 494 | uint16_t request_id = msg->id; |
495 | 495 | ||
496 | for (; rh != NULL; rh = rh->next) | 496 | for (; rh != NULL; rh = rh->next) |
497 | { | 497 | { |
@@ -911,6 +911,14 @@ handle_lookup_timeout (void *cls) | |||
911 | } | 911 | } |
912 | 912 | ||
913 | 913 | ||
914 | static uint16_t | ||
915 | get_request_id () | ||
916 | { | ||
917 | return (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, | ||
918 | UINT16_MAX); | ||
919 | } | ||
920 | |||
921 | |||
914 | /** | 922 | /** |
915 | * Convert a string to one or more IP addresses. | 923 | * Convert a string to one or more IP addresses. |
916 | * | 924 | * |
@@ -945,7 +953,8 @@ GNUNET_RESOLVER_ip_get (const char *hostname, | |||
945 | hostname); | 953 | hostname); |
946 | rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen); | 954 | rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen); |
947 | rh->af = af; | 955 | rh->af = af; |
948 | rh->id = ++last_request_id; | 956 | //rh->id = ++last_request_id; |
957 | rh->id = get_request_id (); | ||
949 | rh->addr_callback = callback; | 958 | rh->addr_callback = callback; |
950 | rh->cls = callback_cls; | 959 | rh->cls = callback_cls; |
951 | GNUNET_memcpy (&rh[1], | 960 | GNUNET_memcpy (&rh[1], |
@@ -1092,7 +1101,8 @@ GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa, | |||
1092 | rh->name_callback = callback; | 1101 | rh->name_callback = callback; |
1093 | rh->cls = cls; | 1102 | rh->cls = cls; |
1094 | rh->af = sa->sa_family; | 1103 | rh->af = sa->sa_family; |
1095 | rh->id = ++last_request_id; | 1104 | //rh->id = ++last_request_id; |
1105 | rh->id = get_request_id (); | ||
1096 | rh->timeout = GNUNET_TIME_relative_to_absolute (timeout); | 1106 | rh->timeout = GNUNET_TIME_relative_to_absolute (timeout); |
1097 | GNUNET_memcpy (&rh[1], | 1107 | GNUNET_memcpy (&rh[1], |
1098 | ip, | 1108 | ip, |