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