xref: /freebsd/lib/libfetch/common.c (revision c0d2581bcb70da8c4ca5b51a5f7fbf94d7dbd99e)
1842a95ccSDag-Erling Smørgrav /*-
22cbbf9daSDag-Erling Smørgrav  * Copyright (c) 1998-2004 Dag-Erling Co�dan 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>
41842a95ccSDag-Erling Smørgrav #include <netdb.h>
4207350d12SDag-Erling Smørgrav #include <pwd.h>
43ba101983SDag-Erling Smørgrav #include <stdarg.h>
440fba3a00SDag-Erling Smørgrav #include <stdlib.h>
450fba3a00SDag-Erling Smørgrav #include <stdio.h>
46842a95ccSDag-Erling Smørgrav #include <string.h>
47842a95ccSDag-Erling Smørgrav #include <unistd.h>
48842a95ccSDag-Erling Smørgrav 
49842a95ccSDag-Erling Smørgrav #include "fetch.h"
50842a95ccSDag-Erling Smørgrav #include "common.h"
51842a95ccSDag-Erling Smørgrav 
52d8acd8dcSDag-Erling Smørgrav 
53842a95ccSDag-Erling Smørgrav /*** Local data **************************************************************/
54842a95ccSDag-Erling Smørgrav 
55842a95ccSDag-Erling Smørgrav /*
56842a95ccSDag-Erling Smørgrav  * Error messages for resolver errors
57842a95ccSDag-Erling Smørgrav  */
58a1b37df2SDag-Erling Smørgrav static struct fetcherr netdb_errlist[] = {
593d82ba43SHajimu UMEMOTO #ifdef EAI_NODATA
6028c645cfSHajimu UMEMOTO 	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
613d82ba43SHajimu UMEMOTO #endif
6228c645cfSHajimu UMEMOTO 	{ EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
6328c645cfSHajimu UMEMOTO 	{ EAI_FAIL,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
6428c645cfSHajimu UMEMOTO 	{ EAI_NONAME,	FETCH_RESOLV,	"No address record" },
65d8acd8dcSDag-Erling Smørgrav 	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
66842a95ccSDag-Erling Smørgrav };
67842a95ccSDag-Erling Smørgrav 
68a1763027SDag-Erling Smørgrav /* End-of-Line */
6938c7e4a6SArchie Cobbs static const char ENDL[2] = "\r\n";
70a1763027SDag-Erling Smørgrav 
71842a95ccSDag-Erling Smørgrav 
72842a95ccSDag-Erling Smørgrav /*** Error-reporting functions ***********************************************/
73842a95ccSDag-Erling Smørgrav 
74842a95ccSDag-Erling Smørgrav /*
75842a95ccSDag-Erling Smørgrav  * Map error code to string
76842a95ccSDag-Erling Smørgrav  */
77ba101983SDag-Erling Smørgrav static struct fetcherr *
78a1b37df2SDag-Erling Smørgrav fetch_finderr(struct fetcherr *p, int e)
79842a95ccSDag-Erling Smørgrav {
80ba101983SDag-Erling Smørgrav 	while (p->num != -1 && p->num != e)
81ba101983SDag-Erling Smørgrav 		p++;
82e19e6098SDag-Erling Smørgrav 	return (p);
83842a95ccSDag-Erling Smørgrav }
84842a95ccSDag-Erling Smørgrav 
85842a95ccSDag-Erling Smørgrav /*
86842a95ccSDag-Erling Smørgrav  * Set error code
87842a95ccSDag-Erling Smørgrav  */
88842a95ccSDag-Erling Smørgrav void
89a1b37df2SDag-Erling Smørgrav fetch_seterr(struct fetcherr *p, int e)
90842a95ccSDag-Erling Smørgrav {
91a1b37df2SDag-Erling Smørgrav 	p = fetch_finderr(p, e);
92ba101983SDag-Erling Smørgrav 	fetchLastErrCode = p->cat;
93ba101983SDag-Erling Smørgrav 	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
94842a95ccSDag-Erling Smørgrav }
95842a95ccSDag-Erling Smørgrav 
96842a95ccSDag-Erling Smørgrav /*
97842a95ccSDag-Erling Smørgrav  * Set error code according to errno
98842a95ccSDag-Erling Smørgrav  */
99842a95ccSDag-Erling Smørgrav void
100a1b37df2SDag-Erling Smørgrav fetch_syserr(void)
101842a95ccSDag-Erling Smørgrav {
102d8acd8dcSDag-Erling Smørgrav 	switch (errno) {
103d8acd8dcSDag-Erling Smørgrav 	case 0:
1040fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_OK;
105d8acd8dcSDag-Erling Smørgrav 		break;
106d8acd8dcSDag-Erling Smørgrav 	case EPERM:
107d8acd8dcSDag-Erling Smørgrav 	case EACCES:
108d8acd8dcSDag-Erling Smørgrav 	case EROFS:
109d8acd8dcSDag-Erling Smørgrav 	case EAUTH:
110d8acd8dcSDag-Erling Smørgrav 	case ENEEDAUTH:
1110fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_AUTH;
112d8acd8dcSDag-Erling Smørgrav 		break;
113d8acd8dcSDag-Erling Smørgrav 	case ENOENT:
114d8acd8dcSDag-Erling Smørgrav 	case EISDIR: /* XXX */
1150fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_UNAVAIL;
116d8acd8dcSDag-Erling Smørgrav 		break;
117d8acd8dcSDag-Erling Smørgrav 	case ENOMEM:
1180fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_MEMORY;
119d8acd8dcSDag-Erling Smørgrav 		break;
120d8acd8dcSDag-Erling Smørgrav 	case EBUSY:
121d8acd8dcSDag-Erling Smørgrav 	case EAGAIN:
1220fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_TEMP;
123d8acd8dcSDag-Erling Smørgrav 		break;
124d8acd8dcSDag-Erling Smørgrav 	case EEXIST:
1250fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_EXISTS;
126d8acd8dcSDag-Erling Smørgrav 		break;
127d8acd8dcSDag-Erling Smørgrav 	case ENOSPC:
1280fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_FULL;
129d8acd8dcSDag-Erling Smørgrav 		break;
130d8acd8dcSDag-Erling Smørgrav 	case EADDRINUSE:
131d8acd8dcSDag-Erling Smørgrav 	case EADDRNOTAVAIL:
132d8acd8dcSDag-Erling Smørgrav 	case ENETDOWN:
133d8acd8dcSDag-Erling Smørgrav 	case ENETUNREACH:
134d8acd8dcSDag-Erling Smørgrav 	case ENETRESET:
135d8acd8dcSDag-Erling Smørgrav 	case EHOSTUNREACH:
1360fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_NETWORK;
137d8acd8dcSDag-Erling Smørgrav 		break;
138d8acd8dcSDag-Erling Smørgrav 	case ECONNABORTED:
139d8acd8dcSDag-Erling Smørgrav 	case ECONNRESET:
1400fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_ABORT;
141d8acd8dcSDag-Erling Smørgrav 		break;
142d8acd8dcSDag-Erling Smørgrav 	case ETIMEDOUT:
1430fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_TIMEOUT;
144d8acd8dcSDag-Erling Smørgrav 		break;
145d8acd8dcSDag-Erling Smørgrav 	case ECONNREFUSED:
146d8acd8dcSDag-Erling Smørgrav 	case EHOSTDOWN:
1470fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_DOWN;
148d8acd8dcSDag-Erling Smørgrav 		break;
149d8acd8dcSDag-Erling Smørgrav default:
1500fba3a00SDag-Erling Smørgrav 		fetchLastErrCode = FETCH_UNKNOWN;
151d8acd8dcSDag-Erling Smørgrav 	}
152e19e6098SDag-Erling Smørgrav 	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
1530fba3a00SDag-Erling Smørgrav }
1540fba3a00SDag-Erling Smørgrav 
1550fba3a00SDag-Erling Smørgrav 
1560fba3a00SDag-Erling Smørgrav /*
1570fba3a00SDag-Erling Smørgrav  * Emit status message
1580fba3a00SDag-Erling Smørgrav  */
159ba101983SDag-Erling Smørgrav void
160a1b37df2SDag-Erling Smørgrav fetch_info(const char *fmt, ...)
1610fba3a00SDag-Erling Smørgrav {
1620fba3a00SDag-Erling Smørgrav 	va_list ap;
1630fba3a00SDag-Erling Smørgrav 
1640fba3a00SDag-Erling Smørgrav 	va_start(ap, fmt);
165ba101983SDag-Erling Smørgrav 	vfprintf(stderr, fmt, ap);
1660fba3a00SDag-Erling Smørgrav 	va_end(ap);
167a68f8b58SDag-Erling Smørgrav 	fputc('\n', stderr);
168842a95ccSDag-Erling Smørgrav }
169842a95ccSDag-Erling Smørgrav 
170842a95ccSDag-Erling Smørgrav 
171842a95ccSDag-Erling Smørgrav /*** Network-related utility functions ***************************************/
172842a95ccSDag-Erling Smørgrav 
173842a95ccSDag-Erling Smørgrav /*
174e828ada7SDag-Erling Smørgrav  * Return the default port for a scheme
175e828ada7SDag-Erling Smørgrav  */
176e828ada7SDag-Erling Smørgrav int
177a1b37df2SDag-Erling Smørgrav fetch_default_port(const char *scheme)
178e828ada7SDag-Erling Smørgrav {
179e828ada7SDag-Erling Smørgrav 	struct servent *se;
180e828ada7SDag-Erling Smørgrav 
181e828ada7SDag-Erling Smørgrav 	if ((se = getservbyname(scheme, "tcp")) != NULL)
182e19e6098SDag-Erling Smørgrav 		return (ntohs(se->s_port));
183e828ada7SDag-Erling Smørgrav 	if (strcasecmp(scheme, SCHEME_FTP) == 0)
184e19e6098SDag-Erling Smørgrav 		return (FTP_DEFAULT_PORT);
185e828ada7SDag-Erling Smørgrav 	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
186e19e6098SDag-Erling Smørgrav 		return (HTTP_DEFAULT_PORT);
187e19e6098SDag-Erling Smørgrav 	return (0);
188e828ada7SDag-Erling Smørgrav }
189e828ada7SDag-Erling Smørgrav 
190e828ada7SDag-Erling Smørgrav /*
191e828ada7SDag-Erling Smørgrav  * Return the default proxy port for a scheme
192e828ada7SDag-Erling Smørgrav  */
193e828ada7SDag-Erling Smørgrav int
194a1b37df2SDag-Erling Smørgrav fetch_default_proxy_port(const char *scheme)
195e828ada7SDag-Erling Smørgrav {
196e828ada7SDag-Erling Smørgrav 	if (strcasecmp(scheme, SCHEME_FTP) == 0)
197e19e6098SDag-Erling Smørgrav 		return (FTP_DEFAULT_PROXY_PORT);
198e828ada7SDag-Erling Smørgrav 	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
199e19e6098SDag-Erling Smørgrav 		return (HTTP_DEFAULT_PROXY_PORT);
200e19e6098SDag-Erling Smørgrav 	return (0);
201e828ada7SDag-Erling Smørgrav }
202e828ada7SDag-Erling Smørgrav 
203f606d589SDag-Erling Smørgrav 
204e828ada7SDag-Erling Smørgrav /*
2059601e333SDag-Erling Smørgrav  * Create a connection for an existing descriptor.
2069601e333SDag-Erling Smørgrav  */
2079601e333SDag-Erling Smørgrav conn_t *
208a1b37df2SDag-Erling Smørgrav fetch_reopen(int sd)
2099601e333SDag-Erling Smørgrav {
2109601e333SDag-Erling Smørgrav 	conn_t *conn;
2119601e333SDag-Erling Smørgrav 
2129601e333SDag-Erling Smørgrav 	/* allocate and fill connection structure */
213930105c1SDag-Erling Smørgrav 	if ((conn = calloc(1, sizeof(*conn))) == NULL)
2149601e333SDag-Erling Smørgrav 		return (NULL);
2159601e333SDag-Erling Smørgrav 	conn->sd = sd;
216f606d589SDag-Erling Smørgrav 	++conn->ref;
217f606d589SDag-Erling Smørgrav 	return (conn);
218f606d589SDag-Erling Smørgrav }
219f606d589SDag-Erling Smørgrav 
220f606d589SDag-Erling Smørgrav 
221f606d589SDag-Erling Smørgrav /*
222f606d589SDag-Erling Smørgrav  * Bump a connection's reference count.
223f606d589SDag-Erling Smørgrav  */
224f606d589SDag-Erling Smørgrav conn_t *
225a1b37df2SDag-Erling Smørgrav fetch_ref(conn_t *conn)
226f606d589SDag-Erling Smørgrav {
227f606d589SDag-Erling Smørgrav 
228f606d589SDag-Erling Smørgrav 	++conn->ref;
2299601e333SDag-Erling Smørgrav 	return (conn);
2309601e333SDag-Erling Smørgrav }
2319601e333SDag-Erling Smørgrav 
2329601e333SDag-Erling Smørgrav 
2339601e333SDag-Erling Smørgrav /*
234c42cb9d9SDag-Erling Smørgrav  * Bind a socket to a specific local address
235c42cb9d9SDag-Erling Smørgrav  */
236c42cb9d9SDag-Erling Smørgrav int
237a1b37df2SDag-Erling Smørgrav fetch_bind(int sd, int af, const char *addr)
238c42cb9d9SDag-Erling Smørgrav {
239c42cb9d9SDag-Erling Smørgrav 	struct addrinfo hints, *res, *res0;
240c42cb9d9SDag-Erling Smørgrav 	int err;
241c42cb9d9SDag-Erling Smørgrav 
242c42cb9d9SDag-Erling Smørgrav 	memset(&hints, 0, sizeof(hints));
243c42cb9d9SDag-Erling Smørgrav 	hints.ai_family = af;
244c42cb9d9SDag-Erling Smørgrav 	hints.ai_socktype = SOCK_STREAM;
245c42cb9d9SDag-Erling Smørgrav 	hints.ai_protocol = 0;
246c42cb9d9SDag-Erling Smørgrav 	if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)
247c42cb9d9SDag-Erling Smørgrav 		return (-1);
248c42cb9d9SDag-Erling Smørgrav 	for (res = res0; res; res = res->ai_next)
249c42cb9d9SDag-Erling Smørgrav 		if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
250c42cb9d9SDag-Erling Smørgrav 			return (0);
251c42cb9d9SDag-Erling Smørgrav 	return (-1);
252c42cb9d9SDag-Erling Smørgrav }
253c42cb9d9SDag-Erling Smørgrav 
254c42cb9d9SDag-Erling Smørgrav 
255c42cb9d9SDag-Erling Smørgrav /*
256842a95ccSDag-Erling Smørgrav  * Establish a TCP connection to the specified port on the specified host.
257842a95ccSDag-Erling Smørgrav  */
258dea29ca1SDag-Erling Smørgrav conn_t *
259a1b37df2SDag-Erling Smørgrav fetch_connect(const char *host, int port, int af, int verbose)
260842a95ccSDag-Erling Smørgrav {
261dea29ca1SDag-Erling Smørgrav 	conn_t *conn;
26228c645cfSHajimu UMEMOTO 	char pbuf[10];
263c42cb9d9SDag-Erling Smørgrav 	const char *bindaddr;
26428c645cfSHajimu UMEMOTO 	struct addrinfo hints, *res, *res0;
26528c645cfSHajimu UMEMOTO 	int sd, err;
266842a95ccSDag-Erling Smørgrav 
267f67efa37SDag-Erling Smørgrav 	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
268842a95ccSDag-Erling Smørgrav 
2690fba3a00SDag-Erling Smørgrav 	if (verbose)
270a1b37df2SDag-Erling Smørgrav 		fetch_info("looking up %s", host);
2710fba3a00SDag-Erling Smørgrav 
27228c645cfSHajimu UMEMOTO 	/* look up host name and set up socket address structure */
27328c645cfSHajimu UMEMOTO 	snprintf(pbuf, sizeof(pbuf), "%d", port);
27428c645cfSHajimu UMEMOTO 	memset(&hints, 0, sizeof(hints));
27528c645cfSHajimu UMEMOTO 	hints.ai_family = af;
27628c645cfSHajimu UMEMOTO 	hints.ai_socktype = SOCK_STREAM;
27728c645cfSHajimu UMEMOTO 	hints.ai_protocol = 0;
27828c645cfSHajimu UMEMOTO 	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
279a1b37df2SDag-Erling Smørgrav 		netdb_seterr(err);
280dea29ca1SDag-Erling Smørgrav 		return (NULL);
281842a95ccSDag-Erling Smørgrav 	}
282c42cb9d9SDag-Erling Smørgrav 	bindaddr = getenv("FETCH_BIND_ADDRESS");
283842a95ccSDag-Erling Smørgrav 
2840fba3a00SDag-Erling Smørgrav 	if (verbose)
285a1b37df2SDag-Erling Smørgrav 		fetch_info("connecting to %s:%d", host, port);
2860fba3a00SDag-Erling Smørgrav 
287842a95ccSDag-Erling Smørgrav 	/* try to connect */
288c42cb9d9SDag-Erling Smørgrav 	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
28928c645cfSHajimu UMEMOTO 		if ((sd = socket(res->ai_family, res->ai_socktype,
290a1763027SDag-Erling Smørgrav 			 res->ai_protocol)) == -1)
29128c645cfSHajimu UMEMOTO 			continue;
292c42cb9d9SDag-Erling Smørgrav 		if (bindaddr != NULL && *bindaddr != '\0' &&
293a1b37df2SDag-Erling Smørgrav 		    fetch_bind(sd, res->ai_family, bindaddr) != 0) {
294a1b37df2SDag-Erling Smørgrav 			fetch_info("failed to bind to '%s'", bindaddr);
295c42cb9d9SDag-Erling Smørgrav 			close(sd);
296c42cb9d9SDag-Erling Smørgrav 			continue;
297c42cb9d9SDag-Erling Smørgrav 		}
298c42cb9d9SDag-Erling Smørgrav 		if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
29928c645cfSHajimu UMEMOTO 			break;
300842a95ccSDag-Erling Smørgrav 		close(sd);
30128c645cfSHajimu UMEMOTO 	}
3027487ef62SHajimu UMEMOTO 	freeaddrinfo(res0);
303a1763027SDag-Erling Smørgrav 	if (sd == -1) {
304a1b37df2SDag-Erling Smørgrav 		fetch_syserr();
305dea29ca1SDag-Erling Smørgrav 		return (NULL);
306842a95ccSDag-Erling Smørgrav 	}
307842a95ccSDag-Erling Smørgrav 
308a1b37df2SDag-Erling Smørgrav 	if ((conn = fetch_reopen(sd)) == NULL) {
309a1b37df2SDag-Erling Smørgrav 		fetch_syserr();
310dea29ca1SDag-Erling Smørgrav 		close(sd);
31140cfbfd5SBill Fenner 	}
312dea29ca1SDag-Erling Smørgrav 	return (conn);
313842a95ccSDag-Erling Smørgrav }
314ce71b736SDag-Erling Smørgrav 
315ce71b736SDag-Erling Smørgrav 
316fc6e9e65SDag-Erling Smørgrav /*
317111e2510SDag-Erling Smørgrav  * Enable SSL on a connection.
318111e2510SDag-Erling Smørgrav  */
319111e2510SDag-Erling Smørgrav int
320a1b37df2SDag-Erling Smørgrav fetch_ssl(conn_t *conn, int verbose)
321111e2510SDag-Erling Smørgrav {
322111e2510SDag-Erling Smørgrav 
3233070f6cbSDag-Erling Smørgrav #ifdef WITH_SSL
324111e2510SDag-Erling Smørgrav 	/* Init the SSL library and context */
325111e2510SDag-Erling Smørgrav 	if (!SSL_library_init()){
326111e2510SDag-Erling Smørgrav 		fprintf(stderr, "SSL library init failed\n");
327111e2510SDag-Erling Smørgrav 		return (-1);
328111e2510SDag-Erling Smørgrav 	}
329111e2510SDag-Erling Smørgrav 
330111e2510SDag-Erling Smørgrav 	SSL_load_error_strings();
331111e2510SDag-Erling Smørgrav 
332111e2510SDag-Erling Smørgrav 	conn->ssl_meth = SSLv23_client_method();
333111e2510SDag-Erling Smørgrav 	conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
334f8020ddeSDag-Erling Smørgrav 	SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
335111e2510SDag-Erling Smørgrav 
336111e2510SDag-Erling Smørgrav 	conn->ssl = SSL_new(conn->ssl_ctx);
337111e2510SDag-Erling Smørgrav 	if (conn->ssl == NULL){
338111e2510SDag-Erling Smørgrav 		fprintf(stderr, "SSL context creation failed\n");
339111e2510SDag-Erling Smørgrav 		return (-1);
340111e2510SDag-Erling Smørgrav 	}
341111e2510SDag-Erling Smørgrav 	SSL_set_fd(conn->ssl, conn->sd);
342111e2510SDag-Erling Smørgrav 	if (SSL_connect(conn->ssl) == -1){
343111e2510SDag-Erling Smørgrav 		ERR_print_errors_fp(stderr);
344111e2510SDag-Erling Smørgrav 		return (-1);
345111e2510SDag-Erling Smørgrav 	}
346111e2510SDag-Erling Smørgrav 
347111e2510SDag-Erling Smørgrav 	if (verbose) {
348111e2510SDag-Erling Smørgrav 		X509_NAME *name;
349111e2510SDag-Erling Smørgrav 		char *str;
350111e2510SDag-Erling Smørgrav 
351111e2510SDag-Erling Smørgrav 		fprintf(stderr, "SSL connection established using %s\n",
352111e2510SDag-Erling Smørgrav 		    SSL_get_cipher(conn->ssl));
353111e2510SDag-Erling Smørgrav 		conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
354111e2510SDag-Erling Smørgrav 		name = X509_get_subject_name(conn->ssl_cert);
355111e2510SDag-Erling Smørgrav 		str = X509_NAME_oneline(name, 0, 0);
356111e2510SDag-Erling Smørgrav 		printf("Certificate subject: %s\n", str);
357111e2510SDag-Erling Smørgrav 		free(str);
358111e2510SDag-Erling Smørgrav 		name = X509_get_issuer_name(conn->ssl_cert);
359111e2510SDag-Erling Smørgrav 		str = X509_NAME_oneline(name, 0, 0);
360111e2510SDag-Erling Smørgrav 		printf("Certificate issuer: %s\n", str);
361111e2510SDag-Erling Smørgrav 		free(str);
362111e2510SDag-Erling Smørgrav 	}
363111e2510SDag-Erling Smørgrav 
364111e2510SDag-Erling Smørgrav 	return (0);
3653070f6cbSDag-Erling Smørgrav #else
3663070f6cbSDag-Erling Smørgrav 	(void)conn;
3673070f6cbSDag-Erling Smørgrav 	(void)verbose;
3683070f6cbSDag-Erling Smørgrav 	fprintf(stderr, "SSL support disabled\n");
3693070f6cbSDag-Erling Smørgrav 	return (-1);
3703070f6cbSDag-Erling Smørgrav #endif
371111e2510SDag-Erling Smørgrav }
372111e2510SDag-Erling Smørgrav 
373f606d589SDag-Erling Smørgrav 
374111e2510SDag-Erling Smørgrav /*
3759601e333SDag-Erling Smørgrav  * Read a character from a connection w/ timeout
3769601e333SDag-Erling Smørgrav  */
3779601e333SDag-Erling Smørgrav ssize_t
378a1b37df2SDag-Erling Smørgrav fetch_read(conn_t *conn, char *buf, size_t len)
3799601e333SDag-Erling Smørgrav {
3805092cf05SDag-Erling Smørgrav 	struct timeval now, timeout, delta;
3819601e333SDag-Erling Smørgrav 	fd_set readfds;
3829601e333SDag-Erling Smørgrav 	ssize_t rlen, total;
3839601e333SDag-Erling Smørgrav 	int r;
3849601e333SDag-Erling Smørgrav 
3859601e333SDag-Erling Smørgrav 	if (fetchTimeout) {
3869601e333SDag-Erling Smørgrav 		FD_ZERO(&readfds);
3879601e333SDag-Erling Smørgrav 		gettimeofday(&timeout, NULL);
3889601e333SDag-Erling Smørgrav 		timeout.tv_sec += fetchTimeout;
3899601e333SDag-Erling Smørgrav 	}
3909601e333SDag-Erling Smørgrav 
3919601e333SDag-Erling Smørgrav 	total = 0;
3929601e333SDag-Erling Smørgrav 	while (len > 0) {
3939601e333SDag-Erling Smørgrav 		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
3949601e333SDag-Erling Smørgrav 			FD_SET(conn->sd, &readfds);
3959601e333SDag-Erling Smørgrav 			gettimeofday(&now, NULL);
3965092cf05SDag-Erling Smørgrav 			delta.tv_sec = timeout.tv_sec - now.tv_sec;
3975092cf05SDag-Erling Smørgrav 			delta.tv_usec = timeout.tv_usec - now.tv_usec;
3985092cf05SDag-Erling Smørgrav 			if (delta.tv_usec < 0) {
3995092cf05SDag-Erling Smørgrav 				delta.tv_usec += 1000000;
4005092cf05SDag-Erling Smørgrav 				delta.tv_sec--;
4019601e333SDag-Erling Smørgrav 			}
4025092cf05SDag-Erling Smørgrav 			if (delta.tv_sec < 0) {
403bb13d0afSDag-Erling Smørgrav 				errno = ETIMEDOUT;
404a1b37df2SDag-Erling Smørgrav 				fetch_syserr();
405bb13d0afSDag-Erling Smørgrav 				return (-1);
406bb13d0afSDag-Erling Smørgrav 			}
4079601e333SDag-Erling Smørgrav 			errno = 0;
4085092cf05SDag-Erling Smørgrav 			r = select(conn->sd + 1, &readfds, NULL, NULL, &delta);
4099601e333SDag-Erling Smørgrav 			if (r == -1) {
4109601e333SDag-Erling Smørgrav 				if (errno == EINTR && fetchRestartCalls)
4119601e333SDag-Erling Smørgrav 					continue;
412a1b37df2SDag-Erling Smørgrav 				fetch_syserr();
4139601e333SDag-Erling Smørgrav 				return (-1);
4149601e333SDag-Erling Smørgrav 			}
4159601e333SDag-Erling Smørgrav 		}
4163070f6cbSDag-Erling Smørgrav #ifdef WITH_SSL
4179601e333SDag-Erling Smørgrav 		if (conn->ssl != NULL)
4189601e333SDag-Erling Smørgrav 			rlen = SSL_read(conn->ssl, buf, len);
4199601e333SDag-Erling Smørgrav 		else
4203070f6cbSDag-Erling Smørgrav #endif
4219601e333SDag-Erling Smørgrav 			rlen = read(conn->sd, buf, len);
4221a5424b1SDag-Erling Smørgrav 		if (rlen == 0)
4231a5424b1SDag-Erling Smørgrav 			break;
4249601e333SDag-Erling Smørgrav 		if (rlen < 0) {
4259601e333SDag-Erling Smørgrav 			if (errno == EINTR && fetchRestartCalls)
4269601e333SDag-Erling Smørgrav 				continue;
4279601e333SDag-Erling Smørgrav 			return (-1);
4289601e333SDag-Erling Smørgrav 		}
4299601e333SDag-Erling Smørgrav 		len -= rlen;
4309601e333SDag-Erling Smørgrav 		buf += rlen;
4319601e333SDag-Erling Smørgrav 		total += rlen;
4329601e333SDag-Erling Smørgrav 	}
4339601e333SDag-Erling Smørgrav 	return (total);
4349601e333SDag-Erling Smørgrav }
4359601e333SDag-Erling Smørgrav 
436f606d589SDag-Erling Smørgrav 
4379601e333SDag-Erling Smørgrav /*
4389601e333SDag-Erling Smørgrav  * Read a line of text from a connection w/ timeout
439fc6e9e65SDag-Erling Smørgrav  */
440fc6e9e65SDag-Erling Smørgrav #define MIN_BUF_SIZE 1024
441fc6e9e65SDag-Erling Smørgrav 
442fc6e9e65SDag-Erling Smørgrav int
443a1b37df2SDag-Erling Smørgrav fetch_getln(conn_t *conn)
444fc6e9e65SDag-Erling Smørgrav {
4459601e333SDag-Erling Smørgrav 	char *tmp;
4469601e333SDag-Erling Smørgrav 	size_t tmpsize;
447bb13d0afSDag-Erling Smørgrav 	ssize_t len;
448fc6e9e65SDag-Erling Smørgrav 	char c;
449fc6e9e65SDag-Erling Smørgrav 
450dea29ca1SDag-Erling Smørgrav 	if (conn->buf == NULL) {
451dea29ca1SDag-Erling Smørgrav 		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
452fc6e9e65SDag-Erling Smørgrav 			errno = ENOMEM;
453e19e6098SDag-Erling Smørgrav 			return (-1);
454fc6e9e65SDag-Erling Smørgrav 		}
455dea29ca1SDag-Erling Smørgrav 		conn->bufsize = MIN_BUF_SIZE;
456fc6e9e65SDag-Erling Smørgrav 	}
457fc6e9e65SDag-Erling Smørgrav 
458dea29ca1SDag-Erling Smørgrav 	conn->buf[0] = '\0';
459dea29ca1SDag-Erling Smørgrav 	conn->buflen = 0;
460fc6e9e65SDag-Erling Smørgrav 
461fc6e9e65SDag-Erling Smørgrav 	do {
462a1b37df2SDag-Erling Smørgrav 		len = fetch_read(conn, &c, 1);
463bb13d0afSDag-Erling Smørgrav 		if (len == -1)
464e19e6098SDag-Erling Smørgrav 			return (-1);
465bb13d0afSDag-Erling Smørgrav 		if (len == 0)
466b68fbebdSDavid E. O'Brien 			break;
467dea29ca1SDag-Erling Smørgrav 		conn->buf[conn->buflen++] = c;
468dea29ca1SDag-Erling Smørgrav 		if (conn->buflen == conn->bufsize) {
469dea29ca1SDag-Erling Smørgrav 			tmp = conn->buf;
470dea29ca1SDag-Erling Smørgrav 			tmpsize = conn->bufsize * 2 + 1;
471dea29ca1SDag-Erling Smørgrav 			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
472fc6e9e65SDag-Erling Smørgrav 				errno = ENOMEM;
473e19e6098SDag-Erling Smørgrav 				return (-1);
474fc6e9e65SDag-Erling Smørgrav 			}
475dea29ca1SDag-Erling Smørgrav 			conn->buf = tmp;
476dea29ca1SDag-Erling Smørgrav 			conn->bufsize = tmpsize;
477fc6e9e65SDag-Erling Smørgrav 		}
478fc6e9e65SDag-Erling Smørgrav 	} while (c != '\n');
479fc6e9e65SDag-Erling Smørgrav 
480dea29ca1SDag-Erling Smørgrav 	conn->buf[conn->buflen] = '\0';
481dea29ca1SDag-Erling Smørgrav 	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
482e19e6098SDag-Erling Smørgrav 	return (0);
483fc6e9e65SDag-Erling Smørgrav }
484fc6e9e65SDag-Erling Smørgrav 
485fc6e9e65SDag-Erling Smørgrav 
486a1763027SDag-Erling Smørgrav /*
4879601e333SDag-Erling Smørgrav  * Write to a connection w/ timeout
4889601e333SDag-Erling Smørgrav  */
4899601e333SDag-Erling Smørgrav ssize_t
490a1b37df2SDag-Erling Smørgrav fetch_write(conn_t *conn, const char *buf, size_t len)
4919601e333SDag-Erling Smørgrav {
492a4a37038SWarner Losh 	struct iovec iov;
493a4a37038SWarner Losh 
494a4a37038SWarner Losh 	iov.iov_base = __DECONST(char *, buf);
495a4a37038SWarner Losh 	iov.iov_len = len;
496a1b37df2SDag-Erling Smørgrav 	return fetch_writev(conn, &iov, 1);
497a4a37038SWarner Losh }
498a4a37038SWarner Losh 
499a4a37038SWarner Losh /*
500a4a37038SWarner Losh  * Write a vector to a connection w/ timeout
501a4a37038SWarner Losh  * Note: can modify the iovec.
502a4a37038SWarner Losh  */
503a4a37038SWarner Losh ssize_t
504a1b37df2SDag-Erling Smørgrav fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
505a4a37038SWarner Losh {
5065092cf05SDag-Erling Smørgrav 	struct timeval now, timeout, delta;
5079601e333SDag-Erling Smørgrav 	fd_set writefds;
5089601e333SDag-Erling Smørgrav 	ssize_t wlen, total;
5099601e333SDag-Erling Smørgrav 	int r;
5109601e333SDag-Erling Smørgrav 
5119601e333SDag-Erling Smørgrav 	if (fetchTimeout) {
5129601e333SDag-Erling Smørgrav 		FD_ZERO(&writefds);
5139601e333SDag-Erling Smørgrav 		gettimeofday(&timeout, NULL);
5149601e333SDag-Erling Smørgrav 		timeout.tv_sec += fetchTimeout;
5159601e333SDag-Erling Smørgrav 	}
5169601e333SDag-Erling Smørgrav 
517a4a37038SWarner Losh 	total = 0;
518a4a37038SWarner Losh 	while (iovcnt > 0) {
5199601e333SDag-Erling Smørgrav 		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
5209601e333SDag-Erling Smørgrav 			FD_SET(conn->sd, &writefds);
5219601e333SDag-Erling Smørgrav 			gettimeofday(&now, NULL);
5225092cf05SDag-Erling Smørgrav 			delta.tv_sec = timeout.tv_sec - now.tv_sec;
5235092cf05SDag-Erling Smørgrav 			delta.tv_usec = timeout.tv_usec - now.tv_usec;
5245092cf05SDag-Erling Smørgrav 			if (delta.tv_usec < 0) {
5255092cf05SDag-Erling Smørgrav 				delta.tv_usec += 1000000;
5265092cf05SDag-Erling Smørgrav 				delta.tv_sec--;
5279601e333SDag-Erling Smørgrav 			}
5285092cf05SDag-Erling Smørgrav 			if (delta.tv_sec < 0) {
5299601e333SDag-Erling Smørgrav 				errno = ETIMEDOUT;
530a1b37df2SDag-Erling Smørgrav 				fetch_syserr();
5319601e333SDag-Erling Smørgrav 				return (-1);
5329601e333SDag-Erling Smørgrav 			}
5339601e333SDag-Erling Smørgrav 			errno = 0;
5345092cf05SDag-Erling Smørgrav 			r = select(conn->sd + 1, NULL, &writefds, NULL, &delta);
5359601e333SDag-Erling Smørgrav 			if (r == -1) {
5369601e333SDag-Erling Smørgrav 				if (errno == EINTR && fetchRestartCalls)
5379601e333SDag-Erling Smørgrav 					continue;
5389601e333SDag-Erling Smørgrav 				return (-1);
5399601e333SDag-Erling Smørgrav 			}
5409601e333SDag-Erling Smørgrav 		}
5419601e333SDag-Erling Smørgrav 		errno = 0;
5423070f6cbSDag-Erling Smørgrav #ifdef WITH_SSL
5439601e333SDag-Erling Smørgrav 		if (conn->ssl != NULL)
544a4a37038SWarner Losh 			wlen = SSL_write(conn->ssl,
545a4a37038SWarner Losh 			    iov->iov_base, iov->iov_len);
5469601e333SDag-Erling Smørgrav 		else
5473070f6cbSDag-Erling Smørgrav #endif
548a4a37038SWarner Losh 			wlen = writev(conn->sd, iov, iovcnt);
549a4a37038SWarner Losh 		if (wlen == 0) {
5509601e333SDag-Erling Smørgrav 			/* we consider a short write a failure */
551a4a37038SWarner Losh 			errno = EPIPE;
552a1b37df2SDag-Erling Smørgrav 			fetch_syserr();
5539601e333SDag-Erling Smørgrav 			return (-1);
554a4a37038SWarner Losh 		}
5559601e333SDag-Erling Smørgrav 		if (wlen < 0) {
5569601e333SDag-Erling Smørgrav 			if (errno == EINTR && fetchRestartCalls)
5579601e333SDag-Erling Smørgrav 				continue;
5589601e333SDag-Erling Smørgrav 			return (-1);
5599601e333SDag-Erling Smørgrav 		}
5609601e333SDag-Erling Smørgrav 		total += wlen;
561a4a37038SWarner Losh 		while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
562a4a37038SWarner Losh 			wlen -= iov->iov_len;
563a4a37038SWarner Losh 			iov++;
564a4a37038SWarner Losh 			iovcnt--;
565a4a37038SWarner Losh 		}
566a4a37038SWarner Losh 		if (iovcnt > 0) {
567a4a37038SWarner Losh 			iov->iov_len -= wlen;
568a4a37038SWarner Losh 			iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;
569a4a37038SWarner Losh 		}
5709601e333SDag-Erling Smørgrav 	}
5719601e333SDag-Erling Smørgrav 	return (total);
5729601e333SDag-Erling Smørgrav }
5739601e333SDag-Erling Smørgrav 
574f606d589SDag-Erling Smørgrav 
5759601e333SDag-Erling Smørgrav /*
5769601e333SDag-Erling Smørgrav  * Write a line of text to a connection w/ timeout
577a1763027SDag-Erling Smørgrav  */
578a1763027SDag-Erling Smørgrav int
579a1b37df2SDag-Erling Smørgrav fetch_putln(conn_t *conn, const char *str, size_t len)
580a1763027SDag-Erling Smørgrav {
581a4a37038SWarner Losh 	struct iovec iov[2];
5827504527eSDag-Erling Smørgrav 	int ret;
58366ffb8a3SDag-Erling Smørgrav 
58466ffb8a3SDag-Erling Smørgrav 	DEBUG(fprintf(stderr, ">>> %s\n", str));
585a4a37038SWarner Losh 	iov[0].iov_base = __DECONST(char *, str);
586a4a37038SWarner Losh 	iov[0].iov_len = len;
587a4a37038SWarner Losh 	iov[1].iov_base = __DECONST(char *, ENDL);
588930105c1SDag-Erling Smørgrav 	iov[1].iov_len = sizeof(ENDL);
5897504527eSDag-Erling Smørgrav 	if (len == 0)
590a1b37df2SDag-Erling Smørgrav 		ret = fetch_writev(conn, &iov[1], 1);
5917504527eSDag-Erling Smørgrav 	else
592a1b37df2SDag-Erling Smørgrav 		ret = fetch_writev(conn, iov, 2);
5937504527eSDag-Erling Smørgrav 	if (ret == -1)
594e19e6098SDag-Erling Smørgrav 		return (-1);
595e19e6098SDag-Erling Smørgrav 	return (0);
596a1763027SDag-Erling Smørgrav }
597a1763027SDag-Erling Smørgrav 
598a1763027SDag-Erling Smørgrav 
599dea29ca1SDag-Erling Smørgrav /*
600dea29ca1SDag-Erling Smørgrav  * Close connection
601dea29ca1SDag-Erling Smørgrav  */
602dea29ca1SDag-Erling Smørgrav int
603a1b37df2SDag-Erling Smørgrav fetch_close(conn_t *conn)
604dea29ca1SDag-Erling Smørgrav {
605dea29ca1SDag-Erling Smørgrav 	int ret;
606dea29ca1SDag-Erling Smørgrav 
607f606d589SDag-Erling Smørgrav 	if (--conn->ref > 0)
608f606d589SDag-Erling Smørgrav 		return (0);
609dea29ca1SDag-Erling Smørgrav 	ret = close(conn->sd);
61055cf7be1SDag-Erling Smørgrav 	free(conn->buf);
611dea29ca1SDag-Erling Smørgrav 	free(conn);
612dea29ca1SDag-Erling Smørgrav 	return (ret);
613dea29ca1SDag-Erling Smørgrav }
614dea29ca1SDag-Erling Smørgrav 
615dea29ca1SDag-Erling Smørgrav 
616ce71b736SDag-Erling Smørgrav /*** Directory-related utility functions *************************************/
617ce71b736SDag-Erling Smørgrav 
618ce71b736SDag-Erling Smørgrav int
619a1b37df2SDag-Erling Smørgrav fetch_add_entry(struct url_ent **p, int *size, int *len,
620f573a5fcSDag-Erling Smørgrav     const char *name, struct url_stat *us)
621ce71b736SDag-Erling Smørgrav {
622ce71b736SDag-Erling Smørgrav 	struct url_ent *tmp;
623ce71b736SDag-Erling Smørgrav 
624ce71b736SDag-Erling Smørgrav 	if (*p == NULL) {
6255a51c23bSDag-Erling Smørgrav 		*size = 0;
626ce71b736SDag-Erling Smørgrav 		*len = 0;
627ce71b736SDag-Erling Smørgrav 	}
628ce71b736SDag-Erling Smørgrav 
629ce71b736SDag-Erling Smørgrav 	if (*len >= *size - 1) {
630930105c1SDag-Erling Smørgrav 		tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
631ce71b736SDag-Erling Smørgrav 		if (tmp == NULL) {
632ce71b736SDag-Erling Smørgrav 			errno = ENOMEM;
633a1b37df2SDag-Erling Smørgrav 			fetch_syserr();
634e19e6098SDag-Erling Smørgrav 			return (-1);
635ce71b736SDag-Erling Smørgrav 		}
6365a51c23bSDag-Erling Smørgrav 		*size = (*size * 2 + 1);
637ce71b736SDag-Erling Smørgrav 		*p = tmp;
638ce71b736SDag-Erling Smørgrav 	}
639ce71b736SDag-Erling Smørgrav 
640ce71b736SDag-Erling Smørgrav 	tmp = *p + *len;
6412b26f942SDag-Erling Smørgrav 	snprintf(tmp->name, PATH_MAX, "%s", name);
642340b079bSDag-Erling Smørgrav 	memcpy(&tmp->stat, us, sizeof(*us));
643ce71b736SDag-Erling Smørgrav 
644ce71b736SDag-Erling Smørgrav 	(*len)++;
645ce71b736SDag-Erling Smørgrav 	(++tmp)->name[0] = 0;
646ce71b736SDag-Erling Smørgrav 
647e19e6098SDag-Erling Smørgrav 	return (0);
648ce71b736SDag-Erling Smørgrav }
64907350d12SDag-Erling Smørgrav 
65007350d12SDag-Erling Smørgrav 
65107350d12SDag-Erling Smørgrav /*** Authentication-related utility functions ********************************/
65207350d12SDag-Erling Smørgrav 
65307350d12SDag-Erling Smørgrav static const char *
654a1b37df2SDag-Erling Smørgrav fetch_read_word(FILE *f)
65507350d12SDag-Erling Smørgrav {
65607350d12SDag-Erling Smørgrav 	static char word[1024];
65707350d12SDag-Erling Smørgrav 
658fc2841a9SColin Percival 	if (fscanf(f, " %1023s ", word) != 1)
65907350d12SDag-Erling Smørgrav 		return (NULL);
66007350d12SDag-Erling Smørgrav 	return (word);
66107350d12SDag-Erling Smørgrav }
66207350d12SDag-Erling Smørgrav 
66307350d12SDag-Erling Smørgrav /*
66407350d12SDag-Erling Smørgrav  * Get authentication data for a URL from .netrc
66507350d12SDag-Erling Smørgrav  */
66607350d12SDag-Erling Smørgrav int
667a1b37df2SDag-Erling Smørgrav fetch_netrc_auth(struct url *url)
66807350d12SDag-Erling Smørgrav {
66907350d12SDag-Erling Smørgrav 	char fn[PATH_MAX];
67007350d12SDag-Erling Smørgrav 	const char *word;
67107350d12SDag-Erling Smørgrav 	char *p;
67207350d12SDag-Erling Smørgrav 	FILE *f;
67307350d12SDag-Erling Smørgrav 
67407350d12SDag-Erling Smørgrav 	if ((p = getenv("NETRC")) != NULL) {
675930105c1SDag-Erling Smørgrav 		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
676a1b37df2SDag-Erling Smørgrav 			fetch_info("$NETRC specifies a file name "
67707350d12SDag-Erling Smørgrav 			    "longer than PATH_MAX");
67807350d12SDag-Erling Smørgrav 			return (-1);
67907350d12SDag-Erling Smørgrav 		}
68007350d12SDag-Erling Smørgrav 	} else {
68107350d12SDag-Erling Smørgrav 		if ((p = getenv("HOME")) != NULL) {
68207350d12SDag-Erling Smørgrav 			struct passwd *pwd;
68307350d12SDag-Erling Smørgrav 
68407350d12SDag-Erling Smørgrav 			if ((pwd = getpwuid(getuid())) == NULL ||
68507350d12SDag-Erling Smørgrav 			    (p = pwd->pw_dir) == NULL)
68607350d12SDag-Erling Smørgrav 				return (-1);
68707350d12SDag-Erling Smørgrav 		}
688930105c1SDag-Erling Smørgrav 		if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
68907350d12SDag-Erling Smørgrav 			return (-1);
69007350d12SDag-Erling Smørgrav 	}
69107350d12SDag-Erling Smørgrav 
69207350d12SDag-Erling Smørgrav 	if ((f = fopen(fn, "r")) == NULL)
69307350d12SDag-Erling Smørgrav 		return (-1);
694a1b37df2SDag-Erling Smørgrav 	while ((word = fetch_read_word(f)) != NULL) {
69507350d12SDag-Erling Smørgrav 		if (strcmp(word, "default") == 0) {
696a1b37df2SDag-Erling Smørgrav 			DEBUG(fetch_info("Using default .netrc settings"));
69707350d12SDag-Erling Smørgrav 			break;
69807350d12SDag-Erling Smørgrav 		}
69907350d12SDag-Erling Smørgrav 		if (strcmp(word, "machine") == 0 &&
700a1b37df2SDag-Erling Smørgrav 		    (word = fetch_read_word(f)) != NULL &&
70107350d12SDag-Erling Smørgrav 		    strcasecmp(word, url->host) == 0) {
702a1b37df2SDag-Erling Smørgrav 			DEBUG(fetch_info("Using .netrc settings for %s", word));
70307350d12SDag-Erling Smørgrav 			break;
70407350d12SDag-Erling Smørgrav 		}
70507350d12SDag-Erling Smørgrav 	}
70607350d12SDag-Erling Smørgrav 	if (word == NULL)
70707350d12SDag-Erling Smørgrav 		goto ferr;
708a1b37df2SDag-Erling Smørgrav 	while ((word = fetch_read_word(f)) != NULL) {
70907350d12SDag-Erling Smørgrav 		if (strcmp(word, "login") == 0) {
710a1b37df2SDag-Erling Smørgrav 			if ((word = fetch_read_word(f)) == NULL)
71107350d12SDag-Erling Smørgrav 				goto ferr;
712930105c1SDag-Erling Smørgrav 			if (snprintf(url->user, sizeof(url->user),
7139015b953SJohn W. De Boskey 				"%s", word) > (int)sizeof(url->user)) {
714a1b37df2SDag-Erling Smørgrav 				fetch_info("login name in .netrc is too long");
71507350d12SDag-Erling Smørgrav 				url->user[0] = '\0';
71607350d12SDag-Erling Smørgrav 			}
71707350d12SDag-Erling Smørgrav 		} else if (strcmp(word, "password") == 0) {
718a1b37df2SDag-Erling Smørgrav 			if ((word = fetch_read_word(f)) == NULL)
71907350d12SDag-Erling Smørgrav 				goto ferr;
720930105c1SDag-Erling Smørgrav 			if (snprintf(url->pwd, sizeof(url->pwd),
7219015b953SJohn W. De Boskey 				"%s", word) > (int)sizeof(url->pwd)) {
722a1b37df2SDag-Erling Smørgrav 				fetch_info("password in .netrc is too long");
72307350d12SDag-Erling Smørgrav 				url->pwd[0] = '\0';
72407350d12SDag-Erling Smørgrav 			}
72507350d12SDag-Erling Smørgrav 		} else if (strcmp(word, "account") == 0) {
726a1b37df2SDag-Erling Smørgrav 			if ((word = fetch_read_word(f)) == NULL)
72707350d12SDag-Erling Smørgrav 				goto ferr;
72807350d12SDag-Erling Smørgrav 			/* XXX not supported! */
72907350d12SDag-Erling Smørgrav 		} else {
73007350d12SDag-Erling Smørgrav 			break;
73107350d12SDag-Erling Smørgrav 		}
73207350d12SDag-Erling Smørgrav 	}
73307350d12SDag-Erling Smørgrav 	fclose(f);
73407350d12SDag-Erling Smørgrav 	return (0);
73507350d12SDag-Erling Smørgrav  ferr:
73607350d12SDag-Erling Smørgrav 	fclose(f);
73707350d12SDag-Erling Smørgrav 	return (-1);
73807350d12SDag-Erling Smørgrav }
73962a2681cSDag-Erling Smørgrav 
74062a2681cSDag-Erling Smørgrav /*
74162a2681cSDag-Erling Smørgrav  * The no_proxy environment variable specifies a set of domains for
74262a2681cSDag-Erling Smørgrav  * which the proxy should not be consulted; the contents is a comma-,
74362a2681cSDag-Erling Smørgrav  * or space-separated list of domain names.  A single asterisk will
74462a2681cSDag-Erling Smørgrav  * override all proxy variables and no transactions will be proxied
74562a2681cSDag-Erling Smørgrav  * (for compatability with lynx and curl, see the discussion at
74662a2681cSDag-Erling Smørgrav  * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
74762a2681cSDag-Erling Smørgrav  */
74862a2681cSDag-Erling Smørgrav int
74962a2681cSDag-Erling Smørgrav fetch_no_proxy_match(const char *host)
75062a2681cSDag-Erling Smørgrav {
75162a2681cSDag-Erling Smørgrav 	const char *no_proxy, *p, *q;
75262a2681cSDag-Erling Smørgrav 	size_t h_len, d_len;
75362a2681cSDag-Erling Smørgrav 
75462a2681cSDag-Erling Smørgrav 	if ((no_proxy = getenv("NO_PROXY")) == NULL &&
75562a2681cSDag-Erling Smørgrav 	    (no_proxy = getenv("no_proxy")) == NULL)
75662a2681cSDag-Erling Smørgrav 		return (0);
75762a2681cSDag-Erling Smørgrav 
75862a2681cSDag-Erling Smørgrav 	/* asterisk matches any hostname */
75962a2681cSDag-Erling Smørgrav 	if (strcmp(no_proxy, "*") == 0)
76062a2681cSDag-Erling Smørgrav 		return (1);
76162a2681cSDag-Erling Smørgrav 
76262a2681cSDag-Erling Smørgrav 	h_len = strlen(host);
76362a2681cSDag-Erling Smørgrav 	p = no_proxy;
76462a2681cSDag-Erling Smørgrav 	do {
76562a2681cSDag-Erling Smørgrav 		/* position p at the beginning of a domain suffix */
766facd9827SDag-Erling Smørgrav 		while (*p == ',' || isspace((unsigned char)*p))
76762a2681cSDag-Erling Smørgrav 			p++;
76862a2681cSDag-Erling Smørgrav 
76962a2681cSDag-Erling Smørgrav 		/* position q at the first separator character */
77062a2681cSDag-Erling Smørgrav 		for (q = p; *q; ++q)
771facd9827SDag-Erling Smørgrav 			if (*q == ',' || isspace((unsigned char)*q))
77262a2681cSDag-Erling Smørgrav 				break;
77362a2681cSDag-Erling Smørgrav 
77462a2681cSDag-Erling Smørgrav 		d_len = q - p;
775c0d2581bSFabien Thomas 		if (d_len > 0 && h_len >= d_len &&
77662a2681cSDag-Erling Smørgrav 		    strncasecmp(host + h_len - d_len,
77762a2681cSDag-Erling Smørgrav 			p, d_len) == 0) {
77862a2681cSDag-Erling Smørgrav 			/* domain name matches */
77962a2681cSDag-Erling Smørgrav 			return (1);
78062a2681cSDag-Erling Smørgrav 		}
78162a2681cSDag-Erling Smørgrav 
78262a2681cSDag-Erling Smørgrav 		p = q + 1;
78362a2681cSDag-Erling Smørgrav 	} while (*q);
78462a2681cSDag-Erling Smørgrav 
78562a2681cSDag-Erling Smørgrav 	return (0);
78662a2681cSDag-Erling Smørgrav }
787