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