hostip.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) 1998 - 2018, 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 https://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. ***************************************************************************/
  22. #include "curl_setup.h"
  23. #ifdef HAVE_NETINET_IN_H
  24. #include <netinet/in.h>
  25. #endif
  26. #ifdef HAVE_NETINET_IN6_H
  27. #include <netinet/in6.h>
  28. #endif
  29. #ifdef HAVE_NETDB_H
  30. #include <netdb.h>
  31. #endif
  32. #ifdef HAVE_ARPA_INET_H
  33. #include <arpa/inet.h>
  34. #endif
  35. #ifdef __VMS
  36. #include <in.h>
  37. #include <inet.h>
  38. #endif
  39. #ifdef HAVE_SETJMP_H
  40. #include <setjmp.h>
  41. #endif
  42. #ifdef HAVE_SIGNAL_H
  43. #include <signal.h>
  44. #endif
  45. #ifdef HAVE_PROCESS_H
  46. #include <process.h>
  47. #endif
  48. #include "urldata.h"
  49. #include "sendf.h"
  50. #include "hostip.h"
  51. #include "hash.h"
  52. #include "rand.h"
  53. #include "share.h"
  54. #include "strerror.h"
  55. #include "url.h"
  56. #include "inet_ntop.h"
  57. #include "multiif.h"
  58. #include "warnless.h"
  59. /* The last 3 #include files should be in this order */
  60. #include "curl_printf.h"
  61. #include "curl_memory.h"
  62. #include "memdebug.h"
  63. #if defined(CURLRES_SYNCH) && \
  64. defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
  65. /* alarm-based timeouts can only be used with all the dependencies satisfied */
  66. #define USE_ALARM_TIMEOUT
  67. #endif
  68. /*
  69. * hostip.c explained
  70. * ==================
  71. *
  72. * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
  73. * source file are these:
  74. *
  75. * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
  76. * that. The host may not be able to resolve IPv6, but we don't really have to
  77. * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
  78. * defined.
  79. *
  80. * CURLRES_ARES - is defined if libcurl is built to use c-ares for
  81. * asynchronous name resolves. This can be Windows or *nix.
  82. *
  83. * CURLRES_THREADED - is defined if libcurl is built to run under (native)
  84. * Windows, and then the name resolve will be done in a new thread, and the
  85. * supported API will be the same as for ares-builds.
  86. *
  87. * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
  88. * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
  89. * defined.
  90. *
  91. * The host*.c sources files are split up like this:
  92. *
  93. * hostip.c - method-independent resolver functions and utility functions
  94. * hostasyn.c - functions for asynchronous name resolves
  95. * hostsyn.c - functions for synchronous name resolves
  96. * hostip4.c - IPv4 specific functions
  97. * hostip6.c - IPv6 specific functions
  98. *
  99. * The two asynchronous name resolver backends are implemented in:
  100. * asyn-ares.c - functions for ares-using name resolves
  101. * asyn-thread.c - functions for threaded name resolves
  102. * The hostip.h is the united header file for all this. It defines the
  103. * CURLRES_* defines based on the config*.h and curl_setup.h defines.
  104. */
  105. /* These two symbols are for the global DNS cache */
  106. static struct curl_hash hostname_cache;
  107. static int host_cache_initialized;
  108. static void freednsentry(void *freethis);
  109. /*
  110. * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
  111. * Global DNS cache is general badness. Do not use. This will be removed in
  112. * a future version. Use the share interface instead!
  113. *
  114. * Returns a struct curl_hash pointer on success, NULL on failure.
  115. */
  116. struct curl_hash *Curl_global_host_cache_init(void)
  117. {
  118. int rc = 0;
  119. if(!host_cache_initialized) {
  120. rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
  121. Curl_str_key_compare, freednsentry);
  122. if(!rc)
  123. host_cache_initialized = 1;
  124. }
  125. return rc?NULL:&hostname_cache;
  126. }
  127. /*
  128. * Destroy and cleanup the global DNS cache
  129. */
  130. void Curl_global_host_cache_dtor(void)
  131. {
  132. if(host_cache_initialized) {
  133. Curl_hash_destroy(&hostname_cache);
  134. host_cache_initialized = 0;
  135. }
  136. }
  137. /*
  138. * Return # of addresses in a Curl_addrinfo struct
  139. */
  140. int Curl_num_addresses(const Curl_addrinfo *addr)
  141. {
  142. int i = 0;
  143. while(addr) {
  144. addr = addr->ai_next;
  145. i++;
  146. }
  147. return i;
  148. }
  149. /*
  150. * Curl_printable_address() returns a printable version of the 1st address
  151. * given in the 'ai' argument. The result will be stored in the buf that is
  152. * bufsize bytes big.
  153. *
  154. * If the conversion fails, it returns NULL.
  155. */
  156. const char *
  157. Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
  158. {
  159. const struct sockaddr_in *sa4;
  160. const struct in_addr *ipaddr4;
  161. #ifdef ENABLE_IPV6
  162. const struct sockaddr_in6 *sa6;
  163. const struct in6_addr *ipaddr6;
  164. #endif
  165. switch(ai->ai_family) {
  166. case AF_INET:
  167. sa4 = (const void *)ai->ai_addr;
  168. ipaddr4 = &sa4->sin_addr;
  169. return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf,
  170. bufsize);
  171. #ifdef ENABLE_IPV6
  172. case AF_INET6:
  173. sa6 = (const void *)ai->ai_addr;
  174. ipaddr6 = &sa6->sin6_addr;
  175. return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf,
  176. bufsize);
  177. #endif
  178. default:
  179. break;
  180. }
  181. return NULL;
  182. }
  183. /*
  184. * Return a hostcache id string for the provided host + port, to be used by
  185. * the DNS caching.
  186. */
  187. static char *
  188. create_hostcache_id(const char *name, int port)
  189. {
  190. /* create and return the new allocated entry */
  191. char *id = aprintf("%s:%d", name, port);
  192. char *ptr = id;
  193. if(ptr) {
  194. /* lower case the name part */
  195. while(*ptr && (*ptr != ':')) {
  196. *ptr = (char)TOLOWER(*ptr);
  197. ptr++;
  198. }
  199. }
  200. return id;
  201. }
  202. struct hostcache_prune_data {
  203. long cache_timeout;
  204. time_t now;
  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. return (0 != c->timestamp)
  220. && (data->now - c->timestamp >= data->cache_timeout);
  221. }
  222. /*
  223. * Prune the DNS cache. This assumes that a lock has already been taken.
  224. */
  225. static void
  226. hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
  227. {
  228. struct hostcache_prune_data user;
  229. user.cache_timeout = cache_timeout;
  230. user.now = now;
  231. Curl_hash_clean_with_criterium(hostcache,
  232. (void *) &user,
  233. hostcache_timestamp_remove);
  234. }
  235. /*
  236. * Library-wide function for pruning the DNS cache. This function takes and
  237. * returns the appropriate locks.
  238. */
  239. void Curl_hostcache_prune(struct Curl_easy *data)
  240. {
  241. time_t now;
  242. if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
  243. /* cache forever means never prune, and NULL hostcache means
  244. we can't do it */
  245. return;
  246. if(data->share)
  247. Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
  248. time(&now);
  249. /* Remove outdated and unused entries from the hostcache */
  250. hostcache_prune(data->dns.hostcache,
  251. data->set.dns_cache_timeout,
  252. now);
  253. if(data->share)
  254. Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
  255. }
  256. #ifdef HAVE_SIGSETJMP
  257. /* Beware this is a global and unique instance. This is used to store the
  258. return address that we can jump back to from inside a signal handler. This
  259. is not thread-safe stuff. */
  260. sigjmp_buf curl_jmpenv;
  261. #endif
  262. /* lookup address, returns entry if found and not stale */
  263. static struct Curl_dns_entry *
  264. fetch_addr(struct connectdata *conn,
  265. const char *hostname,
  266. int port)
  267. {
  268. char *entry_id = NULL;
  269. struct Curl_dns_entry *dns = NULL;
  270. size_t entry_len;
  271. struct Curl_easy *data = conn->data;
  272. /* Create an entry id, based upon the hostname and port */
  273. entry_id = create_hostcache_id(hostname, port);
  274. /* If we can't create the entry id, fail */
  275. if(!entry_id)
  276. return dns;
  277. entry_len = strlen(entry_id);
  278. /* See if its already in our dns cache */
  279. dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
  280. if(dns && (data->set.dns_cache_timeout != -1)) {
  281. /* See whether the returned entry is stale. Done before we release lock */
  282. struct hostcache_prune_data user;
  283. time(&user.now);
  284. user.cache_timeout = data->set.dns_cache_timeout;
  285. if(hostcache_timestamp_remove(&user, dns)) {
  286. infof(data, "Hostname in DNS cache was stale, zapped\n");
  287. dns = NULL; /* the memory deallocation is being handled by the hash */
  288. Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
  289. }
  290. }
  291. /* free the allocated entry_id again */
  292. free(entry_id);
  293. return dns;
  294. }
  295. /*
  296. * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
  297. *
  298. * Curl_resolv() checks initially and multi_runsingle() checks each time
  299. * it discovers the handle in the state WAITRESOLVE whether the hostname
  300. * has already been resolved and the address has already been stored in
  301. * the DNS cache. This short circuits waiting for a lot of pending
  302. * lookups for the same hostname requested by different handles.
  303. *
  304. * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
  305. *
  306. * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
  307. * use, or we'll leak memory!
  308. */
  309. struct Curl_dns_entry *
  310. Curl_fetch_addr(struct connectdata *conn,
  311. const char *hostname,
  312. int port)
  313. {
  314. struct Curl_easy *data = conn->data;
  315. struct Curl_dns_entry *dns = NULL;
  316. if(data->share)
  317. Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
  318. dns = fetch_addr(conn, hostname, port);
  319. if(dns)
  320. dns->inuse++; /* we use it! */
  321. if(data->share)
  322. Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
  323. return dns;
  324. }
  325. /*
  326. * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
  327. * struct by re-linking its linked list.
  328. *
  329. * The addr argument should be the address of a pointer to the head node of a
  330. * `Curl_addrinfo` list and it will be modified to point to the new head after
  331. * shuffling.
  332. *
  333. * Not declared static only to make it easy to use in a unit test!
  334. *
  335. * @unittest: 1608
  336. */
  337. CURLcode Curl_shuffle_addr(struct Curl_easy *data, Curl_addrinfo **addr)
  338. {
  339. CURLcode result = CURLE_OK;
  340. const int num_addrs = Curl_num_addresses(*addr);
  341. if(num_addrs > 1) {
  342. Curl_addrinfo **nodes;
  343. infof(data, "Shuffling %i addresses", num_addrs);
  344. nodes = malloc(num_addrs*sizeof(*nodes));
  345. if(nodes) {
  346. int i;
  347. unsigned int *rnd;
  348. const size_t rnd_size = num_addrs * sizeof(*rnd);
  349. /* build a plain array of Curl_addrinfo pointers */
  350. nodes[0] = *addr;
  351. for(i = 1; i < num_addrs; i++) {
  352. nodes[i] = nodes[i-1]->ai_next;
  353. }
  354. rnd = malloc(rnd_size);
  355. if(rnd) {
  356. /* Fisher-Yates shuffle */
  357. if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
  358. Curl_addrinfo *swap_tmp;
  359. for(i = num_addrs - 1; i > 0; i--) {
  360. swap_tmp = nodes[rnd[i] % (i + 1)];
  361. nodes[rnd[i] % (i + 1)] = nodes[i];
  362. nodes[i] = swap_tmp;
  363. }
  364. /* relink list in the new order */
  365. for(i = 1; i < num_addrs; i++) {
  366. nodes[i-1]->ai_next = nodes[i];
  367. }
  368. nodes[num_addrs-1]->ai_next = NULL;
  369. *addr = nodes[0];
  370. }
  371. free(rnd);
  372. }
  373. else
  374. result = CURLE_OUT_OF_MEMORY;
  375. free(nodes);
  376. }
  377. else
  378. result = CURLE_OUT_OF_MEMORY;
  379. }
  380. return result;
  381. }
  382. /*
  383. * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
  384. *
  385. * When calling Curl_resolv() has resulted in a response with a returned
  386. * address, we call this function to store the information in the dns
  387. * cache etc
  388. *
  389. * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
  390. */
  391. struct Curl_dns_entry *
  392. Curl_cache_addr(struct Curl_easy *data,
  393. Curl_addrinfo *addr,
  394. const char *hostname,
  395. int port)
  396. {
  397. char *entry_id;
  398. size_t entry_len;
  399. struct Curl_dns_entry *dns;
  400. struct Curl_dns_entry *dns2;
  401. /* shuffle addresses if requested */
  402. if(data->set.dns_shuffle_addresses) {
  403. CURLcode result = Curl_shuffle_addr(data, &addr);
  404. if(!result)
  405. return NULL;
  406. }
  407. /* Create an entry id, based upon the hostname and port */
  408. entry_id = create_hostcache_id(hostname, port);
  409. /* If we can't create the entry id, fail */
  410. if(!entry_id)
  411. return NULL;
  412. entry_len = strlen(entry_id);
  413. /* Create a new cache entry */
  414. dns = calloc(1, sizeof(struct Curl_dns_entry));
  415. if(!dns) {
  416. free(entry_id);
  417. return NULL;
  418. }
  419. dns->inuse = 1; /* the cache has the first reference */
  420. dns->addr = addr; /* this is the address(es) */
  421. time(&dns->timestamp);
  422. if(dns->timestamp == 0)
  423. dns->timestamp = 1; /* zero indicates CURLOPT_RESOLVE entry */
  424. /* Store the resolved data in our DNS cache. */
  425. dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
  426. (void *)dns);
  427. if(!dns2) {
  428. free(dns);
  429. free(entry_id);
  430. return NULL;
  431. }
  432. dns = dns2;
  433. dns->inuse++; /* mark entry as in-use */
  434. /* free the allocated entry_id */
  435. free(entry_id);
  436. return dns;
  437. }
  438. /*
  439. * Curl_resolv() is the main name resolve function within libcurl. It resolves
  440. * a name and returns a pointer to the entry in the 'entry' argument (if one
  441. * is provided). This function might return immediately if we're using asynch
  442. * resolves. See the return codes.
  443. *
  444. * The cache entry we return will get its 'inuse' counter increased when this
  445. * function is used. You MUST call Curl_resolv_unlock() later (when you're
  446. * done using this struct) to decrease the counter again.
  447. *
  448. * In debug mode, we specifically test for an interface name "LocalHost"
  449. * and resolve "localhost" instead as a means to permit test cases
  450. * to connect to a local test server with any host name.
  451. *
  452. * Return codes:
  453. *
  454. * CURLRESOLV_ERROR (-1) = error, no pointer
  455. * CURLRESOLV_RESOLVED (0) = OK, pointer provided
  456. * CURLRESOLV_PENDING (1) = waiting for response, no pointer
  457. */
  458. int Curl_resolv(struct connectdata *conn,
  459. const char *hostname,
  460. int port,
  461. struct Curl_dns_entry **entry)
  462. {
  463. struct Curl_dns_entry *dns = NULL;
  464. struct Curl_easy *data = conn->data;
  465. CURLcode result;
  466. int rc = CURLRESOLV_ERROR; /* default to failure */
  467. *entry = NULL;
  468. if(data->share)
  469. Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
  470. dns = fetch_addr(conn, hostname, port);
  471. if(dns) {
  472. infof(data, "Hostname %s was found in DNS cache\n", hostname);
  473. dns->inuse++; /* we use it! */
  474. rc = CURLRESOLV_RESOLVED;
  475. }
  476. if(data->share)
  477. Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
  478. if(!dns) {
  479. /* The entry was not in the cache. Resolve it to IP address */
  480. Curl_addrinfo *addr;
  481. int respwait;
  482. /* Check what IP specifics the app has requested and if we can provide it.
  483. * If not, bail out. */
  484. if(!Curl_ipvalid(conn))
  485. return CURLRESOLV_ERROR;
  486. /* notify the resolver start callback */
  487. if(data->set.resolver_start) {
  488. int st;
  489. Curl_set_in_callback(data, true);
  490. st = data->set.resolver_start(data->state.resolver, NULL,
  491. data->set.resolver_start_client);
  492. Curl_set_in_callback(data, false);
  493. if(st)
  494. return CURLRESOLV_ERROR;
  495. }
  496. /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
  497. non-zero value indicating that we need to wait for the response to the
  498. resolve call */
  499. addr = Curl_getaddrinfo(conn,
  500. #ifdef DEBUGBUILD
  501. (data->set.str[STRING_DEVICE]
  502. && !strcmp(data->set.str[STRING_DEVICE],
  503. "LocalHost"))?"localhost":
  504. #endif
  505. hostname, port, &respwait);
  506. if(!addr) {
  507. if(respwait) {
  508. /* the response to our resolve call will come asynchronously at
  509. a later time, good or bad */
  510. /* First, check that we haven't received the info by now */
  511. result = Curl_resolver_is_resolved(conn, &dns);
  512. if(result) /* error detected */
  513. return CURLRESOLV_ERROR;
  514. if(dns)
  515. rc = CURLRESOLV_RESOLVED; /* pointer provided */
  516. else
  517. rc = CURLRESOLV_PENDING; /* no info yet */
  518. }
  519. }
  520. else {
  521. if(data->share)
  522. Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
  523. /* we got a response, store it in the cache */
  524. dns = Curl_cache_addr(data, addr, hostname, port);
  525. if(data->share)
  526. Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
  527. if(!dns)
  528. /* returned failure, bail out nicely */
  529. Curl_freeaddrinfo(addr);
  530. else
  531. rc = CURLRESOLV_RESOLVED;
  532. }
  533. }
  534. *entry = dns;
  535. return rc;
  536. }
  537. #ifdef USE_ALARM_TIMEOUT
  538. /*
  539. * This signal handler jumps back into the main libcurl code and continues
  540. * execution. This effectively causes the remainder of the application to run
  541. * within a signal handler which is nonportable and could lead to problems.
  542. */
  543. static
  544. RETSIGTYPE alarmfunc(int sig)
  545. {
  546. /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */
  547. (void)sig;
  548. siglongjmp(curl_jmpenv, 1);
  549. }
  550. #endif /* USE_ALARM_TIMEOUT */
  551. /*
  552. * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
  553. * timeout. This function might return immediately if we're using asynch
  554. * resolves. See the return codes.
  555. *
  556. * The cache entry we return will get its 'inuse' counter increased when this
  557. * function is used. You MUST call Curl_resolv_unlock() later (when you're
  558. * done using this struct) to decrease the counter again.
  559. *
  560. * If built with a synchronous resolver and use of signals is not
  561. * disabled by the application, then a nonzero timeout will cause a
  562. * timeout after the specified number of milliseconds. Otherwise, timeout
  563. * is ignored.
  564. *
  565. * Return codes:
  566. *
  567. * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
  568. * CURLRESOLV_ERROR (-1) = error, no pointer
  569. * CURLRESOLV_RESOLVED (0) = OK, pointer provided
  570. * CURLRESOLV_PENDING (1) = waiting for response, no pointer
  571. */
  572. int Curl_resolv_timeout(struct connectdata *conn,
  573. const char *hostname,
  574. int port,
  575. struct Curl_dns_entry **entry,
  576. time_t timeoutms)
  577. {
  578. #ifdef USE_ALARM_TIMEOUT
  579. #ifdef HAVE_SIGACTION
  580. struct sigaction keep_sigact; /* store the old struct here */
  581. volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
  582. struct sigaction sigact;
  583. #else
  584. #ifdef HAVE_SIGNAL
  585. void (*keep_sigact)(int); /* store the old handler here */
  586. #endif /* HAVE_SIGNAL */
  587. #endif /* HAVE_SIGACTION */
  588. volatile long timeout;
  589. volatile unsigned int prev_alarm = 0;
  590. struct Curl_easy *data = conn->data;
  591. #endif /* USE_ALARM_TIMEOUT */
  592. int rc;
  593. *entry = NULL;
  594. if(timeoutms < 0)
  595. /* got an already expired timeout */
  596. return CURLRESOLV_TIMEDOUT;
  597. #ifdef USE_ALARM_TIMEOUT
  598. if(data->set.no_signal)
  599. /* Ignore the timeout when signals are disabled */
  600. timeout = 0;
  601. else
  602. timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
  603. if(!timeout)
  604. /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
  605. return Curl_resolv(conn, hostname, port, entry);
  606. if(timeout < 1000) {
  607. /* The alarm() function only provides integer second resolution, so if
  608. we want to wait less than one second we must bail out already now. */
  609. failf(data,
  610. "remaining timeout of %ld too small to resolve via SIGALRM method",
  611. timeout);
  612. return CURLRESOLV_TIMEDOUT;
  613. }
  614. /* This allows us to time-out from the name resolver, as the timeout
  615. will generate a signal and we will siglongjmp() from that here.
  616. This technique has problems (see alarmfunc).
  617. This should be the last thing we do before calling Curl_resolv(),
  618. as otherwise we'd have to worry about variables that get modified
  619. before we invoke Curl_resolv() (and thus use "volatile"). */
  620. if(sigsetjmp(curl_jmpenv, 1)) {
  621. /* this is coming from a siglongjmp() after an alarm signal */
  622. failf(data, "name lookup timed out");
  623. rc = CURLRESOLV_ERROR;
  624. goto clean_up;
  625. }
  626. else {
  627. /*************************************************************
  628. * Set signal handler to catch SIGALRM
  629. * Store the old value to be able to set it back later!
  630. *************************************************************/
  631. #ifdef HAVE_SIGACTION
  632. sigaction(SIGALRM, NULL, &sigact);
  633. keep_sigact = sigact;
  634. keep_copysig = TRUE; /* yes, we have a copy */
  635. sigact.sa_handler = alarmfunc;
  636. #ifdef SA_RESTART
  637. /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
  638. sigact.sa_flags &= ~SA_RESTART;
  639. #endif
  640. /* now set the new struct */
  641. sigaction(SIGALRM, &sigact, NULL);
  642. #else /* HAVE_SIGACTION */
  643. /* no sigaction(), revert to the much lamer signal() */
  644. #ifdef HAVE_SIGNAL
  645. keep_sigact = signal(SIGALRM, alarmfunc);
  646. #endif
  647. #endif /* HAVE_SIGACTION */
  648. /* alarm() makes a signal get sent when the timeout fires off, and that
  649. will abort system calls */
  650. prev_alarm = alarm(curlx_sltoui(timeout/1000L));
  651. }
  652. #else
  653. #ifndef CURLRES_ASYNCH
  654. if(timeoutms)
  655. infof(conn->data, "timeout on name lookup is not supported\n");
  656. #else
  657. (void)timeoutms; /* timeoutms not used with an async resolver */
  658. #endif
  659. #endif /* USE_ALARM_TIMEOUT */
  660. /* Perform the actual name resolution. This might be interrupted by an
  661. * alarm if it takes too long.
  662. */
  663. rc = Curl_resolv(conn, hostname, port, entry);
  664. #ifdef USE_ALARM_TIMEOUT
  665. clean_up:
  666. if(!prev_alarm)
  667. /* deactivate a possibly active alarm before uninstalling the handler */
  668. alarm(0);
  669. #ifdef HAVE_SIGACTION
  670. if(keep_copysig) {
  671. /* we got a struct as it looked before, now put that one back nice
  672. and clean */
  673. sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
  674. }
  675. #else
  676. #ifdef HAVE_SIGNAL
  677. /* restore the previous SIGALRM handler */
  678. signal(SIGALRM, keep_sigact);
  679. #endif
  680. #endif /* HAVE_SIGACTION */
  681. /* switch back the alarm() to either zero or to what it was before minus
  682. the time we spent until now! */
  683. if(prev_alarm) {
  684. /* there was an alarm() set before us, now put it back */
  685. timediff_t elapsed_secs = Curl_timediff(Curl_now(),
  686. conn->created) / 1000;
  687. /* the alarm period is counted in even number of seconds */
  688. unsigned long alarm_set = prev_alarm - elapsed_secs;
  689. if(!alarm_set ||
  690. ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
  691. /* if the alarm time-left reached zero or turned "negative" (counted
  692. with unsigned values), we should fire off a SIGALRM here, but we
  693. won't, and zero would be to switch it off so we never set it to
  694. less than 1! */
  695. alarm(1);
  696. rc = CURLRESOLV_TIMEDOUT;
  697. failf(data, "Previous alarm fired off!");
  698. }
  699. else
  700. alarm((unsigned int)alarm_set);
  701. }
  702. #endif /* USE_ALARM_TIMEOUT */
  703. return rc;
  704. }
  705. /*
  706. * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
  707. * made, the struct may be destroyed due to pruning. It is important that only
  708. * one unlock is made for each Curl_resolv() call.
  709. *
  710. * May be called with 'data' == NULL for global cache.
  711. */
  712. void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
  713. {
  714. if(data && data->share)
  715. Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
  716. freednsentry(dns);
  717. if(data && data->share)
  718. Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
  719. }
  720. /*
  721. * File-internal: release cache dns entry reference, free if inuse drops to 0
  722. */
  723. static void freednsentry(void *freethis)
  724. {
  725. struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
  726. DEBUGASSERT(dns && (dns->inuse>0));
  727. dns->inuse--;
  728. if(dns->inuse == 0) {
  729. Curl_freeaddrinfo(dns->addr);
  730. free(dns);
  731. }
  732. }
  733. /*
  734. * Curl_mk_dnscache() inits a new DNS cache and returns success/failure.
  735. */
  736. int Curl_mk_dnscache(struct curl_hash *hash)
  737. {
  738. return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
  739. freednsentry);
  740. }
  741. /*
  742. * Curl_hostcache_clean()
  743. *
  744. * This _can_ be called with 'data' == NULL but then of course no locking
  745. * can be done!
  746. */
  747. void Curl_hostcache_clean(struct Curl_easy *data,
  748. struct curl_hash *hash)
  749. {
  750. if(data && data->share)
  751. Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
  752. Curl_hash_clean(hash);
  753. if(data && data->share)
  754. Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
  755. }
  756. CURLcode Curl_loadhostpairs(struct Curl_easy *data)
  757. {
  758. struct curl_slist *hostp;
  759. char hostname[256];
  760. int port = 0;
  761. for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
  762. if(!hostp->data)
  763. continue;
  764. if(hostp->data[0] == '-') {
  765. char *entry_id;
  766. size_t entry_len;
  767. if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
  768. infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
  769. hostp->data);
  770. continue;
  771. }
  772. /* Create an entry id, based upon the hostname and port */
  773. entry_id = create_hostcache_id(hostname, port);
  774. /* If we can't create the entry id, fail */
  775. if(!entry_id) {
  776. return CURLE_OUT_OF_MEMORY;
  777. }
  778. entry_len = strlen(entry_id);
  779. if(data->share)
  780. Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
  781. /* delete entry, ignore if it didn't exist */
  782. Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
  783. if(data->share)
  784. Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
  785. /* free the allocated entry_id again */
  786. free(entry_id);
  787. }
  788. else {
  789. struct Curl_dns_entry *dns;
  790. Curl_addrinfo *head = NULL, *tail = NULL;
  791. char *entry_id;
  792. size_t entry_len;
  793. char address[64];
  794. char *addresses = NULL;
  795. char *addr_begin;
  796. char *addr_end;
  797. char *port_ptr;
  798. char *end_ptr;
  799. char *host_end;
  800. unsigned long tmp_port;
  801. bool error = true;
  802. host_end = strchr(hostp->data, ':');
  803. if(!host_end ||
  804. ((host_end - hostp->data) >= (ptrdiff_t)sizeof(hostname)))
  805. goto err;
  806. memcpy(hostname, hostp->data, host_end - hostp->data);
  807. hostname[host_end - hostp->data] = '\0';
  808. port_ptr = host_end + 1;
  809. tmp_port = strtoul(port_ptr, &end_ptr, 10);
  810. if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':')
  811. goto err;
  812. port = (int)tmp_port;
  813. addresses = end_ptr + 1;
  814. while(*end_ptr) {
  815. size_t alen;
  816. Curl_addrinfo *ai;
  817. addr_begin = end_ptr + 1;
  818. addr_end = strchr(addr_begin, ',');
  819. if(!addr_end)
  820. addr_end = addr_begin + strlen(addr_begin);
  821. end_ptr = addr_end;
  822. /* allow IP(v6) address within [brackets] */
  823. if(*addr_begin == '[') {
  824. if(addr_end == addr_begin || *(addr_end - 1) != ']')
  825. goto err;
  826. ++addr_begin;
  827. --addr_end;
  828. }
  829. alen = addr_end - addr_begin;
  830. if(!alen)
  831. continue;
  832. if(alen >= sizeof(address))
  833. goto err;
  834. memcpy(address, addr_begin, alen);
  835. address[alen] = '\0';
  836. #ifndef ENABLE_IPV6
  837. if(strchr(address, ':')) {
  838. infof(data, "Ignoring resolve address '%s', missing IPv6 support.\n",
  839. address);
  840. continue;
  841. }
  842. #endif
  843. ai = Curl_str2addr(address, port);
  844. if(!ai) {
  845. infof(data, "Resolve address '%s' found illegal!\n", address);
  846. goto err;
  847. }
  848. if(tail) {
  849. tail->ai_next = ai;
  850. tail = tail->ai_next;
  851. }
  852. else {
  853. head = tail = ai;
  854. }
  855. }
  856. if(!head)
  857. goto err;
  858. error = false;
  859. err:
  860. if(error) {
  861. infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
  862. hostp->data);
  863. Curl_freeaddrinfo(head);
  864. continue;
  865. }
  866. /* Create an entry id, based upon the hostname and port */
  867. entry_id = create_hostcache_id(hostname, port);
  868. /* If we can't create the entry id, fail */
  869. if(!entry_id) {
  870. Curl_freeaddrinfo(head);
  871. return CURLE_OUT_OF_MEMORY;
  872. }
  873. entry_len = strlen(entry_id);
  874. if(data->share)
  875. Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
  876. /* See if its already in our dns cache */
  877. dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
  878. /* free the allocated entry_id again */
  879. free(entry_id);
  880. if(!dns) {
  881. /* if not in the cache already, put this host in the cache */
  882. dns = Curl_cache_addr(data, head, hostname, port);
  883. if(dns) {
  884. dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
  885. /* release the returned reference; the cache itself will keep the
  886. * entry alive: */
  887. dns->inuse--;
  888. }
  889. }
  890. else {
  891. /* this is a duplicate, free it again */
  892. infof(data, "RESOLVE %s:%d is already cached, %s not stored!\n",
  893. hostname, port, addresses);
  894. Curl_freeaddrinfo(head);
  895. }
  896. if(data->share)
  897. Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
  898. if(!dns) {
  899. Curl_freeaddrinfo(head);
  900. return CURLE_OUT_OF_MEMORY;
  901. }
  902. infof(data, "Added %s:%d:%s to DNS cache\n",
  903. hostname, port, addresses);
  904. }
  905. }
  906. data->change.resolve = NULL; /* dealt with now */
  907. return CURLE_OK;
  908. }