xref: /freebsd/lib/libfetch/common.c (revision 2a7daafe67dbd6a91ab557b56b2ca12ca87a6022)
1842a95ccSDag-Erling Smørgrav /*-
2578153f1SDag-Erling Smørgrav  * Copyright (c) 1998-2011 Dag-Erling Smørgrav
3842a95ccSDag-Erling Smørgrav  * All rights reserved.
4842a95ccSDag-Erling Smørgrav  *
5842a95ccSDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
6842a95ccSDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
7842a95ccSDag-Erling Smørgrav  * are met:
8842a95ccSDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
9842a95ccSDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer
10842a95ccSDag-Erling Smørgrav  *    in this position and unchanged.
11842a95ccSDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
12842a95ccSDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
13842a95ccSDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
14842a95ccSDag-Erling Smørgrav  * 3. The name of the author may not be used to endorse or promote products
15842a95ccSDag-Erling Smørgrav  *    derived from this software without specific prior written permission
16842a95ccSDag-Erling Smørgrav  *
17842a95ccSDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18842a95ccSDag-Erling Smørgrav  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19842a95ccSDag-Erling Smørgrav  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20842a95ccSDag-Erling Smørgrav  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21842a95ccSDag-Erling Smørgrav  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22842a95ccSDag-Erling Smørgrav  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23842a95ccSDag-Erling Smørgrav  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24842a95ccSDag-Erling Smørgrav  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25842a95ccSDag-Erling Smørgrav  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26842a95ccSDag-Erling Smørgrav  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27842a95ccSDag-Erling Smørgrav  */
28842a95ccSDag-Erling Smørgrav 
29cecb889fSMatthew Dillon #include <sys/cdefs.h>
30cecb889fSMatthew Dillon __FBSDID("$FreeBSD$");
31cecb889fSMatthew Dillon 
320fba3a00SDag-Erling Smørgrav #include <sys/param.h>
33842a95ccSDag-Erling Smørgrav #include <sys/socket.h>
34fc6e9e65SDag-Erling Smørgrav #include <sys/time.h>
35a1763027SDag-Erling Smørgrav #include <sys/uio.h>
3662a2681cSDag-Erling Smørgrav 
37842a95ccSDag-Erling Smørgrav #include <netinet/in.h>
38842a95ccSDag-Erling Smørgrav 
3962a2681cSDag-Erling Smørgrav #include <ctype.h>
40842a95ccSDag-Erling Smørgrav #include <errno.h>
41caaffed8SDag-Erling Smørgrav #include <fcntl.h>
42842a95ccSDag-Erling Smørgrav #include <netdb.h>
4307350d12SDag-Erling Smørgrav #include <pwd.h>
44ba101983SDag-Erling Smørgrav #include <stdarg.h>
450fba3a00SDag-Erling Smørgrav #include <stdlib.h>
460fba3a00SDag-Erling Smørgrav #include <stdio.h>
47842a95ccSDag-Erling Smørgrav #include <string.h>
48842a95ccSDag-Erling Smørgrav #include <unistd.h>
49842a95ccSDag-Erling Smørgrav 
50842a95ccSDag-Erling Smørgrav #include "fetch.h"
51842a95ccSDag-Erling Smørgrav #include "common.h"
52842a95ccSDag-Erling Smørgrav 
53d8acd8dcSDag-Erling Smørgrav 
54842a95ccSDag-Erling Smørgrav /*** Local data **************************************************************/
55842a95ccSDag-Erling Smørgrav 
56842a95ccSDag-Erling Smørgrav /*
57842a95ccSDag-Erling Smørgrav  * Error messages for resolver errors
58842a95ccSDag-Erling Smørgrav  */
59a1b37df2SDag-Erling Smørgrav static struct fetcherr netdb_errlist[] = {
603d82ba43SHajimu UMEMOTO #ifdef EAI_NODATA
6128c645cfSHajimu UMEMOTO 	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
623d82ba43SHajimu UMEMOTO #endif
6328c645cfSHajimu UMEMOTO 	{ EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
6428c645cfSHajimu UMEMOTO 	{ EAI_FAIL,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
6528c645cfSHajimu UMEMOTO 	{ EAI_NONAME,	FETCH_RESOLV,	"No address record" },
66d8acd8dcSDag-Erling Smørgrav 	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
67842a95ccSDag-Erling Smørgrav };
68842a95ccSDag-Erling Smørgrav 
69a1763027SDag-Erling Smørgrav /* End-of-Line */
7038c7e4a6SArchie Cobbs static const char ENDL[2] = "\r\n";
71a1763027SDag-Erling Smørgrav 
72842a95ccSDag-Erling Smørgrav 
73842a95ccSDag-Erling Smørgrav /*** Error-reporting functions ***********************************************/
74842a95ccSDag-Erling Smørgrav 
75842a95ccSDag-Erling Smørgrav /*
76842a95ccSDag-Erling Smørgrav  * Map error code to string
77842a95ccSDag-Erling Smørgrav  */
78ba101983SDag-Erling Smørgrav static struct fetcherr *
79a1b37df2SDag-Erling Smørgrav fetch_finderr(struct fetcherr *p, int e)
80842a95ccSDag-Erling Smørgrav {
81ba101983SDag-Erling Smørgrav 	while (p->num != -1 && p->num != e)
82ba101983SDag-Erling Smørgrav 		p++;
83e19e6098SDag-Erling Smørgrav 	return (p);
84842a95ccSDag-Erling Smørgrav }
85842a95ccSDag-Erling Smørgrav 
86842a95ccSDag-Erling Smørgrav /*
87842a95ccSDag-Erling Smørgrav  * Set error code
88842a95ccSDag-Erling Smørgrav  */
89842a95ccSDag-Erling Smørgrav void
90a1b37df2SDag-Erling Smørgrav fetch_seterr(struct fetcherr *p, int e)
91842a95ccSDag-Erling Smørgrav {
92a1b37df2SDag-Erling Smørgrav 	p = fetch_finderr(p, e);
93ba101983SDag-Erling Smørgrav 	fetchLastErrCode = p->cat;
94ba101983SDag-Erling Smørgrav 	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
95842a95ccSDag-Erling Smørgrav }
96842a95ccSDag-Erling Smørgrav 
97842a95ccSDag-Erling Smørgrav /*
98842a95ccSDag-Erling Smørgrav  * Set error code according to errno
99842a95ccSDag-Erling Smørgrav  */
100842a95ccSDag-Erling Smørgrav void
101a1b37df2SDag-Erling Smørgrav fetch_syserr(void)
102842a95ccSDag-Erling Smørgrav {
103d8acd8dcSDag-Erling Smørgrav 	switch (errno) {
104d8acd8dcSDag-Erling Smørgrav 	case 0:
1050fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_OK;
106d8acd8dcSDag-Erling Smørgrav 		break;
107d8acd8dcSDag-Erling Smørgrav 	case EPERM:
108d8acd8dcSDag-Erling Smørgrav 	case EACCES:
109d8acd8dcSDag-Erling Smørgrav 	case EROFS:
110d8acd8dcSDag-Erling Smørgrav 	case EAUTH:
111d8acd8dcSDag-Erling Smørgrav 	case ENEEDAUTH:
1120fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_AUTH;
113d8acd8dcSDag-Erling Smørgrav 		break;
114d8acd8dcSDag-Erling Smørgrav 	case ENOENT:
115d8acd8dcSDag-Erling Smørgrav 	case EISDIR: /* XXX */
1160fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_UNAVAIL;
117d8acd8dcSDag-Erling Smørgrav 		break;
118d8acd8dcSDag-Erling Smørgrav 	case ENOMEM:
1190fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_MEMORY;
120d8acd8dcSDag-Erling Smørgrav 		break;
121d8acd8dcSDag-Erling Smørgrav 	case EBUSY:
122d8acd8dcSDag-Erling Smørgrav 	case EAGAIN:
1230fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_TEMP;
124d8acd8dcSDag-Erling Smørgrav 		break;
125d8acd8dcSDag-Erling Smørgrav 	case EEXIST:
1260fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_EXISTS;
127d8acd8dcSDag-Erling Smørgrav 		break;
128d8acd8dcSDag-Erling Smørgrav 	case ENOSPC:
1290fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_FULL;
130d8acd8dcSDag-Erling Smørgrav 		break;
131d8acd8dcSDag-Erling Smørgrav 	case EADDRINUSE:
132d8acd8dcSDag-Erling Smørgrav 	case EADDRNOTAVAIL:
133d8acd8dcSDag-Erling Smørgrav 	case ENETDOWN:
134d8acd8dcSDag-Erling Smørgrav 	case ENETUNREACH:
135d8acd8dcSDag-Erling Smørgrav 	case ENETRESET:
136d8acd8dcSDag-Erling Smørgrav 	case EHOSTUNREACH:
1370fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_NETWORK;
138d8acd8dcSDag-Erling Smørgrav 		break;
139d8acd8dcSDag-Erling Smørgrav 	case ECONNABORTED:
140d8acd8dcSDag-Erling Smørgrav 	case ECONNRESET:
1410fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_ABORT;
142d8acd8dcSDag-Erling Smørgrav 		break;
143d8acd8dcSDag-Erling Smørgrav 	case ETIMEDOUT:
1440fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_TIMEOUT;
145d8acd8dcSDag-Erling Smørgrav 		break;
146d8acd8dcSDag-Erling Smørgrav 	case ECONNREFUSED:
147d8acd8dcSDag-Erling Smørgrav 	case EHOSTDOWN:
1480fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_DOWN;
149d8acd8dcSDag-Erling Smørgrav 		break;
150d8acd8dcSDag-Erling Smørgrav default:
1510fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_UNKNOWN;
152d8acd8dcSDag-Erling Smørgrav 	}
153e19e6098SDag-Erling Smørgrav 	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
1540fba3a00SDag-Erling Smørgrav }
1550fba3a00SDag-Erling Smørgrav 
1560fba3a00SDag-Erling Smørgrav 
1570fba3a00SDag-Erling Smørgrav /*
1580fba3a00SDag-Erling Smørgrav  * Emit status message
1590fba3a00SDag-Erling Smørgrav  */
160ba101983SDag-Erling Smørgrav void
161a1b37df2SDag-Erling Smørgrav fetch_info(const char *fmt, ...)
1620fba3a00SDag-Erling Smørgrav {
1630fba3a00SDag-Erling Smørgrav 	va_list ap;
1640fba3a00SDag-Erling Smørgrav 
1650fba3a00SDag-Erling Smørgrav 	va_start(ap, fmt);
166ba101983SDag-Erling Smørgrav 	vfprintf(stderr, fmt, ap);
1670fba3a00SDag-Erling Smørgrav 	va_end(ap);
168a68f8b58SDag-Erling Smørgrav 	fputc('\n', stderr);
169842a95ccSDag-Erling Smørgrav }
170842a95ccSDag-Erling Smørgrav 
171842a95ccSDag-Erling Smørgrav 
172842a95ccSDag-Erling Smørgrav /*** Network-related utility functions ***************************************/
173842a95ccSDag-Erling Smørgrav 
174842a95ccSDag-Erling Smørgrav /*
175e828ada7SDag-Erling Smørgrav  * Return the default port for a scheme
176e828ada7SDag-Erling Smørgrav  */
177e828ada7SDag-Erling Smørgrav int
178a1b37df2SDag-Erling Smørgrav fetch_default_port(const char *scheme)
179e828ada7SDag-Erling Smørgrav {
180e828ada7SDag-Erling Smørgrav 	struct servent *se;
181e828ada7SDag-Erling Smørgrav 
182e828ada7SDag-Erling Smørgrav 	if ((se = getservbyname(scheme, "tcp")) != NULL)
183e19e6098SDag-Erling Smørgrav 		return (ntohs(se->s_port));
184e828ada7SDag-Erling Smørgrav 	if (strcasecmp(scheme, SCHEME_FTP) == 0)
185e19e6098SDag-Erling Smørgrav 		return (FTP_DEFAULT_PORT);
186e828ada7SDag-Erling Smørgrav 	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
187e19e6098SDag-Erling Smørgrav 		return (HTTP_DEFAULT_PORT);
188e19e6098SDag-Erling Smørgrav 	return (0);
189e828ada7SDag-Erling Smørgrav }
190e828ada7SDag-Erling Smørgrav 
191e828ada7SDag-Erling Smørgrav /*
192e828ada7SDag-Erling Smørgrav  * Return the default proxy port for a scheme
193e828ada7SDag-Erling Smørgrav  */
194e828ada7SDag-Erling Smørgrav int
195a1b37df2SDag-Erling Smørgrav fetch_default_proxy_port(const char *scheme)
196e828ada7SDag-Erling Smørgrav {
197e828ada7SDag-Erling Smørgrav 	if (strcasecmp(scheme, SCHEME_FTP) == 0)
198e19e6098SDag-Erling Smørgrav 		return (FTP_DEFAULT_PROXY_PORT);
199e828ada7SDag-Erling Smørgrav 	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
200e19e6098SDag-Erling Smørgrav 		return (HTTP_DEFAULT_PROXY_PORT);
201e19e6098SDag-Erling Smørgrav 	return (0);
202e828ada7SDag-Erling Smørgrav }
203e828ada7SDag-Erling Smørgrav 
204f606d589SDag-Erling Smørgrav 
205e828ada7SDag-Erling Smørgrav /*
2069601e333SDag-Erling Smørgrav  * Create a connection for an existing descriptor.
2079601e333SDag-Erling Smørgrav  */
2089601e333SDag-Erling Smørgrav conn_t *
209a1b37df2SDag-Erling Smørgrav fetch_reopen(int sd)
2109601e333SDag-Erling Smørgrav {
2119601e333SDag-Erling Smørgrav 	conn_t *conn;
2129601e333SDag-Erling Smørgrav 
2139601e333SDag-Erling Smørgrav 	/* allocate and fill connection structure */
214930105c1SDag-Erling Smørgrav 	if ((conn = calloc(1, sizeof(*conn))) == NULL)
2159601e333SDag-Erling Smørgrav 		return (NULL);
21615b68c63SDag-Erling Smørgrav 	fcntl(sd, F_SETFD, FD_CLOEXEC);
2179601e333SDag-Erling Smørgrav 	conn->sd = sd;
218f606d589SDag-Erling Smørgrav 	++conn->ref;
219f606d589SDag-Erling Smørgrav 	return (conn);
220f606d589SDag-Erling Smørgrav }
221f606d589SDag-Erling Smørgrav 
222f606d589SDag-Erling Smørgrav 
223f606d589SDag-Erling Smørgrav /*
224f606d589SDag-Erling Smørgrav  * Bump a connection's reference count.
225f606d589SDag-Erling Smørgrav  */
226f606d589SDag-Erling Smørgrav conn_t *
227a1b37df2SDag-Erling Smørgrav fetch_ref(conn_t *conn)
228f606d589SDag-Erling Smørgrav {
229f606d589SDag-Erling Smørgrav 
230f606d589SDag-Erling Smørgrav 	++conn->ref;
2319601e333SDag-Erling Smørgrav 	return (conn);
2329601e333SDag-Erling Smørgrav }
2339601e333SDag-Erling Smørgrav 
2349601e333SDag-Erling Smørgrav 
2359601e333SDag-Erling Smørgrav /*
236c42cb9d9SDag-Erling Smørgrav  * Bind a socket to a specific local address
237c42cb9d9SDag-Erling Smørgrav  */
238c42cb9d9SDag-Erling Smørgrav int
239a1b37df2SDag-Erling Smørgrav fetch_bind(int sd, int af, const char *addr)
240c42cb9d9SDag-Erling Smørgrav {
241c42cb9d9SDag-Erling Smørgrav 	struct addrinfo hints, *res, *res0;
242c42cb9d9SDag-Erling Smørgrav 	int err;
243c42cb9d9SDag-Erling Smørgrav 
244c42cb9d9SDag-Erling Smørgrav 	memset(&hints, 0, sizeof(hints));
245c42cb9d9SDag-Erling Smørgrav 	hints.ai_family = af;
246c42cb9d9SDag-Erling Smørgrav 	hints.ai_socktype = SOCK_STREAM;
247c42cb9d9SDag-Erling Smørgrav 	hints.ai_protocol = 0;
248c42cb9d9SDag-Erling Smørgrav 	if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)
249c42cb9d9SDag-Erling Smørgrav 		return (-1);
250c42cb9d9SDag-Erling Smørgrav 	for (res = res0; res; res = res->ai_next)
251c42cb9d9SDag-Erling Smørgrav 		if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
252c42cb9d9SDag-Erling Smørgrav 			return (0);
253c42cb9d9SDag-Erling Smørgrav 	return (-1);
254c42cb9d9SDag-Erling Smørgrav }
255c42cb9d9SDag-Erling Smørgrav 
256c42cb9d9SDag-Erling Smørgrav 
257c42cb9d9SDag-Erling Smørgrav /*
258842a95ccSDag-Erling Smørgrav  * Establish a TCP connection to the specified port on the specified host.
259842a95ccSDag-Erling Smørgrav  */
260dea29ca1SDag-Erling Smørgrav conn_t *
261a1b37df2SDag-Erling Smørgrav fetch_connect(const char *host, int port, int af, int verbose)
262842a95ccSDag-Erling Smørgrav {
263dea29ca1SDag-Erling Smørgrav 	conn_t *conn;
26428c645cfSHajimu UMEMOTO 	char pbuf[10];
265c42cb9d9SDag-Erling Smørgrav 	const char *bindaddr;
26628c645cfSHajimu UMEMOTO 	struct addrinfo hints, *res, *res0;
26728c645cfSHajimu UMEMOTO 	int sd, err;
268842a95ccSDag-Erling Smørgrav 
269f67efa37SDag-Erling Smørgrav 	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
270842a95ccSDag-Erling Smørgrav 
2710fba3a00SDag-Erling Smørgrav 	if (verbose)
272a1b37df2SDag-Erling Smørgrav 		fetch_info("looking up %s", host);
2730fba3a00SDag-Erling Smørgrav 
27428c645cfSHajimu UMEMOTO 	/* look up host name and set up socket address structure */
27528c645cfSHajimu UMEMOTO 	snprintf(pbuf, sizeof(pbuf), "%d", port);
27628c645cfSHajimu UMEMOTO 	memset(&hints, 0, sizeof(hints));
27728c645cfSHajimu UMEMOTO 	hints.ai_family = af;
27828c645cfSHajimu UMEMOTO 	hints.ai_socktype = SOCK_STREAM;
27928c645cfSHajimu UMEMOTO 	hints.ai_protocol = 0;
28028c645cfSHajimu UMEMOTO 	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
281a1b37df2SDag-Erling Smørgrav 		netdb_seterr(err);
282dea29ca1SDag-Erling Smørgrav 		return (NULL);
283842a95ccSDag-Erling Smørgrav 	}
284c42cb9d9SDag-Erling Smørgrav 	bindaddr = getenv("FETCH_BIND_ADDRESS");
285842a95ccSDag-Erling Smørgrav 
2860fba3a00SDag-Erling Smørgrav 	if (verbose)
287a1b37df2SDag-Erling Smørgrav 		fetch_info("connecting to %s:%d", host, port);
2880fba3a00SDag-Erling Smørgrav 
289842a95ccSDag-Erling Smørgrav 	/* try to connect */
290c42cb9d9SDag-Erling Smørgrav 	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
29128c645cfSHajimu UMEMOTO 		if ((sd = socket(res->ai_family, res->ai_socktype,
292a1763027SDag-Erling Smørgrav 			 res->ai_protocol)) == -1)
29328c645cfSHajimu UMEMOTO 			continue;
294c42cb9d9SDag-Erling Smørgrav 		if (bindaddr != NULL && *bindaddr != '\0' &&
295a1b37df2SDag-Erling Smørgrav 		    fetch_bind(sd, res->ai_family, bindaddr) != 0) {
296a1b37df2SDag-Erling Smørgrav 			fetch_info("failed to bind to '%s'", bindaddr);
297c42cb9d9SDag-Erling Smørgrav 			close(sd);
298c42cb9d9SDag-Erling Smørgrav 			continue;
299c42cb9d9SDag-Erling Smørgrav 		}
300caaffed8SDag-Erling Smørgrav 		if (connect(sd, res->ai_addr, res->ai_addrlen) == 0 &&
301caaffed8SDag-Erling Smørgrav 		    fcntl(sd, F_SETFL, O_NONBLOCK) == 0)
30228c645cfSHajimu UMEMOTO 			break;
303842a95ccSDag-Erling Smørgrav 		close(sd);
30428c645cfSHajimu UMEMOTO 	}
3057487ef62SHajimu UMEMOTO 	freeaddrinfo(res0);
306a1763027SDag-Erling Smørgrav 	if (sd == -1) {
307a1b37df2SDag-Erling Smørgrav 		fetch_syserr();
308dea29ca1SDag-Erling Smørgrav 		return (NULL);
309842a95ccSDag-Erling Smørgrav 	}
310842a95ccSDag-Erling Smørgrav 
311a1b37df2SDag-Erling Smørgrav 	if ((conn = fetch_reopen(sd)) == NULL) {
312a1b37df2SDag-Erling Smørgrav 		fetch_syserr();
313dea29ca1SDag-Erling Smørgrav 		close(sd);
31440cfbfd5SBill Fenner 	}
315dea29ca1SDag-Erling Smørgrav 	return (conn);
316842a95ccSDag-Erling Smørgrav }
317ce71b736SDag-Erling Smørgrav 
318ce71b736SDag-Erling Smørgrav 
319fc6e9e65SDag-Erling Smørgrav /*
320111e2510SDag-Erling Smørgrav  * Enable SSL on a connection.
321111e2510SDag-Erling Smørgrav  */
322111e2510SDag-Erling Smørgrav int
323a1b37df2SDag-Erling Smørgrav fetch_ssl(conn_t *conn, int verbose)
324111e2510SDag-Erling Smørgrav {
325a9d0c849SEd Maste #ifdef WITH_SSL
326caaffed8SDag-Erling Smørgrav 	int ret, ssl_err;
327111e2510SDag-Erling Smørgrav 
328111e2510SDag-Erling Smørgrav 	/* Init the SSL library and context */
329111e2510SDag-Erling Smørgrav 	if (!SSL_library_init()){
330111e2510SDag-Erling Smørgrav 		fprintf(stderr, "SSL library init failed\n");
331111e2510SDag-Erling Smørgrav 		return (-1);
332111e2510SDag-Erling Smørgrav 	}
333111e2510SDag-Erling Smørgrav 
334111e2510SDag-Erling Smørgrav 	SSL_load_error_strings();
335111e2510SDag-Erling Smørgrav 
336111e2510SDag-Erling Smørgrav 	conn->ssl_meth = SSLv23_client_method();
337111e2510SDag-Erling Smørgrav 	conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
338f8020ddeSDag-Erling Smørgrav 	SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
339111e2510SDag-Erling Smørgrav 
340111e2510SDag-Erling Smørgrav 	conn->ssl = SSL_new(conn->ssl_ctx);
341111e2510SDag-Erling Smørgrav 	if (conn->ssl == NULL){
342111e2510SDag-Erling Smørgrav 		fprintf(stderr, "SSL context creation failed\n");
343111e2510SDag-Erling Smørgrav 		return (-1);
344111e2510SDag-Erling Smørgrav 	}
345111e2510SDag-Erling Smørgrav 	SSL_set_fd(conn->ssl, conn->sd);
346caaffed8SDag-Erling Smørgrav 	while ((ret = SSL_connect(conn->ssl)) == -1) {
347caaffed8SDag-Erling Smørgrav 		ssl_err = SSL_get_error(conn->ssl, ret);
348caaffed8SDag-Erling Smørgrav 		if (ssl_err != SSL_ERROR_WANT_READ &&
349caaffed8SDag-Erling Smørgrav 		    ssl_err != SSL_ERROR_WANT_WRITE) {
350111e2510SDag-Erling Smørgrav 			ERR_print_errors_fp(stderr);
351111e2510SDag-Erling Smørgrav 			return (-1);
352111e2510SDag-Erling Smørgrav 		}
353caaffed8SDag-Erling Smørgrav 	}
354111e2510SDag-Erling Smørgrav 
355111e2510SDag-Erling Smørgrav 	if (verbose) {
356111e2510SDag-Erling Smørgrav 		X509_NAME *name;
357111e2510SDag-Erling Smørgrav 		char *str;
358111e2510SDag-Erling Smørgrav 
359111e2510SDag-Erling Smørgrav 		fprintf(stderr, "SSL connection established using %s\n",
360111e2510SDag-Erling Smørgrav 		    SSL_get_cipher(conn->ssl));
361111e2510SDag-Erling Smørgrav 		conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
362111e2510SDag-Erling Smørgrav 		name = X509_get_subject_name(conn->ssl_cert);
363111e2510SDag-Erling Smørgrav 		str = X509_NAME_oneline(name, 0, 0);
364111e2510SDag-Erling Smørgrav 		printf("Certificate subject: %s\n", str);
365111e2510SDag-Erling Smørgrav 		free(str);
366111e2510SDag-Erling Smørgrav 		name = X509_get_issuer_name(conn->ssl_cert);
367111e2510SDag-Erling Smørgrav 		str = X509_NAME_oneline(name, 0, 0);
368111e2510SDag-Erling Smørgrav 		printf("Certificate issuer: %s\n", str);
369111e2510SDag-Erling Smørgrav 		free(str);
370111e2510SDag-Erling Smørgrav 	}
371111e2510SDag-Erling Smørgrav 
372111e2510SDag-Erling Smørgrav 	return (0);
3733070f6cbSDag-Erling Smørgrav #else
3743070f6cbSDag-Erling Smørgrav 	(void)conn;
3753070f6cbSDag-Erling Smørgrav 	(void)verbose;
3763070f6cbSDag-Erling Smørgrav 	fprintf(stderr, "SSL support disabled\n");
3773070f6cbSDag-Erling Smørgrav 	return (-1);
3783070f6cbSDag-Erling Smørgrav #endif
379111e2510SDag-Erling Smørgrav }
380111e2510SDag-Erling Smørgrav 
381caaffed8SDag-Erling Smørgrav #define FETCH_READ_WAIT		-2
382caaffed8SDag-Erling Smørgrav #define FETCH_READ_ERROR	-1
383caaffed8SDag-Erling Smørgrav #define FETCH_READ_DONE		 0
384caaffed8SDag-Erling Smørgrav 
385caaffed8SDag-Erling Smørgrav #ifdef WITH_SSL
386caaffed8SDag-Erling Smørgrav static ssize_t
387caaffed8SDag-Erling Smørgrav fetch_ssl_read(SSL *ssl, char *buf, size_t len)
388caaffed8SDag-Erling Smørgrav {
389caaffed8SDag-Erling Smørgrav 	ssize_t rlen;
390caaffed8SDag-Erling Smørgrav 	int ssl_err;
391caaffed8SDag-Erling Smørgrav 
392caaffed8SDag-Erling Smørgrav 	rlen = SSL_read(ssl, buf, len);
393caaffed8SDag-Erling Smørgrav 	if (rlen < 0) {
394caaffed8SDag-Erling Smørgrav 		ssl_err = SSL_get_error(ssl, rlen);
395caaffed8SDag-Erling Smørgrav 		if (ssl_err == SSL_ERROR_WANT_READ ||
396caaffed8SDag-Erling Smørgrav 		    ssl_err == SSL_ERROR_WANT_WRITE) {
397caaffed8SDag-Erling Smørgrav 			return (FETCH_READ_WAIT);
398caaffed8SDag-Erling Smørgrav 		} else {
399caaffed8SDag-Erling Smørgrav 			ERR_print_errors_fp(stderr);
400caaffed8SDag-Erling Smørgrav 			return (FETCH_READ_ERROR);
401caaffed8SDag-Erling Smørgrav 		}
402caaffed8SDag-Erling Smørgrav 	}
403caaffed8SDag-Erling Smørgrav 	return (rlen);
404caaffed8SDag-Erling Smørgrav }
405caaffed8SDag-Erling Smørgrav #endif
406caaffed8SDag-Erling Smørgrav 
407*2a7daafeSDag-Erling Smørgrav /*
408*2a7daafeSDag-Erling Smørgrav  * Cache some data that was read from a socket but cannot be immediately
409*2a7daafeSDag-Erling Smørgrav  * returned because of an interrupted system call.
410*2a7daafeSDag-Erling Smørgrav  */
411*2a7daafeSDag-Erling Smørgrav static int
412*2a7daafeSDag-Erling Smørgrav fetch_cache_data(conn_t *conn, char *src, size_t nbytes)
413*2a7daafeSDag-Erling Smørgrav {
414*2a7daafeSDag-Erling Smørgrav 	char *tmp;
415*2a7daafeSDag-Erling Smørgrav 
416*2a7daafeSDag-Erling Smørgrav 	if (conn->cache.size < nbytes) {
417*2a7daafeSDag-Erling Smørgrav 		tmp = realloc(conn->cache.buf, nbytes);
418*2a7daafeSDag-Erling Smørgrav 		if (tmp == NULL) {
419*2a7daafeSDag-Erling Smørgrav 			errno = ENOMEM;
420*2a7daafeSDag-Erling Smørgrav 			fetch_syserr();
421*2a7daafeSDag-Erling Smørgrav 			return (-1);
422*2a7daafeSDag-Erling Smørgrav 		}
423*2a7daafeSDag-Erling Smørgrav 		conn->cache.buf = tmp;
424*2a7daafeSDag-Erling Smørgrav 		conn->cache.size = nbytes;
425*2a7daafeSDag-Erling Smørgrav 	}
426*2a7daafeSDag-Erling Smørgrav 
427*2a7daafeSDag-Erling Smørgrav 	memcpy(conn->cache.buf, src, nbytes);
428*2a7daafeSDag-Erling Smørgrav 	conn->cache.len = nbytes;
429*2a7daafeSDag-Erling Smørgrav 	conn->cache.pos = 0;
430*2a7daafeSDag-Erling Smørgrav 
431*2a7daafeSDag-Erling Smørgrav 	return (0);
432*2a7daafeSDag-Erling Smørgrav }
433*2a7daafeSDag-Erling Smørgrav 
434*2a7daafeSDag-Erling Smørgrav 
435caaffed8SDag-Erling Smørgrav static ssize_t
436caaffed8SDag-Erling Smørgrav fetch_socket_read(int sd, char *buf, size_t len)
437caaffed8SDag-Erling Smørgrav {
438caaffed8SDag-Erling Smørgrav 	ssize_t rlen;
439caaffed8SDag-Erling Smørgrav 
440caaffed8SDag-Erling Smørgrav 	rlen = read(sd, buf, len);
441caaffed8SDag-Erling Smørgrav 	if (rlen < 0) {
442caaffed8SDag-Erling Smørgrav 		if (errno == EAGAIN || (errno == EINTR && fetchRestartCalls))
443caaffed8SDag-Erling Smørgrav 			return (FETCH_READ_WAIT);
444caaffed8SDag-Erling Smørgrav 		else
445caaffed8SDag-Erling Smørgrav 			return (FETCH_READ_ERROR);
446caaffed8SDag-Erling Smørgrav 	}
447caaffed8SDag-Erling Smørgrav 	return (rlen);
448caaffed8SDag-Erling Smørgrav }
449f606d589SDag-Erling Smørgrav 
450111e2510SDag-Erling Smørgrav /*
4519601e333SDag-Erling Smørgrav  * Read a character from a connection w/ timeout
4529601e333SDag-Erling Smørgrav  */
4539601e333SDag-Erling Smørgrav ssize_t
454a1b37df2SDag-Erling Smørgrav fetch_read(conn_t *conn, char *buf, size_t len)
4559601e333SDag-Erling Smørgrav {
4565092cf05SDag-Erling Smørgrav 	struct timeval now, timeout, delta;
4579601e333SDag-Erling Smørgrav 	fd_set readfds;
4589601e333SDag-Erling Smørgrav 	ssize_t rlen, total;
4599601e333SDag-Erling Smørgrav 	int r;
460*2a7daafeSDag-Erling Smørgrav 	char *start;
4619601e333SDag-Erling Smørgrav 
4629601e333SDag-Erling Smørgrav 	if (fetchTimeout) {
4639601e333SDag-Erling Smørgrav 		FD_ZERO(&readfds);
4649601e333SDag-Erling Smørgrav 		gettimeofday(&timeout, NULL);
4659601e333SDag-Erling Smørgrav 		timeout.tv_sec += fetchTimeout;
4669601e333SDag-Erling Smørgrav 	}
4679601e333SDag-Erling Smørgrav 
4689601e333SDag-Erling Smørgrav 	total = 0;
469*2a7daafeSDag-Erling Smørgrav 	start = buf;
470*2a7daafeSDag-Erling Smørgrav 
471*2a7daafeSDag-Erling Smørgrav 	if (conn->cache.len > 0) {
472*2a7daafeSDag-Erling Smørgrav 		/*
473*2a7daafeSDag-Erling Smørgrav 		 * The last invocation of fetch_read was interrupted by a
474*2a7daafeSDag-Erling Smørgrav 		 * signal after some data had been read from the socket. Copy
475*2a7daafeSDag-Erling Smørgrav 		 * the cached data into the supplied buffer before trying to
476*2a7daafeSDag-Erling Smørgrav 		 * read from the socket again.
477*2a7daafeSDag-Erling Smørgrav 		 */
478*2a7daafeSDag-Erling Smørgrav 		total = (conn->cache.len < len) ? conn->cache.len : len;
479*2a7daafeSDag-Erling Smørgrav 		memcpy(buf, conn->cache.buf, total);
480*2a7daafeSDag-Erling Smørgrav 
481*2a7daafeSDag-Erling Smørgrav 		conn->cache.len -= total;
482*2a7daafeSDag-Erling Smørgrav 		conn->cache.pos += total;
483*2a7daafeSDag-Erling Smørgrav 		len -= total;
484*2a7daafeSDag-Erling Smørgrav 		buf+= total;
485*2a7daafeSDag-Erling Smørgrav 	}
486*2a7daafeSDag-Erling Smørgrav 
4879601e333SDag-Erling Smørgrav 	while (len > 0) {
488caaffed8SDag-Erling Smørgrav 		/*
489caaffed8SDag-Erling Smørgrav 		 * The socket is non-blocking.  Instead of the canonical
490caaffed8SDag-Erling Smørgrav 		 * select() -> read(), we do the following:
491caaffed8SDag-Erling Smørgrav 		 *
492caaffed8SDag-Erling Smørgrav 		 * 1) call read() or SSL_read().
493caaffed8SDag-Erling Smørgrav 		 * 2) if an error occurred, return -1.
494caaffed8SDag-Erling Smørgrav 		 * 3) if we received data but we still expect more,
495caaffed8SDag-Erling Smørgrav 		 *    update our counters and loop.
496caaffed8SDag-Erling Smørgrav 		 * 4) if read() or SSL_read() signaled EOF, return.
497caaffed8SDag-Erling Smørgrav 		 * 5) if we did not receive any data but we're not at EOF,
498caaffed8SDag-Erling Smørgrav 		 *    call select().
499caaffed8SDag-Erling Smørgrav 		 *
500caaffed8SDag-Erling Smørgrav 		 * In the SSL case, this is necessary because if we
501caaffed8SDag-Erling Smørgrav 		 * receive a close notification, we have to call
502caaffed8SDag-Erling Smørgrav 		 * SSL_read() one additional time after we've read
503caaffed8SDag-Erling Smørgrav 		 * everything we received.
504caaffed8SDag-Erling Smørgrav 		 *
505caaffed8SDag-Erling Smørgrav 		 * In the non-SSL case, it may improve performance (very
506caaffed8SDag-Erling Smørgrav 		 * slightly) when reading small amounts of data.
507caaffed8SDag-Erling Smørgrav 		 */
508caaffed8SDag-Erling Smørgrav #ifdef WITH_SSL
509caaffed8SDag-Erling Smørgrav 		if (conn->ssl != NULL)
510caaffed8SDag-Erling Smørgrav 			rlen = fetch_ssl_read(conn->ssl, buf, len);
511caaffed8SDag-Erling Smørgrav 		else
512caaffed8SDag-Erling Smørgrav #endif
513caaffed8SDag-Erling Smørgrav 			rlen = fetch_socket_read(conn->sd, buf, len);
514caaffed8SDag-Erling Smørgrav 		if (rlen == 0) {
515caaffed8SDag-Erling Smørgrav 			break;
516caaffed8SDag-Erling Smørgrav 		} else if (rlen > 0) {
517caaffed8SDag-Erling Smørgrav 			len -= rlen;
518caaffed8SDag-Erling Smørgrav 			buf += rlen;
519caaffed8SDag-Erling Smørgrav 			total += rlen;
520caaffed8SDag-Erling Smørgrav 			continue;
521caaffed8SDag-Erling Smørgrav 		} else if (rlen == FETCH_READ_ERROR) {
522*2a7daafeSDag-Erling Smørgrav 			if (errno == EINTR)
523*2a7daafeSDag-Erling Smørgrav 				fetch_cache_data(conn, start, total);
524caaffed8SDag-Erling Smørgrav 			return (-1);
525caaffed8SDag-Erling Smørgrav 		}
526caaffed8SDag-Erling Smørgrav 		// assert(rlen == FETCH_READ_WAIT);
5279601e333SDag-Erling Smørgrav 		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
5289601e333SDag-Erling Smørgrav 			FD_SET(conn->sd, &readfds);
5299601e333SDag-Erling Smørgrav 			gettimeofday(&now, NULL);
5305092cf05SDag-Erling Smørgrav 			delta.tv_sec = timeout.tv_sec - now.tv_sec;
5315092cf05SDag-Erling Smørgrav 			delta.tv_usec = timeout.tv_usec - now.tv_usec;
5325092cf05SDag-Erling Smørgrav 			if (delta.tv_usec < 0) {
5335092cf05SDag-Erling Smørgrav 				delta.tv_usec += 1000000;
5345092cf05SDag-Erling Smørgrav 				delta.tv_sec--;
5359601e333SDag-Erling Smørgrav 			}
5365092cf05SDag-Erling Smørgrav 			if (delta.tv_sec < 0) {
537bb13d0afSDag-Erling Smørgrav 				errno = ETIMEDOUT;
538a1b37df2SDag-Erling Smørgrav 				fetch_syserr();
539bb13d0afSDag-Erling Smørgrav 				return (-1);
540bb13d0afSDag-Erling Smørgrav 			}
5419601e333SDag-Erling Smørgrav 			errno = 0;
5425092cf05SDag-Erling Smørgrav 			r = select(conn->sd + 1, &readfds, NULL, NULL, &delta);
5439601e333SDag-Erling Smørgrav 			if (r == -1) {
544*2a7daafeSDag-Erling Smørgrav 				if (errno == EINTR) {
545*2a7daafeSDag-Erling Smørgrav 					if (fetchRestartCalls)
5469601e333SDag-Erling Smørgrav 						continue;
547*2a7daafeSDag-Erling Smørgrav 					/* Save anything that was read. */
548*2a7daafeSDag-Erling Smørgrav 					fetch_cache_data(conn, start, total);
549*2a7daafeSDag-Erling Smørgrav 				}
550a1b37df2SDag-Erling Smørgrav 				fetch_syserr();
5519601e333SDag-Erling Smørgrav 				return (-1);
5529601e333SDag-Erling Smørgrav 			}
5539601e333SDag-Erling Smørgrav 		}
5549601e333SDag-Erling Smørgrav 	}
5559601e333SDag-Erling Smørgrav 	return (total);
5569601e333SDag-Erling Smørgrav }
5579601e333SDag-Erling Smørgrav 
558f606d589SDag-Erling Smørgrav 
5599601e333SDag-Erling Smørgrav /*
5609601e333SDag-Erling Smørgrav  * Read a line of text from a connection w/ timeout
561fc6e9e65SDag-Erling Smørgrav  */
562fc6e9e65SDag-Erling Smørgrav #define MIN_BUF_SIZE 1024
563fc6e9e65SDag-Erling Smørgrav 
564fc6e9e65SDag-Erling Smørgrav int
565a1b37df2SDag-Erling Smørgrav fetch_getln(conn_t *conn)
566fc6e9e65SDag-Erling Smørgrav {
5679601e333SDag-Erling Smørgrav 	char *tmp;
5689601e333SDag-Erling Smørgrav 	size_t tmpsize;
569bb13d0afSDag-Erling Smørgrav 	ssize_t len;
570fc6e9e65SDag-Erling Smørgrav 	char c;
571fc6e9e65SDag-Erling Smørgrav 
572dea29ca1SDag-Erling Smørgrav 	if (conn->buf == NULL) {
573dea29ca1SDag-Erling Smørgrav 		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
574fc6e9e65SDag-Erling Smørgrav 			errno = ENOMEM;
575e19e6098SDag-Erling Smørgrav 			return (-1);
576fc6e9e65SDag-Erling Smørgrav 		}
577dea29ca1SDag-Erling Smørgrav 		conn->bufsize = MIN_BUF_SIZE;
578fc6e9e65SDag-Erling Smørgrav 	}
579fc6e9e65SDag-Erling Smørgrav 
580dea29ca1SDag-Erling Smørgrav 	conn->buf[0] = '\0';
581dea29ca1SDag-Erling Smørgrav 	conn->buflen = 0;
582fc6e9e65SDag-Erling Smørgrav 
583fc6e9e65SDag-Erling Smørgrav 	do {
584a1b37df2SDag-Erling Smørgrav 		len = fetch_read(conn, &c, 1);
585bb13d0afSDag-Erling Smørgrav 		if (len == -1)
586e19e6098SDag-Erling Smørgrav 			return (-1);
587bb13d0afSDag-Erling Smørgrav 		if (len == 0)
588b68fbebdSDavid E. O'Brien 			break;
589dea29ca1SDag-Erling Smørgrav 		conn->buf[conn->buflen++] = c;
590dea29ca1SDag-Erling Smørgrav 		if (conn->buflen == conn->bufsize) {
591dea29ca1SDag-Erling Smørgrav 			tmp = conn->buf;
592dea29ca1SDag-Erling Smørgrav 			tmpsize = conn->bufsize * 2 + 1;
593dea29ca1SDag-Erling Smørgrav 			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
594fc6e9e65SDag-Erling Smørgrav 				errno = ENOMEM;
595e19e6098SDag-Erling Smørgrav 				return (-1);
596fc6e9e65SDag-Erling Smørgrav 			}
597dea29ca1SDag-Erling Smørgrav 			conn->buf = tmp;
598dea29ca1SDag-Erling Smørgrav 			conn->bufsize = tmpsize;
599fc6e9e65SDag-Erling Smørgrav 		}
600fc6e9e65SDag-Erling Smørgrav 	} while (c != '\n');
601fc6e9e65SDag-Erling Smørgrav 
602dea29ca1SDag-Erling Smørgrav 	conn->buf[conn->buflen] = '\0';
603dea29ca1SDag-Erling Smørgrav 	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
604e19e6098SDag-Erling Smørgrav 	return (0);
605fc6e9e65SDag-Erling Smørgrav }
606fc6e9e65SDag-Erling Smørgrav 
607fc6e9e65SDag-Erling Smørgrav 
608a1763027SDag-Erling Smørgrav /*
6099601e333SDag-Erling Smørgrav  * Write to a connection w/ timeout
6109601e333SDag-Erling Smørgrav  */
6119601e333SDag-Erling Smørgrav ssize_t
612a1b37df2SDag-Erling Smørgrav fetch_write(conn_t *conn, const char *buf, size_t len)
6139601e333SDag-Erling Smørgrav {
614a4a37038SWarner Losh 	struct iovec iov;
615a4a37038SWarner Losh 
616a4a37038SWarner Losh 	iov.iov_base = __DECONST(char *, buf);
617a4a37038SWarner Losh 	iov.iov_len = len;
618a1b37df2SDag-Erling Smørgrav 	return fetch_writev(conn, &iov, 1);
619a4a37038SWarner Losh }
620a4a37038SWarner Losh 
621a4a37038SWarner Losh /*
622a4a37038SWarner Losh  * Write a vector to a connection w/ timeout
623a4a37038SWarner Losh  * Note: can modify the iovec.
624a4a37038SWarner Losh  */
625a4a37038SWarner Losh ssize_t
626a1b37df2SDag-Erling Smørgrav fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
627a4a37038SWarner Losh {
6285092cf05SDag-Erling Smørgrav 	struct timeval now, timeout, delta;
6299601e333SDag-Erling Smørgrav 	fd_set writefds;
6309601e333SDag-Erling Smørgrav 	ssize_t wlen, total;
6319601e333SDag-Erling Smørgrav 	int r;
6329601e333SDag-Erling Smørgrav 
6339601e333SDag-Erling Smørgrav 	if (fetchTimeout) {
6349601e333SDag-Erling Smørgrav 		FD_ZERO(&writefds);
6359601e333SDag-Erling Smørgrav 		gettimeofday(&timeout, NULL);
6369601e333SDag-Erling Smørgrav 		timeout.tv_sec += fetchTimeout;
6379601e333SDag-Erling Smørgrav 	}
6389601e333SDag-Erling Smørgrav 
639a4a37038SWarner Losh 	total = 0;
640a4a37038SWarner Losh 	while (iovcnt > 0) {
6419601e333SDag-Erling Smørgrav 		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
6429601e333SDag-Erling Smørgrav 			FD_SET(conn->sd, &writefds);
6439601e333SDag-Erling Smørgrav 			gettimeofday(&now, NULL);
6445092cf05SDag-Erling Smørgrav 			delta.tv_sec = timeout.tv_sec - now.tv_sec;
6455092cf05SDag-Erling Smørgrav 			delta.tv_usec = timeout.tv_usec - now.tv_usec;
6465092cf05SDag-Erling Smørgrav 			if (delta.tv_usec < 0) {
6475092cf05SDag-Erling Smørgrav 				delta.tv_usec += 1000000;
6485092cf05SDag-Erling Smørgrav 				delta.tv_sec--;
6499601e333SDag-Erling Smørgrav 			}
6505092cf05SDag-Erling Smørgrav 			if (delta.tv_sec < 0) {
6519601e333SDag-Erling Smørgrav 				errno = ETIMEDOUT;
652a1b37df2SDag-Erling Smørgrav 				fetch_syserr();
6539601e333SDag-Erling Smørgrav 				return (-1);
6549601e333SDag-Erling Smørgrav 			}
6559601e333SDag-Erling Smørgrav 			errno = 0;
6565092cf05SDag-Erling Smørgrav 			r = select(conn->sd + 1, NULL, &writefds, NULL, &delta);
6579601e333SDag-Erling Smørgrav 			if (r == -1) {
6589601e333SDag-Erling Smørgrav 				if (errno == EINTR && fetchRestartCalls)
6599601e333SDag-Erling Smørgrav 					continue;
6609601e333SDag-Erling Smørgrav 				return (-1);
6619601e333SDag-Erling Smørgrav 			}
6629601e333SDag-Erling Smørgrav 		}
6639601e333SDag-Erling Smørgrav 		errno = 0;
6643070f6cbSDag-Erling Smørgrav #ifdef WITH_SSL
6659601e333SDag-Erling Smørgrav 		if (conn->ssl != NULL)
666a4a37038SWarner Losh 			wlen = SSL_write(conn->ssl,
667a4a37038SWarner Losh 			    iov->iov_base, iov->iov_len);
6689601e333SDag-Erling Smørgrav 		else
6693070f6cbSDag-Erling Smørgrav #endif
670a4a37038SWarner Losh 			wlen = writev(conn->sd, iov, iovcnt);
671a4a37038SWarner Losh 		if (wlen == 0) {
6729601e333SDag-Erling Smørgrav 			/* we consider a short write a failure */
673caaffed8SDag-Erling Smørgrav 			/* XXX perhaps we shouldn't in the SSL case */
674a4a37038SWarner Losh 			errno = EPIPE;
675a1b37df2SDag-Erling Smørgrav 			fetch_syserr();
6769601e333SDag-Erling Smørgrav 			return (-1);
677a4a37038SWarner Losh 		}
6789601e333SDag-Erling Smørgrav 		if (wlen < 0) {
6799601e333SDag-Erling Smørgrav 			if (errno == EINTR && fetchRestartCalls)
6809601e333SDag-Erling Smørgrav 				continue;
6819601e333SDag-Erling Smørgrav 			return (-1);
6829601e333SDag-Erling Smørgrav 		}
6839601e333SDag-Erling Smørgrav 		total += wlen;
684a4a37038SWarner Losh 		while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
685a4a37038SWarner Losh 			wlen -= iov->iov_len;
686a4a37038SWarner Losh 			iov++;
687a4a37038SWarner Losh 			iovcnt--;
688a4a37038SWarner Losh 		}
689a4a37038SWarner Losh 		if (iovcnt > 0) {
690a4a37038SWarner Losh 			iov->iov_len -= wlen;
691a4a37038SWarner Losh 			iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;
692a4a37038SWarner Losh 		}
6939601e333SDag-Erling Smørgrav 	}
6949601e333SDag-Erling Smørgrav 	return (total);
6959601e333SDag-Erling Smørgrav }
6969601e333SDag-Erling Smørgrav 
697f606d589SDag-Erling Smørgrav 
6989601e333SDag-Erling Smørgrav /*
6999601e333SDag-Erling Smørgrav  * Write a line of text to a connection w/ timeout
700a1763027SDag-Erling Smørgrav  */
701a1763027SDag-Erling Smørgrav int
702a1b37df2SDag-Erling Smørgrav fetch_putln(conn_t *conn, const char *str, size_t len)
703a1763027SDag-Erling Smørgrav {
704a4a37038SWarner Losh 	struct iovec iov[2];
7057504527eSDag-Erling Smørgrav 	int ret;
70666ffb8a3SDag-Erling Smørgrav 
70766ffb8a3SDag-Erling Smørgrav 	DEBUG(fprintf(stderr, ">>> %s\n", str));
708a4a37038SWarner Losh 	iov[0].iov_base = __DECONST(char *, str);
709a4a37038SWarner Losh 	iov[0].iov_len = len;
710a4a37038SWarner Losh 	iov[1].iov_base = __DECONST(char *, ENDL);
711930105c1SDag-Erling Smørgrav 	iov[1].iov_len = sizeof(ENDL);
7127504527eSDag-Erling Smørgrav 	if (len == 0)
713a1b37df2SDag-Erling Smørgrav 		ret = fetch_writev(conn, &iov[1], 1);
7147504527eSDag-Erling Smørgrav 	else
715a1b37df2SDag-Erling Smørgrav 		ret = fetch_writev(conn, iov, 2);
7167504527eSDag-Erling Smørgrav 	if (ret == -1)
717e19e6098SDag-Erling Smørgrav 		return (-1);
718e19e6098SDag-Erling Smørgrav 	return (0);
719a1763027SDag-Erling Smørgrav }
720a1763027SDag-Erling Smørgrav 
721a1763027SDag-Erling Smørgrav 
722dea29ca1SDag-Erling Smørgrav /*
723dea29ca1SDag-Erling Smørgrav  * Close connection
724dea29ca1SDag-Erling Smørgrav  */
725dea29ca1SDag-Erling Smørgrav int
726a1b37df2SDag-Erling Smørgrav fetch_close(conn_t *conn)
727dea29ca1SDag-Erling Smørgrav {
728dea29ca1SDag-Erling Smørgrav 	int ret;
729dea29ca1SDag-Erling Smørgrav 
730f606d589SDag-Erling Smørgrav 	if (--conn->ref > 0)
731f606d589SDag-Erling Smørgrav 		return (0);
732dea29ca1SDag-Erling Smørgrav 	ret = close(conn->sd);
733*2a7daafeSDag-Erling Smørgrav 	free(conn->cache.buf);
73455cf7be1SDag-Erling Smørgrav 	free(conn->buf);
735dea29ca1SDag-Erling Smørgrav 	free(conn);
736dea29ca1SDag-Erling Smørgrav 	return (ret);
737dea29ca1SDag-Erling Smørgrav }
738dea29ca1SDag-Erling Smørgrav 
739dea29ca1SDag-Erling Smørgrav 
740ce71b736SDag-Erling Smørgrav /*** Directory-related utility functions *************************************/
741ce71b736SDag-Erling Smørgrav 
742ce71b736SDag-Erling Smørgrav int
743a1b37df2SDag-Erling Smørgrav fetch_add_entry(struct url_ent **p, int *size, int *len,
744f573a5fcSDag-Erling Smørgrav     const char *name, struct url_stat *us)
745ce71b736SDag-Erling Smørgrav {
746ce71b736SDag-Erling Smørgrav 	struct url_ent *tmp;
747ce71b736SDag-Erling Smørgrav 
748ce71b736SDag-Erling Smørgrav 	if (*p == NULL) {
7495a51c23bSDag-Erling Smørgrav 		*size = 0;
750ce71b736SDag-Erling Smørgrav 		*len = 0;
751ce71b736SDag-Erling Smørgrav 	}
752ce71b736SDag-Erling Smørgrav 
753ce71b736SDag-Erling Smørgrav 	if (*len >= *size - 1) {
754930105c1SDag-Erling Smørgrav 		tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
755ce71b736SDag-Erling Smørgrav 		if (tmp == NULL) {
756ce71b736SDag-Erling Smørgrav 			errno = ENOMEM;
757a1b37df2SDag-Erling Smørgrav 			fetch_syserr();
758e19e6098SDag-Erling Smørgrav 			return (-1);
759ce71b736SDag-Erling Smørgrav 		}
7605a51c23bSDag-Erling Smørgrav 		*size = (*size * 2 + 1);
761ce71b736SDag-Erling Smørgrav 		*p = tmp;
762ce71b736SDag-Erling Smørgrav 	}
763ce71b736SDag-Erling Smørgrav 
764ce71b736SDag-Erling Smørgrav 	tmp = *p + *len;
7652b26f942SDag-Erling Smørgrav 	snprintf(tmp->name, PATH_MAX, "%s", name);
766340b079bSDag-Erling Smørgrav 	memcpy(&tmp->stat, us, sizeof(*us));
767ce71b736SDag-Erling Smørgrav 
768ce71b736SDag-Erling Smørgrav 	(*len)++;
769ce71b736SDag-Erling Smørgrav 	(++tmp)->name[0] = 0;
770ce71b736SDag-Erling Smørgrav 
771e19e6098SDag-Erling Smørgrav 	return (0);
772ce71b736SDag-Erling Smørgrav }
77307350d12SDag-Erling Smørgrav 
77407350d12SDag-Erling Smørgrav 
77507350d12SDag-Erling Smørgrav /*** Authentication-related utility functions ********************************/
77607350d12SDag-Erling Smørgrav 
77707350d12SDag-Erling Smørgrav static const char *
778a1b37df2SDag-Erling Smørgrav fetch_read_word(FILE *f)
77907350d12SDag-Erling Smørgrav {
78007350d12SDag-Erling Smørgrav 	static char word[1024];
78107350d12SDag-Erling Smørgrav 
782fc2841a9SColin Percival 	if (fscanf(f, " %1023s ", word) != 1)
78307350d12SDag-Erling Smørgrav 		return (NULL);
78407350d12SDag-Erling Smørgrav 	return (word);
78507350d12SDag-Erling Smørgrav }
78607350d12SDag-Erling Smørgrav 
78707350d12SDag-Erling Smørgrav /*
78807350d12SDag-Erling Smørgrav  * Get authentication data for a URL from .netrc
78907350d12SDag-Erling Smørgrav  */
79007350d12SDag-Erling Smørgrav int
791a1b37df2SDag-Erling Smørgrav fetch_netrc_auth(struct url *url)
79207350d12SDag-Erling Smørgrav {
79307350d12SDag-Erling Smørgrav 	char fn[PATH_MAX];
79407350d12SDag-Erling Smørgrav 	const char *word;
79507350d12SDag-Erling Smørgrav 	char *p;
79607350d12SDag-Erling Smørgrav 	FILE *f;
79707350d12SDag-Erling Smørgrav 
79807350d12SDag-Erling Smørgrav 	if ((p = getenv("NETRC")) != NULL) {
799930105c1SDag-Erling Smørgrav 		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
800a1b37df2SDag-Erling Smørgrav 			fetch_info("$NETRC specifies a file name "
80107350d12SDag-Erling Smørgrav 			    "longer than PATH_MAX");
80207350d12SDag-Erling Smørgrav 			return (-1);
80307350d12SDag-Erling Smørgrav 		}
80407350d12SDag-Erling Smørgrav 	} else {
80507350d12SDag-Erling Smørgrav 		if ((p = getenv("HOME")) != NULL) {
80607350d12SDag-Erling Smørgrav 			struct passwd *pwd;
80707350d12SDag-Erling Smørgrav 
80807350d12SDag-Erling Smørgrav 			if ((pwd = getpwuid(getuid())) == NULL ||
80907350d12SDag-Erling Smørgrav 			    (p = pwd->pw_dir) == NULL)
81007350d12SDag-Erling Smørgrav 				return (-1);
81107350d12SDag-Erling Smørgrav 		}
812930105c1SDag-Erling Smørgrav 		if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
81307350d12SDag-Erling Smørgrav 			return (-1);
81407350d12SDag-Erling Smørgrav 	}
81507350d12SDag-Erling Smørgrav 
81607350d12SDag-Erling Smørgrav 	if ((f = fopen(fn, "r")) == NULL)
81707350d12SDag-Erling Smørgrav 		return (-1);
818a1b37df2SDag-Erling Smørgrav 	while ((word = fetch_read_word(f)) != NULL) {
81907350d12SDag-Erling Smørgrav 		if (strcmp(word, "default") == 0) {
820a1b37df2SDag-Erling Smørgrav 			DEBUG(fetch_info("Using default .netrc settings"));
82107350d12SDag-Erling Smørgrav 			break;
82207350d12SDag-Erling Smørgrav 		}
82307350d12SDag-Erling Smørgrav 		if (strcmp(word, "machine") == 0 &&
824a1b37df2SDag-Erling Smørgrav 		    (word = fetch_read_word(f)) != NULL &&
82507350d12SDag-Erling Smørgrav 		    strcasecmp(word, url->host) == 0) {
826a1b37df2SDag-Erling Smørgrav 			DEBUG(fetch_info("Using .netrc settings for %s", word));
82707350d12SDag-Erling Smørgrav 			break;
82807350d12SDag-Erling Smørgrav 		}
82907350d12SDag-Erling Smørgrav 	}
83007350d12SDag-Erling Smørgrav 	if (word == NULL)
83107350d12SDag-Erling Smørgrav 		goto ferr;
832a1b37df2SDag-Erling Smørgrav 	while ((word = fetch_read_word(f)) != NULL) {
83307350d12SDag-Erling Smørgrav 		if (strcmp(word, "login") == 0) {
834a1b37df2SDag-Erling Smørgrav 			if ((word = fetch_read_word(f)) == NULL)
83507350d12SDag-Erling Smørgrav 				goto ferr;
836930105c1SDag-Erling Smørgrav 			if (snprintf(url->user, sizeof(url->user),
8379015b953SJohn W. De Boskey 				"%s", word) > (int)sizeof(url->user)) {
838a1b37df2SDag-Erling Smørgrav 				fetch_info("login name in .netrc is too long");
83907350d12SDag-Erling Smørgrav 				url->user[0] = '\0';
84007350d12SDag-Erling Smørgrav 			}
84107350d12SDag-Erling Smørgrav 		} else if (strcmp(word, "password") == 0) {
842a1b37df2SDag-Erling Smørgrav 			if ((word = fetch_read_word(f)) == NULL)
84307350d12SDag-Erling Smørgrav 				goto ferr;
844930105c1SDag-Erling Smørgrav 			if (snprintf(url->pwd, sizeof(url->pwd),
8459015b953SJohn W. De Boskey 				"%s", word) > (int)sizeof(url->pwd)) {
846a1b37df2SDag-Erling Smørgrav 				fetch_info("password in .netrc is too long");
84707350d12SDag-Erling Smørgrav 				url->pwd[0] = '\0';
84807350d12SDag-Erling Smørgrav 			}
84907350d12SDag-Erling Smørgrav 		} else if (strcmp(word, "account") == 0) {
850a1b37df2SDag-Erling Smørgrav 			if ((word = fetch_read_word(f)) == NULL)
85107350d12SDag-Erling Smørgrav 				goto ferr;
85207350d12SDag-Erling Smørgrav 			/* XXX not supported! */
85307350d12SDag-Erling Smørgrav 		} else {
85407350d12SDag-Erling Smørgrav 			break;
85507350d12SDag-Erling Smørgrav 		}
85607350d12SDag-Erling Smørgrav 	}
85707350d12SDag-Erling Smørgrav 	fclose(f);
85807350d12SDag-Erling Smørgrav 	return (0);
85907350d12SDag-Erling Smørgrav  ferr:
86007350d12SDag-Erling Smørgrav 	fclose(f);
86107350d12SDag-Erling Smørgrav 	return (-1);
86207350d12SDag-Erling Smørgrav }
86362a2681cSDag-Erling Smørgrav 
86462a2681cSDag-Erling Smørgrav /*
86562a2681cSDag-Erling Smørgrav  * The no_proxy environment variable specifies a set of domains for
86662a2681cSDag-Erling Smørgrav  * which the proxy should not be consulted; the contents is a comma-,
86762a2681cSDag-Erling Smørgrav  * or space-separated list of domain names.  A single asterisk will
86862a2681cSDag-Erling Smørgrav  * override all proxy variables and no transactions will be proxied
86962a2681cSDag-Erling Smørgrav  * (for compatability with lynx and curl, see the discussion at
87062a2681cSDag-Erling Smørgrav  * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
87162a2681cSDag-Erling Smørgrav  */
87262a2681cSDag-Erling Smørgrav int
87362a2681cSDag-Erling Smørgrav fetch_no_proxy_match(const char *host)
87462a2681cSDag-Erling Smørgrav {
87562a2681cSDag-Erling Smørgrav 	const char *no_proxy, *p, *q;
87662a2681cSDag-Erling Smørgrav 	size_t h_len, d_len;
87762a2681cSDag-Erling Smørgrav 
87862a2681cSDag-Erling Smørgrav 	if ((no_proxy = getenv("NO_PROXY")) == NULL &&
87962a2681cSDag-Erling Smørgrav 	    (no_proxy = getenv("no_proxy")) == NULL)
88062a2681cSDag-Erling Smørgrav 		return (0);
88162a2681cSDag-Erling Smørgrav 
88262a2681cSDag-Erling Smørgrav 	/* asterisk matches any hostname */
88362a2681cSDag-Erling Smørgrav 	if (strcmp(no_proxy, "*") == 0)
88462a2681cSDag-Erling Smørgrav 		return (1);
88562a2681cSDag-Erling Smørgrav 
88662a2681cSDag-Erling Smørgrav 	h_len = strlen(host);
88762a2681cSDag-Erling Smørgrav 	p = no_proxy;
88862a2681cSDag-Erling Smørgrav 	do {
88962a2681cSDag-Erling Smørgrav 		/* position p at the beginning of a domain suffix */
890facd9827SDag-Erling Smørgrav 		while (*p == ',' || isspace((unsigned char)*p))
89162a2681cSDag-Erling Smørgrav 			p++;
89262a2681cSDag-Erling Smørgrav 
89362a2681cSDag-Erling Smørgrav 		/* position q at the first separator character */
89462a2681cSDag-Erling Smørgrav 		for (q = p; *q; ++q)
895facd9827SDag-Erling Smørgrav 			if (*q == ',' || isspace((unsigned char)*q))
89662a2681cSDag-Erling Smørgrav 				break;
89762a2681cSDag-Erling Smørgrav 
89862a2681cSDag-Erling Smørgrav 		d_len = q - p;
899c0d2581bSFabien Thomas 		if (d_len > 0 && h_len >= d_len &&
90062a2681cSDag-Erling Smørgrav 		    strncasecmp(host + h_len - d_len,
90162a2681cSDag-Erling Smørgrav 			p, d_len) == 0) {
90262a2681cSDag-Erling Smørgrav 			/* domain name matches */
90362a2681cSDag-Erling Smørgrav 			return (1);
90462a2681cSDag-Erling Smørgrav 		}
90562a2681cSDag-Erling Smørgrav 
90662a2681cSDag-Erling Smørgrav 		p = q + 1;
90762a2681cSDag-Erling Smørgrav 	} while (*q);
90862a2681cSDag-Erling Smørgrav 
90962a2681cSDag-Erling Smørgrav 	return (0);
91062a2681cSDag-Erling Smørgrav }
911