diff options
Diffstat (limited to 'src/util/gnunet-service-resolver.c')
-rw-r--r-- | src/util/gnunet-service-resolver.c | 1161 |
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 | |||
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 */ |