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