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