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