00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "curl_setup.h"
00024
00025 #ifdef HAVE_NETINET_IN_H
00026 #include <netinet/in.h>
00027 #endif
00028 #ifdef HAVE_NETDB_H
00029 #include <netdb.h>
00030 #endif
00031 #ifdef HAVE_ARPA_INET_H
00032 #include <arpa/inet.h>
00033 #endif
00034 #ifdef __VMS
00035 #include <in.h>
00036 #include <inet.h>
00037 #endif
00038
00039 #ifdef HAVE_SETJMP_H
00040 #include <setjmp.h>
00041 #endif
00042 #ifdef HAVE_SIGNAL_H
00043 #include <signal.h>
00044 #endif
00045
00046 #ifdef HAVE_PROCESS_H
00047 #include <process.h>
00048 #endif
00049
00050 #include "urldata.h"
00051 #include "sendf.h"
00052 #include "hostip.h"
00053 #include "hash.h"
00054 #include "share.h"
00055 #include "strerror.h"
00056 #include "url.h"
00057 #include "inet_ntop.h"
00058 #include "warnless.h"
00059
00060 #include "curl_printf.h"
00061 #include "curl_memory.h"
00062 #include "memdebug.h"
00063
00064 #if defined(CURLRES_SYNCH) && \
00065 defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
00066
00067 #define USE_ALARM_TIMEOUT
00068 #endif
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110 static struct curl_hash hostname_cache;
00111 static int host_cache_initialized;
00112
00113 static void freednsentry(void *freethis);
00114
00115
00116
00117
00118
00119
00120
00121
00122 struct curl_hash *Curl_global_host_cache_init(void)
00123 {
00124 int rc = 0;
00125 if(!host_cache_initialized) {
00126 rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
00127 Curl_str_key_compare, freednsentry);
00128 if(!rc)
00129 host_cache_initialized = 1;
00130 }
00131 return rc?NULL:&hostname_cache;
00132 }
00133
00134
00135
00136
00137 void Curl_global_host_cache_dtor(void)
00138 {
00139 if(host_cache_initialized) {
00140 Curl_hash_destroy(&hostname_cache);
00141 host_cache_initialized = 0;
00142 }
00143 }
00144
00145
00146
00147
00148 int Curl_num_addresses(const Curl_addrinfo *addr)
00149 {
00150 int i = 0;
00151 while(addr) {
00152 addr = addr->ai_next;
00153 i++;
00154 }
00155 return i;
00156 }
00157
00158
00159
00160
00161
00162
00163
00164
00165 const char *
00166 Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
00167 {
00168 const struct sockaddr_in *sa4;
00169 const struct in_addr *ipaddr4;
00170 #ifdef ENABLE_IPV6
00171 const struct sockaddr_in6 *sa6;
00172 const struct in6_addr *ipaddr6;
00173 #endif
00174
00175 switch(ai->ai_family) {
00176 case AF_INET:
00177 sa4 = (const void *)ai->ai_addr;
00178 ipaddr4 = &sa4->sin_addr;
00179 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf,
00180 bufsize);
00181 #ifdef ENABLE_IPV6
00182 case AF_INET6:
00183 sa6 = (const void *)ai->ai_addr;
00184 ipaddr6 = &sa6->sin6_addr;
00185 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf,
00186 bufsize);
00187 #endif
00188 default:
00189 break;
00190 }
00191 return NULL;
00192 }
00193
00194
00195
00196
00197
00198 static char *
00199 create_hostcache_id(const char *name, int port)
00200 {
00201
00202 char *id = aprintf("%s:%d", name, port);
00203 char *ptr = id;
00204 if(ptr) {
00205
00206 while(*ptr && (*ptr != ':')) {
00207 *ptr = (char)TOLOWER(*ptr);
00208 ptr++;
00209 }
00210 }
00211 return id;
00212 }
00213
00214 struct hostcache_prune_data {
00215 long cache_timeout;
00216 time_t now;
00217 };
00218
00219
00220
00221
00222
00223
00224
00225
00226 static int
00227 hostcache_timestamp_remove(void *datap, void *hc)
00228 {
00229 struct hostcache_prune_data *data =
00230 (struct hostcache_prune_data *) datap;
00231 struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
00232
00233 return (0 != c->timestamp)
00234 && (data->now - c->timestamp >= data->cache_timeout);
00235 }
00236
00237
00238
00239
00240 static void
00241 hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
00242 {
00243 struct hostcache_prune_data user;
00244
00245 user.cache_timeout = cache_timeout;
00246 user.now = now;
00247
00248 Curl_hash_clean_with_criterium(hostcache,
00249 (void *) &user,
00250 hostcache_timestamp_remove);
00251 }
00252
00253
00254
00255
00256
00257 void Curl_hostcache_prune(struct Curl_easy *data)
00258 {
00259 time_t now;
00260
00261 if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
00262
00263
00264 return;
00265
00266 if(data->share)
00267 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00268
00269 time(&now);
00270
00271
00272 hostcache_prune(data->dns.hostcache,
00273 data->set.dns_cache_timeout,
00274 now);
00275
00276 if(data->share)
00277 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00278 }
00279
00280 #ifdef HAVE_SIGSETJMP
00281
00282
00283
00284 sigjmp_buf curl_jmpenv;
00285 #endif
00286
00287
00288 static struct Curl_dns_entry *
00289 fetch_addr(struct connectdata *conn,
00290 const char *hostname,
00291 int port)
00292 {
00293 char *entry_id = NULL;
00294 struct Curl_dns_entry *dns = NULL;
00295 size_t entry_len;
00296 struct Curl_easy *data = conn->data;
00297
00298
00299 entry_id = create_hostcache_id(hostname, port);
00300
00301 if(!entry_id)
00302 return dns;
00303
00304 entry_len = strlen(entry_id);
00305
00306
00307 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
00308
00309 if(dns && (data->set.dns_cache_timeout != -1)) {
00310
00311 struct hostcache_prune_data user;
00312
00313 time(&user.now);
00314 user.cache_timeout = data->set.dns_cache_timeout;
00315
00316 if(hostcache_timestamp_remove(&user, dns)) {
00317 infof(data, "Hostname in DNS cache was stale, zapped\n");
00318 dns = NULL;
00319 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1);
00320 }
00321 }
00322
00323
00324 free(entry_id);
00325
00326 return dns;
00327 }
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343 struct Curl_dns_entry *
00344 Curl_fetch_addr(struct connectdata *conn,
00345 const char *hostname,
00346 int port)
00347 {
00348 struct Curl_easy *data = conn->data;
00349 struct Curl_dns_entry *dns = NULL;
00350
00351 if(data->share)
00352 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00353
00354 dns = fetch_addr(conn, hostname, port);
00355
00356 if(dns)
00357 dns->inuse++;
00358
00359 if(data->share)
00360 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00361
00362 return dns;
00363 }
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374 struct Curl_dns_entry *
00375 Curl_cache_addr(struct Curl_easy *data,
00376 Curl_addrinfo *addr,
00377 const char *hostname,
00378 int port)
00379 {
00380 char *entry_id;
00381 size_t entry_len;
00382 struct Curl_dns_entry *dns;
00383 struct Curl_dns_entry *dns2;
00384
00385
00386 entry_id = create_hostcache_id(hostname, port);
00387
00388 if(!entry_id)
00389 return NULL;
00390 entry_len = strlen(entry_id);
00391
00392
00393 dns = calloc(1, sizeof(struct Curl_dns_entry));
00394 if(!dns) {
00395 free(entry_id);
00396 return NULL;
00397 }
00398
00399 dns->inuse = 1;
00400 dns->addr = addr;
00401 time(&dns->timestamp);
00402 if(dns->timestamp == 0)
00403 dns->timestamp = 1;
00404
00405
00406 dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
00407 (void *)dns);
00408 if(!dns2) {
00409 free(dns);
00410 free(entry_id);
00411 return NULL;
00412 }
00413
00414 dns = dns2;
00415 dns->inuse++;
00416
00417
00418 free(entry_id);
00419
00420 return dns;
00421 }
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444 int Curl_resolv(struct connectdata *conn,
00445 const char *hostname,
00446 int port,
00447 struct Curl_dns_entry **entry)
00448 {
00449 struct Curl_dns_entry *dns = NULL;
00450 struct Curl_easy *data = conn->data;
00451 CURLcode result;
00452 int rc = CURLRESOLV_ERROR;
00453
00454 *entry = NULL;
00455
00456 if(data->share)
00457 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00458
00459 dns = fetch_addr(conn, hostname, port);
00460
00461 if(dns) {
00462 infof(data, "Hostname %s was found in DNS cache\n", hostname);
00463 dns->inuse++;
00464 rc = CURLRESOLV_RESOLVED;
00465 }
00466
00467 if(data->share)
00468 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00469
00470 if(!dns) {
00471
00472
00473 Curl_addrinfo *addr;
00474 int respwait;
00475
00476
00477
00478 if(!Curl_ipvalid(conn))
00479 return CURLRESOLV_ERROR;
00480
00481
00482
00483
00484 addr = Curl_getaddrinfo(conn,
00485 #ifdef DEBUGBUILD
00486 (data->set.str[STRING_DEVICE]
00487 && !strcmp(data->set.str[STRING_DEVICE],
00488 "LocalHost"))?"localhost":
00489 #endif
00490 hostname, port, &respwait);
00491
00492 if(!addr) {
00493 if(respwait) {
00494
00495
00496
00497 result = Curl_resolver_is_resolved(conn, &dns);
00498 if(result)
00499 return CURLRESOLV_ERROR;
00500 if(dns)
00501 rc = CURLRESOLV_RESOLVED;
00502 else
00503 rc = CURLRESOLV_PENDING;
00504 }
00505 }
00506 else {
00507 if(data->share)
00508 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00509
00510
00511 dns = Curl_cache_addr(data, addr, hostname, port);
00512
00513 if(data->share)
00514 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00515
00516 if(!dns)
00517
00518 Curl_freeaddrinfo(addr);
00519 else
00520 rc = CURLRESOLV_RESOLVED;
00521 }
00522 }
00523
00524 *entry = dns;
00525
00526 return rc;
00527 }
00528
00529 #ifdef USE_ALARM_TIMEOUT
00530
00531
00532
00533
00534
00535 static
00536 RETSIGTYPE alarmfunc(int sig)
00537 {
00538
00539 (void)sig;
00540 siglongjmp(curl_jmpenv, 1);
00541 return;
00542 }
00543 #endif
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567 int Curl_resolv_timeout(struct connectdata *conn,
00568 const char *hostname,
00569 int port,
00570 struct Curl_dns_entry **entry,
00571 time_t timeoutms)
00572 {
00573 #ifdef USE_ALARM_TIMEOUT
00574 #ifdef HAVE_SIGACTION
00575 struct sigaction keep_sigact;
00576 volatile bool keep_copysig = FALSE;
00577 struct sigaction sigact;
00578 #else
00579 #ifdef HAVE_SIGNAL
00580 void (*keep_sigact)(int);
00581 #endif
00582 #endif
00583 volatile long timeout;
00584 volatile unsigned int prev_alarm = 0;
00585 struct Curl_easy *data = conn->data;
00586 #endif
00587 int rc;
00588
00589 *entry = NULL;
00590
00591 if(timeoutms < 0)
00592
00593 return CURLRESOLV_TIMEDOUT;
00594
00595 #ifdef USE_ALARM_TIMEOUT
00596 if(data->set.no_signal)
00597
00598 timeout = 0;
00599 else
00600 timeout = timeoutms;
00601
00602 if(!timeout)
00603
00604 return Curl_resolv(conn, hostname, port, entry);
00605
00606 if(timeout < 1000) {
00607
00608
00609 failf(data,
00610 "remaining timeout of %ld too small to resolve via SIGALRM method",
00611 timeout);
00612 return CURLRESOLV_TIMEDOUT;
00613 }
00614
00615
00616
00617
00618
00619
00620 if(sigsetjmp(curl_jmpenv, 1)) {
00621
00622 failf(data, "name lookup timed out");
00623 rc = CURLRESOLV_ERROR;
00624 goto clean_up;
00625 }
00626 else {
00627
00628
00629
00630
00631 #ifdef HAVE_SIGACTION
00632 sigaction(SIGALRM, NULL, &sigact);
00633 keep_sigact = sigact;
00634 keep_copysig = TRUE;
00635 sigact.sa_handler = alarmfunc;
00636 #ifdef SA_RESTART
00637
00638 sigact.sa_flags &= ~SA_RESTART;
00639 #endif
00640
00641 sigaction(SIGALRM, &sigact, NULL);
00642 #else
00643
00644 #ifdef HAVE_SIGNAL
00645 keep_sigact = signal(SIGALRM, alarmfunc);
00646 #endif
00647 #endif
00648
00649
00650
00651 prev_alarm = alarm(curlx_sltoui(timeout/1000L));
00652 }
00653
00654 #else
00655 #ifndef CURLRES_ASYNCH
00656 if(timeoutms)
00657 infof(conn->data, "timeout on name lookup is not supported\n");
00658 #else
00659 (void)timeoutms;
00660 #endif
00661 #endif
00662
00663
00664
00665
00666 rc = Curl_resolv(conn, hostname, port, entry);
00667
00668 #ifdef USE_ALARM_TIMEOUT
00669 clean_up:
00670
00671 if(!prev_alarm)
00672
00673 alarm(0);
00674
00675 #ifdef HAVE_SIGACTION
00676 if(keep_copysig) {
00677
00678
00679 sigaction(SIGALRM, &keep_sigact, NULL);
00680 }
00681 #else
00682 #ifdef HAVE_SIGNAL
00683
00684 signal(SIGALRM, keep_sigact);
00685 #endif
00686 #endif
00687
00688
00689
00690 if(prev_alarm) {
00691
00692 unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
00693
00694
00695 unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
00696
00697 if(!alarm_set ||
00698 ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
00699
00700
00701
00702
00703 alarm(1);
00704 rc = CURLRESOLV_TIMEDOUT;
00705 failf(data, "Previous alarm fired off!");
00706 }
00707 else
00708 alarm((unsigned int)alarm_set);
00709 }
00710 #endif
00711
00712 return rc;
00713 }
00714
00715
00716
00717
00718
00719
00720
00721
00722 void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
00723 {
00724 if(data && data->share)
00725 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00726
00727 freednsentry(dns);
00728
00729 if(data && data->share)
00730 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00731 }
00732
00733
00734
00735
00736 static void freednsentry(void *freethis)
00737 {
00738 struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
00739 DEBUGASSERT(dns && (dns->inuse>0));
00740
00741 dns->inuse--;
00742 if(dns->inuse == 0) {
00743 Curl_freeaddrinfo(dns->addr);
00744 free(dns);
00745 }
00746 }
00747
00748
00749
00750
00751 int Curl_mk_dnscache(struct curl_hash *hash)
00752 {
00753 return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
00754 freednsentry);
00755 }
00756
00757
00758
00759
00760
00761
00762
00763
00764 void Curl_hostcache_clean(struct Curl_easy *data,
00765 struct curl_hash *hash)
00766 {
00767 if(data && data->share)
00768 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00769
00770 Curl_hash_clean(hash);
00771
00772 if(data && data->share)
00773 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00774 }
00775
00776
00777 CURLcode Curl_loadhostpairs(struct Curl_easy *data)
00778 {
00779 struct curl_slist *hostp;
00780 char hostname[256];
00781 char address[256];
00782 int port;
00783
00784 for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
00785 if(!hostp->data)
00786 continue;
00787 if(hostp->data[0] == '-') {
00788 char *entry_id;
00789 size_t entry_len;
00790
00791 if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
00792 infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
00793 hostp->data);
00794 continue;
00795 }
00796
00797
00798 entry_id = create_hostcache_id(hostname, port);
00799
00800 if(!entry_id) {
00801 return CURLE_OUT_OF_MEMORY;
00802 }
00803
00804 entry_len = strlen(entry_id);
00805
00806 if(data->share)
00807 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00808
00809
00810 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1);
00811
00812 if(data->share)
00813 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00814
00815
00816 free(entry_id);
00817 }
00818 else {
00819 struct Curl_dns_entry *dns;
00820 Curl_addrinfo *addr;
00821 char *entry_id;
00822 size_t entry_len;
00823
00824 if(3 != sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
00825 address)) {
00826 infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
00827 hostp->data);
00828 continue;
00829 }
00830
00831 addr = Curl_str2addr(address, port);
00832 if(!addr) {
00833 infof(data, "Address in '%s' found illegal!\n", hostp->data);
00834 continue;
00835 }
00836
00837
00838 entry_id = create_hostcache_id(hostname, port);
00839
00840 if(!entry_id) {
00841 Curl_freeaddrinfo(addr);
00842 return CURLE_OUT_OF_MEMORY;
00843 }
00844
00845 entry_len = strlen(entry_id);
00846
00847 if(data->share)
00848 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00849
00850
00851 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
00852
00853
00854 free(entry_id);
00855
00856 if(!dns) {
00857
00858 dns = Curl_cache_addr(data, addr, hostname, port);
00859 if(dns) {
00860 dns->timestamp = 0;
00861
00862
00863 dns->inuse--;
00864 }
00865 }
00866 else
00867
00868 Curl_freeaddrinfo(addr);
00869
00870 if(data->share)
00871 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00872
00873 if(!dns) {
00874 Curl_freeaddrinfo(addr);
00875 return CURLE_OUT_OF_MEMORY;
00876 }
00877 infof(data, "Added %s:%d:%s to DNS cache\n",
00878 hostname, port, address);
00879 }
00880 }
00881 data->change.resolve = NULL;
00882
00883 return CURLE_OK;
00884 }