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