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