Removed inclusion of <sys/types.h> and <sys/stat.h> in .c-files
[platform/upstream/curl.git] / lib / hostip.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2007, 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 void Curl_global_host_cache_init(void)
132 {
133   if (!host_cache_initialized) {
134     Curl_hash_init(&hostname_cache, 7, freednsentry);
135     host_cache_initialized = 1;
136   }
137 }
138
139 /*
140  * Return a pointer to the global cache
141  */
142 struct curl_hash *Curl_global_host_cache_get(void)
143 {
144   return &hostname_cache;
145 }
146
147 /*
148  * Destroy and cleanup the global DNS cache
149  */
150 void Curl_global_host_cache_dtor(void)
151 {
152   if (host_cache_initialized) {
153     Curl_hash_clean(&hostname_cache);
154     host_cache_initialized = 0;
155   }
156 }
157
158 /*
159  * Return # of adresses in a Curl_addrinfo struct
160  */
161 int Curl_num_addresses(const Curl_addrinfo *addr)
162 {
163   int i;
164   for (i = 0; addr; addr = addr->ai_next, i++)
165     ;  /* empty loop */
166   return i;
167 }
168
169 /*
170  * Curl_printable_address() returns a printable version of the 1st address
171  * given in the 'ip' argument. The result will be stored in the buf that is
172  * bufsize bytes big.
173  *
174  * If the conversion fails, it returns NULL.
175  */
176 const char *Curl_printable_address(const Curl_addrinfo *ip,
177                                    char *buf, size_t bufsize)
178 {
179   const void *ip4 = &((const struct sockaddr_in*)ip->ai_addr)->sin_addr;
180   int af = ip->ai_family;
181 #ifdef CURLRES_IPV6
182   const void *ip6 = &((const struct sockaddr_in6*)ip->ai_addr)->sin6_addr;
183 #else
184   const void *ip6 = NULL;
185 #endif
186
187   return Curl_inet_ntop(af, af == AF_INET ? ip4 : ip6, buf, bufsize);
188 }
189
190 /*
191  * Return a hostcache id string for the providing host + port, to be used by
192  * the DNS caching.
193  */
194 static char *
195 create_hostcache_id(const char *server, int port)
196 {
197   /* create and return the new allocated entry */
198   return aprintf("%s:%d", server, port);
199 }
200
201 struct hostcache_prune_data {
202   int cache_timeout;
203   time_t now;
204 };
205
206 /*
207  * This function is set as a callback to be called for every entry in the DNS
208  * cache when we want to prune old unused entries.
209  *
210  * Returning non-zero means remove the entry, return 0 to keep it in the
211  * cache.
212  */
213 static int
214 hostcache_timestamp_remove(void *datap, void *hc)
215 {
216   struct hostcache_prune_data *data =
217     (struct hostcache_prune_data *) datap;
218   struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
219
220   if ((data->now - c->timestamp < data->cache_timeout) ||
221       c->inuse) {
222     /* please don't remove */
223     return 0;
224   }
225
226   /* fine, remove */
227   return 1;
228 }
229
230 /*
231  * Prune the DNS cache. This assumes that a lock has already been taken.
232  */
233 static void
234 hostcache_prune(struct curl_hash *hostcache, int cache_timeout, time_t now)
235 {
236   struct hostcache_prune_data user;
237
238   user.cache_timeout = cache_timeout;
239   user.now = now;
240
241   Curl_hash_clean_with_criterium(hostcache,
242                                  (void *) &user,
243                                  hostcache_timestamp_remove);
244 }
245
246 /*
247  * Library-wide function for pruning the DNS cache. This function takes and
248  * returns the appropriate locks.
249  */
250 void Curl_hostcache_prune(struct SessionHandle *data)
251 {
252   time_t now;
253
254   if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
255     /* cache forever means never prune, and NULL hostcache means
256        we can't do it */
257     return;
258
259   if(data->share)
260     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
261
262   time(&now);
263
264   /* Remove outdated and unused entries from the hostcache */
265   hostcache_prune(data->dns.hostcache,
266                   data->set.dns_cache_timeout,
267                   now);
268
269   if(data->share)
270     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
271 }
272
273 static int
274 remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
275 {
276   struct hostcache_prune_data user;
277
278   if( !dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
279     /* cache forever means never prune, and NULL hostcache means
280        we can't do it */
281     return 0;
282
283   time(&user.now);
284   user.cache_timeout = data->set.dns_cache_timeout;
285
286   if ( !hostcache_timestamp_remove(&user,dns) )
287     return 0;
288
289   /* ok, we do need to clear the cache. although we need to remove just a
290      single entry we clean the entire hash, as no explicit delete function
291      is provided */
292   if(data->share)
293     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
294
295   Curl_hash_clean_with_criterium(data->dns.hostcache,
296                                  (void *) &user,
297                                  hostcache_timestamp_remove);
298
299   if(data->share)
300     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
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   time_t now;
334
335   /* Create an entry id, based upon the hostname and port */
336   entry_id = create_hostcache_id(hostname, port);
337   /* If we can't create the entry id, fail */
338   if (!entry_id)
339     return NULL;
340   entry_len = strlen(entry_id);
341
342   /* Create a new cache entry */
343   dns = (struct Curl_dns_entry *) calloc(sizeof(struct Curl_dns_entry), 1);
344   if (!dns) {
345     free(entry_id);
346     return NULL;
347   }
348
349   dns->inuse = 0;   /* init to not used */
350   dns->addr = addr; /* this is the address(es) */
351
352   /* Store the resolved data in our DNS cache. This function may return a
353      pointer to an existing struct already present in the hash, and it may
354      return the same argument we pass in. Make no assumptions. */
355   dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
356                        (void *)dns);
357   if(!dns2) {
358     /* Major badness, run away. */
359     free(dns);
360     free(entry_id);
361     return NULL;
362   }
363   time(&now);
364   dns = dns2;
365
366   dns->timestamp = now; /* used now */
367   dns->inuse++;         /* mark entry as in-use */
368
369   /* free the allocated entry_id again */
370   free(entry_id);
371
372   return dns;
373 }
374
375 /*
376  * Curl_resolv() is the main name resolve function within libcurl. It resolves
377  * a name and returns a pointer to the entry in the 'entry' argument (if one
378  * is provided). This function might return immediately if we're using asynch
379  * resolves. See the return codes.
380  *
381  * The cache entry we return will get its 'inuse' counter increased when this
382  * function is used. You MUST call Curl_resolv_unlock() later (when you're
383  * done using this struct) to decrease the counter again.
384  *
385  * Return codes:
386  *
387  * CURLRESOLV_ERROR   (-1) = error, no pointer
388  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
389  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
390  */
391
392 int Curl_resolv(struct connectdata *conn,
393                 const char *hostname,
394                 int port,
395                 struct Curl_dns_entry **entry)
396 {
397   char *entry_id = NULL;
398   struct Curl_dns_entry *dns = NULL;
399   size_t entry_len;
400   int wait;
401   struct SessionHandle *data = conn->data;
402   CURLcode result;
403   int rc;
404   *entry = NULL;
405
406 #ifdef HAVE_SIGSETJMP
407   /* this allows us to time-out from the name resolver, as the timeout
408      will generate a signal and we will siglongjmp() from that here */
409   if(!data->set.no_signal) {
410     if (sigsetjmp(curl_jmpenv, 1)) {
411       /* this is coming from a siglongjmp() */
412       failf(data, "name lookup timed out");
413       return CURLRESOLV_ERROR;
414     }
415   }
416 #endif
417
418   /* Create an entry id, based upon the hostname and port */
419   entry_id = create_hostcache_id(hostname, port);
420   /* If we can't create the entry id, fail */
421   if (!entry_id)
422     return CURLRESOLV_ERROR;
423
424   entry_len = strlen(entry_id);
425
426   if(data->share)
427     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
428
429   /* See if its already in our dns cache */
430   dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
431
432   if(data->share)
433     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
434
435   /* free the allocated entry_id again */
436   free(entry_id);
437
438   /* See whether the returned entry is stale. Deliberately done after the
439      locked block */
440   if ( remove_entry_if_stale(data,dns) )
441     dns = NULL; /* the memory deallocation is being handled by the hash */
442
443   rc = CURLRESOLV_ERROR; /* default to failure */
444
445   if (!dns) {
446     /* The entry was not in the cache. Resolve it to IP address */
447
448     Curl_addrinfo *addr;
449
450     /* Check what IP specifics the app has requested and if we can provide it.
451      * If not, bail out. */
452     if(!Curl_ipvalid(data))
453       return CURLRESOLV_ERROR;
454
455     /* If Curl_getaddrinfo() returns NULL, 'wait' might be set to a non-zero
456        value indicating that we need to wait for the response to the resolve
457        call */
458     addr = Curl_getaddrinfo(conn, hostname, port, &wait);
459
460     if (!addr) {
461       if(wait) {
462         /* the response to our resolve call will come asynchronously at
463            a later time, good or bad */
464         /* First, check that we haven't received the info by now */
465         result = Curl_is_resolved(conn, &dns);
466         if(result) /* error detected */
467           return CURLRESOLV_ERROR;
468         if(dns)
469           rc = CURLRESOLV_RESOLVED; /* pointer provided */
470         else
471           rc = CURLRESOLV_PENDING; /* no info yet */
472       }
473     }
474     else {
475       if(data->share)
476         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
477
478       /* we got a response, store it in the cache */
479       dns = Curl_cache_addr(data, addr, hostname, port);
480
481       if(data->share)
482         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
483
484       if(!dns)
485         /* returned failure, bail out nicely */
486         Curl_freeaddrinfo(addr);
487       else
488         rc = CURLRESOLV_RESOLVED;
489     }
490   }
491   else {
492     if(data->share)
493       Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
494     dns->inuse++; /* we use it! */
495     if(data->share)
496       Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
497     rc = CURLRESOLV_RESOLVED;
498   }
499
500   *entry = dns;
501
502   return rc;
503 }
504
505 /*
506  * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
507  * made, the struct may be destroyed due to pruning. It is important that only
508  * one unlock is made for each Curl_resolv() call.
509  */
510 void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
511 {
512   DEBUGASSERT(dns && (dns->inuse>0));
513
514   if(data->share)
515     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
516
517   dns->inuse--;
518
519   if(data->share)
520     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
521 }
522
523 /*
524  * File-internal: free a cache dns entry.
525  */
526 static void freednsentry(void *freethis)
527 {
528   struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
529
530   Curl_freeaddrinfo(p->addr);
531
532   free(p);
533 }
534
535 /*
536  * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
537  */
538 struct curl_hash *Curl_mk_dnscache(void)
539 {
540   return Curl_hash_alloc(7, freednsentry);
541 }
542
543 #ifdef CURLRES_ADDRINFO_COPY
544
545 /* align on even 64bit boundaries */
546 #define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
547
548 /*
549  * Curl_addrinfo_copy() performs a "deep" copy of a hostent into a buffer and
550  * returns a pointer to the malloc()ed copy. You need to call free() on the
551  * returned buffer when you're done with it.
552  */
553 Curl_addrinfo *Curl_addrinfo_copy(const void *org, int port)
554 {
555   const struct hostent *orig = org;
556
557   return Curl_he2ai(orig, port);
558 }
559 #endif /* CURLRES_ADDRINFO_COPY */
560
561 /***********************************************************************
562  * Only for plain-ipv4 and c-ares builds
563  **********************************************************************/
564
565 #if defined(CURLRES_IPV4) || defined(CURLRES_ARES)
566 /*
567  * This is a function for freeing name information in a protocol independent
568  * way.
569  */
570 void Curl_freeaddrinfo(Curl_addrinfo *ai)
571 {
572   Curl_addrinfo *next;
573
574   /* walk over the list and free all entries */
575   while(ai) {
576     next = ai->ai_next;
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   struct hostent *h;
602   struct in_addr *addrentry;
603   struct namebuf buffer;
604   struct namebuf *buf = &buffer;
605
606   h = &buf->hostentry;
607   h->h_addr_list = &buf->h_addr_list[0];
608   addrentry = &buf->addrentry;
609 #ifdef _CRAYC
610   /* On UNICOS, s_addr is a bit field and for some reason assigning to it
611    * doesn't work.  There must be a better fix than this ugly hack.
612    */
613   memcpy(addrentry, &num, SIZEOF_in_addr);
614 #else
615   addrentry->s_addr = num;
616 #endif
617   h->h_addr_list[0] = (char*)addrentry;
618   h->h_addr_list[1] = NULL;
619   h->h_addrtype = AF_INET;
620   h->h_length = sizeof(*addrentry);
621   h->h_name = &buf->h_name[0];
622   h->h_aliases = NULL;
623
624   /* Now store the dotted version of the address */
625   snprintf((char *)h->h_name, 16, "%s", hostname);
626
627   ai = Curl_he2ai(h, port);
628
629   return ai;
630 }
631 #endif
632
633