1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23#include "setup.h"
24
25#include <string.h>
26
27#ifdef HAVE_SYS_SOCKET_H
28#include <sys/socket.h>
29#endif
30#ifdef HAVE_NETINET_IN_H
31#include <netinet/in.h>
32#endif
33#ifdef HAVE_NETDB_H
34#include <netdb.h>
35#endif
36#ifdef HAVE_ARPA_INET_H
37#include <arpa/inet.h>
38#endif
39#ifdef HAVE_STDLIB_H
40#include <stdlib.h>     /* required for free() prototypes */
41#endif
42#ifdef HAVE_UNISTD_H
43#include <unistd.h>     /* for the close() proto */
44#endif
45#ifdef __VMS
46#include <in.h>
47#include <inet.h>
48#include <stdlib.h>
49#endif
50
51#ifdef HAVE_SETJMP_H
52#include <setjmp.h>
53#endif
54#ifdef HAVE_SIGNAL_H
55#include <signal.h>
56#endif
57
58#ifdef HAVE_PROCESS_H
59#include <process.h>
60#endif
61
62#include "urldata.h"
63#include "sendf.h"
64#include "hostip.h"
65#include "hash.h"
66#include "share.h"
67#include "strerror.h"
68#include "url.h"
69#include "inet_ntop.h"
70#include "warnless.h"
71
72#define _MPRINTF_REPLACE /* use our functions only */
73#include <curl/mprintf.h>
74
75#include "curl_memory.h"
76/* The last #include file should be: */
77#include "memdebug.h"
78
79#if defined(CURLRES_SYNCH) && \
80    defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
81/* alarm-based timeouts can only be used with all the dependencies satisfied */
82#define USE_ALARM_TIMEOUT
83#endif
84
85/*
86 * hostip.c explained
87 * ==================
88 *
89 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
90 * source file are these:
91 *
92 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
93 * that. The host may not be able to resolve IPv6, but we don't really have to
94 * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
95 * defined.
96 *
97 * CURLRES_ARES - is defined if libcurl is built to use c-ares for
98 * asynchronous name resolves. This can be Windows or *nix.
99 *
100 * CURLRES_THREADED - is defined if libcurl is built to run under (native)
101 * Windows, and then the name resolve will be done in a new thread, and the
102 * supported API will be the same as for ares-builds.
103 *
104 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
105 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
106 * defined.
107 *
108 * The host*.c sources files are split up like this:
109 *
110 * hostip.c   - method-independent resolver functions and utility functions
111 * hostasyn.c - functions for asynchronous name resolves
112 * hostsyn.c  - functions for synchronous name resolves
113 * hostip4.c  - ipv4-specific functions
114 * hostip6.c  - ipv6-specific functions
115 *
116 * The two asynchronous name resolver backends are implemented in:
117 * asyn-ares.c   - functions for ares-using name resolves
118 * asyn-thread.c - functions for threaded name resolves
119
120 * The hostip.h is the united header file for all this. It defines the
121 * CURLRES_* defines based on the config*.h and setup.h defines.
122 */
123
124/* These two symbols are for the global DNS cache */
125static struct curl_hash hostname_cache;
126static int host_cache_initialized;
127
128static void freednsentry(void *freethis);
129
130/*
131 * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
132 * Global DNS cache is general badness. Do not use. This will be removed in
133 * a future version. Use the share interface instead!
134 *
135 * Returns a struct curl_hash pointer on success, NULL on failure.
136 */
137struct curl_hash *Curl_global_host_cache_init(void)
138{
139  int rc = 0;
140  if(!host_cache_initialized) {
141    rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
142                        Curl_str_key_compare, freednsentry);
143    if(!rc)
144      host_cache_initialized = 1;
145  }
146  return rc?NULL:&hostname_cache;
147}
148
149/*
150 * Destroy and cleanup the global DNS cache
151 */
152void Curl_global_host_cache_dtor(void)
153{
154  if(host_cache_initialized) {
155    Curl_hash_clean(&hostname_cache);
156    host_cache_initialized = 0;
157  }
158}
159
160/*
161 * Return # of adresses in a Curl_addrinfo struct
162 */
163int Curl_num_addresses(const Curl_addrinfo *addr)
164{
165  int i = 0;
166  while(addr) {
167    addr = addr->ai_next;
168    i++;
169  }
170  return i;
171}
172
173/*
174 * Curl_printable_address() returns a printable version of the 1st address
175 * given in the 'ai' argument. The result will be stored in the buf that is
176 * bufsize bytes big.
177 *
178 * If the conversion fails, it returns NULL.
179 */
180const char *
181Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
182{
183  const struct sockaddr_in *sa4;
184  const struct in_addr *ipaddr4;
185#ifdef ENABLE_IPV6
186  const struct sockaddr_in6 *sa6;
187  const struct in6_addr *ipaddr6;
188#endif
189
190  switch (ai->ai_family) {
191    case AF_INET:
192      sa4 = (const void *)ai->ai_addr;
193      ipaddr4 = &sa4->sin_addr;
194      return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf,
195                            bufsize);
196#ifdef ENABLE_IPV6
197    case AF_INET6:
198      sa6 = (const void *)ai->ai_addr;
199      ipaddr6 = &sa6->sin6_addr;
200      return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf,
201                            bufsize);
202#endif
203    default:
204      break;
205  }
206  return NULL;
207}
208
209/*
210 * Return a hostcache id string for the providing host + port, to be used by
211 * the DNS caching.
212 */
213static char *
214create_hostcache_id(const char *server, int port)
215{
216  /* create and return the new allocated entry */
217  return aprintf("%s:%d", server, port);
218}
219
220struct hostcache_prune_data {
221  long cache_timeout;
222  time_t now;
223};
224
225/*
226 * This function is set as a callback to be called for every entry in the DNS
227 * cache when we want to prune old unused entries.
228 *
229 * Returning non-zero means remove the entry, return 0 to keep it in the
230 * cache.
231 */
232static int
233hostcache_timestamp_remove(void *datap, void *hc)
234{
235  struct hostcache_prune_data *data =
236    (struct hostcache_prune_data *) datap;
237  struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
238
239  return (data->now - c->timestamp >= data->cache_timeout);
240}
241
242/*
243 * Prune the DNS cache. This assumes that a lock has already been taken.
244 */
245static void
246hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
247{
248  struct hostcache_prune_data user;
249
250  user.cache_timeout = cache_timeout;
251  user.now = now;
252
253  Curl_hash_clean_with_criterium(hostcache,
254                                 (void *) &user,
255                                 hostcache_timestamp_remove);
256}
257
258/*
259 * Library-wide function for pruning the DNS cache. This function takes and
260 * returns the appropriate locks.
261 */
262void Curl_hostcache_prune(struct SessionHandle *data)
263{
264  time_t now;
265
266  if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
267    /* cache forever means never prune, and NULL hostcache means
268       we can't do it */
269    return;
270
271  if(data->share)
272    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
273
274  time(&now);
275
276  /* Remove outdated and unused entries from the hostcache */
277  hostcache_prune(data->dns.hostcache,
278                  data->set.dns_cache_timeout,
279                  now);
280
281  if(data->share)
282    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
283}
284
285/*
286 * Check if the entry should be pruned. Assumes a locked cache.
287 */
288static int
289remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
290{
291  struct hostcache_prune_data user;
292
293  if(!dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
294    /* cache forever means never prune, and NULL hostcache means
295       we can't do it */
296    return 0;
297
298  time(&user.now);
299  user.cache_timeout = data->set.dns_cache_timeout;
300
301  if(!hostcache_timestamp_remove(&user,dns) )
302    return 0;
303
304  Curl_hash_clean_with_criterium(data->dns.hostcache,
305                                 (void *) &user,
306                                 hostcache_timestamp_remove);
307
308  return 1;
309}
310
311
312#ifdef HAVE_SIGSETJMP
313/* Beware this is a global and unique instance. This is used to store the
314   return address that we can jump back to from inside a signal handler. This
315   is not thread-safe stuff. */
316sigjmp_buf curl_jmpenv;
317#endif
318
319
320/*
321 * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
322 *
323 * When calling Curl_resolv() has resulted in a response with a returned
324 * address, we call this function to store the information in the dns
325 * cache etc
326 *
327 * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
328 */
329struct Curl_dns_entry *
330Curl_cache_addr(struct SessionHandle *data,
331                Curl_addrinfo *addr,
332                const char *hostname,
333                int port)
334{
335  char *entry_id;
336  size_t entry_len;
337  struct Curl_dns_entry *dns;
338  struct Curl_dns_entry *dns2;
339
340  /* Create an entry id, based upon the hostname and port */
341  entry_id = create_hostcache_id(hostname, port);
342  /* If we can't create the entry id, fail */
343  if(!entry_id)
344    return NULL;
345  entry_len = strlen(entry_id);
346
347  /* Create a new cache entry */
348  dns = calloc(1, sizeof(struct Curl_dns_entry));
349  if(!dns) {
350    free(entry_id);
351    return NULL;
352  }
353
354  dns->inuse = 0;   /* init to not used */
355  dns->addr = addr; /* this is the address(es) */
356  time(&dns->timestamp);
357  if(dns->timestamp == 0)
358    dns->timestamp = 1;   /* zero indicates that entry isn't in hash table */
359
360  /* Store the resolved data in our DNS cache. */
361  dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
362                       (void *)dns);
363  if(!dns2) {
364    free(dns);
365    free(entry_id);
366    return NULL;
367  }
368
369  dns = dns2;
370  dns->inuse++;         /* mark entry as in-use */
371
372  /* free the allocated entry_id */
373  free(entry_id);
374
375  return dns;
376}
377
378/*
379 * Curl_resolv() is the main name resolve function within libcurl. It resolves
380 * a name and returns a pointer to the entry in the 'entry' argument (if one
381 * is provided). This function might return immediately if we're using asynch
382 * resolves. See the return codes.
383 *
384 * The cache entry we return will get its 'inuse' counter increased when this
385 * function is used. You MUST call Curl_resolv_unlock() later (when you're
386 * done using this struct) to decrease the counter again.
387 *
388 * In debug mode, we specifically test for an interface name "LocalHost"
389 * and resolve "localhost" instead as a means to permit test cases
390 * to connect to a local test server with any host name.
391 *
392 * Return codes:
393 *
394 * CURLRESOLV_ERROR   (-1) = error, no pointer
395 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
396 * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
397 */
398
399int Curl_resolv(struct connectdata *conn,
400                const char *hostname,
401                int port,
402                struct Curl_dns_entry **entry)
403{
404  char *entry_id = NULL;
405  struct Curl_dns_entry *dns = NULL;
406  size_t entry_len;
407  struct SessionHandle *data = conn->data;
408  CURLcode result;
409  int rc = CURLRESOLV_ERROR; /* default to failure */
410
411  *entry = NULL;
412
413  /* Create an entry id, based upon the hostname and port */
414  entry_id = create_hostcache_id(hostname, port);
415  /* If we can't create the entry id, fail */
416  if(!entry_id)
417    return rc;
418
419  entry_len = strlen(entry_id);
420
421  if(data->share)
422    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
423
424  /* See if its already in our dns cache */
425  dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
426
427  /* free the allocated entry_id again */
428  free(entry_id);
429
430  /* See whether the returned entry is stale. Done before we release lock */
431  if(remove_entry_if_stale(data, dns))
432    dns = NULL; /* the memory deallocation is being handled by the hash */
433
434  if(dns) {
435    dns->inuse++; /* we use it! */
436    rc = CURLRESOLV_RESOLVED;
437  }
438
439  if(data->share)
440    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
441
442  if(!dns) {
443    /* The entry was not in the cache. Resolve it to IP address */
444
445    Curl_addrinfo *addr;
446    int respwait;
447
448    /* Check what IP specifics the app has requested and if we can provide it.
449     * If not, bail out. */
450    if(!Curl_ipvalid(conn))
451      return CURLRESOLV_ERROR;
452
453    /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
454       non-zero value indicating that we need to wait for the response to the
455       resolve call */
456    addr = Curl_getaddrinfo(conn,
457#ifdef DEBUGBUILD
458                            (data->set.str[STRING_DEVICE]
459                             && !strcmp(data->set.str[STRING_DEVICE],
460                                        "LocalHost"))?"localhost":
461#endif
462                            hostname, port, &respwait);
463
464    if(!addr) {
465      if(respwait) {
466        /* the response to our resolve call will come asynchronously at
467           a later time, good or bad */
468        /* First, check that we haven't received the info by now */
469        result = Curl_resolver_is_resolved(conn, &dns);
470        if(result) /* error detected */
471          return CURLRESOLV_ERROR;
472        if(dns)
473          rc = CURLRESOLV_RESOLVED; /* pointer provided */
474        else
475          rc = CURLRESOLV_PENDING; /* no info yet */
476      }
477    }
478    else {
479      if(data->share)
480        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
481
482      /* we got a response, store it in the cache */
483      dns = Curl_cache_addr(data, addr, hostname, port);
484
485      if(data->share)
486        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
487
488      if(!dns)
489        /* returned failure, bail out nicely */
490        Curl_freeaddrinfo(addr);
491      else
492        rc = CURLRESOLV_RESOLVED;
493    }
494  }
495
496  *entry = dns;
497
498  return rc;
499}
500
501#ifdef USE_ALARM_TIMEOUT
502/*
503 * This signal handler jumps back into the main libcurl code and continues
504 * execution.  This effectively causes the remainder of the application to run
505 * within a signal handler which is nonportable and could lead to problems.
506 */
507static
508RETSIGTYPE alarmfunc(int sig)
509{
510  /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
511  (void)sig;
512  siglongjmp(curl_jmpenv, 1);
513  return;
514}
515#endif /* USE_ALARM_TIMEOUT */
516
517/*
518 * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
519 * timeout.  This function might return immediately if we're using asynch
520 * resolves. See the return codes.
521 *
522 * The cache entry we return will get its 'inuse' counter increased when this
523 * function is used. You MUST call Curl_resolv_unlock() later (when you're
524 * done using this struct) to decrease the counter again.
525 *
526 * If built with a synchronous resolver and use of signals is not
527 * disabled by the application, then a nonzero timeout will cause a
528 * timeout after the specified number of milliseconds. Otherwise, timeout
529 * is ignored.
530 *
531 * Return codes:
532 *
533 * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
534 * CURLRESOLV_ERROR   (-1) = error, no pointer
535 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
536 * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
537 */
538
539int Curl_resolv_timeout(struct connectdata *conn,
540                        const char *hostname,
541                        int port,
542                        struct Curl_dns_entry **entry,
543                        long timeoutms)
544{
545#ifdef USE_ALARM_TIMEOUT
546#ifdef HAVE_SIGACTION
547  struct sigaction keep_sigact;   /* store the old struct here */
548  volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */
549  struct sigaction sigact;
550#else
551#ifdef HAVE_SIGNAL
552  void (*keep_sigact)(int);       /* store the old handler here */
553#endif /* HAVE_SIGNAL */
554#endif /* HAVE_SIGACTION */
555  volatile long timeout;
556  volatile unsigned int prev_alarm = 0;
557  struct SessionHandle *data = conn->data;
558#endif /* USE_ALARM_TIMEOUT */
559  int rc;
560
561  *entry = NULL;
562
563#ifdef USE_ALARM_TIMEOUT
564  if(data->set.no_signal)
565    /* Ignore the timeout when signals are disabled */
566    timeout = 0;
567  else
568    timeout = timeoutms;
569
570  if(!timeout)
571    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
572    return Curl_resolv(conn, hostname, port, entry);
573
574  if(timeout < 1000)
575    /* The alarm() function only provides integer second resolution, so if
576       we want to wait less than one second we must bail out already now. */
577    return CURLRESOLV_TIMEDOUT;
578
579  /*************************************************************
580   * Set signal handler to catch SIGALRM
581   * Store the old value to be able to set it back later!
582   *************************************************************/
583#ifdef HAVE_SIGACTION
584  sigaction(SIGALRM, NULL, &sigact);
585  keep_sigact = sigact;
586  keep_copysig = TRUE; /* yes, we have a copy */
587  sigact.sa_handler = alarmfunc;
588#ifdef SA_RESTART
589  /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
590  sigact.sa_flags &= ~SA_RESTART;
591#endif
592  /* now set the new struct */
593  sigaction(SIGALRM, &sigact, NULL);
594#else /* HAVE_SIGACTION */
595  /* no sigaction(), revert to the much lamer signal() */
596#ifdef HAVE_SIGNAL
597  keep_sigact = signal(SIGALRM, alarmfunc);
598#endif
599#endif /* HAVE_SIGACTION */
600
601  /* alarm() makes a signal get sent when the timeout fires off, and that
602     will abort system calls */
603  prev_alarm = alarm(curlx_sltoui(timeout/1000L));
604
605  /* This allows us to time-out from the name resolver, as the timeout
606     will generate a signal and we will siglongjmp() from that here.
607     This technique has problems (see alarmfunc).
608     This should be the last thing we do before calling Curl_resolv(),
609     as otherwise we'd have to worry about variables that get modified
610     before we invoke Curl_resolv() (and thus use "volatile"). */
611  if(sigsetjmp(curl_jmpenv, 1)) {
612    /* this is coming from a siglongjmp() after an alarm signal */
613    failf(data, "name lookup timed out");
614    rc = CURLRESOLV_ERROR;
615    goto clean_up;
616  }
617
618#else
619#ifndef CURLRES_ASYNCH
620  if(timeoutms)
621    infof(conn->data, "timeout on name lookup is not supported\n");
622#else
623  (void)timeoutms; /* timeoutms not used with an async resolver */
624#endif
625#endif /* USE_ALARM_TIMEOUT */
626
627  /* Perform the actual name resolution. This might be interrupted by an
628   * alarm if it takes too long.
629   */
630  rc = Curl_resolv(conn, hostname, port, entry);
631
632#ifdef USE_ALARM_TIMEOUT
633clean_up:
634
635  if(!prev_alarm)
636    /* deactivate a possibly active alarm before uninstalling the handler */
637    alarm(0);
638
639#ifdef HAVE_SIGACTION
640  if(keep_copysig) {
641    /* we got a struct as it looked before, now put that one back nice
642       and clean */
643    sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
644  }
645#else
646#ifdef HAVE_SIGNAL
647  /* restore the previous SIGALRM handler */
648  signal(SIGALRM, keep_sigact);
649#endif
650#endif /* HAVE_SIGACTION */
651
652  /* switch back the alarm() to either zero or to what it was before minus
653     the time we spent until now! */
654  if(prev_alarm) {
655    /* there was an alarm() set before us, now put it back */
656    unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
657
658    /* the alarm period is counted in even number of seconds */
659    unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
660
661    if(!alarm_set ||
662       ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
663      /* if the alarm time-left reached zero or turned "negative" (counted
664         with unsigned values), we should fire off a SIGALRM here, but we
665         won't, and zero would be to switch it off so we never set it to
666         less than 1! */
667      alarm(1);
668      rc = CURLRESOLV_TIMEDOUT;
669      failf(data, "Previous alarm fired off!");
670    }
671    else
672      alarm((unsigned int)alarm_set);
673  }
674#endif /* USE_ALARM_TIMEOUT */
675
676  return rc;
677}
678
679/*
680 * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
681 * made, the struct may be destroyed due to pruning. It is important that only
682 * one unlock is made for each Curl_resolv() call.
683 */
684void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
685{
686  DEBUGASSERT(dns && (dns->inuse>0));
687
688  if(data->share)
689    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
690
691  dns->inuse--;
692  /* only free if nobody is using AND it is not in hostcache (timestamp ==
693     0) */
694  if(dns->inuse == 0 && dns->timestamp == 0) {
695    Curl_freeaddrinfo(dns->addr);
696    free(dns);
697  }
698
699  if(data->share)
700    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
701}
702
703/*
704 * File-internal: free a cache dns entry.
705 */
706static void freednsentry(void *freethis)
707{
708  struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
709
710  /* mark the entry as not in hostcache */
711  p->timestamp = 0;
712  if(p->inuse == 0) {
713    Curl_freeaddrinfo(p->addr);
714    free(p);
715  }
716}
717
718/*
719 * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
720 */
721struct curl_hash *Curl_mk_dnscache(void)
722{
723  return Curl_hash_alloc(7, Curl_hash_str, Curl_str_key_compare, freednsentry);
724}
725
726
727