Woops, partly revert my previous commit and do it slightly differently instead.
[platform/upstream/curl.git] / lib / hostip.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2008, 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  * $Id$
22  ***************************************************************************/
23
24 #include "setup.h"
25
26 #include <string.h>
27
28 #ifdef NEED_MALLOC_H
29 #include <malloc.h>
30 #endif
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
33 #endif
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37 #ifdef HAVE_NETDB_H
38 #include <netdb.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
42 #endif
43 #ifdef HAVE_STDLIB_H
44 #include <stdlib.h>     /* required for free() prototypes */
45 #endif
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>     /* for the close() proto */
48 #endif
49 #ifdef  VMS
50 #include <in.h>
51 #include <inet.h>
52 #include <stdlib.h>
53 #endif
54
55 #ifdef HAVE_SETJMP_H
56 #include <setjmp.h>
57 #endif
58
59 #ifdef HAVE_PROCESS_H
60 #include <process.h>
61 #endif
62
63 #include "urldata.h"
64 #include "sendf.h"
65 #include "hostip.h"
66 #include "hash.h"
67 #include "share.h"
68 #include "strerror.h"
69 #include "url.h"
70 #include "inet_ntop.h"
71
72 #define _MPRINTF_REPLACE /* use our functions only */
73 #include <curl/mprintf.h>
74
75 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
76 #include "inet_ntoa_r.h"
77 #endif
78
79 #include "memory.h"
80 /* The last #include file should be: */
81 #include "memdebug.h"
82
83 /*
84  * hostip.c explained
85  * ==================
86  *
87  * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
88  * source file are these:
89  *
90  * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
91  * that. The host may not be able to resolve IPv6, but we don't really have to
92  * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
93  * defined.
94  *
95  * CURLRES_ARES - is defined if libcurl is built to use c-ares for
96  * asynchronous name resolves. This can be Windows or *nix.
97  *
98  * CURLRES_THREADED - is defined if libcurl is built to run under (native)
99  * Windows, and then the name resolve will be done in a new thread, and the
100  * supported API will be the same as for ares-builds.
101  *
102  * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
103  * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
104  * defined.
105  *
106  * The host*.c sources files are split up like this:
107  *
108  * hostip.c   - method-independent resolver functions and utility functions
109  * hostasyn.c - functions for asynchronous name resolves
110  * hostsyn.c  - functions for synchronous name resolves
111  * hostares.c - functions for ares-using name resolves
112  * hostthre.c - functions for threaded name resolves
113  * hostip4.c  - ipv4-specific functions
114  * hostip6.c  - ipv6-specific functions
115  *
116  * The hostip.h is the united header file for all this. It defines the
117  * CURLRES_* defines based on the config*.h and setup.h defines.
118  */
119
120 /* These two symbols are for the global DNS cache */
121 static struct curl_hash hostname_cache;
122 static int host_cache_initialized;
123
124 static void freednsentry(void *freethis);
125
126 /*
127  * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
128  * Global DNS cache is general badness. Do not use. This will be removed in
129  * a future version. Use the share interface instead!
130  *
131  * Returns a struct curl_hash pointer on success, NULL on failure.
132  */
133 struct curl_hash *Curl_global_host_cache_init(void)
134 {
135   int rc = 0;
136   if(!host_cache_initialized) {
137     rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
138                         Curl_str_key_compare, freednsentry);
139     if(!rc)
140       host_cache_initialized = 1;
141   }
142   return rc?NULL:&hostname_cache;
143 }
144
145 /*
146  * Destroy and cleanup the global DNS cache
147  */
148 void Curl_global_host_cache_dtor(void)
149 {
150   if(host_cache_initialized) {
151     Curl_hash_clean(&hostname_cache);
152     host_cache_initialized = 0;
153   }
154 }
155
156 /*
157  * Return # of adresses in a Curl_addrinfo struct
158  */
159 int Curl_num_addresses(const Curl_addrinfo *addr)
160 {
161   int i;
162   for (i = 0; addr; addr = addr->ai_next, i++)
163     ;  /* empty loop */
164   return i;
165 }
166
167 /*
168  * Curl_printable_address() returns a printable version of the 1st address
169  * given in the 'ip' 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 *Curl_printable_address(const Curl_addrinfo *ip,
175                                    char *buf, size_t bufsize)
176 {
177   const void *ip4 = &((const struct sockaddr_in*)ip->ai_addr)->sin_addr;
178   int af = ip->ai_family;
179 #ifdef CURLRES_IPV6
180   const void *ip6 = &((const struct sockaddr_in6*)ip->ai_addr)->sin6_addr;
181 #else
182   const void *ip6 = NULL;
183 #endif
184
185   return Curl_inet_ntop(af, af == AF_INET ? ip4 : ip6, buf, bufsize);
186 }
187
188 /*
189  * Return a hostcache id string for the providing host + port, to be used by
190  * the DNS caching.
191  */
192 static char *
193 create_hostcache_id(const char *server, int port)
194 {
195   /* create and return the new allocated entry */
196   return aprintf("%s:%d", server, port);
197 }
198
199 struct hostcache_prune_data {
200   long cache_timeout;
201   time_t now;
202 };
203
204 /*
205  * This function is set as a callback to be called for every entry in the DNS
206  * cache when we want to prune old unused entries.
207  *
208  * Returning non-zero means remove the entry, return 0 to keep it in the
209  * cache.
210  */
211 static int
212 hostcache_timestamp_remove(void *datap, void *hc)
213 {
214   struct hostcache_prune_data *data =
215     (struct hostcache_prune_data *) datap;
216   struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
217
218   if((data->now - c->timestamp < data->cache_timeout) ||
219       c->inuse) {
220     /* please don't remove */
221     return 0;
222   }
223
224   /* fine, remove */
225   return 1;
226 }
227
228 /*
229  * Prune the DNS cache. This assumes that a lock has already been taken.
230  */
231 static void
232 hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
233 {
234   struct hostcache_prune_data user;
235
236   user.cache_timeout = cache_timeout;
237   user.now = now;
238
239   Curl_hash_clean_with_criterium(hostcache,
240                                  (void *) &user,
241                                  hostcache_timestamp_remove);
242 }
243
244 /*
245  * Library-wide function for pruning the DNS cache. This function takes and
246  * returns the appropriate locks.
247  */
248 void Curl_hostcache_prune(struct SessionHandle *data)
249 {
250   time_t now;
251
252   if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
253     /* cache forever means never prune, and NULL hostcache means
254        we can't do it */
255     return;
256
257   if(data->share)
258     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
259
260   time(&now);
261
262   /* Remove outdated and unused entries from the hostcache */
263   hostcache_prune(data->dns.hostcache,
264                   data->set.dns_cache_timeout,
265                   now);
266
267   if(data->share)
268     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
269 }
270
271 static int
272 remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
273 {
274   struct hostcache_prune_data user;
275
276   if( !dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
277     /* cache forever means never prune, and NULL hostcache means
278        we can't do it */
279     return 0;
280
281   time(&user.now);
282   user.cache_timeout = data->set.dns_cache_timeout;
283
284   if( !hostcache_timestamp_remove(&user,dns) )
285     return 0;
286
287   /* ok, we do need to clear the cache. although we need to remove just a
288      single entry we clean the entire hash, as no explicit delete function
289      is provided */
290   if(data->share)
291     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
292
293   Curl_hash_clean_with_criterium(data->dns.hostcache,
294                                  (void *) &user,
295                                  hostcache_timestamp_remove);
296
297   if(data->share)
298     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
299
300   return 1;
301 }
302
303
304 #ifdef HAVE_SIGSETJMP
305 /* Beware this is a global and unique instance. This is used to store the
306    return address that we can jump back to from inside a signal handler. This
307    is not thread-safe stuff. */
308 sigjmp_buf curl_jmpenv;
309 #endif
310
311
312 /*
313  * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
314  *
315  * When calling Curl_resolv() has resulted in a response with a returned
316  * address, we call this function to store the information in the dns
317  * cache etc
318  *
319  * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
320  */
321 struct Curl_dns_entry *
322 Curl_cache_addr(struct SessionHandle *data,
323                 Curl_addrinfo *addr,
324                 const char *hostname,
325                 int port)
326 {
327   char *entry_id;
328   size_t entry_len;
329   struct Curl_dns_entry *dns;
330   struct Curl_dns_entry *dns2;
331   time_t now;
332
333   /* Create an entry id, based upon the hostname and port */
334   entry_id = create_hostcache_id(hostname, port);
335   /* If we can't create the entry id, fail */
336   if(!entry_id)
337     return NULL;
338   entry_len = strlen(entry_id);
339
340   /* Create a new cache entry */
341   dns = (struct Curl_dns_entry *) calloc(sizeof(struct Curl_dns_entry), 1);
342   if(!dns) {
343     free(entry_id);
344     return NULL;
345   }
346
347   dns->inuse = 0;   /* init to not used */
348   dns->addr = addr; /* this is the address(es) */
349
350   /* Store the resolved data in our DNS cache. This function may return a
351      pointer to an existing struct already present in the hash, and it may
352      return the same argument we pass in. Make no assumptions. */
353   dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
354                        (void *)dns);
355   if(!dns2) {
356     /* Major badness, run away. */
357     free(dns);
358     free(entry_id);
359     return NULL;
360   }
361   time(&now);
362   dns = dns2;
363
364   dns->timestamp = now; /* used now */
365   dns->inuse++;         /* mark entry as in-use */
366
367   /* free the allocated entry_id again */
368   free(entry_id);
369
370   return dns;
371 }
372
373 /*
374  * Curl_resolv() is the main name resolve function within libcurl. It resolves
375  * a name and returns a pointer to the entry in the 'entry' argument (if one
376  * is provided). This function might return immediately if we're using asynch
377  * resolves. See the return codes.
378  *
379  * The cache entry we return will get its 'inuse' counter increased when this
380  * function is used. You MUST call Curl_resolv_unlock() later (when you're
381  * done using this struct) to decrease the counter again.
382  *
383  * Return codes:
384  *
385  * CURLRESOLV_ERROR   (-1) = error, no pointer
386  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
387  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
388  */
389
390 int Curl_resolv(struct connectdata *conn,
391                 const char *hostname,
392                 int port,
393                 struct Curl_dns_entry **entry)
394 {
395   char *entry_id = NULL;
396   struct Curl_dns_entry *dns = NULL;
397   size_t entry_len;
398   struct SessionHandle *data = conn->data;
399   CURLcode result;
400   int rc;
401   *entry = NULL;
402
403 #ifdef HAVE_SIGSETJMP
404   /* this allows us to time-out from the name resolver, as the timeout
405      will generate a signal and we will siglongjmp() from that here */
406   if(!data->set.no_signal) {
407     if(sigsetjmp(curl_jmpenv, 1)) {
408       /* this is coming from a siglongjmp() */
409       failf(data, "name lookup timed out");
410       return CURLRESOLV_ERROR;
411     }
412   }
413 #endif
414
415   /* Create an entry id, based upon the hostname and port */
416   entry_id = create_hostcache_id(hostname, port);
417   /* If we can't create the entry id, fail */
418   if(!entry_id)
419     return CURLRESOLV_ERROR;
420
421   entry_len = strlen(entry_id);
422
423   if(data->share)
424     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
425
426   /* See if its already in our dns cache */
427   dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
428
429   if(data->share)
430     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
431
432   /* free the allocated entry_id again */
433   free(entry_id);
434
435   /* See whether the returned entry is stale. Deliberately done after the
436      locked block */
437   if( remove_entry_if_stale(data,dns) )
438     dns = NULL; /* the memory deallocation is being handled by the hash */
439
440   rc = CURLRESOLV_ERROR; /* default to failure */
441
442   if(!dns) {
443     /* The entry was not in the cache. Resolve it to IP address */
444
445     Curl_addrinfo *addr;
446     int respwait;
447
448     /* Check what IP specifics the app has requested and if we can provide it.
449      * If not, bail out. */
450     if(!Curl_ipvalid(data))
451       return CURLRESOLV_ERROR;
452
453     /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
454        non-zero value indicating that we need to wait for the response to the
455        resolve call */
456     addr = Curl_getaddrinfo(conn, 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_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   else {
490     if(data->share)
491       Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
492     dns->inuse++; /* we use it! */
493     if(data->share)
494       Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
495     rc = CURLRESOLV_RESOLVED;
496   }
497
498   *entry = dns;
499
500   return rc;
501 }
502
503 /*
504  * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
505  * made, the struct may be destroyed due to pruning. It is important that only
506  * one unlock is made for each Curl_resolv() call.
507  */
508 void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
509 {
510   DEBUGASSERT(dns && (dns->inuse>0));
511
512   if(data->share)
513     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
514
515   dns->inuse--;
516
517   if(data->share)
518     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
519 }
520
521 /*
522  * File-internal: free a cache dns entry.
523  */
524 static void freednsentry(void *freethis)
525 {
526   struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
527
528   Curl_freeaddrinfo(p->addr);
529
530   free(p);
531 }
532
533 /*
534  * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
535  */
536 struct curl_hash *Curl_mk_dnscache(void)
537 {
538   return Curl_hash_alloc(7, Curl_hash_str, Curl_str_key_compare, freednsentry);
539 }
540
541 #ifdef CURLRES_ADDRINFO_COPY
542
543 /* align on even 64bit boundaries */
544 #define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
545
546 /*
547  * Curl_addrinfo_copy() performs a "deep" copy of a hostent into a buffer and
548  * returns a pointer to the malloc()ed copy. You need to call free() on the
549  * returned buffer when you're done with it.
550  */
551 Curl_addrinfo *Curl_addrinfo_copy(const void *org, int port)
552 {
553   const struct hostent *orig = org;
554
555   return Curl_he2ai(orig, port);
556 }
557 #endif /* CURLRES_ADDRINFO_COPY */
558
559 /***********************************************************************
560  * Only for plain-ipv4 and c-ares builds
561  **********************************************************************/
562
563 #if defined(CURLRES_IPV4) || defined(CURLRES_ARES)
564 /*
565  * This is a function for freeing name information in a protocol independent
566  * way.
567  */
568 void Curl_freeaddrinfo(Curl_addrinfo *ai)
569 {
570   Curl_addrinfo *next;
571
572   /* walk over the list and free all entries */
573   while(ai) {
574     next = ai->ai_next;
575     if(ai->ai_canonname)
576       free(ai->ai_canonname);
577     free(ai);
578     ai = next;
579   }
580 }
581
582 struct namebuf {
583   struct hostent hostentry;
584   char *h_addr_list[2];
585   struct in_addr addrentry;
586   char h_name[16]; /* 123.123.123.123 = 15 letters is maximum */
587 };
588
589 /*
590  * Curl_ip2addr() takes a 32bit ipv4 internet address as input parameter
591  * together with a pointer to the string version of the address, and it
592  * returns a Curl_addrinfo chain filled in correctly with information for this
593  * address/host.
594  *
595  * The input parameters ARE NOT checked for validity but they are expected
596  * to have been checked already when this is called.
597  */
598 Curl_addrinfo *Curl_ip2addr(in_addr_t num, const char *hostname, int port)
599 {
600   Curl_addrinfo *ai;
601
602 #if defined(VMS) && \
603     defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
604 #pragma pointer_size save
605 #pragma pointer_size short
606 #pragma message disable PTRMISMATCH
607 #endif
608
609   struct hostent *h;
610   struct in_addr *addrentry;
611   struct namebuf buffer;
612   struct namebuf *buf = &buffer;
613
614   h = &buf->hostentry;
615   h->h_addr_list = &buf->h_addr_list[0];
616   addrentry = &buf->addrentry;
617 #ifdef _CRAYC
618   /* On UNICOS, s_addr is a bit field and for some reason assigning to it
619    * doesn't work.  There must be a better fix than this ugly hack.
620    */
621   memcpy(addrentry, &num, SIZEOF_in_addr);
622 #else
623   addrentry->s_addr = num;
624 #endif
625   h->h_addr_list[0] = (char*)addrentry;
626   h->h_addr_list[1] = NULL;
627   h->h_addrtype = AF_INET;
628   h->h_length = sizeof(*addrentry);
629   h->h_name = &buf->h_name[0];
630   h->h_aliases = NULL;
631
632   /* Now store the dotted version of the address */
633   snprintf((char *)h->h_name, 16, "%s", hostname);
634
635 #if defined(VMS) && \
636     defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
637 #pragma pointer_size restore
638 #pragma message enable PTRMISMATCH
639 #endif
640
641   ai = Curl_he2ai(h, port);
642
643   return ai;
644 }
645 #endif /* CURLRES_IPV4 || CURLRES_ARES */
646
647