use unique 32-bit IDs for DNS requests to avoid random confusions, handle additional...
[oweals/gnunet.git] / src / util / gnunet-service-resolver.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2007-2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14
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/>.
17 */
18
19 /**
20  * @file util/gnunet-service-resolver.c
21  * @brief code to do DNS resolution
22  * @author Christian Grothoff
23  */
24 #include "platform.h"
25 #include "gnunet_util_lib.h"
26 #include "gnunet_protocols.h"
27 #include "gnunet_statistics_service.h"
28 #include "resolver.h"
29
30
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
45 {
46   /**
47    * This is a doubly linked list.
48    */
49   struct RecordListEntry *next;
50
51   /**
52    * This is a doubly linked list.
53    */
54   struct RecordListEntry *prev;
55
56   /**
57    * Cached data.
58    */
59   struct GNUNET_DNSPARSER_Record *record;
60 };
61
62
63 /**
64  * A cached DNS lookup result.
65  */
66 struct ResolveCache
67 {
68   /**
69    * This is a doubly linked list.
70    */
71   struct ResolveCache *next;
72
73   /**
74    * This is a doubly linked list.
75    */
76   struct ResolveCache *prev;
77
78   /**
79    * Which hostname is this cache for?
80    */
81   char *hostname;
82
83   /**
84    * head of a double linked list containing the lookup results
85    */
86   struct RecordListEntry *records_head;
87
88   /**
89    * tail of a double linked list containing the lookup results
90    */
91   struct RecordListEntry *records_tail;
92
93 };
94
95
96 /**
97  * Information about pending lookups.
98  */
99 struct ActiveLookup
100 {
101   /**
102    * Stored in a DLL.
103    */
104   struct ActiveLookup *next;
105
106   /**
107    * Stored in a DLL.
108    */
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;
115
116   /**
117    * handle for cancelling a request
118    */
119   struct GNUNET_DNSSTUB_RequestSocket *resolve_handle;
120
121   /**
122    * handle for the resolution timeout task
123    */
124   struct GNUNET_SCHEDULER_Task *timeout_task;
125
126   /**
127    * Which hostname are we resolving?
128    */
129   char *hostname;
130
131   /**
132    * If @a record_type is #GNUNET_DNSPARSER_TYPE_ALL, did we go again
133    * for the AAAA records yet?
134    */
135   int did_aaaa;
136
137   /**
138    * type of queried DNS record
139    */
140   uint16_t record_type;
141
142   /**
143    * Unique request ID of a client if a query for this hostname/record_type
144    * is currently pending, undefined otherwise.
145    */
146   uint32_t client_request_id;
147
148   /**
149    * Unique DNS request ID of a client if a query for this hostname/record_type
150    * is currently pending, undefined otherwise.
151    */
152   uint16_t dns_id;
153
154 };
155
156
157 /**
158  * Start of the linked list of cached DNS lookup results.
159  */
160 static struct ResolveCache *cache_head;
161
162 /**
163  * Tail of the linked list of cached DNS lookup results.
164  */
165 static struct ResolveCache *cache_tail;
166
167 /**
168  * Start of the linked list of active DNS lookups.
169  */
170 static struct ActiveLookup *lookup_head;
171
172 /**
173  * Tail of the linked list of active DNS lookups.
174  */
175 static struct ActiveLookup *lookup_tail;
176
177 /**
178  * context of dnsstub library
179  */
180 static struct GNUNET_DNSSTUB_Context *dnsstub_ctx;
181
182 /**
183  * My domain, to be appended to the hostname to get a FQDN.
184  */
185 static char *my_domain;
186
187 /**
188  * How many entries do we have in #cache_head DLL?
189  */
190 static unsigned int cache_size;
191
192
193 /**
194  * Remove @a entry from cache.
195  *
196  * @param rc entry to free
197  */
198 static void
199 free_cache_entry (struct ResolveCache *rc)
200 {
201   struct RecordListEntry *pos;
202
203   while (NULL != (pos = rc->records_head))
204   {
205     GNUNET_CONTAINER_DLL_remove (rc->records_head,
206                                  rc->records_tail,
207                                  pos);
208     GNUNET_DNSPARSER_free_record (pos->record);
209     GNUNET_free (pos->record);
210     GNUNET_free (pos);
211   }
212   GNUNET_free_non_null (rc->hostname);
213   GNUNET_CONTAINER_DLL_remove (cache_head,
214                                cache_tail,
215                                rc);
216   cache_size--;
217   GNUNET_free (rc);
218 }
219
220
221 /**
222  * Release resources associated with @a al
223  *
224  * @param al an active lookup
225  */
226 static void
227 free_active_lookup (struct ActiveLookup *al)
228 {
229   GNUNET_CONTAINER_DLL_remove (lookup_head,
230                                lookup_tail,
231                                al);
232   if (NULL != al->resolve_handle)
233   {
234     GNUNET_DNSSTUB_resolve_cancel (al->resolve_handle);
235     al->resolve_handle = NULL;
236   }
237   if (NULL != al->timeout_task)
238   {
239     GNUNET_SCHEDULER_cancel (al->timeout_task);
240     al->timeout_task = NULL;
241   }
242   GNUNET_free_non_null (al->hostname);
243   GNUNET_free (al);
244 }
245
246
247
248 /**
249  * Find out if the configuration file line contains a string
250  * starting with "nameserver ", and if so, return a copy of
251  * the nameserver's IP.
252  *
253  * @param line line to parse
254  * @param line_len number of characters in @a line
255  * @return NULL if no nameserver is configured in this @a line
256  */
257 static char *
258 extract_dns_server (const char* line,
259                     size_t line_len)
260 {
261   if (0 == strncmp (line,
262                     "nameserver ",
263                     strlen ("nameserver ")))
264     return GNUNET_strndup (line + strlen ("nameserver "),
265                            line_len - strlen ("nameserver "));
266   return NULL;
267 }
268
269
270 /**
271  * Find out if the configuration file line contains a string
272  * starting with "search ", and if so, return a copy of
273  * the machine's search domain.
274  *
275  * @param line line to parse
276  * @param line_len number of characters in @a line
277  * @return NULL if no nameserver is configured in this @a line
278  */
279 static char *
280 extract_search_domain (const char* line,
281                        size_t line_len)
282 {
283   if (0 == strncmp (line,
284                     "search ",
285                     strlen ("search ")))
286     return GNUNET_strndup (line + strlen ("search "),
287                            line_len - strlen ("search "));
288   return NULL;
289 }
290
291
292 /**
293  * Reads the list of nameservers from /etc/resolve.conf
294  *
295  * @param server_addrs[out] a list of null-terminated server address strings
296  * @return the number of server addresses in @server_addrs, -1 on error
297  */
298 static int
299 lookup_dns_servers (char ***server_addrs)
300 {
301   struct GNUNET_DISK_FileHandle *fh;
302   char buf[2048];
303   ssize_t bytes_read;
304   size_t read_offset;
305   unsigned int num_dns_servers;
306
307   fh = GNUNET_DISK_file_open ("/etc/resolv.conf",
308                               GNUNET_DISK_OPEN_READ,
309                               GNUNET_DISK_PERM_NONE);
310   if (NULL == fh)
311   {
312     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
313                 "Could not open /etc/resolv.conf. "
314                 "DNS resolution will not be possible.\n");
315     return -1;
316   }
317   bytes_read = GNUNET_DISK_file_read (fh,
318                                       buf,
319                                       sizeof (buf));
320   *server_addrs = NULL;
321   read_offset = 0;
322   num_dns_servers = 0;
323   while (read_offset < bytes_read)
324   {
325     const char *newline;
326     size_t line_len;
327     char *dns_server;
328
329     newline = strchr (buf + read_offset,
330                       '\n');
331     if (NULL == newline)
332       break;
333     line_len = newline - buf - read_offset;
334     dns_server = extract_dns_server (buf + read_offset,
335                                      line_len);
336     if (NULL != dns_server)
337     {
338       GNUNET_array_append (*server_addrs,
339                            num_dns_servers,
340                            dns_server);
341     }
342     else if (NULL == my_domain)
343     {
344       my_domain = extract_search_domain (buf + read_offset,
345                                          line_len);
346     }
347     read_offset += line_len + 1;
348   }
349   GNUNET_DISK_file_close (fh);
350   return (int) num_dns_servers;
351 }
352
353
354 /**
355  * Compute name to use for DNS reverse lookups from @a ip.
356  *
357  * @param ip IP address to resolve, in binary format, network byte order
358  * @param af address family of @a ip, AF_INET or AF_INET6
359  */
360 static char *
361 make_reverse_hostname (const void *ip,
362                        int af)
363 {
364   char *buf = GNUNET_new_array (80,
365                                 char);
366   int pos = 0;
367
368   if (AF_INET == af)
369   {
370     struct in_addr *addr = (struct in_addr *)ip;
371     uint32_t ip_int = addr->s_addr;
372
373     for (int i = 3; i >= 0; i--)
374     {
375       int n = GNUNET_snprintf (buf + pos,
376                                80 - pos,
377                                "%u.",
378                                ((uint8_t *)&ip_int)[i]);
379       if (n < 0)
380       {
381         GNUNET_free (buf);
382         return NULL;
383       }
384       pos += n;
385     }
386     pos += GNUNET_snprintf (buf + pos,
387                             80 - pos,
388                             "in-addr.arpa");
389   }
390   else if (AF_INET6 == af)
391   {
392     struct in6_addr *addr = (struct in6_addr *)ip;
393     for (int i = 15; i >= 0; i--)
394     {
395       int n = GNUNET_snprintf (buf + pos,
396                                80 - pos,
397                                "%x.",
398                                addr->s6_addr[i] & 0xf);
399       if (n < 0)
400       {
401         GNUNET_free (buf);
402         return NULL;
403       }
404       pos += n;
405       n = GNUNET_snprintf (buf + pos,
406                            80 - pos,
407                            "%x.",
408                            addr->s6_addr[i] >> 4);
409       if (n < 0)
410       {
411         GNUNET_free (buf);
412         return NULL;
413       }
414       pos += n;
415     }
416     pos += GNUNET_snprintf (buf + pos,
417                             80 - pos,
418                             "ip6.arpa");
419   }
420   buf[pos] = '\0';
421   return buf;
422 }
423
424
425 /**
426  * Send DNS @a record back to our @a client.
427  *
428  * @param record information to transmit
429  * @param record_type requested record type from client
430  * @param client_request_id to which request are we responding
431  * @param client where to send @a record
432  * @return #GNUNET_YES if we sent a reply,
433  *         #GNUNET_NO if the record type is not understood or
434  *         does not match @a record_type
435  */
436 static int
437 send_reply (struct GNUNET_DNSPARSER_Record *record,
438             uint16_t record_type,
439             uint32_t client_request_id,
440             struct GNUNET_SERVICE_Client *client)
441 {
442   struct GNUNET_RESOLVER_ResponseMessage *msg;
443   struct GNUNET_MQ_Envelope *env;
444   const void *payload;
445   size_t payload_len;
446
447   switch (record->type)
448   {
449   case GNUNET_DNSPARSER_TYPE_CNAME:
450     if (GNUNET_DNSPARSER_TYPE_CNAME != record_type)
451       return GNUNET_NO;
452     payload = record->data.hostname;
453     payload_len = strlen (record->data.hostname) + 1;
454     break;
455   case GNUNET_DNSPARSER_TYPE_PTR:
456     if (GNUNET_DNSPARSER_TYPE_PTR != record_type)
457       return GNUNET_NO;
458     payload = record->data.hostname;
459     payload_len = strlen (record->data.hostname) + 1;
460     break;
461   case GNUNET_DNSPARSER_TYPE_A:
462     if ( (GNUNET_DNSPARSER_TYPE_A != record_type) &&
463          (GNUNET_DNSPARSER_TYPE_ALL != record_type) )
464       return GNUNET_NO;
465     payload = record->data.raw.data;
466     payload_len = record->data.raw.data_len;
467     break;
468   case GNUNET_DNSPARSER_TYPE_AAAA:
469     if ( (GNUNET_DNSPARSER_TYPE_AAAA != record_type) &&
470          (GNUNET_DNSPARSER_TYPE_ALL != record_type) )
471       return GNUNET_NO;
472     payload = record->data.raw.data;
473     payload_len = record->data.raw.data_len;
474     break;
475   default:
476     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
477                 "Cannot handle DNS response type %u: not supported here\n",
478                 record->type);
479     return GNUNET_NO;
480   }
481   env = GNUNET_MQ_msg_extra (msg,
482                              payload_len,
483                              GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
484   msg->client_id = client_request_id;
485   GNUNET_memcpy (&msg[1],
486                  payload,
487                  payload_len);
488   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
489                   env);
490   return GNUNET_YES;
491 }
492
493
494 /**
495  * Send message to @a client that we transmitted all
496  * responses for @a client_request_id
497  *
498  * @param client_request_id to which request are we responding
499  * @param client where to send @a record
500  */
501 static void
502 send_end_msg (uint32_t client_request_id,
503               struct GNUNET_SERVICE_Client *client)
504 {
505   struct GNUNET_RESOLVER_ResponseMessage *msg;
506   struct GNUNET_MQ_Envelope *env;
507
508   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
509               "Sending END message\n");
510   env = GNUNET_MQ_msg (msg,
511                        GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
512   msg->client_id = client_request_id;
513   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
514                   env);
515 }
516
517
518 /**
519  * Remove expired entries from @a rc
520  *
521  * @param rc entry in resolver cache
522  * @return #GNUNET_YES if @a rc was completely expired
523  *         #GNUNET_NO if some entries are left
524  */
525 static int
526 remove_expired (struct ResolveCache *rc)
527 {
528   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
529   struct RecordListEntry *n;
530
531   for (struct RecordListEntry *pos = rc->records_head;
532        NULL != pos;
533        pos = n)
534   {
535     n = pos->next;
536     if (now.abs_value_us > pos->record->expiration_time.abs_value_us)
537       GNUNET_CONTAINER_DLL_remove (rc->records_head,
538                                    rc->records_tail,
539                                    pos);
540   }
541   if (NULL == rc->records_head)
542   {
543     free_cache_entry (rc);
544     return GNUNET_YES;
545   }
546   return GNUNET_NO;
547 }
548
549
550 /**
551  * Process DNS request for @a hostname with request ID @a request_id
552  * from @a client demanding records of type @a record_type.
553  *
554  * @param hostname DNS name to resolve
555  * @param record_type desired record type
556  * @param client_request_id client's request ID
557  * @param client who should get the result?
558  */
559 static void
560 process_get (const char *hostname,
561              uint16_t record_type,
562              uint32_t client_request_id,
563              struct GNUNET_SERVICE_Client *client);
564
565
566 /**
567  * Get an IP address as a string (works for both IPv4 and IPv6).  Note
568  * that the resolution happens asynchronously and that the first call
569  * may not immediately result in the FQN (but instead in a
570  * human-readable IP address).
571  *
572  * @param hostname what hostname was to be resolved
573  * @param record_type what type of record was requested
574  * @param client_request_id unique identification of the client's request
575  * @param client handle to the client making the request (for sending the reply)
576  */
577 static int
578 try_cache (const char *hostname,
579            uint16_t record_type,
580            uint32_t client_request_id,
581            struct GNUNET_SERVICE_Client *client)
582 {
583   struct ResolveCache *pos;
584   struct ResolveCache *next;
585   int found;
586
587   next = cache_head;
588   for (pos = next; NULL != pos; pos = next)
589   {
590     next = pos->next;
591     if (GNUNET_YES == remove_expired (pos))
592       continue;
593     if (0 == strcmp (pos->hostname,
594                      hostname))
595       break;
596   }
597   if (NULL == pos)
598   {
599     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
600                 "No cache entry for '%s'\n",
601                 hostname);
602     return GNUNET_NO;
603   }
604   if (cache_head != pos)
605   {
606     /* move result to head to achieve LRU for cache eviction */
607     GNUNET_CONTAINER_DLL_remove (cache_head,
608                                  cache_tail,
609                                  pos);
610     GNUNET_CONTAINER_DLL_insert (cache_head,
611                                  cache_tail,
612                                  pos);
613   }
614   found = GNUNET_NO;
615   for (struct RecordListEntry *rle = pos->records_head;
616        NULL != rle;
617        rle = rle->next)
618   {
619     const struct GNUNET_DNSPARSER_Record *record = rle->record;
620
621     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
622                 "Found cache entry for '%s', record type '%u'\n",
623                 hostname,
624                 record_type);
625     if ( (GNUNET_DNSPARSER_TYPE_CNAME == record->type) &&
626          (GNUNET_DNSPARSER_TYPE_CNAME != record_type) &&
627          (GNUNET_NO == found) )
628     {
629       const char *hostname = record->data.hostname;
630
631       process_get (hostname,
632                    record_type,
633                    client_request_id,
634                    client);
635       return GNUNET_YES; /* counts as a cache "hit" */
636     }
637     found |= send_reply (rle->record,
638                          record_type,
639                          client_request_id,
640                          client);
641   }
642   if (GNUNET_NO == found)
643     return GNUNET_NO; /* had records, but none matched! */
644   send_end_msg (client_request_id,
645                 client);
646   return GNUNET_YES;
647 }
648
649
650 /**
651  * Create DNS query for @a hostname of type @a type
652  * with DNS request ID @a dns_id.
653  *
654  * @param hostname DNS name to query
655  * @param type requested DNS record type
656  * @param dns_id what should be the DNS request ID
657  * @param packet_buf[out] where to write the request packet
658  * @param packet_size[out] set to size of @a packet_buf on success
659  * @return #GNUNET_OK on success
660  */
661 static int
662 pack (const char *hostname,
663       uint16_t type,
664       uint16_t dns_id,
665       char **packet_buf,
666       size_t *packet_size)
667 {
668   struct GNUNET_DNSPARSER_Query query;
669   struct GNUNET_DNSPARSER_Packet packet;
670
671   query.name = (char *)hostname;
672   query.type = type;
673   query.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
674   memset (&packet,
675           0,
676           sizeof (packet));
677   packet.num_queries = 1;
678   packet.queries = &query;
679   packet.id = htons (dns_id);
680   packet.flags.recursion_desired = 1;
681   if (GNUNET_OK !=
682       GNUNET_DNSPARSER_pack (&packet,
683                              UINT16_MAX,
684                              packet_buf,
685                              packet_size))
686   {
687     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
688                 "Failed to pack query for hostname `%s'\n",
689                 hostname);
690     packet_buf = NULL;
691     return GNUNET_SYSERR;
692   }
693   return GNUNET_OK;
694 }
695
696
697 /**
698  * We got a result from DNS. Add it to the cache and
699  * see if we can make our client happy...
700  *
701  * @param cls the `struct ActiveLookup`
702  * @param dns the DNS response
703  * @param dns_len number of bytes in @a dns
704  */
705 static void
706 handle_resolve_result (void *cls,
707                        const struct GNUNET_TUN_DnsHeader *dns,
708                        size_t dns_len)
709 {
710   struct ActiveLookup *al = cls;
711   struct GNUNET_DNSPARSER_Packet *parsed;
712   struct ResolveCache *rc;
713
714   parsed = GNUNET_DNSPARSER_parse ((const char *)dns,
715                                    dns_len);
716   if (NULL == parsed)
717   {
718     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
719                 "Failed to parse DNS reply (hostname %s, request ID %u)\n",
720                 al->hostname,
721                 al->dns_id);
722     return;
723   }
724   if (al->dns_id != ntohs (parsed->id))
725   {
726     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
727                 "Request ID in DNS reply does not match\n");
728     GNUNET_DNSPARSER_free_packet (parsed);
729     return;
730   }
731   if (0 == parsed->num_answers + parsed->num_authority_records + parsed->num_additional_records)
732   {
733     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
734                 "DNS reply (hostname %s, request ID %u) contains no answers\n",
735                 al->hostname,
736                 (unsigned int) al->client_request_id);
737     GNUNET_DNSPARSER_free_packet (parsed);
738     send_end_msg (al->client_request_id,
739                   al->client);
740     free_active_lookup (al);
741     return;
742   }
743   /* LRU-based cache eviction: we remove from tail */
744   while (cache_size > MAX_CACHE)
745     free_cache_entry (cache_tail);
746
747   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
748               "Got reply for hostname %s and request ID %u\n",
749               al->hostname,
750               (unsigned int) al->client_request_id);
751   /* add to cache */
752   for (unsigned int i = 0; i != parsed->num_answers; i++)
753   {
754     struct GNUNET_DNSPARSER_Record *record = &parsed->answers[i];
755     struct RecordListEntry *rle;
756
757     for (rc = cache_head; NULL != rc; rc = rc->next)
758       if (0 == strcasecmp (rc->hostname,
759                            record->name))
760         break;
761     if (NULL == rc)
762     {
763       rc = GNUNET_new (struct ResolveCache);
764       rc->hostname = GNUNET_strdup (record->name);
765       GNUNET_CONTAINER_DLL_insert (cache_head,
766                                    cache_tail,
767                                    rc);
768       cache_size++;
769     }
770     /* TODO: ought to check first if we have this exact record
771        already in the cache! */
772     rle = GNUNET_new (struct RecordListEntry);
773     rle->record = GNUNET_DNSPARSER_duplicate_record (record);
774     GNUNET_CONTAINER_DLL_insert (rc->records_head,
775                                  rc->records_tail,
776                                  rle);
777   }
778   for (unsigned int i = 0; i != parsed->num_authority_records; i++)
779   {
780     struct GNUNET_DNSPARSER_Record *record = &parsed->authority_records[i];
781     struct RecordListEntry *rle;
782
783     for (rc = cache_head; NULL != rc; rc = rc->next)
784       if (0 == strcasecmp (rc->hostname,
785                            record->name))
786         break;
787     if (NULL == rc)
788     {
789       rc = GNUNET_new (struct ResolveCache);
790       rc->hostname = GNUNET_strdup (record->name);
791       GNUNET_CONTAINER_DLL_insert (cache_head,
792                                    cache_tail,
793                                    rc);
794       cache_size++;
795     }
796     /* TODO: ought to check first if we have this exact record
797        already in the cache! */
798     rle = GNUNET_new (struct RecordListEntry);
799     rle->record = GNUNET_DNSPARSER_duplicate_record (record);
800     GNUNET_CONTAINER_DLL_insert (rc->records_head,
801                                  rc->records_tail,
802                                  rle);
803   }
804   for (unsigned int i = 0; i != parsed->num_additional_records; i++)
805   {
806     struct GNUNET_DNSPARSER_Record *record = &parsed->additional_records[i];
807     struct RecordListEntry *rle;
808
809     for (rc = cache_head; NULL != rc; rc = rc->next)
810       if (0 == strcasecmp (rc->hostname,
811                            record->name))
812         break;
813     if (NULL == rc)
814     {
815       rc = GNUNET_new (struct ResolveCache);
816       rc->hostname = GNUNET_strdup (record->name);
817       GNUNET_CONTAINER_DLL_insert (cache_head,
818                                    cache_tail,
819                                    rc);
820       cache_size++;
821     }
822     /* TODO: ought to check first if we have this exact record
823        already in the cache! */
824     rle = GNUNET_new (struct RecordListEntry);
825     rle->record = GNUNET_DNSPARSER_duplicate_record (record);
826     GNUNET_CONTAINER_DLL_insert (rc->records_head,
827                                  rc->records_tail,
828                                  rle);
829   }
830   /* see if we need to do the 2nd request for AAAA records */
831   if ( (GNUNET_DNSPARSER_TYPE_ALL == al->record_type) &&
832        (GNUNET_NO == al->did_aaaa) )
833   {
834     char *packet_buf;
835     size_t packet_size;
836     uint16_t dns_id;
837
838     dns_id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
839                                                   UINT16_MAX);
840     if (GNUNET_OK ==
841         pack (al->hostname,
842               GNUNET_DNSPARSER_TYPE_AAAA,
843               dns_id,
844               &packet_buf,
845               &packet_size))
846     {
847       al->did_aaaa = GNUNET_YES;
848       al->dns_id = dns_id;
849       GNUNET_DNSSTUB_resolve_cancel (al->resolve_handle);
850       al->resolve_handle =
851         GNUNET_DNSSTUB_resolve (dnsstub_ctx,
852                                 packet_buf,
853                                 packet_size,
854                                 &handle_resolve_result,
855                                 al);
856       return;
857     }
858   }
859
860   /* resume by trying again from cache */
861   if (GNUNET_NO ==
862       try_cache (al->hostname,
863                  al->record_type,
864                  al->client_request_id,
865                  al->client))
866     /* cache failed, tell client we could not get an answer */
867   {
868     send_end_msg (al->client_request_id,
869                   al->client);
870   }
871   free_active_lookup (al);
872   GNUNET_DNSPARSER_free_packet (parsed);
873 }
874
875
876 /**
877  * We encountered a timeout trying to perform a
878  * DNS lookup.
879  *
880  * @param cls a `struct ActiveLookup`
881  */
882 static void
883 handle_resolve_timeout (void *cls)
884 {
885   struct ActiveLookup *al = cls;
886
887   al->timeout_task = NULL;
888   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
889               "DNS lookup timeout!\n");
890   send_end_msg (al->client_request_id,
891                 al->client);
892   free_active_lookup (al);
893 }
894
895
896 /**
897  * Initiate an active lookup, then cache the result and
898  * try to then complete the resolution.
899  *
900  * @param hostname DNS name to resolve
901  * @param record_type record type to locate
902  * @param client_request_id client request ID
903  * @param client handle to the client
904  * @return #GNUNET_OK if the DNS query is now pending
905  */
906 static int
907 resolve_and_cache (const char* hostname,
908                    uint16_t record_type,
909                    uint32_t client_request_id,
910                    struct GNUNET_SERVICE_Client *client)
911 {
912   char *packet_buf;
913   size_t packet_size;
914   struct ActiveLookup *al;
915   uint16_t dns_id;
916   uint16_t type;
917
918   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
919               "resolve_and_cache `%s'\n",
920               hostname);
921   dns_id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
922                                                 UINT16_MAX);
923
924   if (GNUNET_DNSPARSER_TYPE_ALL == record_type)
925     type = GNUNET_DNSPARSER_TYPE_A;
926   else
927     type = record_type;
928   if (GNUNET_OK !=
929       pack (hostname,
930             type,
931             dns_id,
932             &packet_buf,
933             &packet_size))
934   {
935     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
936                 "Failed to pack query for hostname `%s'\n",
937                 hostname);
938     return GNUNET_SYSERR;
939   }
940
941   al = GNUNET_new (struct ActiveLookup);
942   al->hostname = GNUNET_strdup (hostname);
943   al->record_type = record_type;
944   al->client_request_id = client_request_id;
945   al->dns_id = dns_id;
946   al->client = client;
947   al->timeout_task = GNUNET_SCHEDULER_add_delayed (DNS_TIMEOUT,
948                                                    &handle_resolve_timeout,
949                                                    al);
950   al->resolve_handle =
951     GNUNET_DNSSTUB_resolve (dnsstub_ctx,
952                             packet_buf,
953                             packet_size,
954                             &handle_resolve_result,
955                             al);
956   GNUNET_free (packet_buf);
957   GNUNET_CONTAINER_DLL_insert (lookup_head,
958                                lookup_tail,
959                                al);
960   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
961               "Resolving %s, client_request_id = %u, dns_id = %u\n",
962               hostname,
963               (unsigned int) client_request_id,
964               (unsigned int) dns_id);
965   return GNUNET_OK;
966 }
967
968
969 /**
970  * Process DNS request for @a hostname with request ID @a client_request_id
971  * from @a client demanding records of type @a record_type.
972  *
973  * @param hostname DNS name to resolve
974  * @param record_type desired record type
975  * @param client_request_id client's request ID
976  * @param client who should get the result?
977  */
978 static void
979 process_get (const char *hostname,
980              uint16_t record_type,
981              uint32_t client_request_id,
982              struct GNUNET_SERVICE_Client *client)
983 {
984   char fqdn[255];
985   
986   if (  (NULL != my_domain) &&
987         (NULL == strchr (hostname,
988                          (unsigned char) '.')) &&
989         (strlen (hostname) + strlen (my_domain) <= 253) )
990   {
991     GNUNET_snprintf (fqdn,
992                      sizeof (fqdn),
993                      "%s.%s",
994                      hostname,
995                      my_domain);                     
996   }
997   else if (strlen (hostname) < 255)
998   {
999     GNUNET_snprintf (fqdn,
1000                      sizeof (fqdn),
1001                      "%s",
1002                      hostname);
1003   }
1004   else
1005   {
1006     GNUNET_break (0);
1007     GNUNET_SERVICE_client_drop (client);
1008     return;
1009   }
1010   if (GNUNET_NO ==
1011       try_cache (fqdn,
1012                  record_type,
1013                  client_request_id,
1014                  client))
1015   {
1016     if (GNUNET_OK !=
1017         resolve_and_cache (fqdn,
1018                            record_type,
1019                            client_request_id,
1020                            client))
1021     {
1022       send_end_msg (client_request_id,
1023                     client);
1024     }
1025   }
1026 }
1027
1028
1029 /**
1030  * Verify well-formedness of GET-message.
1031  *
1032  * @param cls closure, unused
1033  * @param get the actual message
1034  * @return #GNUNET_OK if @a get is well-formed
1035  */
1036 static int
1037 check_get (void *cls,
1038            const struct GNUNET_RESOLVER_GetMessage *get)
1039 {
1040   uint16_t size;
1041   int direction;
1042   int af;
1043
1044   (void) cls;
1045   size = ntohs (get->header.size) - sizeof (*get);
1046   direction = ntohl (get->direction);
1047   if (GNUNET_NO == direction)
1048   {
1049     /* IP from hostname */
1050     const char *hostname;
1051
1052     hostname = (const char *) &get[1];
1053     if (hostname[size - 1] != '\0')
1054     {
1055       GNUNET_break (0);
1056       return GNUNET_SYSERR;
1057     }
1058     return GNUNET_OK;
1059   }
1060   af = ntohl (get->af);
1061   switch (af)
1062   {
1063   case AF_INET:
1064     if (size != sizeof (struct in_addr))
1065     {
1066       GNUNET_break (0);
1067       return GNUNET_SYSERR;
1068     }
1069     break;
1070   case AF_INET6:
1071     if (size != sizeof (struct in6_addr))
1072     {
1073       GNUNET_break (0);
1074       return GNUNET_SYSERR;
1075     }
1076     break;
1077   default:
1078     GNUNET_break (0);
1079     return GNUNET_SYSERR;
1080   }
1081   return GNUNET_OK;
1082 }
1083
1084
1085 /**
1086  * Handle GET-message.
1087  *
1088  * @param cls identification of the client
1089  * @param msg the actual message
1090  */
1091 static void
1092 handle_get (void *cls,
1093             const struct GNUNET_RESOLVER_GetMessage *msg)
1094 {
1095   struct GNUNET_SERVICE_Client *client = cls;
1096   int direction;
1097   int af;
1098   uint32_t client_request_id;
1099   char *hostname;
1100
1101   direction = ntohl (msg->direction);
1102   af = ntohl (msg->af);
1103   client_request_id = msg->client_id;
1104   GNUNET_SERVICE_client_continue (client);
1105   if (GNUNET_NO == direction)
1106   {
1107     /* IP from hostname */
1108     hostname = GNUNET_strdup ((const char *) &msg[1]);
1109     switch (af)
1110     {
1111       case AF_UNSPEC:
1112       {
1113         process_get (hostname,
1114                      GNUNET_DNSPARSER_TYPE_ALL,
1115                      client_request_id,
1116                      client);
1117         break;
1118       }
1119       case AF_INET:
1120       {
1121         process_get (hostname,
1122                      GNUNET_DNSPARSER_TYPE_A,
1123                      client_request_id,
1124                      client);
1125         break;
1126       }
1127       case AF_INET6:
1128       {
1129         process_get (hostname,
1130                      GNUNET_DNSPARSER_TYPE_AAAA,
1131                      client_request_id,
1132                      client);
1133         break;
1134       }
1135       default:
1136       {
1137         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1138                     "got invalid af: %d\n",
1139                     af);
1140         GNUNET_assert (0);
1141       }
1142     }
1143   }
1144   else
1145   {
1146     /* hostname from IP */
1147     hostname = make_reverse_hostname (&msg[1],
1148                                       af);
1149     process_get (hostname,
1150                  GNUNET_DNSPARSER_TYPE_PTR,
1151                  client_request_id,
1152                  client);
1153   }
1154   GNUNET_free_non_null (hostname);
1155 }
1156
1157
1158 /**
1159  * Service is shutting down, clean up.
1160  *
1161  * @param cls NULL, unused
1162  */
1163 static void
1164 shutdown_task (void *cls)
1165 {
1166   (void) cls;
1167
1168   while (NULL != lookup_head)
1169     free_active_lookup (lookup_head);
1170   while (NULL != cache_head)
1171     free_cache_entry (cache_head);
1172   GNUNET_DNSSTUB_stop (dnsstub_ctx);
1173 }
1174
1175
1176 /**
1177  * Service is starting, initialize everything.
1178  *
1179  * @param cls NULL, unused
1180  * @param cfg our configuration
1181  * @param sh service handle
1182  */
1183 static void
1184 init_cb (void *cls,
1185          const struct GNUNET_CONFIGURATION_Handle *cfg,
1186          struct GNUNET_SERVICE_Handle *sh)
1187 {
1188   char **dns_servers;
1189   int num_dns_servers;
1190
1191   (void) cfg;
1192   (void) sh;
1193   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1194                                  cls);
1195   dnsstub_ctx = GNUNET_DNSSTUB_start (128);
1196   dns_servers = NULL;
1197   num_dns_servers = lookup_dns_servers (&dns_servers);
1198   if (0 >= num_dns_servers)
1199   {
1200     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1201                 _("No DNS server available. DNS resolution will not be possible.\n"));
1202     return;
1203   }
1204   for (int i = 0; i < num_dns_servers; i++)
1205   {
1206     int result = GNUNET_DNSSTUB_add_dns_ip (dnsstub_ctx, dns_servers[i]);
1207     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1208                 "Adding DNS server '%s': %s\n",
1209                 dns_servers[i],
1210                 GNUNET_OK == result ? "success" : "failure");
1211     GNUNET_free (dns_servers[i]);
1212   }
1213   GNUNET_free_non_null (dns_servers);
1214 }
1215
1216
1217 /**
1218  * Callback called when a client connects to the service.
1219  *
1220  * @param cls closure for the service, unused
1221  * @param c the new client that connected to the service
1222  * @param mq the message queue used to send messages to the client
1223  * @return @a c
1224  */
1225 static void *
1226 connect_cb (void *cls,
1227             struct GNUNET_SERVICE_Client *c,
1228             struct GNUNET_MQ_Handle *mq)
1229 {
1230   (void) cls;
1231   (void) mq;
1232
1233   return c;
1234 }
1235
1236
1237 /**
1238  * Callback called when a client disconnected from the service
1239  *
1240  * @param cls closure for the service
1241  * @param c the client that disconnected
1242  * @param internal_cls should be equal to @a c
1243  */
1244 static void
1245 disconnect_cb (void *cls,
1246                struct GNUNET_SERVICE_Client *c,
1247                void *internal_cls)
1248 {
1249   struct ActiveLookup *n;
1250   (void) cls;
1251
1252   GNUNET_assert (c == internal_cls);
1253   n = lookup_head;
1254   for (struct ActiveLookup *al = n;
1255        NULL != al;
1256        al = n)
1257   {
1258     n = al->next;
1259     if (al->client == c)
1260       free_active_lookup (al);
1261   }
1262 }
1263
1264
1265 /**
1266  * Define "main" method using service macro.
1267  */
1268 GNUNET_SERVICE_MAIN
1269 ("resolver",
1270  GNUNET_SERVICE_OPTION_NONE,
1271  &init_cb,
1272  &connect_cb,
1273  &disconnect_cb,
1274  NULL,
1275  GNUNET_MQ_hd_var_size (get,
1276                         GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST,
1277                         struct GNUNET_RESOLVER_GetMessage,
1278                         NULL),
1279  GNUNET_MQ_handler_end ());
1280
1281
1282 #if defined(LINUX) && defined(__GLIBC__)
1283 #include <malloc.h>
1284
1285 /**
1286  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1287  */
1288 void __attribute__ ((constructor))
1289 GNUNET_RESOLVER_memory_init ()
1290 {
1291   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1292   mallopt (M_TOP_PAD, 1 * 1024);
1293   malloc_trim (0);
1294 }
1295 #endif
1296
1297
1298 /* end of gnunet-service-resolver.c */