common.c revision 178234
140939Sdes/*-
2135546Sdes * Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav
340939Sdes * All rights reserved.
440939Sdes *
540939Sdes * Redistribution and use in source and binary forms, with or without
640939Sdes * modification, are permitted provided that the following conditions
740939Sdes * are met:
840939Sdes * 1. Redistributions of source code must retain the above copyright
940939Sdes *    notice, this list of conditions and the following disclaimer
1040939Sdes *    in this position and unchanged.
1140939Sdes * 2. Redistributions in binary form must reproduce the above copyright
1240939Sdes *    notice, this list of conditions and the following disclaimer in the
1340939Sdes *    documentation and/or other materials provided with the distribution.
1440939Sdes * 3. The name of the author may not be used to endorse or promote products
1540939Sdes *    derived from this software without specific prior written permission
1640939Sdes *
1740939Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1840939Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1940939Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2040939Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2140939Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2240939Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2340939Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2440939Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2540939Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2640939Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2740939Sdes */
2840939Sdes
2984203Sdillon#include <sys/cdefs.h>
3084203Sdillon__FBSDID("$FreeBSD: head/lib/libfetch/common.c 178234 2008-04-15 23:29:51Z cperciva $");
3184203Sdillon
3241862Sdes#include <sys/param.h>
3340939Sdes#include <sys/socket.h>
3455557Sdes#include <sys/time.h>
3562981Sdes#include <sys/uio.h>
36174752Sdes
3740939Sdes#include <netinet/in.h>
3840939Sdes
39174752Sdes#include <ctype.h>
4040939Sdes#include <errno.h>
4140939Sdes#include <netdb.h>
42109695Sdes#include <pwd.h>
4360924Sdes#include <stdarg.h>
4441862Sdes#include <stdlib.h>
4541862Sdes#include <stdio.h>
4640939Sdes#include <string.h>
4740939Sdes#include <unistd.h>
4840939Sdes
4940939Sdes#include "fetch.h"
5040939Sdes#include "common.h"
5140939Sdes
5240975Sdes
5340939Sdes/*** Local data **************************************************************/
5440939Sdes
5540939Sdes/*
5640939Sdes * Error messages for resolver errors
5740939Sdes */
58174588Sdesstatic struct fetcherr netdb_errlist[] = {
59121423Sume#ifdef EAI_NODATA
6090267Sdes	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
61121423Sume#endif
6290267Sdes	{ EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
6390267Sdes	{ EAI_FAIL,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
6490267Sdes	{ EAI_NONAME,	FETCH_RESOLV,	"No address record" },
6590267Sdes	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
6640939Sdes};
6740939Sdes
6862981Sdes/* End-of-Line */
6975891Sarchiestatic const char ENDL[2] = "\r\n";
7040939Sdes
7162981Sdes
7240939Sdes/*** Error-reporting functions ***********************************************/
7340939Sdes
7440939Sdes/*
7540939Sdes * Map error code to string
7640939Sdes */
7760924Sdesstatic struct fetcherr *
78174588Sdesfetch_finderr(struct fetcherr *p, int e)
7940939Sdes{
8090267Sdes	while (p->num != -1 && p->num != e)
8190267Sdes		p++;
8290267Sdes	return (p);
8340939Sdes}
8440939Sdes
8540939Sdes/*
8640939Sdes * Set error code
8740939Sdes */
8840939Sdesvoid
89174588Sdesfetch_seterr(struct fetcherr *p, int e)
9040939Sdes{
91174588Sdes	p = fetch_finderr(p, e);
9290267Sdes	fetchLastErrCode = p->cat;
9390267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
9440939Sdes}
9540939Sdes
9640939Sdes/*
9740939Sdes * Set error code according to errno
9840939Sdes */
9940939Sdesvoid
100174588Sdesfetch_syserr(void)
10140939Sdes{
10290267Sdes	switch (errno) {
10390267Sdes	case 0:
10490267Sdes		fetchLastErrCode = FETCH_OK;
10590267Sdes		break;
10690267Sdes	case EPERM:
10790267Sdes	case EACCES:
10890267Sdes	case EROFS:
10990267Sdes	case EAUTH:
11090267Sdes	case ENEEDAUTH:
11190267Sdes		fetchLastErrCode = FETCH_AUTH;
11290267Sdes		break;
11390267Sdes	case ENOENT:
11490267Sdes	case EISDIR: /* XXX */
11590267Sdes		fetchLastErrCode = FETCH_UNAVAIL;
11690267Sdes		break;
11790267Sdes	case ENOMEM:
11890267Sdes		fetchLastErrCode = FETCH_MEMORY;
11990267Sdes		break;
12090267Sdes	case EBUSY:
12190267Sdes	case EAGAIN:
12290267Sdes		fetchLastErrCode = FETCH_TEMP;
12390267Sdes		break;
12490267Sdes	case EEXIST:
12590267Sdes		fetchLastErrCode = FETCH_EXISTS;
12690267Sdes		break;
12790267Sdes	case ENOSPC:
12890267Sdes		fetchLastErrCode = FETCH_FULL;
12990267Sdes		break;
13090267Sdes	case EADDRINUSE:
13190267Sdes	case EADDRNOTAVAIL:
13290267Sdes	case ENETDOWN:
13390267Sdes	case ENETUNREACH:
13490267Sdes	case ENETRESET:
13590267Sdes	case EHOSTUNREACH:
13690267Sdes		fetchLastErrCode = FETCH_NETWORK;
13790267Sdes		break;
13890267Sdes	case ECONNABORTED:
13990267Sdes	case ECONNRESET:
14090267Sdes		fetchLastErrCode = FETCH_ABORT;
14190267Sdes		break;
14290267Sdes	case ETIMEDOUT:
14390267Sdes		fetchLastErrCode = FETCH_TIMEOUT;
14490267Sdes		break;
14590267Sdes	case ECONNREFUSED:
14690267Sdes	case EHOSTDOWN:
14790267Sdes		fetchLastErrCode = FETCH_DOWN;
14890267Sdes		break;
14990267Sdesdefault:
15090267Sdes		fetchLastErrCode = FETCH_UNKNOWN;
15190267Sdes	}
15290267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
15340939Sdes}
15440939Sdes
15540939Sdes
15641862Sdes/*
15741862Sdes * Emit status message
15841862Sdes */
15960924Sdesvoid
160174588Sdesfetch_info(const char *fmt, ...)
16141862Sdes{
16290267Sdes	va_list ap;
16390267Sdes
16490267Sdes	va_start(ap, fmt);
16590267Sdes	vfprintf(stderr, fmt, ap);
16690267Sdes	va_end(ap);
16790267Sdes	fputc('\n', stderr);
16841862Sdes}
16941862Sdes
17041862Sdes
17140939Sdes/*** Network-related utility functions ***************************************/
17240939Sdes
17340939Sdes/*
17468551Sdes * Return the default port for a scheme
17568551Sdes */
17668551Sdesint
177174588Sdesfetch_default_port(const char *scheme)
17868551Sdes{
17990267Sdes	struct servent *se;
18068551Sdes
18190267Sdes	if ((se = getservbyname(scheme, "tcp")) != NULL)
18290267Sdes		return (ntohs(se->s_port));
18390267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
18490267Sdes		return (FTP_DEFAULT_PORT);
18590267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
18690267Sdes		return (HTTP_DEFAULT_PORT);
18790267Sdes	return (0);
18868551Sdes}
18968551Sdes
19068551Sdes/*
19168551Sdes * Return the default proxy port for a scheme
19268551Sdes */
19368551Sdesint
194174588Sdesfetch_default_proxy_port(const char *scheme)
19568551Sdes{
19690267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
19790267Sdes		return (FTP_DEFAULT_PROXY_PORT);
19890267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
19990267Sdes		return (HTTP_DEFAULT_PROXY_PORT);
20090267Sdes	return (0);
20168551Sdes}
20268551Sdes
20398117Sdes
20468551Sdes/*
20597866Sdes * Create a connection for an existing descriptor.
20697866Sdes */
20797866Sdesconn_t *
208174588Sdesfetch_reopen(int sd)
20997866Sdes{
21097866Sdes	conn_t *conn;
21197866Sdes
21297866Sdes	/* allocate and fill connection structure */
213109967Sdes	if ((conn = calloc(1, sizeof(*conn))) == NULL)
21497866Sdes		return (NULL);
21597866Sdes	conn->sd = sd;
21698117Sdes	++conn->ref;
21797866Sdes	return (conn);
21897866Sdes}
21997866Sdes
22097866Sdes
22197866Sdes/*
22298117Sdes * Bump a connection's reference count.
22398117Sdes */
22498117Sdesconn_t *
225174588Sdesfetch_ref(conn_t *conn)
22698117Sdes{
22798117Sdes
22898117Sdes	++conn->ref;
22998117Sdes	return (conn);
23098117Sdes}
23198117Sdes
23298117Sdes
23398117Sdes/*
234111816Sdes * Bind a socket to a specific local address
235111816Sdes */
236111816Sdesint
237174588Sdesfetch_bind(int sd, int af, const char *addr)
238111816Sdes{
239111816Sdes	struct addrinfo hints, *res, *res0;
240111816Sdes	int err;
241111816Sdes
242111816Sdes	memset(&hints, 0, sizeof(hints));
243111816Sdes	hints.ai_family = af;
244111816Sdes	hints.ai_socktype = SOCK_STREAM;
245111816Sdes	hints.ai_protocol = 0;
246111816Sdes	if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)
247111816Sdes		return (-1);
248111816Sdes	for (res = res0; res; res = res->ai_next)
249111816Sdes		if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
250111816Sdes			return (0);
251111816Sdes	return (-1);
252111816Sdes}
253111816Sdes
254111816Sdes
255111816Sdes/*
25640939Sdes * Establish a TCP connection to the specified port on the specified host.
25740939Sdes */
25897856Sdesconn_t *
259174588Sdesfetch_connect(const char *host, int port, int af, int verbose)
26040939Sdes{
26197856Sdes	conn_t *conn;
26290267Sdes	char pbuf[10];
263111816Sdes	const char *bindaddr;
26490267Sdes	struct addrinfo hints, *res, *res0;
26590267Sdes	int sd, err;
26640939Sdes
26790267Sdes	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
26841862Sdes
26990267Sdes	if (verbose)
270174588Sdes		fetch_info("looking up %s", host);
27140939Sdes
27290267Sdes	/* look up host name and set up socket address structure */
27390267Sdes	snprintf(pbuf, sizeof(pbuf), "%d", port);
27490267Sdes	memset(&hints, 0, sizeof(hints));
27590267Sdes	hints.ai_family = af;
27690267Sdes	hints.ai_socktype = SOCK_STREAM;
27790267Sdes	hints.ai_protocol = 0;
27890267Sdes	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
279174588Sdes		netdb_seterr(err);
28097856Sdes		return (NULL);
28190267Sdes	}
282111816Sdes	bindaddr = getenv("FETCH_BIND_ADDRESS");
28390267Sdes
28490267Sdes	if (verbose)
285174588Sdes		fetch_info("connecting to %s:%d", host, port);
28690267Sdes
28790267Sdes	/* try to connect */
288111816Sdes	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
28990267Sdes		if ((sd = socket(res->ai_family, res->ai_socktype,
29062981Sdes			 res->ai_protocol)) == -1)
29190267Sdes			continue;
292111816Sdes		if (bindaddr != NULL && *bindaddr != '\0' &&
293174588Sdes		    fetch_bind(sd, res->ai_family, bindaddr) != 0) {
294174588Sdes			fetch_info("failed to bind to '%s'", bindaddr);
295111816Sdes			close(sd);
296111816Sdes			continue;
297111816Sdes		}
298111816Sdes		if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
29990267Sdes			break;
30090267Sdes		close(sd);
30190267Sdes	}
30290267Sdes	freeaddrinfo(res0);
30390267Sdes	if (sd == -1) {
304174588Sdes		fetch_syserr();
30597856Sdes		return (NULL);
30690267Sdes	}
30740939Sdes
308174588Sdes	if ((conn = fetch_reopen(sd)) == NULL) {
309174588Sdes		fetch_syserr();
31097856Sdes		close(sd);
311103459Sfenner	}
31297856Sdes	return (conn);
31340939Sdes}
31441989Sdes
31541989Sdes
31655557Sdes/*
31797868Sdes * Enable SSL on a connection.
31897868Sdes */
31997868Sdesint
320174588Sdesfetch_ssl(conn_t *conn, int verbose)
32197868Sdes{
32297868Sdes
32397891Sdes#ifdef WITH_SSL
32497868Sdes	/* Init the SSL library and context */
32597868Sdes	if (!SSL_library_init()){
32697868Sdes		fprintf(stderr, "SSL library init failed\n");
32797868Sdes		return (-1);
32897868Sdes	}
32997868Sdes
33097868Sdes	SSL_load_error_strings();
33197868Sdes
33297868Sdes	conn->ssl_meth = SSLv23_client_method();
33397868Sdes	conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
334108579Sdes	SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
33597868Sdes
33697868Sdes	conn->ssl = SSL_new(conn->ssl_ctx);
33797868Sdes	if (conn->ssl == NULL){
33897868Sdes		fprintf(stderr, "SSL context creation failed\n");
33997868Sdes		return (-1);
34097868Sdes	}
34197868Sdes	SSL_set_fd(conn->ssl, conn->sd);
34297868Sdes	if (SSL_connect(conn->ssl) == -1){
34397868Sdes		ERR_print_errors_fp(stderr);
34497868Sdes		return (-1);
34597868Sdes	}
34697868Sdes
34797868Sdes	if (verbose) {
34897868Sdes		X509_NAME *name;
34997868Sdes		char *str;
35097868Sdes
35197868Sdes		fprintf(stderr, "SSL connection established using %s\n",
35297868Sdes		    SSL_get_cipher(conn->ssl));
35397868Sdes		conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
35497868Sdes		name = X509_get_subject_name(conn->ssl_cert);
35597868Sdes		str = X509_NAME_oneline(name, 0, 0);
35697868Sdes		printf("Certificate subject: %s\n", str);
35797868Sdes		free(str);
35897868Sdes		name = X509_get_issuer_name(conn->ssl_cert);
35997868Sdes		str = X509_NAME_oneline(name, 0, 0);
36097868Sdes		printf("Certificate issuer: %s\n", str);
36197868Sdes		free(str);
36297868Sdes	}
36397868Sdes
36497868Sdes	return (0);
36597891Sdes#else
36697891Sdes	(void)conn;
36797891Sdes	(void)verbose;
36897891Sdes	fprintf(stderr, "SSL support disabled\n");
36997891Sdes	return (-1);
37097891Sdes#endif
37197868Sdes}
37297868Sdes
37398117Sdes
37497868Sdes/*
37597866Sdes * Read a character from a connection w/ timeout
37655557Sdes */
37797866Sdesssize_t
378174588Sdesfetch_read(conn_t *conn, char *buf, size_t len)
37955557Sdes{
380177447Sdes	struct timeval now, timeout, delta;
38190267Sdes	fd_set readfds;
38297866Sdes	ssize_t rlen, total;
38390267Sdes	int r;
38490267Sdes
38555557Sdes	if (fetchTimeout) {
38697866Sdes		FD_ZERO(&readfds);
38790267Sdes		gettimeofday(&timeout, NULL);
38890267Sdes		timeout.tv_sec += fetchTimeout;
38955557Sdes	}
39090267Sdes
39197866Sdes	total = 0;
39297866Sdes	while (len > 0) {
39397866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
39497856Sdes			FD_SET(conn->sd, &readfds);
39590267Sdes			gettimeofday(&now, NULL);
396177447Sdes			delta.tv_sec = timeout.tv_sec - now.tv_sec;
397177447Sdes			delta.tv_usec = timeout.tv_usec - now.tv_usec;
398177447Sdes			if (delta.tv_usec < 0) {
399177447Sdes				delta.tv_usec += 1000000;
400177447Sdes				delta.tv_sec--;
40190267Sdes			}
402177447Sdes			if (delta.tv_sec < 0) {
403106186Sdes				errno = ETIMEDOUT;
404174588Sdes				fetch_syserr();
405106186Sdes				return (-1);
406106186Sdes			}
40797866Sdes			errno = 0;
408177447Sdes			r = select(conn->sd + 1, &readfds, NULL, NULL, &delta);
40990267Sdes			if (r == -1) {
41090267Sdes				if (errno == EINTR && fetchRestartCalls)
41190267Sdes					continue;
412174588Sdes				fetch_syserr();
41390267Sdes				return (-1);
41490267Sdes			}
41590267Sdes		}
41697891Sdes#ifdef WITH_SSL
41797866Sdes		if (conn->ssl != NULL)
41897866Sdes			rlen = SSL_read(conn->ssl, buf, len);
41997866Sdes		else
42097891Sdes#endif
42197866Sdes			rlen = read(conn->sd, buf, len);
422106049Sdes		if (rlen == 0)
423106049Sdes			break;
42497866Sdes		if (rlen < 0) {
42590267Sdes			if (errno == EINTR && fetchRestartCalls)
42690267Sdes				continue;
42790267Sdes			return (-1);
42890267Sdes		}
42997866Sdes		len -= rlen;
43097866Sdes		buf += rlen;
43197866Sdes		total += rlen;
43297866Sdes	}
43397866Sdes	return (total);
43497866Sdes}
43597866Sdes
43698117Sdes
43797866Sdes/*
43897866Sdes * Read a line of text from a connection w/ timeout
43997866Sdes */
44097866Sdes#define MIN_BUF_SIZE 1024
44197866Sdes
44297866Sdesint
443174588Sdesfetch_getln(conn_t *conn)
44497866Sdes{
44597866Sdes	char *tmp;
44697866Sdes	size_t tmpsize;
447106186Sdes	ssize_t len;
44897866Sdes	char c;
44997866Sdes
45097866Sdes	if (conn->buf == NULL) {
45197866Sdes		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
45297866Sdes			errno = ENOMEM;
45397866Sdes			return (-1);
45497866Sdes		}
45597866Sdes		conn->bufsize = MIN_BUF_SIZE;
45697866Sdes	}
45797866Sdes
45897866Sdes	conn->buf[0] = '\0';
45997866Sdes	conn->buflen = 0;
46097866Sdes
46197866Sdes	do {
462174588Sdes		len = fetch_read(conn, &c, 1);
463106186Sdes		if (len == -1)
46497866Sdes			return (-1);
465106186Sdes		if (len == 0)
466106137Sobrien			break;
46797856Sdes		conn->buf[conn->buflen++] = c;
46897856Sdes		if (conn->buflen == conn->bufsize) {
46997856Sdes			tmp = conn->buf;
47097856Sdes			tmpsize = conn->bufsize * 2 + 1;
47197856Sdes			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
47290267Sdes				errno = ENOMEM;
47390267Sdes				return (-1);
47490267Sdes			}
47597856Sdes			conn->buf = tmp;
47697856Sdes			conn->bufsize = tmpsize;
47790267Sdes		}
47890267Sdes	} while (c != '\n');
47990267Sdes
48097856Sdes	conn->buf[conn->buflen] = '\0';
48197856Sdes	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
48290267Sdes	return (0);
48355557Sdes}
48455557Sdes
48555557Sdes
48662981Sdes/*
48797866Sdes * Write to a connection w/ timeout
48862981Sdes */
48997866Sdesssize_t
490174588Sdesfetch_write(conn_t *conn, const char *buf, size_t len)
49197866Sdes{
492106175Simp	struct iovec iov;
493106175Simp
494106175Simp	iov.iov_base = __DECONST(char *, buf);
495106175Simp	iov.iov_len = len;
496174588Sdes	return fetch_writev(conn, &iov, 1);
497106175Simp}
498106175Simp
499106175Simp/*
500106175Simp * Write a vector to a connection w/ timeout
501106175Simp * Note: can modify the iovec.
502106175Simp */
503106175Simpssize_t
504174588Sdesfetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
505106175Simp{
506177447Sdes	struct timeval now, timeout, delta;
50797866Sdes	fd_set writefds;
50897866Sdes	ssize_t wlen, total;
50997866Sdes	int r;
51097866Sdes
51197866Sdes	if (fetchTimeout) {
51297866Sdes		FD_ZERO(&writefds);
51397866Sdes		gettimeofday(&timeout, NULL);
51497866Sdes		timeout.tv_sec += fetchTimeout;
51597866Sdes	}
51697866Sdes
517106175Simp	total = 0;
518106175Simp	while (iovcnt > 0) {
51997866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
52097866Sdes			FD_SET(conn->sd, &writefds);
52197866Sdes			gettimeofday(&now, NULL);
522177447Sdes			delta.tv_sec = timeout.tv_sec - now.tv_sec;
523177447Sdes			delta.tv_usec = timeout.tv_usec - now.tv_usec;
524177447Sdes			if (delta.tv_usec < 0) {
525177447Sdes				delta.tv_usec += 1000000;
526177447Sdes				delta.tv_sec--;
52797866Sdes			}
528177447Sdes			if (delta.tv_sec < 0) {
52997866Sdes				errno = ETIMEDOUT;
530174588Sdes				fetch_syserr();
53197866Sdes				return (-1);
53297866Sdes			}
53397866Sdes			errno = 0;
534177447Sdes			r = select(conn->sd + 1, NULL, &writefds, NULL, &delta);
53597866Sdes			if (r == -1) {
53697866Sdes				if (errno == EINTR && fetchRestartCalls)
53797866Sdes					continue;
53897866Sdes				return (-1);
53997866Sdes			}
54097866Sdes		}
54197866Sdes		errno = 0;
54297891Sdes#ifdef WITH_SSL
54397866Sdes		if (conn->ssl != NULL)
544106175Simp			wlen = SSL_write(conn->ssl,
545106175Simp			    iov->iov_base, iov->iov_len);
54697866Sdes		else
54797891Sdes#endif
548106175Simp			wlen = writev(conn->sd, iov, iovcnt);
549106175Simp		if (wlen == 0) {
55097866Sdes			/* we consider a short write a failure */
551106175Simp			errno = EPIPE;
552174588Sdes			fetch_syserr();
55397866Sdes			return (-1);
554106175Simp		}
55597866Sdes		if (wlen < 0) {
55697866Sdes			if (errno == EINTR && fetchRestartCalls)
55797866Sdes				continue;
55897866Sdes			return (-1);
55997866Sdes		}
56097866Sdes		total += wlen;
561106175Simp		while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
562106175Simp			wlen -= iov->iov_len;
563106175Simp			iov++;
564106175Simp			iovcnt--;
565106175Simp		}
566106175Simp		if (iovcnt > 0) {
567106175Simp			iov->iov_len -= wlen;
568106175Simp			iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;
569106175Simp		}
57097866Sdes	}
57197866Sdes	return (total);
57297866Sdes}
57397866Sdes
57498117Sdes
57597866Sdes/*
57697866Sdes * Write a line of text to a connection w/ timeout
57797866Sdes */
57862981Sdesint
579174588Sdesfetch_putln(conn_t *conn, const char *str, size_t len)
58062981Sdes{
581106175Simp	struct iovec iov[2];
582106205Sdes	int ret;
58398748Sdes
58498748Sdes	DEBUG(fprintf(stderr, ">>> %s\n", str));
585106175Simp	iov[0].iov_base = __DECONST(char *, str);
586106175Simp	iov[0].iov_len = len;
587106175Simp	iov[1].iov_base = __DECONST(char *, ENDL);
588109967Sdes	iov[1].iov_len = sizeof(ENDL);
589106205Sdes	if (len == 0)
590174588Sdes		ret = fetch_writev(conn, &iov[1], 1);
591106205Sdes	else
592174588Sdes		ret = fetch_writev(conn, iov, 2);
593106205Sdes	if (ret == -1)
59490267Sdes		return (-1);
59590267Sdes	return (0);
59662981Sdes}
59762981Sdes
59862981Sdes
59997856Sdes/*
60097856Sdes * Close connection
60197856Sdes */
60297856Sdesint
603174588Sdesfetch_close(conn_t *conn)
60497856Sdes{
60597856Sdes	int ret;
60697856Sdes
60798117Sdes	if (--conn->ref > 0)
60898117Sdes		return (0);
60997856Sdes	ret = close(conn->sd);
610141970Sdes	free(conn->buf);
61197856Sdes	free(conn);
61297856Sdes	return (ret);
61397856Sdes}
61497856Sdes
61597856Sdes
61641989Sdes/*** Directory-related utility functions *************************************/
61741989Sdes
61841989Sdesint
619174588Sdesfetch_add_entry(struct url_ent **p, int *size, int *len,
62090267Sdes    const char *name, struct url_stat *us)
62141989Sdes{
62290267Sdes	struct url_ent *tmp;
62341989Sdes
62490267Sdes	if (*p == NULL) {
62590268Sdes		*size = 0;
62690267Sdes		*len = 0;
62741989Sdes	}
62841989Sdes
62990267Sdes	if (*len >= *size - 1) {
630109967Sdes		tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
63190267Sdes		if (tmp == NULL) {
63290267Sdes			errno = ENOMEM;
633174588Sdes			fetch_syserr();
63490267Sdes			return (-1);
63590267Sdes		}
63690268Sdes		*size = (*size * 2 + 1);
63790267Sdes		*p = tmp;
63890267Sdes	}
63941989Sdes
64090267Sdes	tmp = *p + *len;
64190267Sdes	snprintf(tmp->name, PATH_MAX, "%s", name);
642176105Sdes	memcpy(&tmp->stat, us, sizeof(*us));
64341989Sdes
64490267Sdes	(*len)++;
64590267Sdes	(++tmp)->name[0] = 0;
64690267Sdes
64790267Sdes	return (0);
64841989Sdes}
649109695Sdes
650109695Sdes
651109695Sdes/*** Authentication-related utility functions ********************************/
652109695Sdes
653109695Sdesstatic const char *
654174588Sdesfetch_read_word(FILE *f)
655109695Sdes{
656109695Sdes	static char word[1024];
657109695Sdes
658178234Scperciva	if (fscanf(f, " %1023s ", word) != 1)
659109695Sdes		return (NULL);
660109695Sdes	return (word);
661109695Sdes}
662109695Sdes
663109695Sdes/*
664109695Sdes * Get authentication data for a URL from .netrc
665109695Sdes */
666109695Sdesint
667174588Sdesfetch_netrc_auth(struct url *url)
668109695Sdes{
669109695Sdes	char fn[PATH_MAX];
670109695Sdes	const char *word;
671109695Sdes	char *p;
672109695Sdes	FILE *f;
673109695Sdes
674109695Sdes	if ((p = getenv("NETRC")) != NULL) {
675109967Sdes		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
676174588Sdes			fetch_info("$NETRC specifies a file name "
677109695Sdes			    "longer than PATH_MAX");
678109695Sdes			return (-1);
679109695Sdes		}
680109695Sdes	} else {
681109695Sdes		if ((p = getenv("HOME")) != NULL) {
682109695Sdes			struct passwd *pwd;
683109695Sdes
684109695Sdes			if ((pwd = getpwuid(getuid())) == NULL ||
685109695Sdes			    (p = pwd->pw_dir) == NULL)
686109695Sdes				return (-1);
687109695Sdes		}
688109967Sdes		if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
689109695Sdes			return (-1);
690109695Sdes	}
691109695Sdes
692109695Sdes	if ((f = fopen(fn, "r")) == NULL)
693109695Sdes		return (-1);
694174588Sdes	while ((word = fetch_read_word(f)) != NULL) {
695109695Sdes		if (strcmp(word, "default") == 0) {
696174588Sdes			DEBUG(fetch_info("Using default .netrc settings"));
697109695Sdes			break;
698109695Sdes		}
699109695Sdes		if (strcmp(word, "machine") == 0 &&
700174588Sdes		    (word = fetch_read_word(f)) != NULL &&
701109695Sdes		    strcasecmp(word, url->host) == 0) {
702174588Sdes			DEBUG(fetch_info("Using .netrc settings for %s", word));
703109695Sdes			break;
704109695Sdes		}
705109695Sdes	}
706109695Sdes	if (word == NULL)
707109695Sdes		goto ferr;
708174588Sdes	while ((word = fetch_read_word(f)) != NULL) {
709109695Sdes		if (strcmp(word, "login") == 0) {
710174588Sdes			if ((word = fetch_read_word(f)) == NULL)
711109695Sdes				goto ferr;
712109967Sdes			if (snprintf(url->user, sizeof(url->user),
713109960Sjwd				"%s", word) > (int)sizeof(url->user)) {
714174588Sdes				fetch_info("login name in .netrc is too long");
715109695Sdes				url->user[0] = '\0';
716109695Sdes			}
717109695Sdes		} else if (strcmp(word, "password") == 0) {
718174588Sdes			if ((word = fetch_read_word(f)) == NULL)
719109695Sdes				goto ferr;
720109967Sdes			if (snprintf(url->pwd, sizeof(url->pwd),
721109960Sjwd				"%s", word) > (int)sizeof(url->pwd)) {
722174588Sdes				fetch_info("password in .netrc is too long");
723109695Sdes				url->pwd[0] = '\0';
724109695Sdes			}
725109695Sdes		} else if (strcmp(word, "account") == 0) {
726174588Sdes			if ((word = fetch_read_word(f)) == NULL)
727109695Sdes				goto ferr;
728109695Sdes			/* XXX not supported! */
729109695Sdes		} else {
730109695Sdes			break;
731109695Sdes		}
732109695Sdes	}
733109695Sdes	fclose(f);
734109695Sdes	return (0);
735109695Sdes ferr:
736109695Sdes	fclose(f);
737109695Sdes	return (-1);
738109695Sdes}
739174752Sdes
740174752Sdes/*
741174752Sdes * The no_proxy environment variable specifies a set of domains for
742174752Sdes * which the proxy should not be consulted; the contents is a comma-,
743174752Sdes * or space-separated list of domain names.  A single asterisk will
744174752Sdes * override all proxy variables and no transactions will be proxied
745174752Sdes * (for compatability with lynx and curl, see the discussion at
746174752Sdes * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
747174752Sdes */
748174752Sdesint
749174752Sdesfetch_no_proxy_match(const char *host)
750174752Sdes{
751174752Sdes	const char *no_proxy, *p, *q;
752174752Sdes	size_t h_len, d_len;
753174752Sdes
754174752Sdes	if ((no_proxy = getenv("NO_PROXY")) == NULL &&
755174752Sdes	    (no_proxy = getenv("no_proxy")) == NULL)
756174752Sdes		return (0);
757174752Sdes
758174752Sdes	/* asterisk matches any hostname */
759174752Sdes	if (strcmp(no_proxy, "*") == 0)
760174752Sdes		return (1);
761174752Sdes
762174752Sdes	h_len = strlen(host);
763174752Sdes	p = no_proxy;
764174752Sdes	do {
765174752Sdes		/* position p at the beginning of a domain suffix */
766174761Sdes		while (*p == ',' || isspace((unsigned char)*p))
767174752Sdes			p++;
768174752Sdes
769174752Sdes		/* position q at the first separator character */
770174752Sdes		for (q = p; *q; ++q)
771174761Sdes			if (*q == ',' || isspace((unsigned char)*q))
772174752Sdes				break;
773174752Sdes
774174752Sdes		d_len = q - p;
775174752Sdes		if (d_len > 0 && h_len > d_len &&
776174752Sdes		    strncasecmp(host + h_len - d_len,
777174752Sdes			p, d_len) == 0) {
778174752Sdes			/* domain name matches */
779174752Sdes			return (1);
780174752Sdes		}
781174752Sdes
782174752Sdes		p = q + 1;
783174752Sdes	} while (*q);
784174752Sdes
785174752Sdes	return (0);
786174752Sdes}
787