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