common.c revision 141970
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 141970 2005-02-16 12:46:46Z des $");
3184203Sdillon
3241862Sdes#include <sys/param.h>
3340939Sdes#include <sys/socket.h>
3455557Sdes#include <sys/time.h>
3562981Sdes#include <sys/uio.h>
3640939Sdes#include <netinet/in.h>
3740939Sdes
3840939Sdes#include <errno.h>
3940939Sdes#include <netdb.h>
40109695Sdes#include <pwd.h>
4160924Sdes#include <stdarg.h>
4241862Sdes#include <stdlib.h>
4341862Sdes#include <stdio.h>
4440939Sdes#include <string.h>
4540939Sdes#include <unistd.h>
4640939Sdes
4740939Sdes#include "fetch.h"
4840939Sdes#include "common.h"
4940939Sdes
5040975Sdes
5140939Sdes/*** Local data **************************************************************/
5240939Sdes
5340939Sdes/*
5440939Sdes * Error messages for resolver errors
5540939Sdes */
5640939Sdesstatic struct fetcherr _netdb_errlist[] = {
57121423Sume#ifdef EAI_NODATA
5890267Sdes	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
59121423Sume#endif
6090267Sdes	{ EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
6190267Sdes	{ EAI_FAIL,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
6290267Sdes	{ EAI_NONAME,	FETCH_RESOLV,	"No address record" },
6390267Sdes	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
6440939Sdes};
6540939Sdes
6662981Sdes/* End-of-Line */
6775891Sarchiestatic const char ENDL[2] = "\r\n";
6840939Sdes
6962981Sdes
7040939Sdes/*** Error-reporting functions ***********************************************/
7140939Sdes
7240939Sdes/*
7340939Sdes * Map error code to string
7440939Sdes */
7560924Sdesstatic struct fetcherr *
7640975Sdes_fetch_finderr(struct fetcherr *p, int e)
7740939Sdes{
7890267Sdes	while (p->num != -1 && p->num != e)
7990267Sdes		p++;
8090267Sdes	return (p);
8140939Sdes}
8240939Sdes
8340939Sdes/*
8440939Sdes * Set error code
8540939Sdes */
8640939Sdesvoid
8740939Sdes_fetch_seterr(struct fetcherr *p, int e)
8840939Sdes{
8990267Sdes	p = _fetch_finderr(p, e);
9090267Sdes	fetchLastErrCode = p->cat;
9190267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
9240939Sdes}
9340939Sdes
9440939Sdes/*
9540939Sdes * Set error code according to errno
9640939Sdes */
9740939Sdesvoid
9840939Sdes_fetch_syserr(void)
9940939Sdes{
10090267Sdes	switch (errno) {
10190267Sdes	case 0:
10290267Sdes		fetchLastErrCode = FETCH_OK;
10390267Sdes		break;
10490267Sdes	case EPERM:
10590267Sdes	case EACCES:
10690267Sdes	case EROFS:
10790267Sdes	case EAUTH:
10890267Sdes	case ENEEDAUTH:
10990267Sdes		fetchLastErrCode = FETCH_AUTH;
11090267Sdes		break;
11190267Sdes	case ENOENT:
11290267Sdes	case EISDIR: /* XXX */
11390267Sdes		fetchLastErrCode = FETCH_UNAVAIL;
11490267Sdes		break;
11590267Sdes	case ENOMEM:
11690267Sdes		fetchLastErrCode = FETCH_MEMORY;
11790267Sdes		break;
11890267Sdes	case EBUSY:
11990267Sdes	case EAGAIN:
12090267Sdes		fetchLastErrCode = FETCH_TEMP;
12190267Sdes		break;
12290267Sdes	case EEXIST:
12390267Sdes		fetchLastErrCode = FETCH_EXISTS;
12490267Sdes		break;
12590267Sdes	case ENOSPC:
12690267Sdes		fetchLastErrCode = FETCH_FULL;
12790267Sdes		break;
12890267Sdes	case EADDRINUSE:
12990267Sdes	case EADDRNOTAVAIL:
13090267Sdes	case ENETDOWN:
13190267Sdes	case ENETUNREACH:
13290267Sdes	case ENETRESET:
13390267Sdes	case EHOSTUNREACH:
13490267Sdes		fetchLastErrCode = FETCH_NETWORK;
13590267Sdes		break;
13690267Sdes	case ECONNABORTED:
13790267Sdes	case ECONNRESET:
13890267Sdes		fetchLastErrCode = FETCH_ABORT;
13990267Sdes		break;
14090267Sdes	case ETIMEDOUT:
14190267Sdes		fetchLastErrCode = FETCH_TIMEOUT;
14290267Sdes		break;
14390267Sdes	case ECONNREFUSED:
14490267Sdes	case EHOSTDOWN:
14590267Sdes		fetchLastErrCode = FETCH_DOWN;
14690267Sdes		break;
14790267Sdesdefault:
14890267Sdes		fetchLastErrCode = FETCH_UNKNOWN;
14990267Sdes	}
15090267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
15140939Sdes}
15240939Sdes
15340939Sdes
15441862Sdes/*
15541862Sdes * Emit status message
15641862Sdes */
15760924Sdesvoid
15875891Sarchie_fetch_info(const char *fmt, ...)
15941862Sdes{
16090267Sdes	va_list ap;
16190267Sdes
16290267Sdes	va_start(ap, fmt);
16390267Sdes	vfprintf(stderr, fmt, ap);
16490267Sdes	va_end(ap);
16590267Sdes	fputc('\n', stderr);
16641862Sdes}
16741862Sdes
16841862Sdes
16940939Sdes/*** Network-related utility functions ***************************************/
17040939Sdes
17140939Sdes/*
17268551Sdes * Return the default port for a scheme
17368551Sdes */
17468551Sdesint
17575891Sarchie_fetch_default_port(const char *scheme)
17668551Sdes{
17790267Sdes	struct servent *se;
17868551Sdes
17990267Sdes	if ((se = getservbyname(scheme, "tcp")) != NULL)
18090267Sdes		return (ntohs(se->s_port));
18190267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
18290267Sdes		return (FTP_DEFAULT_PORT);
18390267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
18490267Sdes		return (HTTP_DEFAULT_PORT);
18590267Sdes	return (0);
18668551Sdes}
18768551Sdes
18868551Sdes/*
18968551Sdes * Return the default proxy port for a scheme
19068551Sdes */
19168551Sdesint
19275891Sarchie_fetch_default_proxy_port(const char *scheme)
19368551Sdes{
19490267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
19590267Sdes		return (FTP_DEFAULT_PROXY_PORT);
19690267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
19790267Sdes		return (HTTP_DEFAULT_PROXY_PORT);
19890267Sdes	return (0);
19968551Sdes}
20068551Sdes
20198117Sdes
20268551Sdes/*
20397866Sdes * Create a connection for an existing descriptor.
20497866Sdes */
20597866Sdesconn_t *
20697866Sdes_fetch_reopen(int sd)
20797866Sdes{
20897866Sdes	conn_t *conn;
20997866Sdes
21097866Sdes	/* allocate and fill connection structure */
211109967Sdes	if ((conn = calloc(1, sizeof(*conn))) == NULL)
21297866Sdes		return (NULL);
21397866Sdes	conn->sd = sd;
21498117Sdes	++conn->ref;
21597866Sdes	return (conn);
21697866Sdes}
21797866Sdes
21897866Sdes
21997866Sdes/*
22098117Sdes * Bump a connection's reference count.
22198117Sdes */
22298117Sdesconn_t *
22398117Sdes_fetch_ref(conn_t *conn)
22498117Sdes{
22598117Sdes
22698117Sdes	++conn->ref;
22798117Sdes	return (conn);
22898117Sdes}
22998117Sdes
23098117Sdes
23198117Sdes/*
232111816Sdes * Bind a socket to a specific local address
233111816Sdes */
234111816Sdesint
235111816Sdes_fetch_bind(int sd, int af, const char *addr)
236111816Sdes{
237111816Sdes	struct addrinfo hints, *res, *res0;
238111816Sdes	int err;
239111816Sdes
240111816Sdes	memset(&hints, 0, sizeof(hints));
241111816Sdes	hints.ai_family = af;
242111816Sdes	hints.ai_socktype = SOCK_STREAM;
243111816Sdes	hints.ai_protocol = 0;
244111816Sdes	if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)
245111816Sdes		return (-1);
246111816Sdes	for (res = res0; res; res = res->ai_next)
247111816Sdes		if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
248111816Sdes			return (0);
249111816Sdes	return (-1);
250111816Sdes}
251111816Sdes
252111816Sdes
253111816Sdes/*
25440939Sdes * Establish a TCP connection to the specified port on the specified host.
25540939Sdes */
25697856Sdesconn_t *
25775891Sarchie_fetch_connect(const char *host, int port, int af, int verbose)
25840939Sdes{
25997856Sdes	conn_t *conn;
26090267Sdes	char pbuf[10];
261111816Sdes	const char *bindaddr;
26290267Sdes	struct addrinfo hints, *res, *res0;
26390267Sdes	int sd, err;
26440939Sdes
26590267Sdes	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
26641862Sdes
26790267Sdes	if (verbose)
26890267Sdes		_fetch_info("looking up %s", host);
26940939Sdes
27090267Sdes	/* look up host name and set up socket address structure */
27190267Sdes	snprintf(pbuf, sizeof(pbuf), "%d", port);
27290267Sdes	memset(&hints, 0, sizeof(hints));
27390267Sdes	hints.ai_family = af;
27490267Sdes	hints.ai_socktype = SOCK_STREAM;
27590267Sdes	hints.ai_protocol = 0;
27690267Sdes	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
27790267Sdes		_netdb_seterr(err);
27897856Sdes		return (NULL);
27990267Sdes	}
280111816Sdes	bindaddr = getenv("FETCH_BIND_ADDRESS");
28190267Sdes
28290267Sdes	if (verbose)
28390267Sdes		_fetch_info("connecting to %s:%d", host, port);
28490267Sdes
28590267Sdes	/* try to connect */
286111816Sdes	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
28790267Sdes		if ((sd = socket(res->ai_family, res->ai_socktype,
28862981Sdes			 res->ai_protocol)) == -1)
28990267Sdes			continue;
290111816Sdes		if (bindaddr != NULL && *bindaddr != '\0' &&
291111816Sdes		    _fetch_bind(sd, res->ai_family, bindaddr) != 0) {
292111816Sdes			_fetch_info("failed to bind to '%s'", bindaddr);
293111816Sdes			close(sd);
294111816Sdes			continue;
295111816Sdes		}
296111816Sdes		if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
29790267Sdes			break;
29890267Sdes		close(sd);
29990267Sdes	}
30090267Sdes	freeaddrinfo(res0);
30190267Sdes	if (sd == -1) {
30290267Sdes		_fetch_syserr();
30397856Sdes		return (NULL);
30490267Sdes	}
30540939Sdes
306103459Sfenner	if ((conn = _fetch_reopen(sd)) == NULL) {
307103459Sfenner		_fetch_syserr();
30897856Sdes		close(sd);
309103459Sfenner	}
31097856Sdes	return (conn);
31140939Sdes}
31241989Sdes
31341989Sdes
31455557Sdes/*
31597868Sdes * Enable SSL on a connection.
31697868Sdes */
31797868Sdesint
31897868Sdes_fetch_ssl(conn_t *conn, int verbose)
31997868Sdes{
32097868Sdes
32197891Sdes#ifdef WITH_SSL
32297868Sdes	/* Init the SSL library and context */
32397868Sdes	if (!SSL_library_init()){
32497868Sdes		fprintf(stderr, "SSL library init failed\n");
32597868Sdes		return (-1);
32697868Sdes	}
32797868Sdes
32897868Sdes	SSL_load_error_strings();
32997868Sdes
33097868Sdes	conn->ssl_meth = SSLv23_client_method();
33197868Sdes	conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
332108579Sdes	SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
33397868Sdes
33497868Sdes	conn->ssl = SSL_new(conn->ssl_ctx);
33597868Sdes	if (conn->ssl == NULL){
33697868Sdes		fprintf(stderr, "SSL context creation failed\n");
33797868Sdes		return (-1);
33897868Sdes	}
33997868Sdes	SSL_set_fd(conn->ssl, conn->sd);
34097868Sdes	if (SSL_connect(conn->ssl) == -1){
34197868Sdes		ERR_print_errors_fp(stderr);
34297868Sdes		return (-1);
34397868Sdes	}
34497868Sdes
34597868Sdes	if (verbose) {
34697868Sdes		X509_NAME *name;
34797868Sdes		char *str;
34897868Sdes
34997868Sdes		fprintf(stderr, "SSL connection established using %s\n",
35097868Sdes		    SSL_get_cipher(conn->ssl));
35197868Sdes		conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
35297868Sdes		name = X509_get_subject_name(conn->ssl_cert);
35397868Sdes		str = X509_NAME_oneline(name, 0, 0);
35497868Sdes		printf("Certificate subject: %s\n", str);
35597868Sdes		free(str);
35697868Sdes		name = X509_get_issuer_name(conn->ssl_cert);
35797868Sdes		str = X509_NAME_oneline(name, 0, 0);
35897868Sdes		printf("Certificate issuer: %s\n", str);
35997868Sdes		free(str);
36097868Sdes	}
36197868Sdes
36297868Sdes	return (0);
36397891Sdes#else
36497891Sdes	(void)conn;
36597891Sdes	(void)verbose;
36697891Sdes	fprintf(stderr, "SSL support disabled\n");
36797891Sdes	return (-1);
36897891Sdes#endif
36997868Sdes}
37097868Sdes
37198117Sdes
37297868Sdes/*
37397866Sdes * Read a character from a connection w/ timeout
37455557Sdes */
37597866Sdesssize_t
37697866Sdes_fetch_read(conn_t *conn, char *buf, size_t len)
37755557Sdes{
37890267Sdes	struct timeval now, timeout, wait;
37990267Sdes	fd_set readfds;
38097866Sdes	ssize_t rlen, total;
38190267Sdes	int r;
38290267Sdes
38355557Sdes	if (fetchTimeout) {
38497866Sdes		FD_ZERO(&readfds);
38590267Sdes		gettimeofday(&timeout, NULL);
38690267Sdes		timeout.tv_sec += fetchTimeout;
38755557Sdes	}
38890267Sdes
38997866Sdes	total = 0;
39097866Sdes	while (len > 0) {
39197866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
39297856Sdes			FD_SET(conn->sd, &readfds);
39390267Sdes			gettimeofday(&now, NULL);
39490267Sdes			wait.tv_sec = timeout.tv_sec - now.tv_sec;
39590267Sdes			wait.tv_usec = timeout.tv_usec - now.tv_usec;
39690267Sdes			if (wait.tv_usec < 0) {
39790267Sdes				wait.tv_usec += 1000000;
39890267Sdes				wait.tv_sec--;
39990267Sdes			}
400106186Sdes			if (wait.tv_sec < 0) {
401106186Sdes				errno = ETIMEDOUT;
402106186Sdes				_fetch_syserr();
403106186Sdes				return (-1);
404106186Sdes			}
40597866Sdes			errno = 0;
40697856Sdes			r = select(conn->sd + 1, &readfds, NULL, NULL, &wait);
40790267Sdes			if (r == -1) {
40890267Sdes				if (errno == EINTR && fetchRestartCalls)
40990267Sdes					continue;
410106186Sdes				_fetch_syserr();
41190267Sdes				return (-1);
41290267Sdes			}
41390267Sdes		}
41497891Sdes#ifdef WITH_SSL
41597866Sdes		if (conn->ssl != NULL)
41697866Sdes			rlen = SSL_read(conn->ssl, buf, len);
41797866Sdes		else
41897891Sdes#endif
41997866Sdes			rlen = read(conn->sd, buf, len);
420106049Sdes		if (rlen == 0)
421106049Sdes			break;
42297866Sdes		if (rlen < 0) {
42390267Sdes			if (errno == EINTR && fetchRestartCalls)
42490267Sdes				continue;
42590267Sdes			return (-1);
42690267Sdes		}
42797866Sdes		len -= rlen;
42897866Sdes		buf += rlen;
42997866Sdes		total += rlen;
43097866Sdes	}
43197866Sdes	return (total);
43297866Sdes}
43397866Sdes
43498117Sdes
43597866Sdes/*
43697866Sdes * Read a line of text from a connection w/ timeout
43797866Sdes */
43897866Sdes#define MIN_BUF_SIZE 1024
43997866Sdes
44097866Sdesint
44197866Sdes_fetch_getln(conn_t *conn)
44297866Sdes{
44397866Sdes	char *tmp;
44497866Sdes	size_t tmpsize;
445106186Sdes	ssize_t len;
44697866Sdes	char c;
44797866Sdes
44897866Sdes	if (conn->buf == NULL) {
44997866Sdes		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
45097866Sdes			errno = ENOMEM;
45197866Sdes			return (-1);
45297866Sdes		}
45397866Sdes		conn->bufsize = MIN_BUF_SIZE;
45497866Sdes	}
45597866Sdes
45697866Sdes	conn->buf[0] = '\0';
45797866Sdes	conn->buflen = 0;
45897866Sdes
45997866Sdes	do {
460106186Sdes		len = _fetch_read(conn, &c, 1);
461106186Sdes		if (len == -1)
46297866Sdes			return (-1);
463106186Sdes		if (len == 0)
464106137Sobrien			break;
46597856Sdes		conn->buf[conn->buflen++] = c;
46697856Sdes		if (conn->buflen == conn->bufsize) {
46797856Sdes			tmp = conn->buf;
46897856Sdes			tmpsize = conn->bufsize * 2 + 1;
46997856Sdes			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
47090267Sdes				errno = ENOMEM;
47190267Sdes				return (-1);
47290267Sdes			}
47397856Sdes			conn->buf = tmp;
47497856Sdes			conn->bufsize = tmpsize;
47590267Sdes		}
47690267Sdes	} while (c != '\n');
47790267Sdes
47897856Sdes	conn->buf[conn->buflen] = '\0';
47997856Sdes	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
48090267Sdes	return (0);
48155557Sdes}
48255557Sdes
48355557Sdes
48462981Sdes/*
48597866Sdes * Write to a connection w/ timeout
48662981Sdes */
48797866Sdesssize_t
48897866Sdes_fetch_write(conn_t *conn, const char *buf, size_t len)
48997866Sdes{
490106175Simp	struct iovec iov;
491106175Simp
492106175Simp	iov.iov_base = __DECONST(char *, buf);
493106175Simp	iov.iov_len = len;
494106175Simp	return _fetch_writev(conn, &iov, 1);
495106175Simp}
496106175Simp
497106175Simp/*
498106175Simp * Write a vector to a connection w/ timeout
499106175Simp * Note: can modify the iovec.
500106175Simp */
501106175Simpssize_t
502106175Simp_fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
503106175Simp{
50497866Sdes	struct timeval now, timeout, wait;
50597866Sdes	fd_set writefds;
50697866Sdes	ssize_t wlen, total;
50797866Sdes	int r;
50897866Sdes
50997866Sdes	if (fetchTimeout) {
51097866Sdes		FD_ZERO(&writefds);
51197866Sdes		gettimeofday(&timeout, NULL);
51297866Sdes		timeout.tv_sec += fetchTimeout;
51397866Sdes	}
51497866Sdes
515106175Simp	total = 0;
516106175Simp	while (iovcnt > 0) {
51797866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
51897866Sdes			FD_SET(conn->sd, &writefds);
51997866Sdes			gettimeofday(&now, NULL);
52097866Sdes			wait.tv_sec = timeout.tv_sec - now.tv_sec;
52197866Sdes			wait.tv_usec = timeout.tv_usec - now.tv_usec;
52297866Sdes			if (wait.tv_usec < 0) {
52397866Sdes				wait.tv_usec += 1000000;
52497866Sdes				wait.tv_sec--;
52597866Sdes			}
52697866Sdes			if (wait.tv_sec < 0) {
52797866Sdes				errno = ETIMEDOUT;
528106186Sdes				_fetch_syserr();
52997866Sdes				return (-1);
53097866Sdes			}
53197866Sdes			errno = 0;
53297866Sdes			r = select(conn->sd + 1, NULL, &writefds, NULL, &wait);
53397866Sdes			if (r == -1) {
53497866Sdes				if (errno == EINTR && fetchRestartCalls)
53597866Sdes					continue;
53697866Sdes				return (-1);
53797866Sdes			}
53897866Sdes		}
53997866Sdes		errno = 0;
54097891Sdes#ifdef WITH_SSL
54197866Sdes		if (conn->ssl != NULL)
542106175Simp			wlen = SSL_write(conn->ssl,
543106175Simp			    iov->iov_base, iov->iov_len);
54497866Sdes		else
54597891Sdes#endif
546106175Simp			wlen = writev(conn->sd, iov, iovcnt);
547106175Simp		if (wlen == 0) {
54897866Sdes			/* we consider a short write a failure */
549106175Simp			errno = EPIPE;
550106175Simp			_fetch_syserr();
55197866Sdes			return (-1);
552106175Simp		}
55397866Sdes		if (wlen < 0) {
55497866Sdes			if (errno == EINTR && fetchRestartCalls)
55597866Sdes				continue;
55697866Sdes			return (-1);
55797866Sdes		}
55897866Sdes		total += wlen;
559106175Simp		while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
560106175Simp			wlen -= iov->iov_len;
561106175Simp			iov++;
562106175Simp			iovcnt--;
563106175Simp		}
564106175Simp		if (iovcnt > 0) {
565106175Simp			iov->iov_len -= wlen;
566106175Simp			iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;
567106175Simp		}
56897866Sdes	}
56997866Sdes	return (total);
57097866Sdes}
57197866Sdes
57298117Sdes
57397866Sdes/*
57497866Sdes * Write a line of text to a connection w/ timeout
57597866Sdes */
57662981Sdesint
57797856Sdes_fetch_putln(conn_t *conn, const char *str, size_t len)
57862981Sdes{
579106175Simp	struct iovec iov[2];
580106205Sdes	int ret;
58198748Sdes
58298748Sdes	DEBUG(fprintf(stderr, ">>> %s\n", str));
583106175Simp	iov[0].iov_base = __DECONST(char *, str);
584106175Simp	iov[0].iov_len = len;
585106175Simp	iov[1].iov_base = __DECONST(char *, ENDL);
586109967Sdes	iov[1].iov_len = sizeof(ENDL);
587106205Sdes	if (len == 0)
588106205Sdes		ret = _fetch_writev(conn, &iov[1], 1);
589106205Sdes	else
590106205Sdes		ret = _fetch_writev(conn, iov, 2);
591106205Sdes	if (ret == -1)
59290267Sdes		return (-1);
59390267Sdes	return (0);
59462981Sdes}
59562981Sdes
59662981Sdes
59797856Sdes/*
59897856Sdes * Close connection
59997856Sdes */
60097856Sdesint
60197856Sdes_fetch_close(conn_t *conn)
60297856Sdes{
60397856Sdes	int ret;
60497856Sdes
60598117Sdes	if (--conn->ref > 0)
60698117Sdes		return (0);
60797856Sdes	ret = close(conn->sd);
608141970Sdes	free(conn->buf);
60997856Sdes	free(conn);
61097856Sdes	return (ret);
61197856Sdes}
61297856Sdes
61397856Sdes
61441989Sdes/*** Directory-related utility functions *************************************/
61541989Sdes
61641989Sdesint
61741989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len,
61890267Sdes    const char *name, struct url_stat *us)
61941989Sdes{
62090267Sdes	struct url_ent *tmp;
62141989Sdes
62290267Sdes	if (*p == NULL) {
62390268Sdes		*size = 0;
62490267Sdes		*len = 0;
62541989Sdes	}
62641989Sdes
62790267Sdes	if (*len >= *size - 1) {
628109967Sdes		tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
62990267Sdes		if (tmp == NULL) {
63090267Sdes			errno = ENOMEM;
63190267Sdes			_fetch_syserr();
63290267Sdes			return (-1);
63390267Sdes		}
63490268Sdes		*size = (*size * 2 + 1);
63590267Sdes		*p = tmp;
63690267Sdes	}
63741989Sdes
63890267Sdes	tmp = *p + *len;
63990267Sdes	snprintf(tmp->name, PATH_MAX, "%s", name);
640109967Sdes	bcopy(us, &tmp->stat, sizeof(*us));
64141989Sdes
64290267Sdes	(*len)++;
64390267Sdes	(++tmp)->name[0] = 0;
64490267Sdes
64590267Sdes	return (0);
64641989Sdes}
647109695Sdes
648109695Sdes
649109695Sdes/*** Authentication-related utility functions ********************************/
650109695Sdes
651109695Sdesstatic const char *
652109695Sdes_fetch_read_word(FILE *f)
653109695Sdes{
654109695Sdes	static char word[1024];
655109695Sdes
656109695Sdes	if (fscanf(f, " %1024s ", word) != 1)
657109695Sdes		return (NULL);
658109695Sdes	return (word);
659109695Sdes}
660109695Sdes
661109695Sdes/*
662109695Sdes * Get authentication data for a URL from .netrc
663109695Sdes */
664109695Sdesint
665109695Sdes_fetch_netrc_auth(struct url *url)
666109695Sdes{
667109695Sdes	char fn[PATH_MAX];
668109695Sdes	const char *word;
669109695Sdes	char *p;
670109695Sdes	FILE *f;
671109695Sdes
672109695Sdes	if ((p = getenv("NETRC")) != NULL) {
673109967Sdes		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
674109695Sdes			_fetch_info("$NETRC specifies a file name "
675109695Sdes			    "longer than PATH_MAX");
676109695Sdes			return (-1);
677109695Sdes		}
678109695Sdes	} else {
679109695Sdes		if ((p = getenv("HOME")) != NULL) {
680109695Sdes			struct passwd *pwd;
681109695Sdes
682109695Sdes			if ((pwd = getpwuid(getuid())) == NULL ||
683109695Sdes			    (p = pwd->pw_dir) == NULL)
684109695Sdes				return (-1);
685109695Sdes		}
686109967Sdes		if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
687109695Sdes			return (-1);
688109695Sdes	}
689109695Sdes
690109695Sdes	if ((f = fopen(fn, "r")) == NULL)
691109695Sdes		return (-1);
692109695Sdes	while ((word = _fetch_read_word(f)) != NULL) {
693109695Sdes		if (strcmp(word, "default") == 0) {
694109695Sdes			DEBUG(_fetch_info("Using default .netrc settings"));
695109695Sdes			break;
696109695Sdes		}
697109695Sdes		if (strcmp(word, "machine") == 0 &&
698109695Sdes		    (word = _fetch_read_word(f)) != NULL &&
699109695Sdes		    strcasecmp(word, url->host) == 0) {
700109695Sdes			DEBUG(_fetch_info("Using .netrc settings for %s", word));
701109695Sdes			break;
702109695Sdes		}
703109695Sdes	}
704109695Sdes	if (word == NULL)
705109695Sdes		goto ferr;
706109695Sdes	while ((word = _fetch_read_word(f)) != NULL) {
707109695Sdes		if (strcmp(word, "login") == 0) {
708109695Sdes			if ((word = _fetch_read_word(f)) == NULL)
709109695Sdes				goto ferr;
710109967Sdes			if (snprintf(url->user, sizeof(url->user),
711109960Sjwd				"%s", word) > (int)sizeof(url->user)) {
712109695Sdes				_fetch_info("login name in .netrc is too long");
713109695Sdes				url->user[0] = '\0';
714109695Sdes			}
715109695Sdes		} else if (strcmp(word, "password") == 0) {
716109695Sdes			if ((word = _fetch_read_word(f)) == NULL)
717109695Sdes				goto ferr;
718109967Sdes			if (snprintf(url->pwd, sizeof(url->pwd),
719109960Sjwd				"%s", word) > (int)sizeof(url->pwd)) {
720109695Sdes				_fetch_info("password in .netrc is too long");
721109695Sdes				url->pwd[0] = '\0';
722109695Sdes			}
723109695Sdes		} else if (strcmp(word, "account") == 0) {
724109695Sdes			if ((word = _fetch_read_word(f)) == NULL)
725109695Sdes				goto ferr;
726109695Sdes			/* XXX not supported! */
727109695Sdes		} else {
728109695Sdes			break;
729109695Sdes		}
730109695Sdes	}
731109695Sdes	fclose(f);
732109695Sdes	return (0);
733109695Sdes ferr:
734109695Sdes	fclose(f);
735109695Sdes	return (-1);
736109695Sdes}
737