1/* $NetBSD: common.c,v 1.7 2024/02/02 22:19:05 christos Exp $ */ 2/*- 3 * Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav 4 * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer 12 * in this position and unchanged. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * $FreeBSD: common.c,v 1.53 2007/12/19 00:26:36 des Exp $ 31 */ 32 33#if HAVE_CONFIG_H 34#include "config.h" 35#endif 36#ifndef NETBSD 37#include <nbcompat.h> 38#endif 39 40#include <sys/types.h> 41#include <sys/socket.h> 42#include <sys/time.h> 43#include <sys/uio.h> 44#if defined(HAVE_POLL_H) || defined(NETBSD) 45#include <poll.h> 46#define HAVE_POLL 47#elif HAVE_SYS_POLL_H 48#define HAVE_POLL 49#include <sys/poll.h> 50#endif 51 52#include <netinet/in.h> 53#include <arpa/inet.h> 54 55#include <ctype.h> 56#include <errno.h> 57#if defined(HAVE_INTTYPES_H) || defined(NETBSD) 58#include <inttypes.h> 59#endif 60#ifndef NETBSD 61#include <nbcompat/netdb.h> 62#else 63#include <netdb.h> 64#endif 65#include <pwd.h> 66#include <stdarg.h> 67#include <stdlib.h> 68#include <stdio.h> 69#include <string.h> 70#include <unistd.h> 71 72#ifndef MSG_NOSIGNAL 73#include <signal.h> 74#endif 75 76#include "fetch.h" 77#include "common.h" 78 79/*** Local data **************************************************************/ 80 81/* 82 * Error messages for resolver errors 83 */ 84static struct fetcherr netdb_errlist[] = { 85#ifdef EAI_NODATA 86 { EAI_NODATA, FETCH_RESOLV, "Host not found" }, 87#endif 88 { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, 89 { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, 90 { EAI_NONAME, FETCH_RESOLV, "No address record" }, 91 { -1, FETCH_UNKNOWN, "Unknown resolver error" } 92}; 93 94/*** Error-reporting functions ***********************************************/ 95 96/* 97 * Map error code to string 98 */ 99static struct fetcherr * 100fetch_finderr(struct fetcherr *p, int e) 101{ 102 while (p->num != -1 && p->num != e) 103 p++; 104 return (p); 105} 106 107/* 108 * Set error code 109 */ 110void 111fetch_seterr(struct fetcherr *p, int e) 112{ 113 p = fetch_finderr(p, e); 114 fetchLastErrCode = p->cat; 115 snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); 116} 117 118/* 119 * Set error code according to errno 120 */ 121void 122fetch_syserr(void) 123{ 124 switch (errno) { 125 case 0: 126 fetchLastErrCode = FETCH_OK; 127 break; 128 case EPERM: 129 case EACCES: 130 case EROFS: 131#ifdef EAUTH 132 case EAUTH: 133#endif 134#ifdef ENEEDAUTH 135 case ENEEDAUTH: 136#endif 137 fetchLastErrCode = FETCH_AUTH; 138 break; 139 case ENOENT: 140 case EISDIR: /* XXX */ 141 fetchLastErrCode = FETCH_UNAVAIL; 142 break; 143 case ENOMEM: 144 fetchLastErrCode = FETCH_MEMORY; 145 break; 146 case EBUSY: 147 case EAGAIN: 148 fetchLastErrCode = FETCH_TEMP; 149 break; 150 case EEXIST: 151 fetchLastErrCode = FETCH_EXISTS; 152 break; 153 case ENOSPC: 154 fetchLastErrCode = FETCH_FULL; 155 break; 156 case EADDRINUSE: 157 case EADDRNOTAVAIL: 158 case ENETDOWN: 159 case ENETUNREACH: 160 case ENETRESET: 161 case EHOSTUNREACH: 162 fetchLastErrCode = FETCH_NETWORK; 163 break; 164 case ECONNABORTED: 165 case ECONNRESET: 166 fetchLastErrCode = FETCH_ABORT; 167 break; 168 case ETIMEDOUT: 169 fetchLastErrCode = FETCH_TIMEOUT; 170 break; 171 case ECONNREFUSED: 172 case EHOSTDOWN: 173 fetchLastErrCode = FETCH_DOWN; 174 break; 175default: 176 fetchLastErrCode = FETCH_UNKNOWN; 177 } 178 snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); 179} 180 181 182/* 183 * Emit status message 184 */ 185void 186fetch_info(const char *fmt, ...) 187{ 188 va_list ap; 189 190 va_start(ap, fmt); 191 vfprintf(stderr, fmt, ap); 192 va_end(ap); 193 fputc('\n', stderr); 194} 195 196 197/*** Network-related utility functions ***************************************/ 198 199/* 200 * Return the default port for a scheme 201 */ 202int 203fetch_default_port(const char *scheme) 204{ 205 struct servent *se; 206 207 if ((se = getservbyname(scheme, "tcp")) != NULL) 208 return (ntohs(se->s_port)); 209 if (strcasecmp(scheme, SCHEME_FTP) == 0) 210 return (FTP_DEFAULT_PORT); 211 if (strcasecmp(scheme, SCHEME_HTTP) == 0) 212 return (HTTP_DEFAULT_PORT); 213 return (0); 214} 215 216/* 217 * Return the default proxy port for a scheme 218 */ 219int 220fetch_default_proxy_port(const char *scheme) 221{ 222 if (strcasecmp(scheme, SCHEME_FTP) == 0) 223 return (FTP_DEFAULT_PROXY_PORT); 224 if (strcasecmp(scheme, SCHEME_HTTP) == 0) 225 return (HTTP_DEFAULT_PROXY_PORT); 226 return (0); 227} 228 229 230/* 231 * Create a connection for an existing descriptor. 232 */ 233conn_t * 234fetch_reopen(int sd) 235{ 236 conn_t *conn; 237 238 /* allocate and fill connection structure */ 239 if ((conn = calloc(1, sizeof(*conn))) == NULL) 240 return (NULL); 241 conn->ftp_home = NULL; 242 conn->cache_url = NULL; 243 conn->next_buf = NULL; 244 conn->next_len = 0; 245 conn->sd = sd; 246#ifdef HAVE_POLL 247 conn->buf_events = POLLIN; 248#endif 249 return (conn); 250} 251 252 253/* 254 * Bind a socket to a specific local address 255 */ 256int 257fetch_bind(int sd, int af, const char *addr) 258{ 259 struct addrinfo hints, *res, *res0; 260 261 memset(&hints, 0, sizeof(hints)); 262 hints.ai_family = af; 263 hints.ai_socktype = SOCK_STREAM; 264 hints.ai_protocol = 0; 265 if (getaddrinfo(addr, NULL, &hints, &res0)) 266 return (-1); 267 for (res = res0; res; res = res->ai_next) { 268 if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) 269 return (0); 270 } 271 return (-1); 272} 273 274 275/* 276 * Establish a TCP connection to the specified port on the specified host. 277 */ 278conn_t * 279fetch_connect(struct url *url, int af, int verbose) 280{ 281 conn_t *conn; 282 char pbuf[10]; 283 const char *bindaddr; 284 struct addrinfo hints, *res, *res0; 285 int sd, error; 286 287 if (verbose) 288 fetch_info("looking up %s", url->host); 289 290 /* look up host name and set up socket address structure */ 291 snprintf(pbuf, sizeof(pbuf), "%d", url->port); 292 memset(&hints, 0, sizeof(hints)); 293 hints.ai_family = af; 294 hints.ai_socktype = SOCK_STREAM; 295 hints.ai_protocol = 0; 296 if ((error = getaddrinfo(url->host, pbuf, &hints, &res0)) != 0) { 297 netdb_seterr(error); 298 return (NULL); 299 } 300 bindaddr = getenv("FETCH_BIND_ADDRESS"); 301 302 if (verbose) 303 fetch_info("connecting to %s:%d", url->host, url->port); 304 305 /* try to connect */ 306 for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) { 307 if ((sd = socket(res->ai_family, res->ai_socktype, 308 res->ai_protocol)) == -1) 309 continue; 310 if (bindaddr != NULL && *bindaddr != '\0' && 311 fetch_bind(sd, res->ai_family, bindaddr) != 0) { 312 fetch_info("failed to bind to '%s'", bindaddr); 313 close(sd); 314 continue; 315 } 316 if (connect(sd, res->ai_addr, res->ai_addrlen) == 0) 317 break; 318 close(sd); 319 } 320 freeaddrinfo(res0); 321 if (sd == -1) { 322 fetch_syserr(); 323 return (NULL); 324 } 325 326 if ((conn = fetch_reopen(sd)) == NULL) { 327 fetch_syserr(); 328 close(sd); 329 return (NULL); 330 } 331 conn->cache_url = fetchCopyURL(url); 332 conn->cache_af = af; 333 return (conn); 334} 335 336static conn_t *connection_cache; 337static int cache_global_limit = 0; 338static int cache_per_host_limit = 0; 339 340/* 341 * Initialise cache with the given limits. 342 */ 343void 344fetchConnectionCacheInit(int global_limit, int per_host_limit) 345{ 346 347 if (global_limit < 0) 348 cache_global_limit = INT_MAX; 349 else if (per_host_limit > global_limit) 350 cache_global_limit = per_host_limit; 351 else 352 cache_global_limit = global_limit; 353 if (per_host_limit < 0) 354 cache_per_host_limit = INT_MAX; 355 else 356 cache_per_host_limit = per_host_limit; 357} 358 359/* 360 * Flush cache and free all associated resources. 361 */ 362void 363fetchConnectionCacheClose(void) 364{ 365 conn_t *conn; 366 367 while ((conn = connection_cache) != NULL) { 368 connection_cache = conn->next_cached; 369 (*conn->cache_close)(conn); 370 } 371} 372 373/* 374 * Check connection cache for an existing entry matching 375 * protocol/host/port/user/password/family. 376 */ 377conn_t * 378fetch_cache_get(const struct url *url, int af) 379{ 380 conn_t *conn, *last_conn = NULL; 381 382 for (conn = connection_cache; conn; last_conn = conn, 383 conn = conn->next_cached) 384 { 385 if (conn->cache_url->port == url->port && 386 strcmp(conn->cache_url->scheme, url->scheme) == 0 && 387 strcmp(conn->cache_url->host, url->host) == 0 && 388 strcmp(conn->cache_url->user, url->user) == 0 && 389 strcmp(conn->cache_url->pwd, url->pwd) == 0 && 390 (conn->cache_af == AF_UNSPEC || af == AF_UNSPEC || 391 conn->cache_af == af)) { 392 if (last_conn != NULL) 393 last_conn->next_cached = conn->next_cached; 394 else 395 connection_cache = conn->next_cached; 396 return conn; 397 } 398 } 399 400 return NULL; 401} 402 403/* 404 * Put the connection back into the cache for reuse. 405 * If the connection is freed due to LRU or if the cache 406 * is explicitly closed, the given callback is called. 407 */ 408void 409fetch_cache_put(conn_t *conn, int (*closecb)(conn_t *)) 410{ 411 conn_t *iter, *last, *oiter; 412 int global_count, host_count, added; 413 414 if (conn->cache_url == NULL || cache_global_limit == 0) { 415 (*closecb)(conn); 416 return; 417 } 418 419 global_count = host_count = 0; 420 last = NULL; 421 for (iter = connection_cache; iter; ) { 422 ++global_count; 423 added = !strcmp(conn->cache_url->host, iter->cache_url->host); 424 if (added) 425 ++host_count; 426 if (global_count < cache_global_limit && 427 host_count < cache_per_host_limit) { 428 oiter = NULL; 429 last = iter; 430 } else { 431 --global_count; 432 if (added) 433 --host_count; 434 if (last != NULL) 435 last->next_cached = iter->next_cached; 436 else 437 connection_cache = iter->next_cached; 438 oiter = iter; 439 } 440 iter = iter->next_cached; 441 if (oiter) 442 (*oiter->cache_close)(oiter); 443 } 444 445 conn->cache_close = closecb; 446 conn->next_cached = connection_cache; 447 connection_cache = conn; 448} 449 450/* 451 * Enable SSL on a connection. 452 */ 453int 454fetch_ssl(conn_t *conn, const struct url *URL, int verbose) 455{ 456 457#ifdef WITH_SSL 458 /* Init the SSL library and context */ 459 if (!SSL_library_init()){ 460 fprintf(stderr, "SSL library init failed\n"); 461 return (-1); 462 } 463 464 SSL_load_error_strings(); 465 466 conn->ssl_meth = SSLv23_client_method(); 467 conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth); 468 SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY); 469 if (getenv("SSL_NO_VERIFY_PEER") == NULL) { 470 SSL_CTX_set_default_verify_paths(conn->ssl_ctx); 471 SSL_CTX_set_verify(conn->ssl_ctx, SSL_VERIFY_PEER, NULL); 472 } 473 474 conn->ssl = SSL_new(conn->ssl_ctx); 475 if (conn->ssl == NULL){ 476 fprintf(stderr, "SSL context creation failed\n"); 477 return (-1); 478 } 479 conn->buf_events = 0; 480 SSL_set_fd(conn->ssl, conn->sd); 481#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) 482 if (!SSL_set_tlsext_host_name(conn->ssl, (char *)(uintptr_t)URL->host)) 483 { 484 fprintf(stderr, 485 "TLS server name indication extension failed for host %s\n", 486 URL->host); 487 return (-1); 488 } 489#endif 490 if (SSL_connect(conn->ssl) == -1){ 491 ERR_print_errors_fp(stderr); 492 return (-1); 493 } 494 495 if (verbose) { 496 X509_NAME *name; 497 char *str; 498 499 fprintf(stderr, "SSL connection established using %s\n", 500 SSL_get_cipher(conn->ssl)); 501 conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); 502 name = X509_get_subject_name(conn->ssl_cert); 503 str = X509_NAME_oneline(name, 0, 0); 504 printf("Certificate subject: %s\n", str); 505 free(str); 506 name = X509_get_issuer_name(conn->ssl_cert); 507 str = X509_NAME_oneline(name, 0, 0); 508 printf("Certificate issuer: %s\n", str); 509 free(str); 510 } 511 512 return (0); 513#else 514 (void)conn; 515 (void)verbose; 516 fprintf(stderr, "SSL support disabled\n"); 517 return (-1); 518#endif 519} 520 521#ifdef HAVE_POLL 522static int 523compute_timeout(const struct timeval *tv) 524{ 525 struct timeval cur; 526 527 gettimeofday(&cur, NULL); 528 return (tv->tv_sec - cur.tv_sec) * 1000 529 + (tv->tv_usec - cur.tv_usec) / 1000; 530} 531#endif 532 533/* 534 * Read a character from a connection w/ timeout 535 */ 536ssize_t 537fetch_read(conn_t *conn, char *buf, size_t len) 538{ 539 struct timeval timeout_end; 540#ifdef HAVE_POLL 541 struct pollfd pfd; 542#else 543 fd_set readfds; 544#endif 545 int timeout_cur; 546 ssize_t rlen; 547 int r; 548 549 if (len == 0) 550 return 0; 551 552 if (conn->next_len != 0) { 553 if (conn->next_len < len) 554 len = conn->next_len; 555 memmove(buf, conn->next_buf, len); 556 conn->next_len -= len; 557 conn->next_buf += len; 558 return len; 559 } 560 561 if (fetchTimeout) { 562#ifndef HAVE_POLL 563 FD_ZERO(&readfds); 564#endif 565 gettimeofday(&timeout_end, NULL); 566 timeout_end.tv_sec += fetchTimeout; 567 } 568 569 for (;;) { 570#ifdef HAVE_POLL 571 pfd.fd = conn->sd; 572 pfd.events = conn->buf_events; 573 if (fetchTimeout && pfd.events) { 574 do { 575 timeout_cur = compute_timeout(&timeout_end); 576 if (timeout_cur < 0) { 577 errno = ETIMEDOUT; 578 fetch_syserr(); 579 return (-1); 580 } 581 errno = 0; 582 r = poll(&pfd, 1, timeout_cur); 583 if (r == -1) { 584 if (errno == EINTR && fetchRestartCalls) 585 continue; 586 fetch_syserr(); 587 return (-1); 588 } 589 } while (pfd.revents == 0); 590#else 591 while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) { 592 struct timeval waittv, now; 593 FD_SET(conn->sd, &readfds); 594 gettimeofday(&now, NULL); 595 waittv.tv_sec = timeout_end.tv_sec - now.tv_sec; 596 waittv.tv_usec = timeout_end.tv_usec - now.tv_usec; 597 if (waittv.tv_usec < 0) { 598 waittv.tv_usec += 1000000; 599 waittv.tv_sec--; 600 } 601 if (waittv.tv_sec < 0) { 602 errno = ETIMEDOUT; 603 fetch_syserr(); 604 return (-1); 605 } 606 errno = 0; 607 r = select(conn->sd + 1, &readfds, NULL, NULL, &waittv); 608 if (r == -1) { 609 if (errno == EINTR && fetchRestartCalls) 610 continue; 611 fetch_syserr(); 612 return (-1); 613 } 614#endif 615 } 616#ifdef WITH_SSL 617 if (conn->ssl != NULL) { 618 rlen = SSL_read(conn->ssl, buf, len); 619 if (rlen == -1) { 620 switch (SSL_get_error(conn->ssl, rlen)) { 621 case SSL_ERROR_WANT_READ: 622 conn->buf_events = POLLIN; 623 break; 624 case SSL_ERROR_WANT_WRITE: 625 conn->buf_events = POLLOUT; 626 break; 627 default: 628 errno = EIO; 629 fetch_syserr(); 630 return -1; 631 } 632 } else { 633 /* Assume buffering on the SSL layer. */ 634 conn->buf_events = 0; 635 } 636 } else 637#endif 638 rlen = read(conn->sd, buf, len); 639 if (rlen >= 0) 640 break; 641 642 if (errno != EINTR || !fetchRestartCalls) 643 return (-1); 644 } 645 return (rlen); 646} 647 648 649/* 650 * Read a line of text from a connection w/ timeout 651 */ 652#define MIN_BUF_SIZE 1024 653 654int 655fetch_getln(conn_t *conn) 656{ 657 char *tmp, *next; 658 size_t tmpsize; 659 ssize_t len; 660 661 if (conn->buf == NULL) { 662 if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) { 663 errno = ENOMEM; 664 return (-1); 665 } 666 conn->bufsize = MIN_BUF_SIZE; 667 } 668 669 conn->buflen = 0; 670 next = NULL; 671 672 do { 673 /* 674 * conn->bufsize != conn->buflen at this point, 675 * so the buffer can be NUL-terminated below for 676 * the case of len == 0. 677 */ 678 len = fetch_read(conn, conn->buf + conn->buflen, 679 conn->bufsize - conn->buflen); 680 if (len == -1) 681 return (-1); 682 if (len == 0) 683 break; 684 next = memchr(conn->buf + conn->buflen, '\n', (size_t)len); 685 conn->buflen += len; 686 if (conn->buflen == conn->bufsize && next == NULL) { 687 tmp = conn->buf; 688 tmpsize = conn->bufsize * 2; 689 if (tmpsize < conn->bufsize) { 690 errno = ENOMEM; 691 return (-1); 692 } 693 if ((tmp = realloc(tmp, tmpsize)) == NULL) { 694 errno = ENOMEM; 695 return (-1); 696 } 697 conn->buf = tmp; 698 conn->bufsize = tmpsize; 699 } 700 } while (next == NULL); 701 702 if (next != NULL) { 703 *next = '\0'; 704 conn->next_buf = next + 1; 705 conn->next_len = conn->buflen - (conn->next_buf - conn->buf); 706 conn->buflen = next - conn->buf; 707 } else { 708 conn->buf[conn->buflen] = '\0'; 709 conn->next_len = 0; 710 } 711 return (0); 712} 713 714/* 715 * Write a vector to a connection w/ timeout 716 * Note: can modify the iovec. 717 */ 718ssize_t 719fetch_write(conn_t *conn, const void *buf, size_t len) 720{ 721 struct timeval now, timeout, waittv; 722 fd_set writefds; 723 ssize_t wlen, total; 724 int r; 725#ifndef MSG_NOSIGNAL 726 static int killed_sigpipe; 727#endif 728 729#ifndef MSG_NOSIGNAL 730 if (!killed_sigpipe) { 731 signal(SIGPIPE, SIG_IGN); 732 killed_sigpipe = 1; 733 } 734#endif 735 736 737 if (fetchTimeout) { 738 FD_ZERO(&writefds); 739 gettimeofday(&timeout, NULL); 740 timeout.tv_sec += fetchTimeout; 741 } 742 743 total = 0; 744 while (len) { 745 while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) { 746 FD_SET(conn->sd, &writefds); 747 gettimeofday(&now, NULL); 748 waittv.tv_sec = timeout.tv_sec - now.tv_sec; 749 waittv.tv_usec = timeout.tv_usec - now.tv_usec; 750 if (waittv.tv_usec < 0) { 751 waittv.tv_usec += 1000000; 752 waittv.tv_sec--; 753 } 754 if (waittv.tv_sec < 0) { 755 errno = ETIMEDOUT; 756 fetch_syserr(); 757 return (-1); 758 } 759 errno = 0; 760 r = select(conn->sd + 1, NULL, &writefds, NULL, &waittv); 761 if (r == -1) { 762 if (errno == EINTR && fetchRestartCalls) 763 continue; 764 return (-1); 765 } 766 } 767 errno = 0; 768#ifdef WITH_SSL 769 if (conn->ssl != NULL) 770 wlen = SSL_write(conn->ssl, buf, (int)len); 771 else 772#endif 773#ifndef MSG_NOSIGNAL 774 wlen = send(conn->sd, buf, len, 0); 775#else 776 wlen = send(conn->sd, buf, len, MSG_NOSIGNAL); 777#endif 778 if (wlen == 0) { 779 /* we consider a short write a failure */ 780 errno = EPIPE; 781 fetch_syserr(); 782 return (-1); 783 } 784 if (wlen < 0) { 785 if (errno == EINTR && fetchRestartCalls) 786 continue; 787 return (-1); 788 } 789 total += wlen; 790 buf = (const char *)buf + wlen; 791 len -= wlen; 792 } 793 return (total); 794} 795 796 797/* 798 * Close connection 799 */ 800int 801fetch_close(conn_t *conn) 802{ 803 int ret; 804 805#ifdef WITH_SSL 806 if (conn->ssl) { 807 SSL_shutdown(conn->ssl); 808 SSL_set_connect_state(conn->ssl); 809 SSL_free(conn->ssl); 810 conn->ssl = NULL; 811 } 812 if (conn->ssl_ctx) { 813 SSL_CTX_free(conn->ssl_ctx); 814 conn->ssl_ctx = NULL; 815 } 816 if (conn->ssl_cert) { 817 X509_free(conn->ssl_cert); 818 conn->ssl_cert = NULL; 819 } 820#endif 821 ret = close(conn->sd); 822 if (conn->cache_url) 823 fetchFreeURL(conn->cache_url); 824 free(conn->ftp_home); 825 free(conn->buf); 826 free(conn); 827 return (ret); 828} 829 830 831/*** Directory-related utility functions *************************************/ 832 833int 834fetch_add_entry(struct url_list *ue, struct url *base, const char *name, 835 int pre_quoted) 836{ 837 struct url *tmp; 838 char *tmp_name; 839 size_t base_doc_len, name_len, i; 840 unsigned char c; 841 842 if (strchr(name, '/') != NULL || 843 strcmp(name, "..") == 0 || 844 strcmp(name, ".") == 0) 845 return 0; 846 847 if (strcmp(base->doc, "/") == 0) 848 base_doc_len = 0; 849 else 850 base_doc_len = strlen(base->doc); 851 852 name_len = 1; 853 for (i = 0; name[i] != '\0'; ++i) { 854 if ((!pre_quoted && name[i] == '%') || 855 !fetch_urlpath_safe(name[i])) 856 name_len += 3; 857 else 858 ++name_len; 859 } 860 861 tmp_name = malloc( base_doc_len + name_len + 1); 862 if (tmp_name == NULL) { 863 errno = ENOMEM; 864 fetch_syserr(); 865 return (-1); 866 } 867 868 if (ue->length + 1 >= ue->alloc_size) { 869 tmp = realloc(ue->urls, (ue->alloc_size * 2 + 1) * sizeof(*tmp)); 870 if (tmp == NULL) { 871 free(tmp_name); 872 errno = ENOMEM; 873 fetch_syserr(); 874 return (-1); 875 } 876 ue->alloc_size = ue->alloc_size * 2 + 1; 877 ue->urls = tmp; 878 } 879 880 tmp = ue->urls + ue->length; 881 strcpy(tmp->scheme, base->scheme); 882 strcpy(tmp->user, base->user); 883 strcpy(tmp->pwd, base->pwd); 884 strcpy(tmp->host, base->host); 885 tmp->port = base->port; 886 tmp->doc = tmp_name; 887 memcpy(tmp->doc, base->doc, base_doc_len); 888 tmp->doc[base_doc_len] = '/'; 889 890 for (i = base_doc_len + 1; *name != '\0'; ++name) { 891 if ((!pre_quoted && *name == '%') || 892 !fetch_urlpath_safe(*name)) { 893 tmp->doc[i++] = '%'; 894 c = (unsigned char)*name / 16; 895 if (c < 10) 896 tmp->doc[i++] = '0' + c; 897 else 898 tmp->doc[i++] = 'a' - 10 + c; 899 c = (unsigned char)*name % 16; 900 if (c < 10) 901 tmp->doc[i++] = '0' + c; 902 else 903 tmp->doc[i++] = 'a' - 10 + c; 904 } else { 905 tmp->doc[i++] = *name; 906 } 907 } 908 tmp->doc[i] = '\0'; 909 910 tmp->offset = 0; 911 tmp->length = 0; 912 tmp->last_modified = -1; 913 914 ++ue->length; 915 916 return (0); 917} 918 919void 920fetchInitURLList(struct url_list *ue) 921{ 922 ue->length = ue->alloc_size = 0; 923 ue->urls = NULL; 924} 925 926int 927fetchAppendURLList(struct url_list *dst, const struct url_list *src) 928{ 929 size_t i, j, len; 930 931 len = dst->length + src->length; 932 if (len > dst->alloc_size) { 933 struct url *tmp; 934 935 tmp = realloc(dst->urls, len * sizeof(*tmp)); 936 if (tmp == NULL) { 937 errno = ENOMEM; 938 fetch_syserr(); 939 return (-1); 940 } 941 dst->alloc_size = len; 942 dst->urls = tmp; 943 } 944 945 for (i = 0, j = dst->length; i < src->length; ++i, ++j) { 946 dst->urls[j] = src->urls[i]; 947 dst->urls[j].doc = strdup(src->urls[i].doc); 948 if (dst->urls[j].doc == NULL) { 949 while (i-- > 0) 950 free(dst->urls[j].doc); 951 fetch_syserr(); 952 return -1; 953 } 954 } 955 dst->length = len; 956 957 return 0; 958} 959 960void 961fetchFreeURLList(struct url_list *ue) 962{ 963 size_t i; 964 965 for (i = 0; i < ue->length; ++i) 966 free(ue->urls[i].doc); 967 free(ue->urls); 968 ue->length = ue->alloc_size = 0; 969} 970 971 972/*** Authentication-related utility functions ********************************/ 973 974static const char * 975fetch_read_word(FILE *f) 976{ 977 static char word[1024]; 978 979 if (fscanf(f, " %1023s ", word) != 1) 980 return (NULL); 981 return (word); 982} 983 984/* 985 * Get authentication data for a URL from .netrc 986 */ 987int 988fetch_netrc_auth(struct url *url) 989{ 990 char fn[PATH_MAX]; 991 const char *word; 992 char *p; 993 FILE *f; 994 995 if ((p = getenv("NETRC")) != NULL) { 996 if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) { 997 fetch_info("$NETRC specifies a file name " 998 "longer than PATH_MAX"); 999 return (-1); 1000 } 1001 } else { 1002 if ((p = getenv("HOME")) != NULL) { 1003 struct passwd *pwd; 1004 1005 if ((pwd = getpwuid(getuid())) == NULL || 1006 (p = pwd->pw_dir) == NULL) 1007 return (-1); 1008 } 1009 if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn)) 1010 return (-1); 1011 } 1012 1013 if ((f = fopen(fn, "r")) == NULL) 1014 return (-1); 1015 while ((word = fetch_read_word(f)) != NULL) { 1016 if (strcmp(word, "default") == 0) 1017 break; 1018 if (strcmp(word, "machine") == 0 && 1019 (word = fetch_read_word(f)) != NULL && 1020 strcasecmp(word, url->host) == 0) { 1021 break; 1022 } 1023 } 1024 if (word == NULL) 1025 goto ferr; 1026 while ((word = fetch_read_word(f)) != NULL) { 1027 if (strcmp(word, "login") == 0) { 1028 if ((word = fetch_read_word(f)) == NULL) 1029 goto ferr; 1030 if (snprintf(url->user, sizeof(url->user), 1031 "%s", word) > (int)sizeof(url->user)) { 1032 fetch_info("login name in .netrc is too long"); 1033 url->user[0] = '\0'; 1034 } 1035 } else if (strcmp(word, "password") == 0) { 1036 if ((word = fetch_read_word(f)) == NULL) 1037 goto ferr; 1038 if (snprintf(url->pwd, sizeof(url->pwd), 1039 "%s", word) > (int)sizeof(url->pwd)) { 1040 fetch_info("password in .netrc is too long"); 1041 url->pwd[0] = '\0'; 1042 } 1043 } else if (strcmp(word, "account") == 0) { 1044 if ((word = fetch_read_word(f)) == NULL) 1045 goto ferr; 1046 /* XXX not supported! */ 1047 } else { 1048 break; 1049 } 1050 } 1051 fclose(f); 1052 return (0); 1053 ferr: 1054 fclose(f); 1055 return (-1); 1056} 1057 1058/* 1059 * The no_proxy environment variable specifies a set of domains for 1060 * which the proxy should not be consulted; the contents is a comma-, 1061 * or space-separated list of domain names. A single asterisk will 1062 * override all proxy variables and no transactions will be proxied 1063 * (for compatability with lynx and curl, see the discussion at 1064 * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>). 1065 */ 1066int 1067fetch_no_proxy_match(const char *host) 1068{ 1069 const char *no_proxy, *p, *q; 1070 size_t h_len, d_len; 1071 1072 if ((no_proxy = getenv("NO_PROXY")) == NULL && 1073 (no_proxy = getenv("no_proxy")) == NULL) 1074 return (0); 1075 1076 /* asterisk matches any hostname */ 1077 if (strcmp(no_proxy, "*") == 0) 1078 return (1); 1079 1080 h_len = strlen(host); 1081 p = no_proxy; 1082 do { 1083 /* position p at the beginning of a domain suffix */ 1084 while (*p == ',' || isspace((unsigned char)*p)) 1085 p++; 1086 1087 /* position q at the first separator character */ 1088 for (q = p; *q; ++q) 1089 if (*q == ',' || isspace((unsigned char)*q)) 1090 break; 1091 1092 d_len = q - p; 1093 if (d_len > 0 && h_len > d_len && 1094 strncasecmp(host + h_len - d_len, 1095 p, d_len) == 0) { 1096 /* domain name matches */ 1097 return (1); 1098 } 1099 1100 p = q + 1; 1101 } while (*q); 1102 1103 return (0); 1104} 1105 1106struct fetchIO { 1107 void *io_cookie; 1108 ssize_t (*io_read)(void *, void *, size_t); 1109 ssize_t (*io_write)(void *, const void *, size_t); 1110 void (*io_close)(void *); 1111}; 1112 1113void 1114fetchIO_close(fetchIO *f) 1115{ 1116 if (f->io_close != NULL) 1117 (*f->io_close)(f->io_cookie); 1118 1119 free(f); 1120} 1121 1122fetchIO * 1123fetchIO_unopen(void *io_cookie, ssize_t (*io_read)(void *, void *, size_t), 1124 ssize_t (*io_write)(void *, const void *, size_t), 1125 void (*io_close)(void *)) 1126{ 1127 fetchIO *f; 1128 1129 f = malloc(sizeof(*f)); 1130 if (f == NULL) 1131 return f; 1132 1133 f->io_cookie = io_cookie; 1134 f->io_read = io_read; 1135 f->io_write = io_write; 1136 f->io_close = io_close; 1137 1138 return f; 1139} 1140 1141ssize_t 1142fetchIO_read(fetchIO *f, void *buf, size_t len) 1143{ 1144 if (f->io_read == NULL) 1145 return EBADF; 1146 return (*f->io_read)(f->io_cookie, buf, len); 1147} 1148 1149ssize_t 1150fetchIO_write(fetchIO *f, const void *buf, size_t len) 1151{ 1152 if (f->io_read == NULL) 1153 return EBADF; 1154 return (*f->io_write)(f->io_cookie, buf, len); 1155} 1156