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