common.c revision 258347
140939Sdes/*-
2226537Sdes * Copyright (c) 1998-2011 Dag-Erling Sm��rgrav
3253680Sdes * Copyright (c) 2013 Michael Gmelin <freebsd@grem.de>
440939Sdes * All rights reserved.
540939Sdes *
640939Sdes * Redistribution and use in source and binary forms, with or without
740939Sdes * modification, are permitted provided that the following conditions
840939Sdes * are met:
940939Sdes * 1. Redistributions of source code must retain the above copyright
1040939Sdes *    notice, this list of conditions and the following disclaimer
1140939Sdes *    in this position and unchanged.
1240939Sdes * 2. Redistributions in binary form must reproduce the above copyright
1340939Sdes *    notice, this list of conditions and the following disclaimer in the
1440939Sdes *    documentation and/or other materials provided with the distribution.
1540939Sdes * 3. The name of the author may not be used to endorse or promote products
1640939Sdes *    derived from this software without specific prior written permission
1740939Sdes *
1840939Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1940939Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2040939Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2140939Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2240939Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2340939Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2440939Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2540939Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2640939Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2740939Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2840939Sdes */
2940939Sdes
3084203Sdillon#include <sys/cdefs.h>
3184203Sdillon__FBSDID("$FreeBSD: head/lib/libfetch/common.c 258347 2013-11-19 15:35:26Z bdrewery $");
3284203Sdillon
3341862Sdes#include <sys/param.h>
3440939Sdes#include <sys/socket.h>
3555557Sdes#include <sys/time.h>
3662981Sdes#include <sys/uio.h>
37174752Sdes
3840939Sdes#include <netinet/in.h>
3940939Sdes
40174752Sdes#include <ctype.h>
4140939Sdes#include <errno.h>
42210568Sdes#include <fcntl.h>
4340939Sdes#include <netdb.h>
44109695Sdes#include <pwd.h>
4560924Sdes#include <stdarg.h>
4641862Sdes#include <stdlib.h>
4741862Sdes#include <stdio.h>
4840939Sdes#include <string.h>
4940939Sdes#include <unistd.h>
5040939Sdes
51253680Sdes#ifdef WITH_SSL
52253680Sdes#include <openssl/x509v3.h>
53253680Sdes#endif
54253680Sdes
5540939Sdes#include "fetch.h"
5640939Sdes#include "common.h"
5740939Sdes
5840975Sdes
5940939Sdes/*** Local data **************************************************************/
6040939Sdes
6140939Sdes/*
6240939Sdes * Error messages for resolver errors
6340939Sdes */
64174588Sdesstatic struct fetcherr netdb_errlist[] = {
65121423Sume#ifdef EAI_NODATA
6690267Sdes	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
67121423Sume#endif
6890267Sdes	{ EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
6990267Sdes	{ EAI_FAIL,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
7090267Sdes	{ EAI_NONAME,	FETCH_RESOLV,	"No address record" },
7190267Sdes	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
7240939Sdes};
7340939Sdes
7462981Sdes/* End-of-Line */
7575891Sarchiestatic const char ENDL[2] = "\r\n";
7640939Sdes
7762981Sdes
7840939Sdes/*** Error-reporting functions ***********************************************/
7940939Sdes
8040939Sdes/*
8140939Sdes * Map error code to string
8240939Sdes */
8360924Sdesstatic struct fetcherr *
84174588Sdesfetch_finderr(struct fetcherr *p, int e)
8540939Sdes{
8690267Sdes	while (p->num != -1 && p->num != e)
8790267Sdes		p++;
8890267Sdes	return (p);
8940939Sdes}
9040939Sdes
9140939Sdes/*
9240939Sdes * Set error code
9340939Sdes */
9440939Sdesvoid
95174588Sdesfetch_seterr(struct fetcherr *p, int e)
9640939Sdes{
97174588Sdes	p = fetch_finderr(p, e);
9890267Sdes	fetchLastErrCode = p->cat;
9990267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
10040939Sdes}
10140939Sdes
10240939Sdes/*
10340939Sdes * Set error code according to errno
10440939Sdes */
10540939Sdesvoid
106174588Sdesfetch_syserr(void)
10740939Sdes{
10890267Sdes	switch (errno) {
10990267Sdes	case 0:
11090267Sdes		fetchLastErrCode = FETCH_OK;
11190267Sdes		break;
11290267Sdes	case EPERM:
11390267Sdes	case EACCES:
11490267Sdes	case EROFS:
11590267Sdes	case EAUTH:
11690267Sdes	case ENEEDAUTH:
11790267Sdes		fetchLastErrCode = FETCH_AUTH;
11890267Sdes		break;
11990267Sdes	case ENOENT:
12090267Sdes	case EISDIR: /* XXX */
12190267Sdes		fetchLastErrCode = FETCH_UNAVAIL;
12290267Sdes		break;
12390267Sdes	case ENOMEM:
12490267Sdes		fetchLastErrCode = FETCH_MEMORY;
12590267Sdes		break;
12690267Sdes	case EBUSY:
12790267Sdes	case EAGAIN:
12890267Sdes		fetchLastErrCode = FETCH_TEMP;
12990267Sdes		break;
13090267Sdes	case EEXIST:
13190267Sdes		fetchLastErrCode = FETCH_EXISTS;
13290267Sdes		break;
13390267Sdes	case ENOSPC:
13490267Sdes		fetchLastErrCode = FETCH_FULL;
13590267Sdes		break;
13690267Sdes	case EADDRINUSE:
13790267Sdes	case EADDRNOTAVAIL:
13890267Sdes	case ENETDOWN:
13990267Sdes	case ENETUNREACH:
14090267Sdes	case ENETRESET:
14190267Sdes	case EHOSTUNREACH:
14290267Sdes		fetchLastErrCode = FETCH_NETWORK;
14390267Sdes		break;
14490267Sdes	case ECONNABORTED:
14590267Sdes	case ECONNRESET:
14690267Sdes		fetchLastErrCode = FETCH_ABORT;
14790267Sdes		break;
14890267Sdes	case ETIMEDOUT:
14990267Sdes		fetchLastErrCode = FETCH_TIMEOUT;
15090267Sdes		break;
15190267Sdes	case ECONNREFUSED:
15290267Sdes	case EHOSTDOWN:
15390267Sdes		fetchLastErrCode = FETCH_DOWN;
15490267Sdes		break;
15590267Sdesdefault:
15690267Sdes		fetchLastErrCode = FETCH_UNKNOWN;
15790267Sdes	}
15890267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
15940939Sdes}
16040939Sdes
16140939Sdes
16241862Sdes/*
16341862Sdes * Emit status message
16441862Sdes */
16560924Sdesvoid
166174588Sdesfetch_info(const char *fmt, ...)
16741862Sdes{
16890267Sdes	va_list ap;
16990267Sdes
17090267Sdes	va_start(ap, fmt);
17190267Sdes	vfprintf(stderr, fmt, ap);
17290267Sdes	va_end(ap);
17390267Sdes	fputc('\n', stderr);
17441862Sdes}
17541862Sdes
17641862Sdes
17740939Sdes/*** Network-related utility functions ***************************************/
17840939Sdes
17940939Sdes/*
18068551Sdes * Return the default port for a scheme
18168551Sdes */
18268551Sdesint
183174588Sdesfetch_default_port(const char *scheme)
18468551Sdes{
18590267Sdes	struct servent *se;
18668551Sdes
18790267Sdes	if ((se = getservbyname(scheme, "tcp")) != NULL)
18890267Sdes		return (ntohs(se->s_port));
18990267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
19090267Sdes		return (FTP_DEFAULT_PORT);
19190267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
19290267Sdes		return (HTTP_DEFAULT_PORT);
19390267Sdes	return (0);
19468551Sdes}
19568551Sdes
19668551Sdes/*
19768551Sdes * Return the default proxy port for a scheme
19868551Sdes */
19968551Sdesint
200174588Sdesfetch_default_proxy_port(const char *scheme)
20168551Sdes{
20290267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
20390267Sdes		return (FTP_DEFAULT_PROXY_PORT);
20490267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
20590267Sdes		return (HTTP_DEFAULT_PROXY_PORT);
20690267Sdes	return (0);
20768551Sdes}
20868551Sdes
20998117Sdes
21068551Sdes/*
21197866Sdes * Create a connection for an existing descriptor.
21297866Sdes */
21397866Sdesconn_t *
214174588Sdesfetch_reopen(int sd)
21597866Sdes{
21697866Sdes	conn_t *conn;
217236193Sjilles	int opt = 1;
21897866Sdes
21997866Sdes	/* allocate and fill connection structure */
220109967Sdes	if ((conn = calloc(1, sizeof(*conn))) == NULL)
22197866Sdes		return (NULL);
222221830Sdes	fcntl(sd, F_SETFD, FD_CLOEXEC);
223236193Sjilles	setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof opt);
22497866Sdes	conn->sd = sd;
22598117Sdes	++conn->ref;
22697866Sdes	return (conn);
22797866Sdes}
22897866Sdes
22997866Sdes
23097866Sdes/*
23198117Sdes * Bump a connection's reference count.
23298117Sdes */
23398117Sdesconn_t *
234174588Sdesfetch_ref(conn_t *conn)
23598117Sdes{
23698117Sdes
23798117Sdes	++conn->ref;
23898117Sdes	return (conn);
23998117Sdes}
24098117Sdes
24198117Sdes
24298117Sdes/*
243111816Sdes * Bind a socket to a specific local address
244111816Sdes */
245111816Sdesint
246174588Sdesfetch_bind(int sd, int af, const char *addr)
247111816Sdes{
248111816Sdes	struct addrinfo hints, *res, *res0;
249111816Sdes	int err;
250111816Sdes
251111816Sdes	memset(&hints, 0, sizeof(hints));
252111816Sdes	hints.ai_family = af;
253111816Sdes	hints.ai_socktype = SOCK_STREAM;
254111816Sdes	hints.ai_protocol = 0;
255111816Sdes	if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)
256111816Sdes		return (-1);
257111816Sdes	for (res = res0; res; res = res->ai_next)
258111816Sdes		if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
259111816Sdes			return (0);
260111816Sdes	return (-1);
261111816Sdes}
262111816Sdes
263111816Sdes
264111816Sdes/*
26540939Sdes * Establish a TCP connection to the specified port on the specified host.
26640939Sdes */
26797856Sdesconn_t *
268174588Sdesfetch_connect(const char *host, int port, int af, int verbose)
26940939Sdes{
27097856Sdes	conn_t *conn;
27190267Sdes	char pbuf[10];
272111816Sdes	const char *bindaddr;
27390267Sdes	struct addrinfo hints, *res, *res0;
27490267Sdes	int sd, err;
27540939Sdes
27690267Sdes	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
27741862Sdes
27890267Sdes	if (verbose)
279174588Sdes		fetch_info("looking up %s", host);
28040939Sdes
28190267Sdes	/* look up host name and set up socket address structure */
28290267Sdes	snprintf(pbuf, sizeof(pbuf), "%d", port);
28390267Sdes	memset(&hints, 0, sizeof(hints));
28490267Sdes	hints.ai_family = af;
28590267Sdes	hints.ai_socktype = SOCK_STREAM;
28690267Sdes	hints.ai_protocol = 0;
28790267Sdes	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
288174588Sdes		netdb_seterr(err);
28997856Sdes		return (NULL);
29090267Sdes	}
291111816Sdes	bindaddr = getenv("FETCH_BIND_ADDRESS");
29290267Sdes
29390267Sdes	if (verbose)
294174588Sdes		fetch_info("connecting to %s:%d", host, port);
29590267Sdes
29690267Sdes	/* try to connect */
297111816Sdes	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
29890267Sdes		if ((sd = socket(res->ai_family, res->ai_socktype,
29962981Sdes			 res->ai_protocol)) == -1)
30090267Sdes			continue;
301111816Sdes		if (bindaddr != NULL && *bindaddr != '\0' &&
302174588Sdes		    fetch_bind(sd, res->ai_family, bindaddr) != 0) {
303174588Sdes			fetch_info("failed to bind to '%s'", bindaddr);
304111816Sdes			close(sd);
305111816Sdes			continue;
306111816Sdes		}
307210568Sdes		if (connect(sd, res->ai_addr, res->ai_addrlen) == 0 &&
308210568Sdes		    fcntl(sd, F_SETFL, O_NONBLOCK) == 0)
30990267Sdes			break;
31090267Sdes		close(sd);
31190267Sdes	}
31290267Sdes	freeaddrinfo(res0);
31390267Sdes	if (sd == -1) {
314174588Sdes		fetch_syserr();
31597856Sdes		return (NULL);
31690267Sdes	}
31740939Sdes
318174588Sdes	if ((conn = fetch_reopen(sd)) == NULL) {
319174588Sdes		fetch_syserr();
32097856Sdes		close(sd);
321103459Sfenner	}
32297856Sdes	return (conn);
32340939Sdes}
32441989Sdes
325253680Sdes#ifdef WITH_SSL
326253680Sdes/*
327253680Sdes * Convert characters A-Z to lowercase (intentionally avoid any locale
328253680Sdes * specific conversions).
329253680Sdes */
330253680Sdesstatic char
331253680Sdesfetch_ssl_tolower(char in)
332253680Sdes{
333253680Sdes	if (in >= 'A' && in <= 'Z')
334253680Sdes		return (in + 32);
335253680Sdes	else
336253680Sdes		return (in);
337253680Sdes}
33841989Sdes
33955557Sdes/*
340253680Sdes * isalpha implementation that intentionally avoids any locale specific
341253680Sdes * conversions.
342253680Sdes */
343253680Sdesstatic int
344253680Sdesfetch_ssl_isalpha(char in)
345253680Sdes{
346253680Sdes	return ((in >= 'A' && in <= 'Z') || (in >= 'a' && in <= 'z'));
347253680Sdes}
348253680Sdes
349253680Sdes/*
350253680Sdes * Check if passed hostnames a and b are equal.
351253680Sdes */
352253680Sdesstatic int
353253680Sdesfetch_ssl_hname_equal(const char *a, size_t alen, const char *b,
354253680Sdes    size_t blen)
355253680Sdes{
356253680Sdes	size_t i;
357253680Sdes
358253680Sdes	if (alen != blen)
359253680Sdes		return (0);
360253680Sdes	for (i = 0; i < alen; ++i) {
361253680Sdes		if (fetch_ssl_tolower(a[i]) != fetch_ssl_tolower(b[i]))
362253680Sdes			return (0);
363253680Sdes	}
364253680Sdes	return (1);
365253680Sdes}
366253680Sdes
367253680Sdes/*
368253680Sdes * Check if domain label is traditional, meaning that only A-Z, a-z, 0-9
369253680Sdes * and '-' (hyphen) are allowed. Hyphens have to be surrounded by alpha-
370253680Sdes * numeric characters. Double hyphens (like they're found in IDN a-labels
371253680Sdes * 'xn--') are not allowed. Empty labels are invalid.
372253680Sdes */
373253680Sdesstatic int
374253680Sdesfetch_ssl_is_trad_domain_label(const char *l, size_t len, int wcok)
375253680Sdes{
376253680Sdes	size_t i;
377253680Sdes
378253680Sdes	if (!len || l[0] == '-' || l[len-1] == '-')
379253680Sdes		return (0);
380253680Sdes	for (i = 0; i < len; ++i) {
381253680Sdes		if (!isdigit(l[i]) &&
382253680Sdes		    !fetch_ssl_isalpha(l[i]) &&
383253680Sdes		    !(l[i] == '*' && wcok) &&
384253680Sdes		    !(l[i] == '-' && l[i - 1] != '-'))
385253680Sdes			return (0);
386253680Sdes	}
387253680Sdes	return (1);
388253680Sdes}
389253680Sdes
390253680Sdes/*
391253680Sdes * Check if host name consists only of numbers. This might indicate an IP
392253680Sdes * address, which is not a good idea for CN wildcard comparison.
393253680Sdes */
394253680Sdesstatic int
395253680Sdesfetch_ssl_hname_is_only_numbers(const char *hostname, size_t len)
396253680Sdes{
397253680Sdes	size_t i;
398253680Sdes
399253680Sdes	for (i = 0; i < len; ++i) {
400253680Sdes		if (!((hostname[i] >= '0' && hostname[i] <= '9') ||
401253680Sdes		    hostname[i] == '.'))
402253680Sdes			return (0);
403253680Sdes	}
404253680Sdes	return (1);
405253680Sdes}
406253680Sdes
407253680Sdes/*
408253680Sdes * Check if the host name h passed matches the pattern passed in m which
409253680Sdes * is usually part of subjectAltName or CN of a certificate presented to
410253680Sdes * the client. This includes wildcard matching. The algorithm is based on
411253680Sdes * RFC6125, sections 6.4.3 and 7.2, which clarifies RFC2818 and RFC3280.
412253680Sdes */
413253680Sdesstatic int
414253680Sdesfetch_ssl_hname_match(const char *h, size_t hlen, const char *m,
415253680Sdes    size_t mlen)
416253680Sdes{
417253680Sdes	int delta, hdotidx, mdot1idx, wcidx;
418253680Sdes	const char *hdot, *mdot1, *mdot2;
419253680Sdes	const char *wc; /* wildcard */
420253680Sdes
421253680Sdes	if (!(h && *h && m && *m))
422253680Sdes		return (0);
423253680Sdes	if ((wc = strnstr(m, "*", mlen)) == NULL)
424253680Sdes		return (fetch_ssl_hname_equal(h, hlen, m, mlen));
425253680Sdes	wcidx = wc - m;
426253680Sdes	/* hostname should not be just dots and numbers */
427253680Sdes	if (fetch_ssl_hname_is_only_numbers(h, hlen))
428253680Sdes		return (0);
429253680Sdes	/* only one wildcard allowed in pattern */
430253680Sdes	if (strnstr(wc + 1, "*", mlen - wcidx - 1) != NULL)
431253680Sdes		return (0);
432253680Sdes	/*
433253680Sdes	 * there must be at least two more domain labels and
434253680Sdes	 * wildcard has to be in the leftmost label (RFC6125)
435253680Sdes	 */
436253680Sdes	mdot1 = strnstr(m, ".", mlen);
437253680Sdes	if (mdot1 == NULL || mdot1 < wc || (mlen - (mdot1 - m)) < 4)
438253680Sdes		return (0);
439253680Sdes	mdot1idx = mdot1 - m;
440253680Sdes	mdot2 = strnstr(mdot1 + 1, ".", mlen - mdot1idx - 1);
441253680Sdes	if (mdot2 == NULL || (mlen - (mdot2 - m)) < 2)
442253680Sdes		return (0);
443253680Sdes	/* hostname must contain a dot and not be the 1st char */
444253680Sdes	hdot = strnstr(h, ".", hlen);
445253680Sdes	if (hdot == NULL || hdot == h)
446253680Sdes		return (0);
447253680Sdes	hdotidx = hdot - h;
448253680Sdes	/*
449253680Sdes	 * host part of hostname must be at least as long as
450253680Sdes	 * pattern it's supposed to match
451253680Sdes	 */
452253680Sdes	if (hdotidx < mdot1idx)
453253680Sdes		return (0);
454253680Sdes	/*
455253680Sdes	 * don't allow wildcards in non-traditional domain names
456253680Sdes	 * (IDN, A-label, U-label...)
457253680Sdes	 */
458253680Sdes	if (!fetch_ssl_is_trad_domain_label(h, hdotidx, 0) ||
459253680Sdes	    !fetch_ssl_is_trad_domain_label(m, mdot1idx, 1))
460253680Sdes		return (0);
461253680Sdes	/* match domain part (part after first dot) */
462253680Sdes	if (!fetch_ssl_hname_equal(hdot, hlen - hdotidx, mdot1,
463253680Sdes	    mlen - mdot1idx))
464253680Sdes		return (0);
465253680Sdes	/* match part left of wildcard */
466253680Sdes	if (!fetch_ssl_hname_equal(h, wcidx, m, wcidx))
467253680Sdes		return (0);
468253680Sdes	/* match part right of wildcard */
469253680Sdes	delta = mdot1idx - wcidx - 1;
470253680Sdes	if (!fetch_ssl_hname_equal(hdot - delta, delta,
471253680Sdes	    mdot1 - delta, delta))
472253680Sdes		return (0);
473253680Sdes	/* all tests succeded, it's a match */
474253680Sdes	return (1);
475253680Sdes}
476253680Sdes
477253680Sdes/*
478253680Sdes * Get numeric host address info - returns NULL if host was not an IP
479253680Sdes * address. The caller is responsible for deallocation using
480253680Sdes * freeaddrinfo(3).
481253680Sdes */
482253680Sdesstatic struct addrinfo *
483253680Sdesfetch_ssl_get_numeric_addrinfo(const char *hostname, size_t len)
484253680Sdes{
485253680Sdes	struct addrinfo hints, *res;
486253680Sdes	char *host;
487253680Sdes
488253680Sdes	host = (char *)malloc(len + 1);
489253680Sdes	memcpy(host, hostname, len);
490253680Sdes	host[len] = '\0';
491253680Sdes	memset(&hints, 0, sizeof(hints));
492253680Sdes	hints.ai_family = PF_UNSPEC;
493253680Sdes	hints.ai_socktype = SOCK_STREAM;
494253680Sdes	hints.ai_protocol = 0;
495253680Sdes	hints.ai_flags = AI_NUMERICHOST;
496253680Sdes	/* port is not relevant for this purpose */
497253680Sdes	getaddrinfo(host, "443", &hints, &res);
498253680Sdes	free(host);
499253680Sdes	return res;
500253680Sdes}
501253680Sdes
502253680Sdes/*
503253680Sdes * Compare ip address in addrinfo with address passes.
504253680Sdes */
505253680Sdesstatic int
506253680Sdesfetch_ssl_ipaddr_match_bin(const struct addrinfo *lhost, const char *rhost,
507253680Sdes    size_t rhostlen)
508253680Sdes{
509253680Sdes	const void *left;
510253680Sdes
511253680Sdes	if (lhost->ai_family == AF_INET && rhostlen == 4) {
512253680Sdes		left = (void *)&((struct sockaddr_in*)(void *)
513253680Sdes		    lhost->ai_addr)->sin_addr.s_addr;
514253680Sdes#ifdef INET6
515253680Sdes	} else if (lhost->ai_family == AF_INET6 && rhostlen == 16) {
516253680Sdes		left = (void *)&((struct sockaddr_in6 *)(void *)
517253680Sdes		    lhost->ai_addr)->sin6_addr;
518253680Sdes#endif
519253680Sdes	} else
520253680Sdes		return (0);
521253680Sdes	return (!memcmp(left, (const void *)rhost, rhostlen) ? 1 : 0);
522253680Sdes}
523253680Sdes
524253680Sdes/*
525253680Sdes * Compare ip address in addrinfo with host passed. If host is not an IP
526253680Sdes * address, comparison will fail.
527253680Sdes */
528253680Sdesstatic int
529253680Sdesfetch_ssl_ipaddr_match(const struct addrinfo *laddr, const char *r,
530253680Sdes    size_t rlen)
531253680Sdes{
532253680Sdes	struct addrinfo *raddr;
533253680Sdes	int ret;
534253680Sdes	char *rip;
535253680Sdes
536253680Sdes	ret = 0;
537253680Sdes	if ((raddr = fetch_ssl_get_numeric_addrinfo(r, rlen)) == NULL)
538253680Sdes		return 0; /* not a numeric host */
539253680Sdes
540253680Sdes	if (laddr->ai_family == raddr->ai_family) {
541253680Sdes		if (laddr->ai_family == AF_INET) {
542253680Sdes			rip = (char *)&((struct sockaddr_in *)(void *)
543253680Sdes			    raddr->ai_addr)->sin_addr.s_addr;
544253680Sdes			ret = fetch_ssl_ipaddr_match_bin(laddr, rip, 4);
545253680Sdes#ifdef INET6
546253680Sdes		} else if (laddr->ai_family == AF_INET6) {
547253680Sdes			rip = (char *)&((struct sockaddr_in6 *)(void *)
548253680Sdes			    raddr->ai_addr)->sin6_addr;
549253680Sdes			ret = fetch_ssl_ipaddr_match_bin(laddr, rip, 16);
550253680Sdes#endif
551253680Sdes		}
552253680Sdes
553253680Sdes	}
554253680Sdes	freeaddrinfo(raddr);
555253680Sdes	return (ret);
556253680Sdes}
557253680Sdes
558253680Sdes/*
559253680Sdes * Verify server certificate by subjectAltName.
560253680Sdes */
561253680Sdesstatic int
562253680Sdesfetch_ssl_verify_altname(STACK_OF(GENERAL_NAME) *altnames,
563253680Sdes    const char *host, struct addrinfo *ip)
564253680Sdes{
565253680Sdes	const GENERAL_NAME *name;
566253680Sdes	size_t nslen;
567253680Sdes	int i;
568253680Sdes	const char *ns;
569253680Sdes
570253680Sdes	for (i = 0; i < sk_GENERAL_NAME_num(altnames); ++i) {
571253680Sdes#if OPENSSL_VERSION_NUMBER < 0x10000000L
572253680Sdes		/*
573253680Sdes		 * This is a workaround, since the following line causes
574253680Sdes		 * alignment issues in clang:
575253680Sdes		 * name = sk_GENERAL_NAME_value(altnames, i);
576253680Sdes		 * OpenSSL explicitly warns not to use those macros
577253680Sdes		 * directly, but there isn't much choice (and there
578253680Sdes		 * shouldn't be any ill side effects)
579253680Sdes		 */
580253680Sdes		name = (GENERAL_NAME *)SKM_sk_value(void, altnames, i);
581253680Sdes#else
582253680Sdes		name = sk_GENERAL_NAME_value(altnames, i);
583253680Sdes#endif
584253680Sdes		ns = (const char *)ASN1_STRING_data(name->d.ia5);
585253680Sdes		nslen = (size_t)ASN1_STRING_length(name->d.ia5);
586253680Sdes
587253680Sdes		if (name->type == GEN_DNS && ip == NULL &&
588253680Sdes		    fetch_ssl_hname_match(host, strlen(host), ns, nslen))
589253680Sdes			return (1);
590253680Sdes		else if (name->type == GEN_IPADD && ip != NULL &&
591253680Sdes		    fetch_ssl_ipaddr_match_bin(ip, ns, nslen))
592253680Sdes			return (1);
593253680Sdes	}
594253680Sdes	return (0);
595253680Sdes}
596253680Sdes
597253680Sdes/*
598253680Sdes * Verify server certificate by CN.
599253680Sdes */
600253680Sdesstatic int
601253680Sdesfetch_ssl_verify_cn(X509_NAME *subject, const char *host,
602253680Sdes    struct addrinfo *ip)
603253680Sdes{
604253680Sdes	ASN1_STRING *namedata;
605253680Sdes	X509_NAME_ENTRY *nameentry;
606253680Sdes	int cnlen, lastpos, loc, ret;
607253680Sdes	unsigned char *cn;
608253680Sdes
609253680Sdes	ret = 0;
610253680Sdes	lastpos = -1;
611253680Sdes	loc = -1;
612253680Sdes	cn = NULL;
613253680Sdes	/* get most specific CN (last entry in list) and compare */
614253680Sdes	while ((lastpos = X509_NAME_get_index_by_NID(subject,
615253680Sdes	    NID_commonName, lastpos)) != -1)
616253680Sdes		loc = lastpos;
617253680Sdes
618253680Sdes	if (loc > -1) {
619253680Sdes		nameentry = X509_NAME_get_entry(subject, loc);
620253680Sdes		namedata = X509_NAME_ENTRY_get_data(nameentry);
621253680Sdes		cnlen = ASN1_STRING_to_UTF8(&cn, namedata);
622253680Sdes		if (ip == NULL &&
623253680Sdes		    fetch_ssl_hname_match(host, strlen(host), cn, cnlen))
624253680Sdes			ret = 1;
625253680Sdes		else if (ip != NULL && fetch_ssl_ipaddr_match(ip, cn, cnlen))
626253680Sdes			ret = 1;
627253680Sdes		OPENSSL_free(cn);
628253680Sdes	}
629253680Sdes	return (ret);
630253680Sdes}
631253680Sdes
632253680Sdes/*
633253680Sdes * Verify that server certificate subjectAltName/CN matches
634253680Sdes * hostname. First check, if there are alternative subject names. If yes,
635253680Sdes * those have to match. Only if those don't exist it falls back to
636253680Sdes * checking the subject's CN.
637253680Sdes */
638253680Sdesstatic int
639253680Sdesfetch_ssl_verify_hname(X509 *cert, const char *host)
640253680Sdes{
641253680Sdes	struct addrinfo *ip;
642253680Sdes	STACK_OF(GENERAL_NAME) *altnames;
643253680Sdes	X509_NAME *subject;
644253680Sdes	int ret;
645253680Sdes
646253680Sdes	ret = 0;
647253680Sdes	ip = fetch_ssl_get_numeric_addrinfo(host, strlen(host));
648253680Sdes	altnames = X509_get_ext_d2i(cert, NID_subject_alt_name,
649253680Sdes	    NULL, NULL);
650253680Sdes
651253680Sdes	if (altnames != NULL) {
652253680Sdes		ret = fetch_ssl_verify_altname(altnames, host, ip);
653253680Sdes	} else {
654253680Sdes		subject = X509_get_subject_name(cert);
655253680Sdes		if (subject != NULL)
656253680Sdes			ret = fetch_ssl_verify_cn(subject, host, ip);
657253680Sdes	}
658253680Sdes
659253680Sdes	if (ip != NULL)
660253680Sdes		freeaddrinfo(ip);
661253680Sdes	if (altnames != NULL)
662253680Sdes		GENERAL_NAMES_free(altnames);
663253680Sdes	return (ret);
664253680Sdes}
665253680Sdes
666253680Sdes/*
667253680Sdes * Configure transport security layer based on environment.
668253680Sdes */
669253680Sdesstatic void
670253680Sdesfetch_ssl_setup_transport_layer(SSL_CTX *ctx, int verbose)
671253680Sdes{
672253680Sdes	long ssl_ctx_options;
673253680Sdes
674253680Sdes	ssl_ctx_options = SSL_OP_ALL | SSL_OP_NO_TICKET;
675253680Sdes	if (getenv("SSL_ALLOW_SSL2") == NULL)
676253680Sdes		ssl_ctx_options |= SSL_OP_NO_SSLv2;
677253680Sdes	if (getenv("SSL_NO_SSL3") != NULL)
678253680Sdes		ssl_ctx_options |= SSL_OP_NO_SSLv3;
679253680Sdes	if (getenv("SSL_NO_TLS1") != NULL)
680253680Sdes		ssl_ctx_options |= SSL_OP_NO_TLSv1;
681253680Sdes	if (verbose)
682253680Sdes		fetch_info("SSL options: %x", ssl_ctx_options);
683253680Sdes	SSL_CTX_set_options(ctx, ssl_ctx_options);
684253680Sdes}
685253680Sdes
686253680Sdes
687253680Sdes/*
688253680Sdes * Configure peer verification based on environment.
689253680Sdes */
690253680Sdesstatic int
691253680Sdesfetch_ssl_setup_peer_verification(SSL_CTX *ctx, int verbose)
692253680Sdes{
693253680Sdes	X509_LOOKUP *crl_lookup;
694253680Sdes	X509_STORE *crl_store;
695253680Sdes	const char *ca_cert_file, *ca_cert_path, *crl_file;
696253680Sdes
697253680Sdes	if (getenv("SSL_NO_VERIFY_PEER") == NULL) {
698253680Sdes		ca_cert_file = getenv("SSL_CA_CERT_FILE") != NULL ?
699253680Sdes		    getenv("SSL_CA_CERT_FILE") : "/etc/ssl/cert.pem";
700253680Sdes		ca_cert_path = getenv("SSL_CA_CERT_PATH");
701253680Sdes		if (verbose) {
702253680Sdes			fetch_info("Peer verification enabled");
703253680Sdes			if (ca_cert_file != NULL)
704253680Sdes				fetch_info("Using CA cert file: %s",
705253680Sdes				    ca_cert_file);
706253680Sdes			if (ca_cert_path != NULL)
707253680Sdes				fetch_info("Using CA cert path: %s",
708253680Sdes				    ca_cert_path);
709253680Sdes		}
710253680Sdes		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER,
711253680Sdes		    fetch_ssl_cb_verify_crt);
712253680Sdes		SSL_CTX_load_verify_locations(ctx, ca_cert_file,
713253680Sdes		    ca_cert_path);
714253680Sdes		if ((crl_file = getenv("SSL_CRL_FILE")) != NULL) {
715253680Sdes			if (verbose)
716253680Sdes				fetch_info("Using CRL file: %s", crl_file);
717253680Sdes			crl_store = SSL_CTX_get_cert_store(ctx);
718253680Sdes			crl_lookup = X509_STORE_add_lookup(crl_store,
719253680Sdes			    X509_LOOKUP_file());
720253680Sdes			if (crl_lookup == NULL ||
721253680Sdes			    !X509_load_crl_file(crl_lookup, crl_file,
722253680Sdes				X509_FILETYPE_PEM)) {
723253680Sdes				fprintf(stderr,
724253680Sdes				    "Could not load CRL file %s\n",
725253680Sdes				    crl_file);
726253680Sdes				return (0);
727253680Sdes			}
728253680Sdes			X509_STORE_set_flags(crl_store,
729253680Sdes			    X509_V_FLAG_CRL_CHECK |
730253680Sdes			    X509_V_FLAG_CRL_CHECK_ALL);
731253680Sdes		}
732253680Sdes	}
733253680Sdes	return (1);
734253680Sdes}
735253680Sdes
736253680Sdes/*
737253680Sdes * Configure client certificate based on environment.
738253680Sdes */
739253680Sdesstatic int
740253680Sdesfetch_ssl_setup_client_certificate(SSL_CTX *ctx, int verbose)
741253680Sdes{
742253680Sdes	const char *client_cert_file, *client_key_file;
743253680Sdes
744253680Sdes	if ((client_cert_file = getenv("SSL_CLIENT_CERT_FILE")) != NULL) {
745253680Sdes		client_key_file = getenv("SSL_CLIENT_KEY_FILE") != NULL ?
746253680Sdes		    getenv("SSL_CLIENT_KEY_FILE") : client_cert_file;
747253680Sdes		if (verbose) {
748253680Sdes			fetch_info("Using client cert file: %s",
749253680Sdes			    client_cert_file);
750253680Sdes			fetch_info("Using client key file: %s",
751253680Sdes			    client_key_file);
752253680Sdes		}
753253680Sdes		if (SSL_CTX_use_certificate_chain_file(ctx,
754253680Sdes			client_cert_file) != 1) {
755253680Sdes			fprintf(stderr,
756253680Sdes			    "Could not load client certificate %s\n",
757253680Sdes			    client_cert_file);
758253680Sdes			return (0);
759253680Sdes		}
760253680Sdes		if (SSL_CTX_use_PrivateKey_file(ctx, client_key_file,
761253680Sdes			SSL_FILETYPE_PEM) != 1) {
762253680Sdes			fprintf(stderr,
763253680Sdes			    "Could not load client key %s\n",
764253680Sdes			    client_key_file);
765253680Sdes			return (0);
766253680Sdes		}
767253680Sdes	}
768253680Sdes	return (1);
769253680Sdes}
770253680Sdes
771253680Sdes/*
772253680Sdes * Callback for SSL certificate verification, this is called on server
773253680Sdes * cert verification. It takes no decision, but informs the user in case
774253680Sdes * verification failed.
775253680Sdes */
776253680Sdesint
777253680Sdesfetch_ssl_cb_verify_crt(int verified, X509_STORE_CTX *ctx)
778253680Sdes{
779253680Sdes	X509 *crt;
780253680Sdes	X509_NAME *name;
781253680Sdes	char *str;
782253680Sdes
783253680Sdes	str = NULL;
784253680Sdes	if (!verified) {
785253680Sdes		if ((crt = X509_STORE_CTX_get_current_cert(ctx)) != NULL &&
786253680Sdes		    (name = X509_get_subject_name(crt)) != NULL)
787253680Sdes			str = X509_NAME_oneline(name, 0, 0);
788253680Sdes		fprintf(stderr, "Certificate verification failed for %s\n",
789253680Sdes		    str != NULL ? str : "no relevant certificate");
790253680Sdes		OPENSSL_free(str);
791253680Sdes	}
792253680Sdes	return (verified);
793253680Sdes}
794253680Sdes
795253680Sdes#endif
796253680Sdes
797253680Sdes/*
79897868Sdes * Enable SSL on a connection.
79997868Sdes */
80097868Sdesint
801253680Sdesfetch_ssl(conn_t *conn, const struct url *URL, int verbose)
80297868Sdes{
803214256Semaste#ifdef WITH_SSL
804210568Sdes	int ret, ssl_err;
805253680Sdes	X509_NAME *name;
806253680Sdes	char *str;
80797868Sdes
80897868Sdes	/* Init the SSL library and context */
80997868Sdes	if (!SSL_library_init()){
81097868Sdes		fprintf(stderr, "SSL library init failed\n");
81197868Sdes		return (-1);
81297868Sdes	}
81397868Sdes
81497868Sdes	SSL_load_error_strings();
81597868Sdes
81697868Sdes	conn->ssl_meth = SSLv23_client_method();
81797868Sdes	conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
818108579Sdes	SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
81997868Sdes
820253680Sdes	fetch_ssl_setup_transport_layer(conn->ssl_ctx, verbose);
821253680Sdes	if (!fetch_ssl_setup_peer_verification(conn->ssl_ctx, verbose))
822253680Sdes		return (-1);
823253680Sdes	if (!fetch_ssl_setup_client_certificate(conn->ssl_ctx, verbose))
824253680Sdes		return (-1);
825253680Sdes
82697868Sdes	conn->ssl = SSL_new(conn->ssl_ctx);
827253680Sdes	if (conn->ssl == NULL) {
82897868Sdes		fprintf(stderr, "SSL context creation failed\n");
82997868Sdes		return (-1);
83097868Sdes	}
83197868Sdes	SSL_set_fd(conn->ssl, conn->sd);
832258347Sbdrewery
833258347Sbdrewery#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
834258347Sbdrewery	if (!SSL_set_tlsext_host_name(conn->ssl, URL->host)) {
835258347Sbdrewery		fprintf(stderr,
836258347Sbdrewery		    "TLS server name indication extension failed for host %s\n",
837258347Sbdrewery		    URL->host);
838258347Sbdrewery		return (-1);
839258347Sbdrewery	}
840258347Sbdrewery#endif
841210568Sdes	while ((ret = SSL_connect(conn->ssl)) == -1) {
842210568Sdes		ssl_err = SSL_get_error(conn->ssl, ret);
843210568Sdes		if (ssl_err != SSL_ERROR_WANT_READ &&
844210568Sdes		    ssl_err != SSL_ERROR_WANT_WRITE) {
845210568Sdes			ERR_print_errors_fp(stderr);
846210568Sdes			return (-1);
847210568Sdes		}
84897868Sdes	}
849253680Sdes	conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
85097868Sdes
851253680Sdes	if (conn->ssl_cert == NULL) {
852253680Sdes		fprintf(stderr, "No server SSL certificate\n");
853253680Sdes		return (-1);
854253680Sdes	}
855253680Sdes
856253680Sdes	if (getenv("SSL_NO_VERIFY_HOSTNAME") == NULL) {
857253680Sdes		if (verbose)
858253680Sdes			fetch_info("Verify hostname");
859253680Sdes		if (!fetch_ssl_verify_hname(conn->ssl_cert, URL->host)) {
860253680Sdes			fprintf(stderr,
861253680Sdes			    "SSL certificate subject doesn't match host %s\n",
862253680Sdes			    URL->host);
863253680Sdes			return (-1);
864253680Sdes		}
865253680Sdes	}
866253680Sdes
86797868Sdes	if (verbose) {
868253680Sdes		fetch_info("SSL connection established using %s",
86997868Sdes		    SSL_get_cipher(conn->ssl));
87097868Sdes		name = X509_get_subject_name(conn->ssl_cert);
87197868Sdes		str = X509_NAME_oneline(name, 0, 0);
872253680Sdes		fetch_info("Certificate subject: %s", str);
873253680Sdes		OPENSSL_free(str);
87497868Sdes		name = X509_get_issuer_name(conn->ssl_cert);
87597868Sdes		str = X509_NAME_oneline(name, 0, 0);
876253680Sdes		fetch_info("Certificate issuer: %s", str);
877253680Sdes		OPENSSL_free(str);
87897868Sdes	}
87997868Sdes
88097868Sdes	return (0);
88197891Sdes#else
88297891Sdes	(void)conn;
88397891Sdes	(void)verbose;
88497891Sdes	fprintf(stderr, "SSL support disabled\n");
88597891Sdes	return (-1);
88697891Sdes#endif
88797868Sdes}
88897868Sdes
889210568Sdes#define FETCH_READ_WAIT		-2
890210568Sdes#define FETCH_READ_ERROR	-1
891210568Sdes#define FETCH_READ_DONE		 0
89298117Sdes
893210568Sdes#ifdef WITH_SSL
894210568Sdesstatic ssize_t
895210568Sdesfetch_ssl_read(SSL *ssl, char *buf, size_t len)
896210568Sdes{
897210568Sdes	ssize_t rlen;
898210568Sdes	int ssl_err;
899210568Sdes
900210568Sdes	rlen = SSL_read(ssl, buf, len);
901210568Sdes	if (rlen < 0) {
902210568Sdes		ssl_err = SSL_get_error(ssl, rlen);
903210568Sdes		if (ssl_err == SSL_ERROR_WANT_READ ||
904210568Sdes		    ssl_err == SSL_ERROR_WANT_WRITE) {
905210568Sdes			return (FETCH_READ_WAIT);
906210568Sdes		} else {
907210568Sdes			ERR_print_errors_fp(stderr);
908210568Sdes			return (FETCH_READ_ERROR);
909210568Sdes		}
910210568Sdes	}
911210568Sdes	return (rlen);
912210568Sdes}
913210568Sdes#endif
914210568Sdes
915230307Sdes/*
916230307Sdes * Cache some data that was read from a socket but cannot be immediately
917230307Sdes * returned because of an interrupted system call.
918230307Sdes */
919230307Sdesstatic int
920230307Sdesfetch_cache_data(conn_t *conn, char *src, size_t nbytes)
921230307Sdes{
922230307Sdes	char *tmp;
923230307Sdes
924230307Sdes	if (conn->cache.size < nbytes) {
925230307Sdes		tmp = realloc(conn->cache.buf, nbytes);
926230307Sdes		if (tmp == NULL) {
927230307Sdes			fetch_syserr();
928230307Sdes			return (-1);
929230307Sdes		}
930230307Sdes		conn->cache.buf = tmp;
931230307Sdes		conn->cache.size = nbytes;
932230307Sdes	}
933230307Sdes
934230307Sdes	memcpy(conn->cache.buf, src, nbytes);
935230307Sdes	conn->cache.len = nbytes;
936230307Sdes	conn->cache.pos = 0;
937230307Sdes
938230307Sdes	return (0);
939230307Sdes}
940230307Sdes
941230307Sdes
942210568Sdesstatic ssize_t
943210568Sdesfetch_socket_read(int sd, char *buf, size_t len)
944210568Sdes{
945210568Sdes	ssize_t rlen;
946210568Sdes
947210568Sdes	rlen = read(sd, buf, len);
948210568Sdes	if (rlen < 0) {
949210568Sdes		if (errno == EAGAIN || (errno == EINTR && fetchRestartCalls))
950210568Sdes			return (FETCH_READ_WAIT);
951210568Sdes		else
952210568Sdes			return (FETCH_READ_ERROR);
953210568Sdes	}
954210568Sdes	return (rlen);
955210568Sdes}
956210568Sdes
95797868Sdes/*
95897866Sdes * Read a character from a connection w/ timeout
95955557Sdes */
96097866Sdesssize_t
961174588Sdesfetch_read(conn_t *conn, char *buf, size_t len)
96255557Sdes{
963177447Sdes	struct timeval now, timeout, delta;
96490267Sdes	fd_set readfds;
96597866Sdes	ssize_t rlen, total;
966230307Sdes	char *start;
96790267Sdes
968234837Sdes	if (fetchTimeout > 0) {
96990267Sdes		gettimeofday(&timeout, NULL);
97090267Sdes		timeout.tv_sec += fetchTimeout;
97155557Sdes	}
97290267Sdes
97397866Sdes	total = 0;
974230307Sdes	start = buf;
975230307Sdes
976230307Sdes	if (conn->cache.len > 0) {
977230307Sdes		/*
978230307Sdes		 * The last invocation of fetch_read was interrupted by a
979230307Sdes		 * signal after some data had been read from the socket. Copy
980230307Sdes		 * the cached data into the supplied buffer before trying to
981230307Sdes		 * read from the socket again.
982230307Sdes		 */
983230307Sdes		total = (conn->cache.len < len) ? conn->cache.len : len;
984230307Sdes		memcpy(buf, conn->cache.buf, total);
985230307Sdes
986230307Sdes		conn->cache.len -= total;
987230307Sdes		conn->cache.pos += total;
988230307Sdes		len -= total;
989230478Sdes		buf += total;
990230307Sdes	}
991230307Sdes
99297866Sdes	while (len > 0) {
993210568Sdes		/*
994210568Sdes		 * The socket is non-blocking.  Instead of the canonical
995210568Sdes		 * select() -> read(), we do the following:
996210568Sdes		 *
997210568Sdes		 * 1) call read() or SSL_read().
998210568Sdes		 * 2) if an error occurred, return -1.
999210568Sdes		 * 3) if we received data but we still expect more,
1000210568Sdes		 *    update our counters and loop.
1001210568Sdes		 * 4) if read() or SSL_read() signaled EOF, return.
1002210568Sdes		 * 5) if we did not receive any data but we're not at EOF,
1003210568Sdes		 *    call select().
1004210568Sdes		 *
1005210568Sdes		 * In the SSL case, this is necessary because if we
1006210568Sdes		 * receive a close notification, we have to call
1007210568Sdes		 * SSL_read() one additional time after we've read
1008210568Sdes		 * everything we received.
1009210568Sdes		 *
1010210568Sdes		 * In the non-SSL case, it may improve performance (very
1011210568Sdes		 * slightly) when reading small amounts of data.
1012210568Sdes		 */
1013210568Sdes#ifdef WITH_SSL
1014210568Sdes		if (conn->ssl != NULL)
1015210568Sdes			rlen = fetch_ssl_read(conn->ssl, buf, len);
1016210568Sdes		else
1017210568Sdes#endif
1018210568Sdes			rlen = fetch_socket_read(conn->sd, buf, len);
1019210568Sdes		if (rlen == 0) {
1020210568Sdes			break;
1021210568Sdes		} else if (rlen > 0) {
1022210568Sdes			len -= rlen;
1023210568Sdes			buf += rlen;
1024210568Sdes			total += rlen;
1025210568Sdes			continue;
1026210568Sdes		} else if (rlen == FETCH_READ_ERROR) {
1027230307Sdes			if (errno == EINTR)
1028230307Sdes				fetch_cache_data(conn, start, total);
1029210568Sdes			return (-1);
1030210568Sdes		}
1031210568Sdes		// assert(rlen == FETCH_READ_WAIT);
1032234837Sdes		FD_ZERO(&readfds);
1033234837Sdes		while (!FD_ISSET(conn->sd, &readfds)) {
103497856Sdes			FD_SET(conn->sd, &readfds);
1035234837Sdes			if (fetchTimeout > 0) {
1036234837Sdes				gettimeofday(&now, NULL);
1037234837Sdes				if (!timercmp(&timeout, &now, >)) {
1038234837Sdes					errno = ETIMEDOUT;
1039234837Sdes					fetch_syserr();
1040234837Sdes					return (-1);
1041234837Sdes				}
1042234837Sdes				timersub(&timeout, &now, &delta);
104390267Sdes			}
104497866Sdes			errno = 0;
1045234837Sdes			if (select(conn->sd + 1, &readfds, NULL, NULL,
1046234837Sdes				fetchTimeout > 0 ? &delta : NULL) < 0) {
1047230307Sdes				if (errno == EINTR) {
1048230307Sdes					if (fetchRestartCalls)
1049230307Sdes						continue;
1050230307Sdes					/* Save anything that was read. */
1051230307Sdes					fetch_cache_data(conn, start, total);
1052230307Sdes				}
1053174588Sdes				fetch_syserr();
105490267Sdes				return (-1);
105590267Sdes			}
105690267Sdes		}
105797866Sdes	}
105897866Sdes	return (total);
105997866Sdes}
106097866Sdes
106198117Sdes
106297866Sdes/*
106397866Sdes * Read a line of text from a connection w/ timeout
106497866Sdes */
106597866Sdes#define MIN_BUF_SIZE 1024
106697866Sdes
106797866Sdesint
1068174588Sdesfetch_getln(conn_t *conn)
106997866Sdes{
107097866Sdes	char *tmp;
107197866Sdes	size_t tmpsize;
1072106186Sdes	ssize_t len;
107397866Sdes	char c;
107497866Sdes
107597866Sdes	if (conn->buf == NULL) {
107697866Sdes		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
107797866Sdes			errno = ENOMEM;
107897866Sdes			return (-1);
107997866Sdes		}
108097866Sdes		conn->bufsize = MIN_BUF_SIZE;
108197866Sdes	}
108297866Sdes
108397866Sdes	conn->buf[0] = '\0';
108497866Sdes	conn->buflen = 0;
108597866Sdes
108697866Sdes	do {
1087174588Sdes		len = fetch_read(conn, &c, 1);
1088106186Sdes		if (len == -1)
108997866Sdes			return (-1);
1090106186Sdes		if (len == 0)
1091106137Sobrien			break;
109297856Sdes		conn->buf[conn->buflen++] = c;
109397856Sdes		if (conn->buflen == conn->bufsize) {
109497856Sdes			tmp = conn->buf;
109597856Sdes			tmpsize = conn->bufsize * 2 + 1;
109697856Sdes			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
109790267Sdes				errno = ENOMEM;
109890267Sdes				return (-1);
109990267Sdes			}
110097856Sdes			conn->buf = tmp;
110197856Sdes			conn->bufsize = tmpsize;
110290267Sdes		}
110390267Sdes	} while (c != '\n');
110490267Sdes
110597856Sdes	conn->buf[conn->buflen] = '\0';
110697856Sdes	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
110790267Sdes	return (0);
110855557Sdes}
110955557Sdes
111055557Sdes
111162981Sdes/*
111297866Sdes * Write to a connection w/ timeout
111362981Sdes */
111497866Sdesssize_t
1115174588Sdesfetch_write(conn_t *conn, const char *buf, size_t len)
111697866Sdes{
1117106175Simp	struct iovec iov;
1118106175Simp
1119106175Simp	iov.iov_base = __DECONST(char *, buf);
1120106175Simp	iov.iov_len = len;
1121174588Sdes	return fetch_writev(conn, &iov, 1);
1122106175Simp}
1123106175Simp
1124106175Simp/*
1125106175Simp * Write a vector to a connection w/ timeout
1126106175Simp * Note: can modify the iovec.
1127106175Simp */
1128106175Simpssize_t
1129174588Sdesfetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
1130106175Simp{
1131177447Sdes	struct timeval now, timeout, delta;
113297866Sdes	fd_set writefds;
113397866Sdes	ssize_t wlen, total;
113497866Sdes	int r;
113597866Sdes
113697866Sdes	if (fetchTimeout) {
113797866Sdes		FD_ZERO(&writefds);
113897866Sdes		gettimeofday(&timeout, NULL);
113997866Sdes		timeout.tv_sec += fetchTimeout;
114097866Sdes	}
114197866Sdes
1142106175Simp	total = 0;
1143106175Simp	while (iovcnt > 0) {
114497866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
114597866Sdes			FD_SET(conn->sd, &writefds);
114697866Sdes			gettimeofday(&now, NULL);
1147177447Sdes			delta.tv_sec = timeout.tv_sec - now.tv_sec;
1148177447Sdes			delta.tv_usec = timeout.tv_usec - now.tv_usec;
1149177447Sdes			if (delta.tv_usec < 0) {
1150177447Sdes				delta.tv_usec += 1000000;
1151177447Sdes				delta.tv_sec--;
115297866Sdes			}
1153177447Sdes			if (delta.tv_sec < 0) {
115497866Sdes				errno = ETIMEDOUT;
1155174588Sdes				fetch_syserr();
115697866Sdes				return (-1);
115797866Sdes			}
115897866Sdes			errno = 0;
1159177447Sdes			r = select(conn->sd + 1, NULL, &writefds, NULL, &delta);
116097866Sdes			if (r == -1) {
116197866Sdes				if (errno == EINTR && fetchRestartCalls)
116297866Sdes					continue;
116397866Sdes				return (-1);
116497866Sdes			}
116597866Sdes		}
116697866Sdes		errno = 0;
116797891Sdes#ifdef WITH_SSL
116897866Sdes		if (conn->ssl != NULL)
1169106175Simp			wlen = SSL_write(conn->ssl,
1170106175Simp			    iov->iov_base, iov->iov_len);
117197866Sdes		else
117297891Sdes#endif
1173106175Simp			wlen = writev(conn->sd, iov, iovcnt);
1174106175Simp		if (wlen == 0) {
117597866Sdes			/* we consider a short write a failure */
1176210568Sdes			/* XXX perhaps we shouldn't in the SSL case */
1177106175Simp			errno = EPIPE;
1178174588Sdes			fetch_syserr();
117997866Sdes			return (-1);
1180106175Simp		}
118197866Sdes		if (wlen < 0) {
118297866Sdes			if (errno == EINTR && fetchRestartCalls)
118397866Sdes				continue;
118497866Sdes			return (-1);
118597866Sdes		}
118697866Sdes		total += wlen;
1187106175Simp		while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
1188106175Simp			wlen -= iov->iov_len;
1189106175Simp			iov++;
1190106175Simp			iovcnt--;
1191106175Simp		}
1192106175Simp		if (iovcnt > 0) {
1193106175Simp			iov->iov_len -= wlen;
1194106175Simp			iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;
1195106175Simp		}
119697866Sdes	}
119797866Sdes	return (total);
119897866Sdes}
119997866Sdes
120098117Sdes
120197866Sdes/*
120297866Sdes * Write a line of text to a connection w/ timeout
120397866Sdes */
120462981Sdesint
1205174588Sdesfetch_putln(conn_t *conn, const char *str, size_t len)
120662981Sdes{
1207106175Simp	struct iovec iov[2];
1208106205Sdes	int ret;
120998748Sdes
121098748Sdes	DEBUG(fprintf(stderr, ">>> %s\n", str));
1211106175Simp	iov[0].iov_base = __DECONST(char *, str);
1212106175Simp	iov[0].iov_len = len;
1213106175Simp	iov[1].iov_base = __DECONST(char *, ENDL);
1214109967Sdes	iov[1].iov_len = sizeof(ENDL);
1215106205Sdes	if (len == 0)
1216174588Sdes		ret = fetch_writev(conn, &iov[1], 1);
1217106205Sdes	else
1218174588Sdes		ret = fetch_writev(conn, iov, 2);
1219106205Sdes	if (ret == -1)
122090267Sdes		return (-1);
122190267Sdes	return (0);
122262981Sdes}
122362981Sdes
122462981Sdes
122597856Sdes/*
122697856Sdes * Close connection
122797856Sdes */
122897856Sdesint
1229174588Sdesfetch_close(conn_t *conn)
123097856Sdes{
123197856Sdes	int ret;
123297856Sdes
123398117Sdes	if (--conn->ref > 0)
123498117Sdes		return (0);
1235253680Sdes#ifdef WITH_SSL
1236253680Sdes	if (conn->ssl) {
1237253680Sdes		SSL_shutdown(conn->ssl);
1238253680Sdes		SSL_set_connect_state(conn->ssl);
1239253680Sdes		SSL_free(conn->ssl);
1240253680Sdes		conn->ssl = NULL;
1241253680Sdes	}
1242253680Sdes	if (conn->ssl_ctx) {
1243253680Sdes		SSL_CTX_free(conn->ssl_ctx);
1244253680Sdes		conn->ssl_ctx = NULL;
1245253680Sdes	}
1246253680Sdes	if (conn->ssl_cert) {
1247253680Sdes		X509_free(conn->ssl_cert);
1248253680Sdes		conn->ssl_cert = NULL;
1249253680Sdes	}
1250253680Sdes#endif
125197856Sdes	ret = close(conn->sd);
1252230307Sdes	free(conn->cache.buf);
1253141970Sdes	free(conn->buf);
125497856Sdes	free(conn);
125597856Sdes	return (ret);
125697856Sdes}
125797856Sdes
125897856Sdes
125941989Sdes/*** Directory-related utility functions *************************************/
126041989Sdes
126141989Sdesint
1262174588Sdesfetch_add_entry(struct url_ent **p, int *size, int *len,
126390267Sdes    const char *name, struct url_stat *us)
126441989Sdes{
126590267Sdes	struct url_ent *tmp;
126641989Sdes
126790267Sdes	if (*p == NULL) {
126890268Sdes		*size = 0;
126990267Sdes		*len = 0;
127041989Sdes	}
127141989Sdes
127290267Sdes	if (*len >= *size - 1) {
1273109967Sdes		tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
127490267Sdes		if (tmp == NULL) {
127590267Sdes			errno = ENOMEM;
1276174588Sdes			fetch_syserr();
127790267Sdes			return (-1);
127890267Sdes		}
127990268Sdes		*size = (*size * 2 + 1);
128090267Sdes		*p = tmp;
128190267Sdes	}
128241989Sdes
128390267Sdes	tmp = *p + *len;
128490267Sdes	snprintf(tmp->name, PATH_MAX, "%s", name);
1285176105Sdes	memcpy(&tmp->stat, us, sizeof(*us));
128641989Sdes
128790267Sdes	(*len)++;
128890267Sdes	(++tmp)->name[0] = 0;
128990267Sdes
129090267Sdes	return (0);
129141989Sdes}
1292109695Sdes
1293109695Sdes
1294109695Sdes/*** Authentication-related utility functions ********************************/
1295109695Sdes
1296109695Sdesstatic const char *
1297174588Sdesfetch_read_word(FILE *f)
1298109695Sdes{
1299109695Sdes	static char word[1024];
1300109695Sdes
1301178234Scperciva	if (fscanf(f, " %1023s ", word) != 1)
1302109695Sdes		return (NULL);
1303109695Sdes	return (word);
1304109695Sdes}
1305109695Sdes
1306109695Sdes/*
1307109695Sdes * Get authentication data for a URL from .netrc
1308109695Sdes */
1309109695Sdesint
1310174588Sdesfetch_netrc_auth(struct url *url)
1311109695Sdes{
1312109695Sdes	char fn[PATH_MAX];
1313109695Sdes	const char *word;
1314109695Sdes	char *p;
1315109695Sdes	FILE *f;
1316109695Sdes
1317109695Sdes	if ((p = getenv("NETRC")) != NULL) {
1318109967Sdes		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
1319174588Sdes			fetch_info("$NETRC specifies a file name "
1320109695Sdes			    "longer than PATH_MAX");
1321109695Sdes			return (-1);
1322109695Sdes		}
1323109695Sdes	} else {
1324109695Sdes		if ((p = getenv("HOME")) != NULL) {
1325109695Sdes			struct passwd *pwd;
1326109695Sdes
1327109695Sdes			if ((pwd = getpwuid(getuid())) == NULL ||
1328109695Sdes			    (p = pwd->pw_dir) == NULL)
1329109695Sdes				return (-1);
1330109695Sdes		}
1331109967Sdes		if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
1332109695Sdes			return (-1);
1333109695Sdes	}
1334109695Sdes
1335109695Sdes	if ((f = fopen(fn, "r")) == NULL)
1336109695Sdes		return (-1);
1337174588Sdes	while ((word = fetch_read_word(f)) != NULL) {
1338109695Sdes		if (strcmp(word, "default") == 0) {
1339174588Sdes			DEBUG(fetch_info("Using default .netrc settings"));
1340109695Sdes			break;
1341109695Sdes		}
1342109695Sdes		if (strcmp(word, "machine") == 0 &&
1343174588Sdes		    (word = fetch_read_word(f)) != NULL &&
1344109695Sdes		    strcasecmp(word, url->host) == 0) {
1345174588Sdes			DEBUG(fetch_info("Using .netrc settings for %s", word));
1346109695Sdes			break;
1347109695Sdes		}
1348109695Sdes	}
1349109695Sdes	if (word == NULL)
1350109695Sdes		goto ferr;
1351174588Sdes	while ((word = fetch_read_word(f)) != NULL) {
1352109695Sdes		if (strcmp(word, "login") == 0) {
1353174588Sdes			if ((word = fetch_read_word(f)) == NULL)
1354109695Sdes				goto ferr;
1355109967Sdes			if (snprintf(url->user, sizeof(url->user),
1356109960Sjwd				"%s", word) > (int)sizeof(url->user)) {
1357174588Sdes				fetch_info("login name in .netrc is too long");
1358109695Sdes				url->user[0] = '\0';
1359109695Sdes			}
1360109695Sdes		} else if (strcmp(word, "password") == 0) {
1361174588Sdes			if ((word = fetch_read_word(f)) == NULL)
1362109695Sdes				goto ferr;
1363109967Sdes			if (snprintf(url->pwd, sizeof(url->pwd),
1364109960Sjwd				"%s", word) > (int)sizeof(url->pwd)) {
1365174588Sdes				fetch_info("password in .netrc is too long");
1366109695Sdes				url->pwd[0] = '\0';
1367109695Sdes			}
1368109695Sdes		} else if (strcmp(word, "account") == 0) {
1369174588Sdes			if ((word = fetch_read_word(f)) == NULL)
1370109695Sdes				goto ferr;
1371109695Sdes			/* XXX not supported! */
1372109695Sdes		} else {
1373109695Sdes			break;
1374109695Sdes		}
1375109695Sdes	}
1376109695Sdes	fclose(f);
1377109695Sdes	return (0);
1378109695Sdes ferr:
1379109695Sdes	fclose(f);
1380109695Sdes	return (-1);
1381109695Sdes}
1382174752Sdes
1383174752Sdes/*
1384174752Sdes * The no_proxy environment variable specifies a set of domains for
1385174752Sdes * which the proxy should not be consulted; the contents is a comma-,
1386174752Sdes * or space-separated list of domain names.  A single asterisk will
1387174752Sdes * override all proxy variables and no transactions will be proxied
1388174752Sdes * (for compatability with lynx and curl, see the discussion at
1389174752Sdes * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
1390174752Sdes */
1391174752Sdesint
1392174752Sdesfetch_no_proxy_match(const char *host)
1393174752Sdes{
1394174752Sdes	const char *no_proxy, *p, *q;
1395174752Sdes	size_t h_len, d_len;
1396174752Sdes
1397174752Sdes	if ((no_proxy = getenv("NO_PROXY")) == NULL &&
1398174752Sdes	    (no_proxy = getenv("no_proxy")) == NULL)
1399174752Sdes		return (0);
1400174752Sdes
1401174752Sdes	/* asterisk matches any hostname */
1402174752Sdes	if (strcmp(no_proxy, "*") == 0)
1403174752Sdes		return (1);
1404174752Sdes
1405174752Sdes	h_len = strlen(host);
1406174752Sdes	p = no_proxy;
1407174752Sdes	do {
1408174752Sdes		/* position p at the beginning of a domain suffix */
1409174761Sdes		while (*p == ',' || isspace((unsigned char)*p))
1410174752Sdes			p++;
1411174752Sdes
1412174752Sdes		/* position q at the first separator character */
1413174752Sdes		for (q = p; *q; ++q)
1414174761Sdes			if (*q == ',' || isspace((unsigned char)*q))
1415174752Sdes				break;
1416174752Sdes
1417174752Sdes		d_len = q - p;
1418198339Sfabient		if (d_len > 0 && h_len >= d_len &&
1419174752Sdes		    strncasecmp(host + h_len - d_len,
1420174752Sdes			p, d_len) == 0) {
1421174752Sdes			/* domain name matches */
1422174752Sdes			return (1);
1423174752Sdes		}
1424174752Sdes
1425174752Sdes		p = q + 1;
1426174752Sdes	} while (*q);
1427174752Sdes
1428174752Sdes	return (0);
1429174752Sdes}
1430