hostip.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) 1998 - 2004, Daniel Stenberg, <[email protected]>, 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. #include "setup.h"
  24. #include <string.h>
  25. #include <errno.h>
  26. #define _REENTRANT
  27. #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
  28. #include <malloc.h>
  29. #else
  30. #ifdef HAVE_SYS_TYPES_H
  31. #include <sys/types.h>
  32. #endif
  33. #ifdef HAVE_SYS_SOCKET_H
  34. #include <sys/socket.h>
  35. #endif
  36. #ifdef HAVE_NETINET_IN_H
  37. #include <netinet/in.h>
  38. #endif
  39. #ifdef HAVE_NETDB_H
  40. #include <netdb.h>
  41. #endif
  42. #ifdef HAVE_ARPA_INET_H
  43. #include <arpa/inet.h>
  44. #endif
  45. #ifdef HAVE_STDLIB_H
  46. #include <stdlib.h> /* required for free() prototypes */
  47. #endif
  48. #ifdef HAVE_UNISTD_H
  49. #include <unistd.h> /* for the close() proto */
  50. #endif
  51. #ifdef VMS
  52. #include <in.h>
  53. #include <inet.h>
  54. #include <stdlib.h>
  55. #endif
  56. #endif
  57. #ifdef HAVE_SETJMP_H
  58. #include <setjmp.h>
  59. #endif
  60. #ifdef WIN32
  61. #include <process.h>
  62. #endif
  63. #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
  64. #undef in_addr_t
  65. #define in_addr_t unsigned long
  66. #endif
  67. #include "urldata.h"
  68. #include "sendf.h"
  69. #include "hostip.h"
  70. #include "hash.h"
  71. #include "share.h"
  72. #include "strerror.h"
  73. #include "url.h"
  74. #include "inet_ntop.h"
  75. #define _MPRINTF_REPLACE /* use our functions only */
  76. #include <curl/mprintf.h>
  77. #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
  78. #include "inet_ntoa_r.h"
  79. #endif
  80. #include "curl_memory.h"
  81. /* The last #include file should be: */
  82. #include "memdebug.h"
  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. It cannot have ENABLE_IPV6 defined at the same
  97. * time, as c-ares has no ipv6 support. This can be Windows or *nix.
  98. *
  99. * CURLRES_THREADED - is defined if libcurl is built to run under (native)
  100. * Windows, and then the name resolve will be done in a new thread, and the
  101. * supported API will be the same as for ares-builds.
  102. *
  103. * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
  104. * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
  105. * defined.
  106. *
  107. * The host*.c sources files are split up like this:
  108. *
  109. * hostip.c - method-independent resolver functions and utility functions
  110. * hostasyn.c - functions for asynchronous name resolves
  111. * hostsyn.c - functions for synchronous name resolves
  112. * hostares.c - functions for ares-using name resolves
  113. * hostthre.c - functions for threaded name resolves
  114. * hostip4.c - ipv4-specific functions
  115. * hostip6.c - ipv6-specific functions
  116. *
  117. * The hostip.h is the united header file for all this. It defines the
  118. * CURLRES_* defines based on the config*.h and setup.h defines.
  119. */
  120. /* These two symbols are for the global DNS cache */
  121. static curl_hash hostname_cache;
  122. static int host_cache_initialized;
  123. static void freednsentry(void *freethis);
  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. void Curl_global_host_cache_init(void)
  130. {
  131. if (!host_cache_initialized) {
  132. Curl_hash_init(&hostname_cache, 7, freednsentry);
  133. host_cache_initialized = 1;
  134. }
  135. }
  136. /*
  137. * Return a pointer to the global cache
  138. */
  139. curl_hash *Curl_global_host_cache_get(void)
  140. {
  141. return &hostname_cache;
  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. * Return # of adresses in a Curl_addrinfo struct
  155. */
  156. int Curl_num_addresses(const Curl_addrinfo *addr)
  157. {
  158. int i;
  159. for (i = 0; addr; addr = addr->ai_next, i++);
  160. return i;
  161. }
  162. #define GET_SIN_ADDR_FROM_CURL_ADDRINFO(ai_addr, si, sin, sinaddr, ip) \
  163. { \
  164. union { \
  165. struct si* vsi; \
  166. struct sin* vsin;\
  167. } vi; \
  168. vi.vsi = ai_addr; \
  169. ip = &(vi.vsin->sinaddr); \
  170. }
  171. /*
  172. * Curl_printable_address() returns a printable version of the 1st address
  173. * given in the 'ip' argument. The result will be stored in the buf that is
  174. * bufsize bytes big.
  175. *
  176. * If the conversion fails, it returns NULL.
  177. */
  178. const char *Curl_printable_address(const Curl_addrinfo *ip,
  179. char *buf, size_t bufsize)
  180. {
  181. int af = ip->ai_family;
  182. const void *ip4;
  183. #ifdef CURLRES_IPV6
  184. const void *ip6;
  185. GET_SIN_ADDR_FROM_CURL_ADDRINFO(ip->ai_addr, sockaddr, sockaddr_in6,
  186. sin6_addr, ip6);
  187. #else
  188. const void *ip6 = NULL;
  189. #endif
  190. GET_SIN_ADDR_FROM_CURL_ADDRINFO(ip->ai_addr, sockaddr, sockaddr_in,
  191. sin_addr, ip4);
  192. return Curl_inet_ntop(af, af == AF_INET ? ip4 : ip6, buf, bufsize);
  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. struct hostcache_prune_data {
  205. int cache_timeout;
  206. time_t now;
  207. };
  208. /*
  209. * This function is set as a callback to be called for every entry in the DNS
  210. * cache when we want to prune old unused entries.
  211. *
  212. * Returning non-zero means remove the entry, return 0 to keep it in the
  213. * cache.
  214. */
  215. static int
  216. hostcache_timestamp_remove(void *datap, void *hc)
  217. {
  218. struct hostcache_prune_data *data =
  219. (struct hostcache_prune_data *) datap;
  220. struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
  221. if ((data->now - c->timestamp < data->cache_timeout) ||
  222. c->inuse) {
  223. /* please don't remove */
  224. return 0;
  225. }
  226. /* fine, remove */
  227. return 1;
  228. }
  229. /*
  230. * Prune the DNS cache. This assumes that a lock has already been taken.
  231. */
  232. static void
  233. hostcache_prune(curl_hash *hostcache, int cache_timeout, time_t now)
  234. {
  235. struct hostcache_prune_data user;
  236. user.cache_timeout = cache_timeout;
  237. user.now = now;
  238. Curl_hash_clean_with_criterium(hostcache,
  239. (void *) &user,
  240. hostcache_timestamp_remove);
  241. }
  242. /*
  243. * Library-wide function for pruning the DNS cache. This function takes and
  244. * returns the appropriate locks.
  245. */
  246. void Curl_hostcache_prune(struct SessionHandle *data)
  247. {
  248. time_t now;
  249. if(data->set.dns_cache_timeout == -1)
  250. /* cache forever means never prune! */
  251. return;
  252. if(data->share)
  253. Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
  254. time(&now);
  255. /* Remove outdated and unused entries from the hostcache */
  256. hostcache_prune(data->hostcache,
  257. data->set.dns_cache_timeout,
  258. now);
  259. if(data->share)
  260. Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
  261. }
  262. #ifdef HAVE_SIGSETJMP
  263. /* Beware this is a global and unique instance. This is used to store the
  264. return address that we can jump back to from inside a signal handler. This
  265. is not thread-safe stuff. */
  266. sigjmp_buf curl_jmpenv;
  267. #endif
  268. /*
  269. * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
  270. *
  271. * When calling Curl_resolv() has resulted in a response with a returned
  272. * address, we call this function to store the information in the dns
  273. * cache etc
  274. *
  275. * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
  276. */
  277. struct Curl_dns_entry *
  278. Curl_cache_addr(struct SessionHandle *data,
  279. Curl_addrinfo *addr,
  280. char *hostname,
  281. int port)
  282. {
  283. char *entry_id;
  284. size_t entry_len;
  285. struct Curl_dns_entry *dns;
  286. struct Curl_dns_entry *dns2;
  287. time_t now;
  288. /* Create an entry id, based upon the hostname and port */
  289. entry_id = create_hostcache_id(hostname, port);
  290. /* If we can't create the entry id, fail */
  291. if (!entry_id)
  292. return NULL;
  293. entry_len = strlen(entry_id);
  294. /* Create a new cache entry */
  295. dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry));
  296. if (!dns) {
  297. free(entry_id);
  298. return NULL;
  299. }
  300. dns->inuse = 0; /* init to not used */
  301. dns->addr = addr; /* this is the address(es) */
  302. /* Store the resolved data in our DNS cache. This function may return a
  303. pointer to an existing struct already present in the hash, and it may
  304. return the same argument we pass in. Make no assumptions. */
  305. dns2 = Curl_hash_add(data->hostcache, entry_id, entry_len+1, (void *)dns);
  306. if(!dns2) {
  307. /* Major badness, run away. */
  308. free(dns);
  309. free(entry_id);
  310. return NULL;
  311. }
  312. time(&now);
  313. dns = dns2;
  314. dns->timestamp = now; /* used now */
  315. dns->inuse++; /* mark entry as in-use */
  316. /* free the allocated entry_id again */
  317. free(entry_id);
  318. return dns;
  319. }
  320. /*
  321. * Curl_resolv() is the main name resolve function within libcurl. It resolves
  322. * a name and returns a pointer to the entry in the 'entry' argument (if one
  323. * is provided). This function might return immediately if we're using asynch
  324. * resolves. See the return codes.
  325. *
  326. * The cache entry we return will get its 'inuse' counter increased when this
  327. * function is used. You MUST call Curl_resolv_unlock() later (when you're
  328. * done using this struct) to decrease the counter again.
  329. *
  330. * Return codes:
  331. *
  332. * CURLRESOLV_ERROR (-1) = error, no pointer
  333. * CURLRESOLV_RESOLVED (0) = OK, pointer provided
  334. * CURLRESOLV_PENDING (1) = waiting for response, no pointer
  335. */
  336. int Curl_resolv(struct connectdata *conn,
  337. char *hostname,
  338. int port,
  339. struct Curl_dns_entry **entry)
  340. {
  341. char *entry_id;
  342. struct Curl_dns_entry *dns = NULL;
  343. size_t entry_len;
  344. int wait;
  345. struct SessionHandle *data = conn->data;
  346. CURLcode result;
  347. /* default to failure */
  348. int rc;
  349. *entry = NULL;
  350. #ifdef HAVE_SIGSETJMP
  351. /* this allows us to time-out from the name resolver, as the timeout
  352. will generate a signal and we will siglongjmp() from that here */
  353. if(!data->set.no_signal && sigsetjmp(curl_jmpenv, 1)) {
  354. /* this is coming from a siglongjmp() */
  355. failf(data, "name lookup timed out");
  356. return CURLRESOLV_ERROR;
  357. }
  358. #endif
  359. rc = CURLRESOLV_ERROR;
  360. /* Create an entry id, based upon the hostname and port */
  361. entry_id = create_hostcache_id(hostname, port);
  362. /* If we can't create the entry id, fail */
  363. if (!entry_id)
  364. return CURLRESOLV_ERROR;
  365. entry_len = strlen(entry_id);
  366. if(data->share)
  367. Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
  368. /* See if its already in our dns cache */
  369. dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1);
  370. if(data->share)
  371. Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
  372. /* free the allocated entry_id again */
  373. free(entry_id);
  374. if (!dns) {
  375. /* The entry was not in the cache. Resolve it to IP address */
  376. Curl_addrinfo *addr;
  377. /* Check what IP specifics the app has requested and if we can provide it.
  378. * If not, bail out. */
  379. if(!Curl_ipvalid(data))
  380. return CURLRESOLV_ERROR;
  381. /* If Curl_getaddrinfo() returns NULL, 'wait' might be set to a non-zero
  382. value indicating that we need to wait for the response to the resolve
  383. call */
  384. addr = Curl_getaddrinfo(conn, hostname, port, &wait);
  385. if (!addr) {
  386. if(wait) {
  387. /* the response to our resolve call will come asynchronously at
  388. a later time, good or bad */
  389. /* First, check that we haven't received the info by now */
  390. result = Curl_is_resolved(conn, &dns);
  391. if(result) /* error detected */
  392. return CURLRESOLV_ERROR;
  393. if(dns)
  394. rc = CURLRESOLV_RESOLVED; /* pointer provided */
  395. else
  396. rc = CURLRESOLV_PENDING; /* no info yet */
  397. }
  398. }
  399. else {
  400. if(data->share)
  401. Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
  402. /* we got a response, store it in the cache */
  403. dns = Curl_cache_addr(data, addr, hostname, port);
  404. if(data->share)
  405. Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
  406. if(!dns)
  407. /* returned failure, bail out nicely */
  408. Curl_freeaddrinfo(addr);
  409. else
  410. rc = CURLRESOLV_RESOLVED;
  411. }
  412. }
  413. else {
  414. dns->inuse++; /* we use it! */
  415. rc = CURLRESOLV_RESOLVED;
  416. }
  417. *entry = dns;
  418. return rc;
  419. }
  420. /*
  421. * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
  422. * made, the struct may be destroyed due to pruning. It is important that only
  423. * one unlock is made for each Curl_resolv() call.
  424. */
  425. void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
  426. {
  427. curlassert(dns && (dns->inuse>0));
  428. if(data->share)
  429. Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
  430. dns->inuse--;
  431. if(data->share)
  432. Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
  433. }
  434. /*
  435. * File-internal: free a cache dns entry.
  436. */
  437. static void freednsentry(void *freethis)
  438. {
  439. struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
  440. Curl_freeaddrinfo(p->addr);
  441. free(p);
  442. }
  443. /*
  444. * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
  445. */
  446. curl_hash *Curl_mk_dnscache(void)
  447. {
  448. return Curl_hash_alloc(7, freednsentry);
  449. }
  450. #ifdef CURLRES_ADDRINFO_COPY
  451. /* align on even 64bit boundaries */
  452. #define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
  453. /*
  454. * Curl_addrinfo_copy() performs a "deep" copy of a hostent into a buffer and
  455. * returns a pointer to the malloc()ed copy. You need to call free() on the
  456. * returned buffer when you're done with it.
  457. */
  458. Curl_addrinfo *Curl_addrinfo_copy(void *org, int port)
  459. {
  460. struct hostent *orig = org;
  461. return Curl_he2ai(orig, port);
  462. }
  463. #endif /* CURLRES_ADDRINFO_COPY */