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