• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2016, 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 https://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 /* The last 3 #include files should be in this order */
60 #include "curl_printf.h"
61 #include "curl_memory.h"
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 Curl_easy * data)257 void Curl_hostcache_prune(struct Curl_easy *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 Curl_easy *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 Curl_easy *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)
357     dns->inuse++; /* we use it! */
358 
359   if(data->share)
360     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
361 
362   return dns;
363 }
364 
365 /*
366  * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
367  *
368  * When calling Curl_resolv() has resulted in a response with a returned
369  * address, we call this function to store the information in the dns
370  * cache etc
371  *
372  * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
373  */
374 struct Curl_dns_entry *
Curl_cache_addr(struct Curl_easy * data,Curl_addrinfo * addr,const char * hostname,int port)375 Curl_cache_addr(struct Curl_easy *data,
376                 Curl_addrinfo *addr,
377                 const char *hostname,
378                 int port)
379 {
380   char *entry_id;
381   size_t entry_len;
382   struct Curl_dns_entry *dns;
383   struct Curl_dns_entry *dns2;
384 
385   /* Create an entry id, based upon the hostname and port */
386   entry_id = create_hostcache_id(hostname, port);
387   /* If we can't create the entry id, fail */
388   if(!entry_id)
389     return NULL;
390   entry_len = strlen(entry_id);
391 
392   /* Create a new cache entry */
393   dns = calloc(1, sizeof(struct Curl_dns_entry));
394   if(!dns) {
395     free(entry_id);
396     return NULL;
397   }
398 
399   dns->inuse = 1;   /* the cache has the first reference */
400   dns->addr = addr; /* this is the address(es) */
401   time(&dns->timestamp);
402   if(dns->timestamp == 0)
403     dns->timestamp = 1;   /* zero indicates CURLOPT_RESOLVE entry */
404 
405   /* Store the resolved data in our DNS cache. */
406   dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
407                        (void *)dns);
408   if(!dns2) {
409     free(dns);
410     free(entry_id);
411     return NULL;
412   }
413 
414   dns = dns2;
415   dns->inuse++;         /* mark entry as in-use */
416 
417   /* free the allocated entry_id */
418   free(entry_id);
419 
420   return dns;
421 }
422 
423 /*
424  * Curl_resolv() is the main name resolve function within libcurl. It resolves
425  * a name and returns a pointer to the entry in the 'entry' argument (if one
426  * is provided). This function might return immediately if we're using asynch
427  * resolves. See the return codes.
428  *
429  * The cache entry we return will get its 'inuse' counter increased when this
430  * function is used. You MUST call Curl_resolv_unlock() later (when you're
431  * done using this struct) to decrease the counter again.
432  *
433  * In debug mode, we specifically test for an interface name "LocalHost"
434  * and resolve "localhost" instead as a means to permit test cases
435  * to connect to a local test server with any host name.
436  *
437  * Return codes:
438  *
439  * CURLRESOLV_ERROR   (-1) = error, no pointer
440  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
441  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
442  */
443 
Curl_resolv(struct connectdata * conn,const char * hostname,int port,struct Curl_dns_entry ** entry)444 int Curl_resolv(struct connectdata *conn,
445                 const char *hostname,
446                 int port,
447                 struct Curl_dns_entry **entry)
448 {
449   struct Curl_dns_entry *dns = NULL;
450   struct Curl_easy *data = conn->data;
451   CURLcode result;
452   int rc = CURLRESOLV_ERROR; /* default to failure */
453 
454   *entry = NULL;
455 
456   if(data->share)
457     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
458 
459   dns = fetch_addr(conn, hostname, port);
460 
461   if(dns) {
462     infof(data, "Hostname %s was found in DNS cache\n", hostname);
463     dns->inuse++; /* we use it! */
464     rc = CURLRESOLV_RESOLVED;
465   }
466 
467   if(data->share)
468     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
469 
470   if(!dns) {
471     /* The entry was not in the cache. Resolve it to IP address */
472 
473     Curl_addrinfo *addr;
474     int respwait;
475 
476     /* Check what IP specifics the app has requested and if we can provide it.
477      * If not, bail out. */
478     if(!Curl_ipvalid(conn))
479       return CURLRESOLV_ERROR;
480 
481     /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
482        non-zero value indicating that we need to wait for the response to the
483        resolve call */
484     addr = Curl_getaddrinfo(conn,
485 #ifdef DEBUGBUILD
486                             (data->set.str[STRING_DEVICE]
487                              && !strcmp(data->set.str[STRING_DEVICE],
488                                         "LocalHost"))?"localhost":
489 #endif
490                             hostname, port, &respwait);
491 
492     if(!addr) {
493       if(respwait) {
494         /* the response to our resolve call will come asynchronously at
495            a later time, good or bad */
496         /* First, check that we haven't received the info by now */
497         result = Curl_resolver_is_resolved(conn, &dns);
498         if(result) /* error detected */
499           return CURLRESOLV_ERROR;
500         if(dns)
501           rc = CURLRESOLV_RESOLVED; /* pointer provided */
502         else
503           rc = CURLRESOLV_PENDING; /* no info yet */
504       }
505     }
506     else {
507       if(data->share)
508         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
509 
510       /* we got a response, store it in the cache */
511       dns = Curl_cache_addr(data, addr, hostname, port);
512 
513       if(data->share)
514         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
515 
516       if(!dns)
517         /* returned failure, bail out nicely */
518         Curl_freeaddrinfo(addr);
519       else
520         rc = CURLRESOLV_RESOLVED;
521     }
522   }
523 
524   *entry = dns;
525 
526   return rc;
527 }
528 
529 #ifdef USE_ALARM_TIMEOUT
530 /*
531  * This signal handler jumps back into the main libcurl code and continues
532  * execution.  This effectively causes the remainder of the application to run
533  * within a signal handler which is nonportable and could lead to problems.
534  */
535 static
alarmfunc(int sig)536 RETSIGTYPE alarmfunc(int sig)
537 {
538   /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
539   (void)sig;
540   siglongjmp(curl_jmpenv, 1);
541   return;
542 }
543 #endif /* USE_ALARM_TIMEOUT */
544 
545 /*
546  * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
547  * timeout.  This function might return immediately if we're using asynch
548  * resolves. See the return codes.
549  *
550  * The cache entry we return will get its 'inuse' counter increased when this
551  * function is used. You MUST call Curl_resolv_unlock() later (when you're
552  * done using this struct) to decrease the counter again.
553  *
554  * If built with a synchronous resolver and use of signals is not
555  * disabled by the application, then a nonzero timeout will cause a
556  * timeout after the specified number of milliseconds. Otherwise, timeout
557  * is ignored.
558  *
559  * Return codes:
560  *
561  * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
562  * CURLRESOLV_ERROR   (-1) = error, no pointer
563  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
564  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
565  */
566 
Curl_resolv_timeout(struct connectdata * conn,const char * hostname,int port,struct Curl_dns_entry ** entry,time_t timeoutms)567 int Curl_resolv_timeout(struct connectdata *conn,
568                         const char *hostname,
569                         int port,
570                         struct Curl_dns_entry **entry,
571                         time_t timeoutms)
572 {
573 #ifdef USE_ALARM_TIMEOUT
574 #ifdef HAVE_SIGACTION
575   struct sigaction keep_sigact;   /* store the old struct here */
576   volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */
577   struct sigaction sigact;
578 #else
579 #ifdef HAVE_SIGNAL
580   void (*keep_sigact)(int);       /* store the old handler here */
581 #endif /* HAVE_SIGNAL */
582 #endif /* HAVE_SIGACTION */
583   volatile long timeout;
584   volatile unsigned int prev_alarm = 0;
585   struct Curl_easy *data = conn->data;
586 #endif /* USE_ALARM_TIMEOUT */
587   int rc;
588 
589   *entry = NULL;
590 
591   if(timeoutms < 0)
592     /* got an already expired timeout */
593     return CURLRESOLV_TIMEDOUT;
594 
595 #ifdef USE_ALARM_TIMEOUT
596   if(data->set.no_signal)
597     /* Ignore the timeout when signals are disabled */
598     timeout = 0;
599   else
600     timeout = timeoutms;
601 
602   if(!timeout)
603     /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
604     return Curl_resolv(conn, hostname, port, entry);
605 
606   if(timeout < 1000) {
607     /* The alarm() function only provides integer second resolution, so if
608        we want to wait less than one second we must bail out already now. */
609     failf(data,
610         "remaining timeout of %ld too small to resolve via SIGALRM method",
611         timeout);
612     return CURLRESOLV_TIMEDOUT;
613   }
614   /* This allows us to time-out from the name resolver, as the timeout
615      will generate a signal and we will siglongjmp() from that here.
616      This technique has problems (see alarmfunc).
617      This should be the last thing we do before calling Curl_resolv(),
618      as otherwise we'd have to worry about variables that get modified
619      before we invoke Curl_resolv() (and thus use "volatile"). */
620   if(sigsetjmp(curl_jmpenv, 1)) {
621     /* this is coming from a siglongjmp() after an alarm signal */
622     failf(data, "name lookup timed out");
623     rc = CURLRESOLV_ERROR;
624     goto clean_up;
625   }
626   else {
627     /*************************************************************
628      * Set signal handler to catch SIGALRM
629      * Store the old value to be able to set it back later!
630      *************************************************************/
631 #ifdef HAVE_SIGACTION
632     sigaction(SIGALRM, NULL, &sigact);
633     keep_sigact = sigact;
634     keep_copysig = TRUE; /* yes, we have a copy */
635     sigact.sa_handler = alarmfunc;
636 #ifdef SA_RESTART
637     /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
638     sigact.sa_flags &= ~SA_RESTART;
639 #endif
640     /* now set the new struct */
641     sigaction(SIGALRM, &sigact, NULL);
642 #else /* HAVE_SIGACTION */
643     /* no sigaction(), revert to the much lamer signal() */
644 #ifdef HAVE_SIGNAL
645     keep_sigact = signal(SIGALRM, alarmfunc);
646 #endif
647 #endif /* HAVE_SIGACTION */
648 
649     /* alarm() makes a signal get sent when the timeout fires off, and that
650        will abort system calls */
651     prev_alarm = alarm(curlx_sltoui(timeout/1000L));
652   }
653 
654 #else
655 #ifndef CURLRES_ASYNCH
656   if(timeoutms)
657     infof(conn->data, "timeout on name lookup is not supported\n");
658 #else
659   (void)timeoutms; /* timeoutms not used with an async resolver */
660 #endif
661 #endif /* USE_ALARM_TIMEOUT */
662 
663   /* Perform the actual name resolution. This might be interrupted by an
664    * alarm if it takes too long.
665    */
666   rc = Curl_resolv(conn, hostname, port, entry);
667 
668 #ifdef USE_ALARM_TIMEOUT
669 clean_up:
670 
671   if(!prev_alarm)
672     /* deactivate a possibly active alarm before uninstalling the handler */
673     alarm(0);
674 
675 #ifdef HAVE_SIGACTION
676   if(keep_copysig) {
677     /* we got a struct as it looked before, now put that one back nice
678        and clean */
679     sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
680   }
681 #else
682 #ifdef HAVE_SIGNAL
683   /* restore the previous SIGALRM handler */
684   signal(SIGALRM, keep_sigact);
685 #endif
686 #endif /* HAVE_SIGACTION */
687 
688   /* switch back the alarm() to either zero or to what it was before minus
689      the time we spent until now! */
690   if(prev_alarm) {
691     /* there was an alarm() set before us, now put it back */
692     unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
693 
694     /* the alarm period is counted in even number of seconds */
695     unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
696 
697     if(!alarm_set ||
698        ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
699       /* if the alarm time-left reached zero or turned "negative" (counted
700          with unsigned values), we should fire off a SIGALRM here, but we
701          won't, and zero would be to switch it off so we never set it to
702          less than 1! */
703       alarm(1);
704       rc = CURLRESOLV_TIMEDOUT;
705       failf(data, "Previous alarm fired off!");
706     }
707     else
708       alarm((unsigned int)alarm_set);
709   }
710 #endif /* USE_ALARM_TIMEOUT */
711 
712   return rc;
713 }
714 
715 /*
716  * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
717  * made, the struct may be destroyed due to pruning. It is important that only
718  * one unlock is made for each Curl_resolv() call.
719  *
720  * May be called with 'data' == NULL for global cache.
721  */
Curl_resolv_unlock(struct Curl_easy * data,struct Curl_dns_entry * dns)722 void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
723 {
724   if(data && data->share)
725     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
726 
727   freednsentry(dns);
728 
729   if(data && data->share)
730     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
731 }
732 
733 /*
734  * File-internal: release cache dns entry reference, free if inuse drops to 0
735  */
freednsentry(void * freethis)736 static void freednsentry(void *freethis)
737 {
738   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
739   DEBUGASSERT(dns && (dns->inuse>0));
740 
741   dns->inuse--;
742   if(dns->inuse == 0) {
743     Curl_freeaddrinfo(dns->addr);
744     free(dns);
745   }
746 }
747 
748 /*
749  * Curl_mk_dnscache() inits a new DNS cache and returns success/failure.
750  */
Curl_mk_dnscache(struct curl_hash * hash)751 int Curl_mk_dnscache(struct curl_hash *hash)
752 {
753   return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
754                         freednsentry);
755 }
756 
757 /*
758  * Curl_hostcache_clean()
759  *
760  * This _can_ be called with 'data' == NULL but then of course no locking
761  * can be done!
762  */
763 
Curl_hostcache_clean(struct Curl_easy * data,struct curl_hash * hash)764 void Curl_hostcache_clean(struct Curl_easy *data,
765                           struct curl_hash *hash)
766 {
767   if(data && data->share)
768     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
769 
770   Curl_hash_clean(hash);
771 
772   if(data && data->share)
773     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
774 }
775 
776 
Curl_loadhostpairs(struct Curl_easy * data)777 CURLcode Curl_loadhostpairs(struct Curl_easy *data)
778 {
779   struct curl_slist *hostp;
780   char hostname[256];
781   char address[256];
782   int port;
783 
784   for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
785     if(!hostp->data)
786       continue;
787     if(hostp->data[0] == '-') {
788       char *entry_id;
789       size_t entry_len;
790 
791       if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
792         infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
793               hostp->data);
794         continue;
795       }
796 
797       /* Create an entry id, based upon the hostname and port */
798       entry_id = create_hostcache_id(hostname, port);
799       /* If we can't create the entry id, fail */
800       if(!entry_id) {
801         return CURLE_OUT_OF_MEMORY;
802       }
803 
804       entry_len = strlen(entry_id);
805 
806       if(data->share)
807         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
808 
809       /* delete entry, ignore if it didn't exist */
810       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1);
811 
812       if(data->share)
813         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
814 
815       /* free the allocated entry_id again */
816       free(entry_id);
817     }
818     else {
819       struct Curl_dns_entry *dns;
820       Curl_addrinfo *addr;
821       char *entry_id;
822       size_t entry_len;
823 
824       if(3 != sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
825                      address)) {
826         infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
827               hostp->data);
828         continue;
829       }
830 
831       addr = Curl_str2addr(address, port);
832       if(!addr) {
833         infof(data, "Address in '%s' found illegal!\n", hostp->data);
834         continue;
835       }
836 
837       /* Create an entry id, based upon the hostname and port */
838       entry_id = create_hostcache_id(hostname, port);
839       /* If we can't create the entry id, fail */
840       if(!entry_id) {
841         Curl_freeaddrinfo(addr);
842         return CURLE_OUT_OF_MEMORY;
843       }
844 
845       entry_len = strlen(entry_id);
846 
847       if(data->share)
848         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
849 
850       /* See if its already in our dns cache */
851       dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
852 
853       /* free the allocated entry_id again */
854       free(entry_id);
855 
856       if(!dns) {
857         /* if not in the cache already, put this host in the cache */
858         dns = Curl_cache_addr(data, addr, hostname, port);
859         if(dns) {
860           dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
861           /* release the returned reference; the cache itself will keep the
862            * entry alive: */
863           dns->inuse--;
864         }
865       }
866       else
867         /* this is a duplicate, free it again */
868         Curl_freeaddrinfo(addr);
869 
870       if(data->share)
871         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
872 
873       if(!dns) {
874         Curl_freeaddrinfo(addr);
875         return CURLE_OUT_OF_MEMORY;
876       }
877       infof(data, "Added %s:%d:%s to DNS cache\n",
878             hostname, port, address);
879     }
880   }
881   data->change.resolve = NULL; /* dealt with now */
882 
883   return CURLE_OK;
884 }
885