Imported Upstream version 7.50.2
[platform/upstream/curl.git] / lib / hostip.c
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  */
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  */
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  */
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 *
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 *
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
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
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  */
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 *
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 *
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 *
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
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
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
567 int Curl_resolv_timeout(struct connectdata *conn,
568                         const char *hostname,
569                         int port,
570                         struct Curl_dns_entry **entry,
571                         long 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     return CURLRESOLV_TIMEDOUT;
610
611   /* This allows us to time-out from the name resolver, as the timeout
612      will generate a signal and we will siglongjmp() from that here.
613      This technique has problems (see alarmfunc).
614      This should be the last thing we do before calling Curl_resolv(),
615      as otherwise we'd have to worry about variables that get modified
616      before we invoke Curl_resolv() (and thus use "volatile"). */
617   if(sigsetjmp(curl_jmpenv, 1)) {
618     /* this is coming from a siglongjmp() after an alarm signal */
619     failf(data, "name lookup timed out");
620     rc = CURLRESOLV_ERROR;
621     goto clean_up;
622   }
623   else {
624     /*************************************************************
625      * Set signal handler to catch SIGALRM
626      * Store the old value to be able to set it back later!
627      *************************************************************/
628 #ifdef HAVE_SIGACTION
629     sigaction(SIGALRM, NULL, &sigact);
630     keep_sigact = sigact;
631     keep_copysig = TRUE; /* yes, we have a copy */
632     sigact.sa_handler = alarmfunc;
633 #ifdef SA_RESTART
634     /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
635     sigact.sa_flags &= ~SA_RESTART;
636 #endif
637     /* now set the new struct */
638     sigaction(SIGALRM, &sigact, NULL);
639 #else /* HAVE_SIGACTION */
640     /* no sigaction(), revert to the much lamer signal() */
641 #ifdef HAVE_SIGNAL
642     keep_sigact = signal(SIGALRM, alarmfunc);
643 #endif
644 #endif /* HAVE_SIGACTION */
645
646     /* alarm() makes a signal get sent when the timeout fires off, and that
647        will abort system calls */
648     prev_alarm = alarm(curlx_sltoui(timeout/1000L));
649   }
650
651 #else
652 #ifndef CURLRES_ASYNCH
653   if(timeoutms)
654     infof(conn->data, "timeout on name lookup is not supported\n");
655 #else
656   (void)timeoutms; /* timeoutms not used with an async resolver */
657 #endif
658 #endif /* USE_ALARM_TIMEOUT */
659
660   /* Perform the actual name resolution. This might be interrupted by an
661    * alarm if it takes too long.
662    */
663   rc = Curl_resolv(conn, hostname, port, entry);
664
665 #ifdef USE_ALARM_TIMEOUT
666 clean_up:
667
668   if(!prev_alarm)
669     /* deactivate a possibly active alarm before uninstalling the handler */
670     alarm(0);
671
672 #ifdef HAVE_SIGACTION
673   if(keep_copysig) {
674     /* we got a struct as it looked before, now put that one back nice
675        and clean */
676     sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
677   }
678 #else
679 #ifdef HAVE_SIGNAL
680   /* restore the previous SIGALRM handler */
681   signal(SIGALRM, keep_sigact);
682 #endif
683 #endif /* HAVE_SIGACTION */
684
685   /* switch back the alarm() to either zero or to what it was before minus
686      the time we spent until now! */
687   if(prev_alarm) {
688     /* there was an alarm() set before us, now put it back */
689     unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
690
691     /* the alarm period is counted in even number of seconds */
692     unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
693
694     if(!alarm_set ||
695        ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
696       /* if the alarm time-left reached zero or turned "negative" (counted
697          with unsigned values), we should fire off a SIGALRM here, but we
698          won't, and zero would be to switch it off so we never set it to
699          less than 1! */
700       alarm(1);
701       rc = CURLRESOLV_TIMEDOUT;
702       failf(data, "Previous alarm fired off!");
703     }
704     else
705       alarm((unsigned int)alarm_set);
706   }
707 #endif /* USE_ALARM_TIMEOUT */
708
709   return rc;
710 }
711
712 /*
713  * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
714  * made, the struct may be destroyed due to pruning. It is important that only
715  * one unlock is made for each Curl_resolv() call.
716  *
717  * May be called with 'data' == NULL for global cache.
718  */
719 void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
720 {
721   if(data && data->share)
722     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
723
724   freednsentry(dns);
725
726   if(data && data->share)
727     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
728 }
729
730 /*
731  * File-internal: release cache dns entry reference, free if inuse drops to 0
732  */
733 static void freednsentry(void *freethis)
734 {
735   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
736   DEBUGASSERT(dns && (dns->inuse>0));
737
738   dns->inuse--;
739   if(dns->inuse == 0) {
740     Curl_freeaddrinfo(dns->addr);
741     free(dns);
742   }
743 }
744
745 /*
746  * Curl_mk_dnscache() inits a new DNS cache and returns success/failure.
747  */
748 int Curl_mk_dnscache(struct curl_hash *hash)
749 {
750   return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
751                         freednsentry);
752 }
753
754 /*
755  * Curl_hostcache_clean()
756  *
757  * This _can_ be called with 'data' == NULL but then of course no locking
758  * can be done!
759  */
760
761 void Curl_hostcache_clean(struct Curl_easy *data,
762                           struct curl_hash *hash)
763 {
764   if(data && data->share)
765     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
766
767   Curl_hash_clean(hash);
768
769   if(data && data->share)
770     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
771 }
772
773
774 CURLcode Curl_loadhostpairs(struct Curl_easy *data)
775 {
776   struct curl_slist *hostp;
777   char hostname[256];
778   char address[256];
779   int port;
780
781   for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
782     if(!hostp->data)
783       continue;
784     if(hostp->data[0] == '-') {
785       char *entry_id;
786       size_t entry_len;
787
788       if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
789         infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
790               hostp->data);
791         continue;
792       }
793
794       /* Create an entry id, based upon the hostname and port */
795       entry_id = create_hostcache_id(hostname, port);
796       /* If we can't create the entry id, fail */
797       if(!entry_id) {
798         return CURLE_OUT_OF_MEMORY;
799       }
800
801       entry_len = strlen(entry_id);
802
803       if(data->share)
804         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
805
806       /* delete entry, ignore if it didn't exist */
807       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1);
808
809       if(data->share)
810         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
811
812       /* free the allocated entry_id again */
813       free(entry_id);
814     }
815     else {
816       struct Curl_dns_entry *dns;
817       Curl_addrinfo *addr;
818       char *entry_id;
819       size_t entry_len;
820
821       if(3 != sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
822                      address)) {
823         infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
824               hostp->data);
825         continue;
826       }
827
828       addr = Curl_str2addr(address, port);
829       if(!addr) {
830         infof(data, "Address in '%s' found illegal!\n", hostp->data);
831         continue;
832       }
833
834       /* Create an entry id, based upon the hostname and port */
835       entry_id = create_hostcache_id(hostname, port);
836       /* If we can't create the entry id, fail */
837       if(!entry_id) {
838         Curl_freeaddrinfo(addr);
839         return CURLE_OUT_OF_MEMORY;
840       }
841
842       entry_len = strlen(entry_id);
843
844       if(data->share)
845         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
846
847       /* See if its already in our dns cache */
848       dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
849
850       /* free the allocated entry_id again */
851       free(entry_id);
852
853       if(!dns) {
854         /* if not in the cache already, put this host in the cache */
855         dns = Curl_cache_addr(data, addr, hostname, port);
856         if(dns) {
857           dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
858           /* release the returned reference; the cache itself will keep the
859            * entry alive: */
860           dns->inuse--;
861         }
862       }
863       else
864         /* this is a duplicate, free it again */
865         Curl_freeaddrinfo(addr);
866
867       if(data->share)
868         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
869
870       if(!dns) {
871         Curl_freeaddrinfo(addr);
872         return CURLE_OUT_OF_MEMORY;
873       }
874       infof(data, "Added %s:%d:%s to DNS cache\n",
875             hostname, port, address);
876     }
877   }
878   data->change.resolve = NULL; /* dealt with now */
879
880   return CURLE_OK;
881 }