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