diff options
author | Christian Grothoff <christian@grothoff.org> | 2018-08-14 15:30:42 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2018-08-14 15:30:42 +0200 |
commit | 1c757482ba7ca48fa108f1613cccc5c775bd957b (patch) | |
tree | d2d1dab3bc5935caf9c65bf3131bc163475ead38 /src/util/gnunet-service-resolver.c | |
parent | 11d4a9262ecaa7b29b1b549c324c4806591eb8b2 (diff) | |
download | gnunet-1c757482ba7ca48fa108f1613cccc5c775bd957b.tar.gz gnunet-1c757482ba7ca48fa108f1613cccc5c775bd957b.zip |
fix CNAME handling, caching, out-of-bounds accesses, etc. in gnunet-service-resolver
Diffstat (limited to 'src/util/gnunet-service-resolver.c')
-rw-r--r-- | src/util/gnunet-service-resolver.c | 869 |
1 files changed, 562 insertions, 307 deletions
diff --git a/src/util/gnunet-service-resolver.c b/src/util/gnunet-service-resolver.c index 5b890261b..06af57509 100644 --- a/src/util/gnunet-service-resolver.c +++ b/src/util/gnunet-service-resolver.c | |||
@@ -11,7 +11,7 @@ | |||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Affero General Public License for more details. | 13 | Affero General Public License for more details. |
14 | 14 | ||
15 | You should have received a copy of the GNU Affero General Public License | 15 | You should have received a copy of the GNU Affero General Public License |
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | */ | 17 | */ |
@@ -28,15 +28,38 @@ | |||
28 | #include "resolver.h" | 28 | #include "resolver.h" |
29 | 29 | ||
30 | 30 | ||
31 | struct Record | 31 | /** |
32 | * How long do we wait for DNS answers? | ||
33 | */ | ||
34 | #define DNS_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) | ||
35 | |||
36 | /** | ||
37 | * Maximum number of hostnames we cache results for. | ||
38 | */ | ||
39 | #define MAX_CACHE 1024 | ||
40 | |||
41 | /** | ||
42 | * Entry in list of cached DNS records for a hostname. | ||
43 | */ | ||
44 | struct RecordListEntry | ||
32 | { | 45 | { |
33 | struct Record *next; | 46 | /** |
47 | * This is a doubly linked list. | ||
48 | */ | ||
49 | struct RecordListEntry *next; | ||
34 | 50 | ||
35 | struct Record *prev; | 51 | /** |
52 | * This is a doubly linked list. | ||
53 | */ | ||
54 | struct RecordListEntry *prev; | ||
36 | 55 | ||
56 | /** | ||
57 | * Cached data. | ||
58 | */ | ||
37 | struct GNUNET_DNSPARSER_Record *record; | 59 | struct GNUNET_DNSPARSER_Record *record; |
38 | }; | 60 | }; |
39 | 61 | ||
62 | |||
40 | /** | 63 | /** |
41 | * A cached DNS lookup result. | 64 | * A cached DNS lookup result. |
42 | */ | 65 | */ |
@@ -53,30 +76,42 @@ struct ResolveCache | |||
53 | struct ResolveCache *prev; | 76 | struct ResolveCache *prev; |
54 | 77 | ||
55 | /** | 78 | /** |
56 | * type of queried DNS record | 79 | * Which hostname is this cache for? |
57 | */ | 80 | */ |
58 | uint16_t record_type; | 81 | char *hostname; |
59 | 82 | ||
60 | /** | 83 | /** |
61 | * a pointer to the request_id if a query for this hostname/record_type | 84 | * head of a double linked list containing the lookup results |
62 | * is currently pending, NULL otherwise. | ||
63 | */ | 85 | */ |
64 | int16_t *request_id; | 86 | struct RecordListEntry *records_head; |
65 | 87 | ||
66 | /** | 88 | /** |
67 | * The client that queried the records contained in this cache entry. | 89 | * tail of a double linked list containing the lookup results |
68 | */ | 90 | */ |
69 | struct GNUNET_SERVICE_Client *client; | 91 | struct RecordListEntry *records_tail; |
70 | 92 | ||
93 | }; | ||
94 | |||
95 | |||
96 | /** | ||
97 | * Information about pending lookups. | ||
98 | */ | ||
99 | struct ActiveLookup | ||
100 | { | ||
71 | /** | 101 | /** |
72 | * head of a double linked list containing the lookup results | 102 | * Stored in a DLL. |
73 | */ | 103 | */ |
74 | struct Record *records_head; | 104 | struct ActiveLookup *next; |
75 | 105 | ||
76 | /** | 106 | /** |
77 | * tail of a double linked list containing the lookup results | 107 | * Stored in a DLL. |
78 | */ | 108 | */ |
79 | struct Record *records_tail; | 109 | struct ActiveLookup *prev; |
110 | |||
111 | /** | ||
112 | * The client that queried the records contained in this cache entry. | ||
113 | */ | ||
114 | struct GNUNET_SERVICE_Client *client; | ||
80 | 115 | ||
81 | /** | 116 | /** |
82 | * handle for cancelling a request | 117 | * handle for cancelling a request |
@@ -88,6 +123,28 @@ struct ResolveCache | |||
88 | */ | 123 | */ |
89 | struct GNUNET_SCHEDULER_Task *timeout_task; | 124 | struct GNUNET_SCHEDULER_Task *timeout_task; |
90 | 125 | ||
126 | /** | ||
127 | * Which hostname are we resolving? | ||
128 | */ | ||
129 | char *hostname; | ||
130 | |||
131 | /** | ||
132 | * type of queried DNS record | ||
133 | */ | ||
134 | uint16_t record_type; | ||
135 | |||
136 | /** | ||
137 | * Unique request ID of a client if a query for this hostname/record_type | ||
138 | * is currently pending, undefined otherwise. | ||
139 | */ | ||
140 | uint16_t request_id; | ||
141 | |||
142 | /** | ||
143 | * Unique DNS request ID of a client if a query for this hostname/record_type | ||
144 | * is currently pending, undefined otherwise. | ||
145 | */ | ||
146 | uint16_t dns_id; | ||
147 | |||
91 | }; | 148 | }; |
92 | 149 | ||
93 | 150 | ||
@@ -102,69 +159,117 @@ static struct ResolveCache *cache_head; | |||
102 | static struct ResolveCache *cache_tail; | 159 | static struct ResolveCache *cache_tail; |
103 | 160 | ||
104 | /** | 161 | /** |
162 | * Start of the linked list of active DNS lookups. | ||
163 | */ | ||
164 | static struct ActiveLookup *lookup_head; | ||
165 | |||
166 | /** | ||
167 | * Tail of the linked list of active DNS lookups. | ||
168 | */ | ||
169 | static struct ActiveLookup *lookup_tail; | ||
170 | |||
171 | /** | ||
105 | * context of dnsstub library | 172 | * context of dnsstub library |
106 | */ | 173 | */ |
107 | static struct GNUNET_DNSSTUB_Context *dnsstub_ctx; | 174 | static struct GNUNET_DNSSTUB_Context *dnsstub_ctx; |
108 | 175 | ||
176 | /** | ||
177 | * How many entries do we have in #cache_head DLL? | ||
178 | */ | ||
179 | static unsigned int cache_size; | ||
109 | 180 | ||
110 | void free_cache_entry (struct ResolveCache *entry) | 181 | /** |
182 | * Remove @a entry from cache. | ||
183 | * | ||
184 | * @param rc entry to free | ||
185 | */ | ||
186 | static void | ||
187 | free_cache_entry (struct ResolveCache *rc) | ||
111 | { | 188 | { |
112 | struct Record *pos; | 189 | struct RecordListEntry *pos; |
113 | struct Record *next; | 190 | |
114 | 191 | while (NULL != (pos = rc->records_head)) | |
115 | next = entry->records_head; | ||
116 | while (NULL != (pos = next)) | ||
117 | { | 192 | { |
118 | next = pos->next; | 193 | GNUNET_CONTAINER_DLL_remove (rc->records_head, |
119 | GNUNET_CONTAINER_DLL_remove (entry->records_head, | 194 | rc->records_tail, |
120 | entry->records_tail, | ||
121 | pos); | 195 | pos); |
122 | if (NULL != pos->record) | 196 | GNUNET_DNSPARSER_free_record (pos->record); |
123 | { | 197 | GNUNET_free (pos->record); |
124 | GNUNET_DNSPARSER_free_record (pos->record); | ||
125 | GNUNET_free (pos->record); | ||
126 | } | ||
127 | GNUNET_free (pos); | 198 | GNUNET_free (pos); |
128 | } | 199 | } |
129 | if (NULL != entry->resolve_handle) | 200 | GNUNET_free_non_null (rc->hostname); |
201 | GNUNET_CONTAINER_DLL_remove (cache_head, | ||
202 | cache_tail, | ||
203 | rc); | ||
204 | cache_size--; | ||
205 | GNUNET_free (rc); | ||
206 | } | ||
207 | |||
208 | |||
209 | /** | ||
210 | * Release resources associated with @a al | ||
211 | * | ||
212 | * @param al an active lookup | ||
213 | */ | ||
214 | static void | ||
215 | free_active_lookup (struct ActiveLookup *al) | ||
216 | { | ||
217 | GNUNET_CONTAINER_DLL_remove (lookup_head, | ||
218 | lookup_tail, | ||
219 | al); | ||
220 | if (NULL != al->resolve_handle) | ||
130 | { | 221 | { |
131 | GNUNET_DNSSTUB_resolve_cancel (entry->resolve_handle); | 222 | GNUNET_DNSSTUB_resolve_cancel (al->resolve_handle); |
132 | entry->resolve_handle = NULL; | 223 | al->resolve_handle = NULL; |
133 | } | 224 | } |
134 | if (NULL != entry->timeout_task) | 225 | if (NULL != al->timeout_task) |
135 | { | 226 | { |
136 | GNUNET_SCHEDULER_cancel (entry->timeout_task); | 227 | GNUNET_SCHEDULER_cancel (al->timeout_task); |
137 | entry->timeout_task = NULL; | 228 | al->timeout_task = NULL; |
138 | } | 229 | } |
139 | GNUNET_free_non_null (entry->request_id); | 230 | GNUNET_free_non_null (al->hostname); |
140 | GNUNET_free (entry); | 231 | GNUNET_free (al); |
141 | } | 232 | } |
142 | 233 | ||
143 | 234 | ||
144 | static char* | 235 | |
145 | extract_dns_server (const char* line, size_t line_len) | 236 | /** |
237 | * Find out if the configuration file line contains a string | ||
238 | * starting with "nameserver ", and if so, return a copy of | ||
239 | * the nameserver's IP. | ||
240 | * | ||
241 | * @param line line to parse | ||
242 | * @param line_len number of characters in @a line | ||
243 | * @return NULL if no nameserver is configured in this @a line | ||
244 | */ | ||
245 | static char * | ||
246 | extract_dns_server (const char* line, | ||
247 | size_t line_len) | ||
146 | { | 248 | { |
147 | if (0 == strncmp (line, "nameserver ", 11)) | 249 | if (0 == strncmp (line, |
148 | return GNUNET_strndup (line + 11, line_len - 11); | 250 | "nameserver ", |
251 | strlen ("nameserver "))) | ||
252 | return GNUNET_strndup (line + strlen ("nameserver "), | ||
253 | line_len - strlen ("nameserver ")); | ||
149 | return NULL; | 254 | return NULL; |
150 | } | 255 | } |
151 | 256 | ||
152 | 257 | ||
153 | /** | 258 | /** |
154 | * reads the list of nameservers from /etc/resolve.conf | 259 | * Reads the list of nameservers from /etc/resolve.conf |
155 | * | 260 | * |
156 | * @param server_addrs[out] a list of null-terminated server address strings | 261 | * @param server_addrs[out] a list of null-terminated server address strings |
157 | * @return the number of server addresses in @server_addrs, -1 on error | 262 | * @return the number of server addresses in @server_addrs, -1 on error |
158 | */ | 263 | */ |
159 | static ssize_t | 264 | static int |
160 | lookup_dns_servers (char ***server_addrs) | 265 | lookup_dns_servers (char ***server_addrs) |
161 | { | 266 | { |
162 | struct GNUNET_DISK_FileHandle *fh; | 267 | struct GNUNET_DISK_FileHandle *fh; |
163 | char buf[2048]; | 268 | char buf[2048]; |
164 | ssize_t bytes_read; | 269 | ssize_t bytes_read; |
165 | size_t read_offset = 0; | 270 | size_t read_offset; |
166 | unsigned int num_dns_servers = 0; | 271 | unsigned int num_dns_servers; |
167 | 272 | ||
168 | fh = GNUNET_DISK_file_open ("/etc/resolv.conf", | 273 | fh = GNUNET_DISK_file_open ("/etc/resolv.conf", |
169 | GNUNET_DISK_OPEN_READ, | 274 | GNUNET_DISK_OPEN_READ, |
170 | GNUNET_DISK_PERM_NONE); | 275 | GNUNET_DISK_PERM_NONE); |
@@ -179,41 +284,51 @@ lookup_dns_servers (char ***server_addrs) | |||
179 | buf, | 284 | buf, |
180 | sizeof (buf)); | 285 | sizeof (buf)); |
181 | *server_addrs = NULL; | 286 | *server_addrs = NULL; |
287 | read_offset = 0; | ||
288 | num_dns_servers = 0; | ||
182 | while (read_offset < bytes_read) | 289 | while (read_offset < bytes_read) |
183 | { | 290 | { |
184 | char *newline; | 291 | const char *newline; |
185 | size_t line_len; | 292 | size_t line_len; |
186 | char *dns_server; | 293 | char *dns_server; |
187 | 294 | ||
188 | newline = strchr (buf + read_offset, '\n'); | 295 | newline = strchr (buf + read_offset, |
296 | '\n'); | ||
189 | if (NULL == newline) | 297 | if (NULL == newline) |
190 | { | ||
191 | break; | 298 | break; |
192 | } | ||
193 | line_len = newline - buf - read_offset; | 299 | line_len = newline - buf - read_offset; |
194 | dns_server = extract_dns_server (buf + read_offset, line_len); | 300 | dns_server = extract_dns_server (buf + read_offset, |
301 | line_len); | ||
195 | if (NULL != dns_server) | 302 | if (NULL != dns_server) |
196 | { | ||
197 | GNUNET_array_append (*server_addrs, | 303 | GNUNET_array_append (*server_addrs, |
198 | num_dns_servers, | 304 | num_dns_servers, |
199 | dns_server); | 305 | dns_server); |
200 | } | ||
201 | read_offset += line_len + 1; | 306 | read_offset += line_len + 1; |
202 | } | 307 | } |
203 | GNUNET_DISK_file_close (fh); | 308 | GNUNET_DISK_file_close (fh); |
204 | return num_dns_servers; | 309 | return (int) num_dns_servers; |
205 | } | 310 | } |
206 | 311 | ||
207 | 312 | ||
313 | /** | ||
314 | * Compute name to use for DNS reverse lookups from @a ip. | ||
315 | * | ||
316 | * @param ip IP address to resolve, in binary format, network byte order | ||
317 | * @param af address family of @a ip, AF_INET or AF_INET6 | ||
318 | */ | ||
208 | static char * | 319 | static char * |
209 | make_reverse_hostname (const void *ip, int af) | 320 | make_reverse_hostname (const void *ip, |
321 | int af) | ||
210 | { | 322 | { |
211 | char *buf = GNUNET_new_array (80, char); | 323 | char *buf = GNUNET_new_array (80, |
324 | char); | ||
212 | int pos = 0; | 325 | int pos = 0; |
326 | |||
213 | if (AF_INET == af) | 327 | if (AF_INET == af) |
214 | { | 328 | { |
215 | struct in_addr *addr = (struct in_addr *)ip; | 329 | struct in_addr *addr = (struct in_addr *)ip; |
216 | uint32_t ip_int = addr->s_addr; | 330 | uint32_t ip_int = addr->s_addr; |
331 | |||
217 | for (int i = 3; i >= 0; i--) | 332 | for (int i = 3; i >= 0; i--) |
218 | { | 333 | { |
219 | int n = GNUNET_snprintf (buf + pos, | 334 | int n = GNUNET_snprintf (buf + pos, |
@@ -227,21 +342,29 @@ make_reverse_hostname (const void *ip, int af) | |||
227 | } | 342 | } |
228 | pos += n; | 343 | pos += n; |
229 | } | 344 | } |
230 | pos += GNUNET_snprintf (buf + pos, 80 - pos, "in-addr.arpa"); | 345 | pos += GNUNET_snprintf (buf + pos, |
346 | 80 - pos, | ||
347 | "in-addr.arpa"); | ||
231 | } | 348 | } |
232 | else if (AF_INET6 == af) | 349 | else if (AF_INET6 == af) |
233 | { | 350 | { |
234 | struct in6_addr *addr = (struct in6_addr *)ip; | 351 | struct in6_addr *addr = (struct in6_addr *)ip; |
235 | for (int i = 15; i >= 0; i--) | 352 | for (int i = 15; i >= 0; i--) |
236 | { | 353 | { |
237 | int n = GNUNET_snprintf (buf + pos, 80 - pos, "%x.", addr->s6_addr[i] & 0xf); | 354 | int n = GNUNET_snprintf (buf + pos, |
355 | 80 - pos, | ||
356 | "%x.", | ||
357 | addr->s6_addr[i] & 0xf); | ||
238 | if (n < 0) | 358 | if (n < 0) |
239 | { | 359 | { |
240 | GNUNET_free (buf); | 360 | GNUNET_free (buf); |
241 | return NULL; | 361 | return NULL; |
242 | } | 362 | } |
243 | pos += n; | 363 | pos += n; |
244 | n = GNUNET_snprintf (buf + pos, 80 - pos, "%x.", addr->s6_addr[i] >> 4); | 364 | n = GNUNET_snprintf (buf + pos, |
365 | 80 - pos, | ||
366 | "%x.", | ||
367 | addr->s6_addr[i] >> 4); | ||
245 | if (n < 0) | 368 | if (n < 0) |
246 | { | 369 | { |
247 | GNUNET_free (buf); | 370 | GNUNET_free (buf); |
@@ -249,45 +372,70 @@ make_reverse_hostname (const void *ip, int af) | |||
249 | } | 372 | } |
250 | pos += n; | 373 | pos += n; |
251 | } | 374 | } |
252 | pos += GNUNET_snprintf (buf + pos, 80 - pos, "ip6.arpa"); | 375 | pos += GNUNET_snprintf (buf + pos, |
376 | 80 - pos, | ||
377 | "ip6.arpa"); | ||
253 | } | 378 | } |
254 | buf[pos] = '\0'; | 379 | buf[pos] = '\0'; |
255 | return buf; | 380 | return buf; |
256 | } | 381 | } |
257 | 382 | ||
258 | 383 | ||
259 | static void | 384 | /** |
385 | * Send DNS @a record back to our @a client. | ||
386 | * | ||
387 | * @param record information to transmit | ||
388 | * @param record_type requested record type from client | ||
389 | * @param request_id to which request are we responding | ||
390 | * @param client where to send @a record | ||
391 | * @return #GNUNET_YES if we sent a reply, | ||
392 | * #GNUNET_NO if the record type is not understood or | ||
393 | * does not match @a record_type | ||
394 | */ | ||
395 | static int | ||
260 | send_reply (struct GNUNET_DNSPARSER_Record *record, | 396 | send_reply (struct GNUNET_DNSPARSER_Record *record, |
397 | uint16_t record_type, | ||
261 | uint16_t request_id, | 398 | uint16_t request_id, |
262 | struct GNUNET_SERVICE_Client *client) | 399 | struct GNUNET_SERVICE_Client *client) |
263 | { | 400 | { |
264 | struct GNUNET_RESOLVER_ResponseMessage *msg; | 401 | struct GNUNET_RESOLVER_ResponseMessage *msg; |
265 | struct GNUNET_MQ_Envelope *env; | 402 | struct GNUNET_MQ_Envelope *env; |
266 | void *payload; | 403 | const void *payload; |
267 | size_t payload_len; | 404 | size_t payload_len; |
268 | 405 | ||
269 | switch (record->type) | 406 | switch (record->type) |
270 | { | 407 | { |
271 | case GNUNET_DNSPARSER_TYPE_PTR: | 408 | case GNUNET_DNSPARSER_TYPE_CNAME: |
272 | { | 409 | if (GNUNET_DNSPARSER_TYPE_CNAME != record_type) |
273 | char *hostname = record->data.hostname; | 410 | return GNUNET_NO; |
274 | payload = hostname; | 411 | payload = record->data.hostname; |
275 | payload_len = strlen (hostname) + 1; | 412 | payload_len = strlen (record->data.hostname) + 1; |
276 | break; | 413 | break; |
277 | } | 414 | case GNUNET_DNSPARSER_TYPE_PTR: |
278 | case GNUNET_DNSPARSER_TYPE_A: | 415 | if (GNUNET_DNSPARSER_TYPE_PTR != record_type) |
279 | case GNUNET_DNSPARSER_TYPE_AAAA: | 416 | return GNUNET_NO; |
280 | { | 417 | payload = record->data.hostname; |
281 | payload = record->data.raw.data; | 418 | payload_len = strlen (record->data.hostname) + 1; |
282 | payload_len = record->data.raw.data_len; | 419 | break; |
283 | break; | 420 | case GNUNET_DNSPARSER_TYPE_A: |
284 | } | 421 | if ( (GNUNET_DNSPARSER_TYPE_A != record_type) && |
285 | default: | 422 | (GNUNET_DNSPARSER_TYPE_ALL != record_type) ) |
286 | { | 423 | return GNUNET_NO; |
287 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 424 | payload = record->data.raw.data; |
288 | "Cannot handle DNS response type: unimplemented\n"); | 425 | payload_len = record->data.raw.data_len; |
289 | return; | 426 | break; |
290 | } | 427 | case GNUNET_DNSPARSER_TYPE_AAAA: |
428 | if ( (GNUNET_DNSPARSER_TYPE_AAAA != record_type) && | ||
429 | (GNUNET_DNSPARSER_TYPE_ALL != record_type) ) | ||
430 | return GNUNET_NO; | ||
431 | payload = record->data.raw.data; | ||
432 | payload_len = record->data.raw.data_len; | ||
433 | break; | ||
434 | default: | ||
435 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
436 | "Cannot handle DNS response type %u: not supported here\n", | ||
437 | record->type); | ||
438 | return GNUNET_NO; | ||
291 | } | 439 | } |
292 | env = GNUNET_MQ_msg_extra (msg, | 440 | env = GNUNET_MQ_msg_extra (msg, |
293 | payload_len, | 441 | payload_len, |
@@ -298,9 +446,17 @@ send_reply (struct GNUNET_DNSPARSER_Record *record, | |||
298 | payload_len); | 446 | payload_len); |
299 | GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), | 447 | GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), |
300 | env); | 448 | env); |
449 | return GNUNET_YES; | ||
301 | } | 450 | } |
302 | 451 | ||
303 | 452 | ||
453 | /** | ||
454 | * Send message to @a client that we transmitted all | ||
455 | * responses for @a request_id | ||
456 | * | ||
457 | * @param request_id to which request are we responding | ||
458 | * @param client where to send @a record | ||
459 | */ | ||
304 | static void | 460 | static void |
305 | send_end_msg (uint16_t request_id, | 461 | send_end_msg (uint16_t request_id, |
306 | struct GNUNET_SERVICE_Client *client) | 462 | struct GNUNET_SERVICE_Client *client) |
@@ -309,7 +465,7 @@ send_end_msg (uint16_t request_id, | |||
309 | struct GNUNET_MQ_Envelope *env; | 465 | struct GNUNET_MQ_Envelope *env; |
310 | 466 | ||
311 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 467 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
312 | "Sending end message\n"); | 468 | "Sending END message\n"); |
313 | env = GNUNET_MQ_msg (msg, | 469 | env = GNUNET_MQ_msg (msg, |
314 | GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); | 470 | GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); |
315 | msg->id = request_id; | 471 | msg->id = request_id; |
@@ -318,102 +474,263 @@ send_end_msg (uint16_t request_id, | |||
318 | } | 474 | } |
319 | 475 | ||
320 | 476 | ||
477 | /** | ||
478 | * Remove expired entries from @a rc | ||
479 | * | ||
480 | * @param rc entry in resolver cache | ||
481 | * @return #GNUNET_YES if @a rc was completely expired | ||
482 | * #GNUNET_NO if some entries are left | ||
483 | */ | ||
484 | static int | ||
485 | remove_expired (struct ResolveCache *rc) | ||
486 | { | ||
487 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
488 | struct RecordListEntry *n; | ||
489 | |||
490 | for (struct RecordListEntry *pos = rc->records_head; | ||
491 | NULL != pos; | ||
492 | pos = n) | ||
493 | { | ||
494 | n = pos->next; | ||
495 | if (now.abs_value_us > pos->record->expiration_time.abs_value_us) | ||
496 | GNUNET_CONTAINER_DLL_remove (rc->records_head, | ||
497 | rc->records_tail, | ||
498 | pos); | ||
499 | } | ||
500 | if (NULL == rc->records_head) | ||
501 | { | ||
502 | free_cache_entry (rc); | ||
503 | return GNUNET_YES; | ||
504 | } | ||
505 | return GNUNET_NO; | ||
506 | } | ||
507 | |||
508 | |||
509 | /** | ||
510 | * Process DNS request for @a hostname with request ID @a request_id | ||
511 | * from @a client demanding records of type @a record_type. | ||
512 | * | ||
513 | * @param hostname DNS name to resolve | ||
514 | * @param record_type desired record type | ||
515 | * @param request_id client's request ID | ||
516 | * @param client who should get the result? | ||
517 | */ | ||
518 | static void | ||
519 | process_get (const char *hostname, | ||
520 | uint16_t record_type, | ||
521 | uint16_t request_id, | ||
522 | struct GNUNET_SERVICE_Client *client); | ||
523 | |||
524 | |||
525 | /** | ||
526 | * Get an IP address as a string (works for both IPv4 and IPv6). Note | ||
527 | * that the resolution happens asynchronously and that the first call | ||
528 | * may not immediately result in the FQN (but instead in a | ||
529 | * human-readable IP address). | ||
530 | * | ||
531 | * @param hostname what hostname was to be resolved | ||
532 | * @param record_type what type of record was requested | ||
533 | * @param request_id unique identification of the client's request | ||
534 | * @param client handle to the client making the request (for sending the reply) | ||
535 | */ | ||
536 | static int | ||
537 | try_cache (const char *hostname, | ||
538 | uint16_t record_type, | ||
539 | uint16_t request_id, | ||
540 | struct GNUNET_SERVICE_Client *client) | ||
541 | { | ||
542 | struct ResolveCache *pos; | ||
543 | struct ResolveCache *next; | ||
544 | int found; | ||
545 | |||
546 | next = cache_head; | ||
547 | for (pos = next; NULL != pos; pos = next) | ||
548 | { | ||
549 | next = pos->next; | ||
550 | if (GNUNET_YES == remove_expired (pos)) | ||
551 | continue; | ||
552 | if (0 == strcmp (pos->hostname, | ||
553 | hostname)) | ||
554 | break; | ||
555 | } | ||
556 | if (NULL == pos) | ||
557 | { | ||
558 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
559 | "No cache entry for '%s'\n", | ||
560 | hostname); | ||
561 | return GNUNET_NO; | ||
562 | } | ||
563 | if (cache_head != pos) | ||
564 | { | ||
565 | /* move result to head to achieve LRU for cache eviction */ | ||
566 | GNUNET_CONTAINER_DLL_remove (cache_head, | ||
567 | cache_tail, | ||
568 | pos); | ||
569 | GNUNET_CONTAINER_DLL_insert (cache_head, | ||
570 | cache_tail, | ||
571 | pos); | ||
572 | } | ||
573 | found = GNUNET_NO; | ||
574 | for (struct RecordListEntry *rle = pos->records_head; | ||
575 | NULL != rle; | ||
576 | rle = rle->next) | ||
577 | { | ||
578 | const struct GNUNET_DNSPARSER_Record *record = rle->record; | ||
579 | |||
580 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
581 | "Found cache entry for '%s', record type '%u'\n", | ||
582 | hostname, | ||
583 | record_type); | ||
584 | if ( (GNUNET_DNSPARSER_TYPE_CNAME == record->type) && | ||
585 | (GNUNET_DNSPARSER_TYPE_CNAME != record_type) && | ||
586 | (GNUNET_NO == found) ) | ||
587 | { | ||
588 | const char *hostname = record->data.hostname; | ||
589 | |||
590 | process_get (hostname, | ||
591 | record_type, | ||
592 | request_id, | ||
593 | client); | ||
594 | return GNUNET_YES; /* counts as a cache "hit" */ | ||
595 | } | ||
596 | found |= send_reply (rle->record, | ||
597 | record_type, | ||
598 | request_id, | ||
599 | client); | ||
600 | } | ||
601 | if (GNUNET_NO == found) | ||
602 | return GNUNET_NO; /* had records, but none matched! */ | ||
603 | send_end_msg (request_id, | ||
604 | client); | ||
605 | return GNUNET_YES; | ||
606 | } | ||
607 | |||
608 | |||
609 | /** | ||
610 | * We got a result from DNS. Add it to the cache and | ||
611 | * see if we can make our client happy... | ||
612 | * | ||
613 | * @param cls the `struct ActiveLookup` | ||
614 | * @param dns the DNS response | ||
615 | * @param dns_len number of bytes in @a dns | ||
616 | */ | ||
321 | static void | 617 | static void |
322 | handle_resolve_result (void *cls, | 618 | handle_resolve_result (void *cls, |
323 | const struct GNUNET_TUN_DnsHeader *dns, | 619 | const struct GNUNET_TUN_DnsHeader *dns, |
324 | size_t dns_len) | 620 | size_t dns_len) |
325 | { | 621 | { |
326 | struct ResolveCache *cache = cls; | 622 | struct ActiveLookup *al = cls; |
327 | struct GNUNET_DNSPARSER_Packet *parsed; | 623 | struct GNUNET_DNSPARSER_Packet *parsed; |
328 | uint16_t request_id = *cache->request_id; | 624 | struct ResolveCache *rc; |
329 | struct GNUNET_SERVICE_Client *client = cache->client; | ||
330 | 625 | ||
331 | parsed = GNUNET_DNSPARSER_parse ((const char *)dns, | 626 | parsed = GNUNET_DNSPARSER_parse ((const char *)dns, |
332 | dns_len); | 627 | dns_len); |
333 | if (NULL == parsed) | 628 | if (NULL == parsed) |
334 | { | 629 | { |
335 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 630 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
336 | "Failed to parse DNS reply (request ID %u\n", | 631 | "Failed to parse DNS reply (hostname %s, request ID %u)\n", |
337 | request_id); | 632 | al->hostname, |
633 | al->dns_id); | ||
338 | return; | 634 | return; |
339 | } | 635 | } |
340 | if (request_id != ntohs (parsed->id)) | 636 | if (al->dns_id != ntohs (parsed->id)) |
341 | { | 637 | { |
342 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 638 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
343 | "Request ID in DNS reply does not match\n"); | 639 | "Request ID in DNS reply does not match\n"); |
640 | GNUNET_DNSPARSER_free_packet (parsed); | ||
344 | return; | 641 | return; |
345 | } | 642 | } |
346 | else if (0 == parsed->num_answers) | 643 | if (0 == parsed->num_answers) |
347 | { | 644 | { |
348 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 645 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
349 | "DNS reply (request ID %u) contains no answers\n", | 646 | "DNS reply (hostname %s, request ID %u) contains no answers\n", |
350 | request_id); | 647 | al->hostname, |
351 | GNUNET_CONTAINER_DLL_remove (cache_head, | 648 | al->request_id); |
352 | cache_tail, | 649 | GNUNET_DNSPARSER_free_packet (parsed); |
353 | cache); | 650 | send_end_msg (al->request_id, |
354 | free_cache_entry (cache); | 651 | al->client); |
355 | cache = NULL; | 652 | free_active_lookup (al); |
356 | } | 653 | return; |
357 | else | ||
358 | { | ||
359 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
360 | "Got reply for request ID %u\n", | ||
361 | request_id); | ||
362 | for (unsigned int i = 0; i != parsed->num_answers; i++) | ||
363 | { | ||
364 | struct Record *cache_entry = GNUNET_new (struct Record); | ||
365 | struct GNUNET_DNSPARSER_Record *record = &parsed->answers[i]; | ||
366 | cache_entry->record = GNUNET_DNSPARSER_duplicate_record (record); | ||
367 | GNUNET_CONTAINER_DLL_insert (cache->records_head, | ||
368 | cache->records_tail, | ||
369 | cache_entry); | ||
370 | send_reply (cache_entry->record, | ||
371 | request_id, | ||
372 | cache->client); | ||
373 | } | ||
374 | GNUNET_free_non_null (cache->request_id); | ||
375 | cache->request_id = NULL; | ||
376 | } | 654 | } |
377 | send_end_msg (request_id, | 655 | /* LRU-based cache eviction: we remove from tail */ |
378 | client); | 656 | while (cache_size > MAX_CACHE) |
379 | if (NULL != cache) | 657 | free_cache_entry (cache_tail); |
380 | cache->client = NULL; | 658 | |
381 | if (NULL != cache) | 659 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
660 | "Got reply for hostname %s and request ID %u\n", | ||
661 | al->hostname, | ||
662 | al->request_id); | ||
663 | /* add to cache */ | ||
664 | for (unsigned int i = 0; i != parsed->num_answers; i++) | ||
382 | { | 665 | { |
383 | if (NULL != cache->timeout_task) | 666 | struct GNUNET_DNSPARSER_Record *record = &parsed->answers[i]; |
384 | { | 667 | struct RecordListEntry *rle; |
385 | GNUNET_SCHEDULER_cancel (cache->timeout_task); | 668 | |
386 | cache->timeout_task = NULL; | 669 | for (rc = cache_head; NULL != rc; rc = rc->next) |
387 | } | 670 | if (0 == strcasecmp (rc->hostname, |
388 | if (NULL != cache->resolve_handle) | 671 | record->name)) |
672 | break; | ||
673 | if (NULL == rc) | ||
389 | { | 674 | { |
390 | GNUNET_DNSSTUB_resolve_cancel (cache->resolve_handle); | 675 | rc = GNUNET_new (struct ResolveCache); |
391 | cache->resolve_handle = NULL; | 676 | rc->hostname = GNUNET_strdup (record->name); |
677 | GNUNET_CONTAINER_DLL_insert (cache_head, | ||
678 | cache_tail, | ||
679 | rc); | ||
680 | cache_size++; | ||
392 | } | 681 | } |
682 | /* TODO: ought to check first if we have this exact record | ||
683 | already in the cache! */ | ||
684 | rle = GNUNET_new (struct RecordListEntry); | ||
685 | rle->record = GNUNET_DNSPARSER_duplicate_record (record); | ||
686 | GNUNET_CONTAINER_DLL_insert (rc->records_head, | ||
687 | rc->records_tail, | ||
688 | rle); | ||
393 | } | 689 | } |
690 | |||
691 | /* resume by trying again from cache */ | ||
692 | if (GNUNET_NO == | ||
693 | try_cache (al->hostname, | ||
694 | al->record_type, | ||
695 | al->request_id, | ||
696 | al->client)) | ||
697 | /* cache failed, tell client we could not get an answer */ | ||
698 | send_end_msg (al->request_id, | ||
699 | al->client); | ||
700 | free_active_lookup (al); | ||
394 | GNUNET_DNSPARSER_free_packet (parsed); | 701 | GNUNET_DNSPARSER_free_packet (parsed); |
395 | } | 702 | } |
396 | 703 | ||
397 | 704 | ||
705 | /** | ||
706 | * We encountered a timeout trying to perform a | ||
707 | * DNS lookup. | ||
708 | * | ||
709 | * @param cls a `struct ActiveLookup` | ||
710 | */ | ||
398 | static void | 711 | static void |
399 | handle_resolve_timeout (void *cls) | 712 | handle_resolve_timeout (void *cls) |
400 | { | 713 | { |
401 | struct ResolveCache *cache = cls; | 714 | struct ActiveLookup *al = cls; |
402 | 715 | ||
403 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 716 | al->timeout_task = NULL; |
404 | "timeout!\n"); | 717 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
405 | if (NULL != cache->resolve_handle) | 718 | "DNS lookup timeout!\n"); |
406 | { | 719 | send_end_msg (al->request_id, |
407 | GNUNET_DNSSTUB_resolve_cancel (cache->resolve_handle); | 720 | al->client); |
408 | cache->resolve_handle = NULL; | 721 | free_active_lookup (al); |
409 | } | ||
410 | GNUNET_CONTAINER_DLL_remove (cache_head, | ||
411 | cache_tail, | ||
412 | cache); | ||
413 | free_cache_entry (cache); | ||
414 | } | 722 | } |
415 | 723 | ||
416 | 724 | ||
725 | /** | ||
726 | * Initiate an active lookup, then cache the result and | ||
727 | * try to then complete the resolution. | ||
728 | * | ||
729 | * @param hostname DNS name to resolve | ||
730 | * @param record_type record type to locate | ||
731 | * @param request_id client request ID | ||
732 | * @param client handle to the client | ||
733 | */ | ||
417 | static int | 734 | static int |
418 | resolve_and_cache (const char* hostname, | 735 | resolve_and_cache (const char* hostname, |
419 | uint16_t record_type, | 736 | uint16_t record_type, |
@@ -424,10 +741,11 @@ resolve_and_cache (const char* hostname, | |||
424 | size_t packet_size; | 741 | size_t packet_size; |
425 | struct GNUNET_DNSPARSER_Query query; | 742 | struct GNUNET_DNSPARSER_Query query; |
426 | struct GNUNET_DNSPARSER_Packet packet; | 743 | struct GNUNET_DNSPARSER_Packet packet; |
427 | struct ResolveCache *cache; | 744 | struct ActiveLookup *al; |
428 | struct GNUNET_TIME_Relative timeout = | 745 | uint16_t dns_id; |
429 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5); | ||
430 | 746 | ||
747 | dns_id =(uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, | ||
748 | UINT16_MAX); | ||
431 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 749 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
432 | "resolve_and_cache\n"); | 750 | "resolve_and_cache\n"); |
433 | query.name = (char *)hostname; | 751 | query.name = (char *)hostname; |
@@ -438,9 +756,9 @@ resolve_and_cache (const char* hostname, | |||
438 | sizeof (packet)); | 756 | sizeof (packet)); |
439 | packet.num_queries = 1; | 757 | packet.num_queries = 1; |
440 | packet.queries = &query; | 758 | packet.queries = &query; |
441 | packet.id = htons (request_id); | 759 | packet.id = htons (dns_id); |
442 | packet.flags.recursion_desired = 1; | 760 | packet.flags.recursion_desired = 1; |
443 | if (GNUNET_OK != | 761 | if (GNUNET_OK != |
444 | GNUNET_DNSPARSER_pack (&packet, | 762 | GNUNET_DNSPARSER_pack (&packet, |
445 | UINT16_MAX, | 763 | UINT16_MAX, |
446 | &packet_buf, | 764 | &packet_buf, |
@@ -450,136 +768,67 @@ resolve_and_cache (const char* hostname, | |||
450 | "Failed to pack query for hostname `%s'\n", | 768 | "Failed to pack query for hostname `%s'\n", |
451 | hostname); | 769 | hostname); |
452 | return GNUNET_SYSERR; | 770 | return GNUNET_SYSERR; |
453 | 771 | ||
454 | } | 772 | } |
455 | cache = GNUNET_malloc (sizeof (struct ResolveCache)); | 773 | al = GNUNET_new (struct ActiveLookup); |
456 | cache->record_type = record_type; | 774 | al->hostname = GNUNET_strdup (hostname); |
457 | cache->request_id = GNUNET_memdup (&request_id, sizeof (request_id)); | 775 | al->record_type = record_type; |
458 | cache->client = client; | 776 | al->request_id = request_id; |
459 | cache->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout, | 777 | al->dns_id = dns_id; |
460 | &handle_resolve_timeout, | 778 | al->client = client; |
461 | cache); | 779 | al->timeout_task = GNUNET_SCHEDULER_add_delayed (DNS_TIMEOUT, |
462 | cache->resolve_handle = | 780 | &handle_resolve_timeout, |
781 | al); | ||
782 | al->resolve_handle = | ||
463 | GNUNET_DNSSTUB_resolve (dnsstub_ctx, | 783 | GNUNET_DNSSTUB_resolve (dnsstub_ctx, |
464 | packet_buf, | 784 | packet_buf, |
465 | packet_size, | 785 | packet_size, |
466 | &handle_resolve_result, | 786 | &handle_resolve_result, |
467 | cache); | 787 | al); |
468 | GNUNET_CONTAINER_DLL_insert (cache_head, | 788 | GNUNET_free (packet_buf); |
469 | cache_tail, | 789 | GNUNET_CONTAINER_DLL_insert (lookup_head, |
470 | cache); | 790 | lookup_tail, |
791 | al); | ||
471 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 792 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
472 | "resolve %s, request_id = %u\n", | 793 | "Resolving %s, request_id = %u, dns_id = %u\n", |
473 | hostname, | 794 | hostname, |
474 | request_id); | 795 | (unsigned int) request_id, |
475 | GNUNET_free (packet_buf); | 796 | (unsigned int) dns_id); |
476 | return GNUNET_OK; | 797 | return GNUNET_OK; |
477 | } | 798 | } |
478 | 799 | ||
479 | 800 | ||
480 | static const char * | ||
481 | get_hostname (struct ResolveCache *cache_entry) | ||
482 | { | ||
483 | if (NULL != cache_entry->records_head) | ||
484 | { | ||
485 | GNUNET_assert (NULL != cache_entry->records_head); | ||
486 | GNUNET_assert (NULL != cache_entry->records_head->record); | ||
487 | GNUNET_assert (NULL != cache_entry->records_head->record->name); | ||
488 | return cache_entry->records_head->record->name; | ||
489 | } | ||
490 | return NULL; | ||
491 | } | ||
492 | |||
493 | |||
494 | static const uint16_t * | ||
495 | get_record_type (struct ResolveCache *cache_entry) | ||
496 | { | ||
497 | if (NULL != cache_entry->records_head) | ||
498 | return &cache_entry->record_type; | ||
499 | return NULL; | ||
500 | } | ||
501 | |||
502 | |||
503 | static const struct GNUNET_TIME_Absolute * | ||
504 | get_expiration_time (struct ResolveCache *cache_entry) | ||
505 | { | ||
506 | if (NULL != cache_entry->records_head) | ||
507 | return &cache_entry->records_head->record->expiration_time; | ||
508 | return NULL; | ||
509 | } | ||
510 | |||
511 | |||
512 | static int | ||
513 | remove_if_expired (struct ResolveCache *cache_entry) | ||
514 | { | ||
515 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
516 | |||
517 | if ( (NULL != cache_entry->records_head) && | ||
518 | (now.abs_value_us > get_expiration_time (cache_entry)->abs_value_us) ) | ||
519 | { | ||
520 | GNUNET_CONTAINER_DLL_remove (cache_head, | ||
521 | cache_tail, | ||
522 | cache_entry); | ||
523 | free_cache_entry (cache_entry); | ||
524 | return GNUNET_YES; | ||
525 | } | ||
526 | return GNUNET_NO; | ||
527 | } | ||
528 | |||
529 | |||
530 | /** | 801 | /** |
531 | * Get an IP address as a string (works for both IPv4 and IPv6). Note | 802 | * Process DNS request for @a hostname with request ID @a request_id |
532 | * that the resolution happens asynchronously and that the first call | 803 | * from @a client demanding records of type @a record_type. |
533 | * may not immediately result in the FQN (but instead in a | ||
534 | * human-readable IP address). | ||
535 | * | 804 | * |
536 | * @param client handle to the client making the request (for sending the reply) | 805 | * @param hostname DNS name to resolve |
537 | * @param af AF_INET or AF_INET6 | 806 | * @param record_type desired record type |
538 | * @param ip `struct in_addr` or `struct in6_addr` | 807 | * @param request_id client's request ID |
808 | * @param client who should get the result? | ||
539 | */ | 809 | */ |
540 | static int | 810 | static void |
541 | try_cache (const char *hostname, | 811 | process_get (const char *hostname, |
542 | uint16_t record_type, | 812 | uint16_t record_type, |
543 | uint16_t request_id, | 813 | uint16_t request_id, |
544 | struct GNUNET_SERVICE_Client *client) | 814 | struct GNUNET_SERVICE_Client *client) |
545 | { | 815 | { |
546 | struct ResolveCache *pos; | 816 | if (GNUNET_NO == |
547 | struct ResolveCache *next; | 817 | try_cache (hostname, |
548 | 818 | record_type, | |
549 | next = cache_head; | 819 | request_id, |
550 | while ( (NULL != (pos = next)) && | 820 | client)) |
551 | ( (NULL == pos->records_head) || | ||
552 | (0 != strcmp (get_hostname (pos), hostname)) || | ||
553 | (*get_record_type (pos) != record_type) ) ) | ||
554 | { | 821 | { |
555 | next = pos->next; | 822 | if (GNUNET_OK != |
556 | remove_if_expired (pos); | 823 | resolve_and_cache (hostname, |
557 | } | 824 | record_type, |
558 | if (NULL != pos) | 825 | request_id, |
559 | { | 826 | client)) |
560 | if (GNUNET_NO == remove_if_expired (pos)) | ||
561 | { | 827 | { |
562 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
563 | "found cache entry for '%s', record type '%u'\n", | ||
564 | hostname, | ||
565 | record_type); | ||
566 | struct Record *cache_pos = pos->records_head; | ||
567 | while (NULL != cache_pos) | ||
568 | { | ||
569 | send_reply (cache_pos->record, | ||
570 | request_id, | ||
571 | client); | ||
572 | cache_pos = cache_pos->next; | ||
573 | } | ||
574 | send_end_msg (request_id, | 828 | send_end_msg (request_id, |
575 | client); | 829 | client); |
576 | return GNUNET_YES; | ||
577 | } | 830 | } |
578 | } | 831 | } |
579 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
580 | "no cache entry for '%s'\n", | ||
581 | hostname); | ||
582 | return GNUNET_NO; | ||
583 | } | 832 | } |
584 | 833 | ||
585 | 834 | ||
@@ -639,23 +888,6 @@ check_get (void *cls, | |||
639 | } | 888 | } |
640 | 889 | ||
641 | 890 | ||
642 | static void | ||
643 | process_get (const char *hostname, | ||
644 | uint16_t record_type, | ||
645 | uint16_t request_id, | ||
646 | struct GNUNET_SERVICE_Client *client) | ||
647 | { | ||
648 | if (GNUNET_NO == try_cache (hostname, record_type, request_id, client)) | ||
649 | { | ||
650 | int result = resolve_and_cache (hostname, | ||
651 | record_type, | ||
652 | request_id, | ||
653 | client); | ||
654 | GNUNET_assert (GNUNET_OK == result); | ||
655 | } | ||
656 | } | ||
657 | |||
658 | |||
659 | /** | 891 | /** |
660 | * Handle GET-message. | 892 | * Handle GET-message. |
661 | * | 893 | * |
@@ -670,7 +902,7 @@ handle_get (void *cls, | |||
670 | int direction; | 902 | int direction; |
671 | int af; | 903 | int af; |
672 | uint16_t request_id; | 904 | uint16_t request_id; |
673 | const char *hostname; | 905 | char *hostname; |
674 | 906 | ||
675 | direction = ntohl (msg->direction); | 907 | direction = ntohl (msg->direction); |
676 | af = ntohl (msg->af); | 908 | af = ntohl (msg->af); |
@@ -683,17 +915,26 @@ handle_get (void *cls, | |||
683 | { | 915 | { |
684 | case AF_UNSPEC: | 916 | case AF_UNSPEC: |
685 | { | 917 | { |
686 | process_get (hostname, GNUNET_DNSPARSER_TYPE_ALL, request_id, client); | 918 | process_get (hostname, |
919 | GNUNET_DNSPARSER_TYPE_ALL, | ||
920 | request_id, | ||
921 | client); | ||
687 | break; | 922 | break; |
688 | } | 923 | } |
689 | case AF_INET: | 924 | case AF_INET: |
690 | { | 925 | { |
691 | process_get (hostname, GNUNET_DNSPARSER_TYPE_A, request_id, client); | 926 | process_get (hostname, |
927 | GNUNET_DNSPARSER_TYPE_A, | ||
928 | request_id, | ||
929 | client); | ||
692 | break; | 930 | break; |
693 | } | 931 | } |
694 | case AF_INET6: | 932 | case AF_INET6: |
695 | { | 933 | { |
696 | process_get (hostname, GNUNET_DNSPARSER_TYPE_AAAA, request_id, client); | 934 | process_get (hostname, |
935 | GNUNET_DNSPARSER_TYPE_AAAA, | ||
936 | request_id, | ||
937 | client); | ||
697 | break; | 938 | break; |
698 | } | 939 | } |
699 | default: | 940 | default: |
@@ -708,50 +949,63 @@ handle_get (void *cls, | |||
708 | else | 949 | else |
709 | { | 950 | { |
710 | /* hostname from IP */ | 951 | /* hostname from IP */ |
711 | hostname = make_reverse_hostname (&msg[1], af); | 952 | hostname = make_reverse_hostname (&msg[1], |
712 | process_get (hostname, GNUNET_DNSPARSER_TYPE_PTR, request_id, client); | 953 | af); |
954 | process_get (hostname, | ||
955 | GNUNET_DNSPARSER_TYPE_PTR, | ||
956 | request_id, | ||
957 | client); | ||
713 | } | 958 | } |
714 | GNUNET_free_non_null ((char *)hostname); | 959 | GNUNET_free_non_null (hostname); |
715 | GNUNET_SERVICE_client_continue (client); | 960 | GNUNET_SERVICE_client_continue (client); |
716 | } | 961 | } |
717 | 962 | ||
718 | 963 | ||
719 | static void | 964 | /** |
965 | * Service is shutting down, clean up. | ||
966 | * | ||
967 | * @param cls NULL, unused | ||
968 | */ | ||
969 | static void | ||
720 | shutdown_task (void *cls) | 970 | shutdown_task (void *cls) |
721 | { | 971 | { |
722 | (void) cls; | 972 | (void) cls; |
723 | struct ResolveCache *pos; | ||
724 | 973 | ||
725 | while (NULL != (pos = cache_head)) | 974 | while (NULL != lookup_head) |
726 | { | 975 | free_active_lookup (lookup_head); |
727 | GNUNET_CONTAINER_DLL_remove (cache_head, | 976 | while (NULL != cache_head) |
728 | cache_tail, | 977 | free_cache_entry (cache_head); |
729 | pos); | ||
730 | free_cache_entry (pos); | ||
731 | } | ||
732 | GNUNET_DNSSTUB_stop (dnsstub_ctx); | 978 | GNUNET_DNSSTUB_stop (dnsstub_ctx); |
733 | } | 979 | } |
734 | 980 | ||
735 | 981 | ||
982 | /** | ||
983 | * Service is starting, initialize everything. | ||
984 | * | ||
985 | * @param cls NULL, unused | ||
986 | * @param cfg our configuration | ||
987 | * @param sh service handle | ||
988 | */ | ||
736 | static void | 989 | static void |
737 | init_cb (void *cls, | 990 | init_cb (void *cls, |
738 | const struct GNUNET_CONFIGURATION_Handle *cfg, | 991 | const struct GNUNET_CONFIGURATION_Handle *cfg, |
739 | struct GNUNET_SERVICE_Handle *sh) | 992 | struct GNUNET_SERVICE_Handle *sh) |
740 | { | 993 | { |
994 | char **dns_servers; | ||
995 | int num_dns_servers; | ||
996 | |||
741 | (void) cfg; | 997 | (void) cfg; |
742 | (void) sh; | 998 | (void) sh; |
743 | |||
744 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | 999 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, |
745 | cls); | 1000 | cls); |
746 | dnsstub_ctx = GNUNET_DNSSTUB_start (128); | 1001 | dnsstub_ctx = GNUNET_DNSSTUB_start (128); |
747 | char **dns_servers; | 1002 | num_dns_servers = lookup_dns_servers (&dns_servers); |
748 | ssize_t num_dns_servers = lookup_dns_servers (&dns_servers); | 1003 | if (0 >= num_dns_servers) |
749 | if (0 == num_dns_servers) | ||
750 | { | 1004 | { |
751 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 1005 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
752 | "no DNS server available. DNS resolution will not be possible.\n"); | 1006 | _("No DNS server available. DNS resolution will not be possible.\n")); |
753 | } | 1007 | } |
754 | for (int i = 0; i != num_dns_servers; i++) | 1008 | for (int i = 0; i < num_dns_servers; i++) |
755 | { | 1009 | { |
756 | int result = GNUNET_DNSSTUB_add_dns_ip (dnsstub_ctx, dns_servers[i]); | 1010 | int result = GNUNET_DNSSTUB_add_dns_ip (dnsstub_ctx, dns_servers[i]); |
757 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 1011 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
@@ -796,18 +1050,19 @@ disconnect_cb (void *cls, | |||
796 | struct GNUNET_SERVICE_Client *c, | 1050 | struct GNUNET_SERVICE_Client *c, |
797 | void *internal_cls) | 1051 | void *internal_cls) |
798 | { | 1052 | { |
1053 | struct ActiveLookup *n; | ||
799 | (void) cls; | 1054 | (void) cls; |
800 | struct ResolveCache *pos = cache_head; | ||
801 | 1055 | ||
802 | while (NULL != pos) | 1056 | GNUNET_assert (c == internal_cls); |
1057 | n = lookup_head; | ||
1058 | for (struct ActiveLookup *al = n; | ||
1059 | NULL != al; | ||
1060 | al = n) | ||
803 | { | 1061 | { |
804 | if (pos->client == c) | 1062 | n = al->next; |
805 | { | 1063 | if (al->client == c) |
806 | pos->client = NULL; | 1064 | free_active_lookup (al); |
807 | } | ||
808 | pos = pos->next; | ||
809 | } | 1065 | } |
810 | GNUNET_assert (c == internal_cls); | ||
811 | } | 1066 | } |
812 | 1067 | ||
813 | 1068 | ||