common.c revision 111816
140939Sdes/*-
240939Sdes * Copyright (c) 1998 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 111816 2003-03-03 12:35:03Z 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[] = {
5790267Sdes	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
5890267Sdes	{ EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
5990267Sdes	{ EAI_FAIL,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
6090267Sdes	{ EAI_NONAME,	FETCH_RESOLV,	"No address record" },
6190267Sdes	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
6240939Sdes};
6340939Sdes
6462981Sdes/* End-of-Line */
6575891Sarchiestatic const char ENDL[2] = "\r\n";
6640939Sdes
6762981Sdes
6840939Sdes/*** Error-reporting functions ***********************************************/
6940939Sdes
7040939Sdes/*
7140939Sdes * Map error code to string
7240939Sdes */
7360924Sdesstatic struct fetcherr *
7440975Sdes_fetch_finderr(struct fetcherr *p, int e)
7540939Sdes{
7690267Sdes	while (p->num != -1 && p->num != e)
7790267Sdes		p++;
7890267Sdes	return (p);
7940939Sdes}
8040939Sdes
8140939Sdes/*
8240939Sdes * Set error code
8340939Sdes */
8440939Sdesvoid
8540939Sdes_fetch_seterr(struct fetcherr *p, int e)
8640939Sdes{
8790267Sdes	p = _fetch_finderr(p, e);
8890267Sdes	fetchLastErrCode = p->cat;
8990267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
9040939Sdes}
9140939Sdes
9240939Sdes/*
9340939Sdes * Set error code according to errno
9440939Sdes */
9540939Sdesvoid
9640939Sdes_fetch_syserr(void)
9740939Sdes{
9890267Sdes	switch (errno) {
9990267Sdes	case 0:
10090267Sdes		fetchLastErrCode = FETCH_OK;
10190267Sdes		break;
10290267Sdes	case EPERM:
10390267Sdes	case EACCES:
10490267Sdes	case EROFS:
10590267Sdes	case EAUTH:
10690267Sdes	case ENEEDAUTH:
10790267Sdes		fetchLastErrCode = FETCH_AUTH;
10890267Sdes		break;
10990267Sdes	case ENOENT:
11090267Sdes	case EISDIR: /* XXX */
11190267Sdes		fetchLastErrCode = FETCH_UNAVAIL;
11290267Sdes		break;
11390267Sdes	case ENOMEM:
11490267Sdes		fetchLastErrCode = FETCH_MEMORY;
11590267Sdes		break;
11690267Sdes	case EBUSY:
11790267Sdes	case EAGAIN:
11890267Sdes		fetchLastErrCode = FETCH_TEMP;
11990267Sdes		break;
12090267Sdes	case EEXIST:
12190267Sdes		fetchLastErrCode = FETCH_EXISTS;
12290267Sdes		break;
12390267Sdes	case ENOSPC:
12490267Sdes		fetchLastErrCode = FETCH_FULL;
12590267Sdes		break;
12690267Sdes	case EADDRINUSE:
12790267Sdes	case EADDRNOTAVAIL:
12890267Sdes	case ENETDOWN:
12990267Sdes	case ENETUNREACH:
13090267Sdes	case ENETRESET:
13190267Sdes	case EHOSTUNREACH:
13290267Sdes		fetchLastErrCode = FETCH_NETWORK;
13390267Sdes		break;
13490267Sdes	case ECONNABORTED:
13590267Sdes	case ECONNRESET:
13690267Sdes		fetchLastErrCode = FETCH_ABORT;
13790267Sdes		break;
13890267Sdes	case ETIMEDOUT:
13990267Sdes		fetchLastErrCode = FETCH_TIMEOUT;
14090267Sdes		break;
14190267Sdes	case ECONNREFUSED:
14290267Sdes	case EHOSTDOWN:
14390267Sdes		fetchLastErrCode = FETCH_DOWN;
14490267Sdes		break;
14590267Sdesdefault:
14690267Sdes		fetchLastErrCode = FETCH_UNKNOWN;
14790267Sdes	}
14890267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
14940939Sdes}
15040939Sdes
15140939Sdes
15241862Sdes/*
15341862Sdes * Emit status message
15441862Sdes */
15560924Sdesvoid
15675891Sarchie_fetch_info(const char *fmt, ...)
15741862Sdes{
15890267Sdes	va_list ap;
15990267Sdes
16090267Sdes	va_start(ap, fmt);
16190267Sdes	vfprintf(stderr, fmt, ap);
16290267Sdes	va_end(ap);
16390267Sdes	fputc('\n', stderr);
16441862Sdes}
16541862Sdes
16641862Sdes
16740939Sdes/*** Network-related utility functions ***************************************/
16840939Sdes
16940939Sdes/*
17068551Sdes * Return the default port for a scheme
17168551Sdes */
17268551Sdesint
17375891Sarchie_fetch_default_port(const char *scheme)
17468551Sdes{
17590267Sdes	struct servent *se;
17668551Sdes
17790267Sdes	if ((se = getservbyname(scheme, "tcp")) != NULL)
17890267Sdes		return (ntohs(se->s_port));
17990267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
18090267Sdes		return (FTP_DEFAULT_PORT);
18190267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
18290267Sdes		return (HTTP_DEFAULT_PORT);
18390267Sdes	return (0);
18468551Sdes}
18568551Sdes
18668551Sdes/*
18768551Sdes * Return the default proxy port for a scheme
18868551Sdes */
18968551Sdesint
19075891Sarchie_fetch_default_proxy_port(const char *scheme)
19168551Sdes{
19290267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
19390267Sdes		return (FTP_DEFAULT_PROXY_PORT);
19490267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
19590267Sdes		return (HTTP_DEFAULT_PROXY_PORT);
19690267Sdes	return (0);
19768551Sdes}
19868551Sdes
19998117Sdes
20068551Sdes/*
20197866Sdes * Create a connection for an existing descriptor.
20297866Sdes */
20397866Sdesconn_t *
20497866Sdes_fetch_reopen(int sd)
20597866Sdes{
20697866Sdes	conn_t *conn;
20797866Sdes
20897866Sdes	/* allocate and fill connection structure */
209109967Sdes	if ((conn = calloc(1, sizeof(*conn))) == NULL)
21097866Sdes		return (NULL);
21197866Sdes	conn->sd = sd;
21298117Sdes	++conn->ref;
21397866Sdes	return (conn);
21497866Sdes}
21597866Sdes
21697866Sdes
21797866Sdes/*
21898117Sdes * Bump a connection's reference count.
21998117Sdes */
22098117Sdesconn_t *
22198117Sdes_fetch_ref(conn_t *conn)
22298117Sdes{
22398117Sdes
22498117Sdes	++conn->ref;
22598117Sdes	return (conn);
22698117Sdes}
22798117Sdes
22898117Sdes
22998117Sdes/*
230111816Sdes * Bind a socket to a specific local address
231111816Sdes */
232111816Sdesint
233111816Sdes_fetch_bind(int sd, int af, const char *addr)
234111816Sdes{
235111816Sdes	struct addrinfo hints, *res, *res0;
236111816Sdes	int err;
237111816Sdes
238111816Sdes	memset(&hints, 0, sizeof(hints));
239111816Sdes	hints.ai_family = af;
240111816Sdes	hints.ai_socktype = SOCK_STREAM;
241111816Sdes	hints.ai_protocol = 0;
242111816Sdes	if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)
243111816Sdes		return (-1);
244111816Sdes	for (res = res0; res; res = res->ai_next)
245111816Sdes		if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
246111816Sdes			return (0);
247111816Sdes	return (-1);
248111816Sdes}
249111816Sdes
250111816Sdes
251111816Sdes/*
25240939Sdes * Establish a TCP connection to the specified port on the specified host.
25340939Sdes */
25497856Sdesconn_t *
25575891Sarchie_fetch_connect(const char *host, int port, int af, int verbose)
25640939Sdes{
25797856Sdes	conn_t *conn;
25890267Sdes	char pbuf[10];
259111816Sdes	const char *bindaddr;
26090267Sdes	struct addrinfo hints, *res, *res0;
26190267Sdes	int sd, err;
26240939Sdes
26390267Sdes	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
26441862Sdes
26590267Sdes	if (verbose)
26690267Sdes		_fetch_info("looking up %s", host);
26740939Sdes
26890267Sdes	/* look up host name and set up socket address structure */
26990267Sdes	snprintf(pbuf, sizeof(pbuf), "%d", port);
27090267Sdes	memset(&hints, 0, sizeof(hints));
27190267Sdes	hints.ai_family = af;
27290267Sdes	hints.ai_socktype = SOCK_STREAM;
27390267Sdes	hints.ai_protocol = 0;
27490267Sdes	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
27590267Sdes		_netdb_seterr(err);
27697856Sdes		return (NULL);
27790267Sdes	}
278111816Sdes	bindaddr = getenv("FETCH_BIND_ADDRESS");
27990267Sdes
28090267Sdes	if (verbose)
28190267Sdes		_fetch_info("connecting to %s:%d", host, port);
28290267Sdes
28390267Sdes	/* try to connect */
284111816Sdes	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
28590267Sdes		if ((sd = socket(res->ai_family, res->ai_socktype,
28662981Sdes			 res->ai_protocol)) == -1)
28790267Sdes			continue;
288111816Sdes		if (bindaddr != NULL && *bindaddr != '\0' &&
289111816Sdes		    _fetch_bind(sd, res->ai_family, bindaddr) != 0) {
290111816Sdes			_fetch_info("failed to bind to '%s'", bindaddr);
291111816Sdes			close(sd);
292111816Sdes			continue;
293111816Sdes		}
294111816Sdes		if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
29590267Sdes			break;
29690267Sdes		close(sd);
29790267Sdes	}
29890267Sdes	freeaddrinfo(res0);
29990267Sdes	if (sd == -1) {
30090267Sdes		_fetch_syserr();
30197856Sdes		return (NULL);
30290267Sdes	}
30340939Sdes
304103459Sfenner	if ((conn = _fetch_reopen(sd)) == NULL) {
305103459Sfenner		_fetch_syserr();
30697856Sdes		close(sd);
307103459Sfenner	}
30897856Sdes	return (conn);
30940939Sdes}
31041989Sdes
31141989Sdes
31255557Sdes/*
31397868Sdes * Enable SSL on a connection.
31497868Sdes */
31597868Sdesint
31697868Sdes_fetch_ssl(conn_t *conn, int verbose)
31797868Sdes{
31897868Sdes
31997891Sdes#ifdef WITH_SSL
32097868Sdes	/* Init the SSL library and context */
32197868Sdes	if (!SSL_library_init()){
32297868Sdes		fprintf(stderr, "SSL library init failed\n");
32397868Sdes		return (-1);
32497868Sdes	}
32597868Sdes
32697868Sdes	SSL_load_error_strings();
32797868Sdes
32897868Sdes	conn->ssl_meth = SSLv23_client_method();
32997868Sdes	conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
330108579Sdes	SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
33197868Sdes
33297868Sdes	conn->ssl = SSL_new(conn->ssl_ctx);
33397868Sdes	if (conn->ssl == NULL){
33497868Sdes		fprintf(stderr, "SSL context creation failed\n");
33597868Sdes		return (-1);
33697868Sdes	}
33797868Sdes	SSL_set_fd(conn->ssl, conn->sd);
33897868Sdes	if (SSL_connect(conn->ssl) == -1){
33997868Sdes		ERR_print_errors_fp(stderr);
34097868Sdes		return (-1);
34197868Sdes	}
34297868Sdes
34397868Sdes	if (verbose) {
34497868Sdes		X509_NAME *name;
34597868Sdes		char *str;
34697868Sdes
34797868Sdes		fprintf(stderr, "SSL connection established using %s\n",
34897868Sdes		    SSL_get_cipher(conn->ssl));
34997868Sdes		conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
35097868Sdes		name = X509_get_subject_name(conn->ssl_cert);
35197868Sdes		str = X509_NAME_oneline(name, 0, 0);
35297868Sdes		printf("Certificate subject: %s\n", str);
35397868Sdes		free(str);
35497868Sdes		name = X509_get_issuer_name(conn->ssl_cert);
35597868Sdes		str = X509_NAME_oneline(name, 0, 0);
35697868Sdes		printf("Certificate issuer: %s\n", str);
35797868Sdes		free(str);
35897868Sdes	}
35997868Sdes
36097868Sdes	return (0);
36197891Sdes#else
36297891Sdes	(void)conn;
36397891Sdes	(void)verbose;
36497891Sdes	fprintf(stderr, "SSL support disabled\n");
36597891Sdes	return (-1);
36697891Sdes#endif
36797868Sdes}
36897868Sdes
36998117Sdes
37097868Sdes/*
37197866Sdes * Read a character from a connection w/ timeout
37255557Sdes */
37397866Sdesssize_t
37497866Sdes_fetch_read(conn_t *conn, char *buf, size_t len)
37555557Sdes{
37690267Sdes	struct timeval now, timeout, wait;
37790267Sdes	fd_set readfds;
37897866Sdes	ssize_t rlen, total;
37990267Sdes	int r;
38090267Sdes
38155557Sdes	if (fetchTimeout) {
38297866Sdes		FD_ZERO(&readfds);
38390267Sdes		gettimeofday(&timeout, NULL);
38490267Sdes		timeout.tv_sec += fetchTimeout;
38555557Sdes	}
38690267Sdes
38797866Sdes	total = 0;
38897866Sdes	while (len > 0) {
38997866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
39097856Sdes			FD_SET(conn->sd, &readfds);
39190267Sdes			gettimeofday(&now, NULL);
39290267Sdes			wait.tv_sec = timeout.tv_sec - now.tv_sec;
39390267Sdes			wait.tv_usec = timeout.tv_usec - now.tv_usec;
39490267Sdes			if (wait.tv_usec < 0) {
39590267Sdes				wait.tv_usec += 1000000;
39690267Sdes				wait.tv_sec--;
39790267Sdes			}
398106186Sdes			if (wait.tv_sec < 0) {
399106186Sdes				errno = ETIMEDOUT;
400106186Sdes				_fetch_syserr();
401106186Sdes				return (-1);
402106186Sdes			}
40397866Sdes			errno = 0;
40497856Sdes			r = select(conn->sd + 1, &readfds, NULL, NULL, &wait);
40590267Sdes			if (r == -1) {
40690267Sdes				if (errno == EINTR && fetchRestartCalls)
40790267Sdes					continue;
408106186Sdes				_fetch_syserr();
40990267Sdes				return (-1);
41090267Sdes			}
41190267Sdes		}
41297891Sdes#ifdef WITH_SSL
41397866Sdes		if (conn->ssl != NULL)
41497866Sdes			rlen = SSL_read(conn->ssl, buf, len);
41597866Sdes		else
41697891Sdes#endif
41797866Sdes			rlen = read(conn->sd, buf, len);
418106049Sdes		if (rlen == 0)
419106049Sdes			break;
42097866Sdes		if (rlen < 0) {
42190267Sdes			if (errno == EINTR && fetchRestartCalls)
42290267Sdes				continue;
42390267Sdes			return (-1);
42490267Sdes		}
42597866Sdes		len -= rlen;
42697866Sdes		buf += rlen;
42797866Sdes		total += rlen;
42897866Sdes	}
42997866Sdes	return (total);
43097866Sdes}
43197866Sdes
43298117Sdes
43397866Sdes/*
43497866Sdes * Read a line of text from a connection w/ timeout
43597866Sdes */
43697866Sdes#define MIN_BUF_SIZE 1024
43797866Sdes
43897866Sdesint
43997866Sdes_fetch_getln(conn_t *conn)
44097866Sdes{
44197866Sdes	char *tmp;
44297866Sdes	size_t tmpsize;
443106186Sdes	ssize_t len;
44497866Sdes	char c;
44597866Sdes
44697866Sdes	if (conn->buf == NULL) {
44797866Sdes		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
44897866Sdes			errno = ENOMEM;
44997866Sdes			return (-1);
45097866Sdes		}
45197866Sdes		conn->bufsize = MIN_BUF_SIZE;
45297866Sdes	}
45397866Sdes
45497866Sdes	conn->buf[0] = '\0';
45597866Sdes	conn->buflen = 0;
45697866Sdes
45797866Sdes	do {
458106186Sdes		len = _fetch_read(conn, &c, 1);
459106186Sdes		if (len == -1)
46097866Sdes			return (-1);
461106186Sdes		if (len == 0)
462106137Sobrien			break;
46397856Sdes		conn->buf[conn->buflen++] = c;
46497856Sdes		if (conn->buflen == conn->bufsize) {
46597856Sdes			tmp = conn->buf;
46697856Sdes			tmpsize = conn->bufsize * 2 + 1;
46797856Sdes			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
46890267Sdes				errno = ENOMEM;
46990267Sdes				return (-1);
47090267Sdes			}
47197856Sdes			conn->buf = tmp;
47297856Sdes			conn->bufsize = tmpsize;
47390267Sdes		}
47490267Sdes	} while (c != '\n');
47590267Sdes
47697856Sdes	conn->buf[conn->buflen] = '\0';
47797856Sdes	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
47890267Sdes	return (0);
47955557Sdes}
48055557Sdes
48155557Sdes
48262981Sdes/*
48397866Sdes * Write to a connection w/ timeout
48462981Sdes */
48597866Sdesssize_t
48697866Sdes_fetch_write(conn_t *conn, const char *buf, size_t len)
48797866Sdes{
488106175Simp	struct iovec iov;
489106175Simp
490106175Simp	iov.iov_base = __DECONST(char *, buf);
491106175Simp	iov.iov_len = len;
492106175Simp	return _fetch_writev(conn, &iov, 1);
493106175Simp}
494106175Simp
495106175Simp/*
496106175Simp * Write a vector to a connection w/ timeout
497106175Simp * Note: can modify the iovec.
498106175Simp */
499106175Simpssize_t
500106175Simp_fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
501106175Simp{
50297866Sdes	struct timeval now, timeout, wait;
50397866Sdes	fd_set writefds;
50497866Sdes	ssize_t wlen, total;
50597866Sdes	int r;
50697866Sdes
50797866Sdes	if (fetchTimeout) {
50897866Sdes		FD_ZERO(&writefds);
50997866Sdes		gettimeofday(&timeout, NULL);
51097866Sdes		timeout.tv_sec += fetchTimeout;
51197866Sdes	}
51297866Sdes
513106175Simp	total = 0;
514106175Simp	while (iovcnt > 0) {
51597866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
51697866Sdes			FD_SET(conn->sd, &writefds);
51797866Sdes			gettimeofday(&now, NULL);
51897866Sdes			wait.tv_sec = timeout.tv_sec - now.tv_sec;
51997866Sdes			wait.tv_usec = timeout.tv_usec - now.tv_usec;
52097866Sdes			if (wait.tv_usec < 0) {
52197866Sdes				wait.tv_usec += 1000000;
52297866Sdes				wait.tv_sec--;
52397866Sdes			}
52497866Sdes			if (wait.tv_sec < 0) {
52597866Sdes				errno = ETIMEDOUT;
526106186Sdes				_fetch_syserr();
52797866Sdes				return (-1);
52897866Sdes			}
52997866Sdes			errno = 0;
53097866Sdes			r = select(conn->sd + 1, NULL, &writefds, NULL, &wait);
53197866Sdes			if (r == -1) {
53297866Sdes				if (errno == EINTR && fetchRestartCalls)
53397866Sdes					continue;
53497866Sdes				return (-1);
53597866Sdes			}
53697866Sdes		}
53797866Sdes		errno = 0;
53897891Sdes#ifdef WITH_SSL
53997866Sdes		if (conn->ssl != NULL)
540106175Simp			wlen = SSL_write(conn->ssl,
541106175Simp			    iov->iov_base, iov->iov_len);
54297866Sdes		else
54397891Sdes#endif
544106175Simp			wlen = writev(conn->sd, iov, iovcnt);
545106175Simp		if (wlen == 0) {
54697866Sdes			/* we consider a short write a failure */
547106175Simp			errno = EPIPE;
548106175Simp			_fetch_syserr();
54997866Sdes			return (-1);
550106175Simp		}
55197866Sdes		if (wlen < 0) {
55297866Sdes			if (errno == EINTR && fetchRestartCalls)
55397866Sdes				continue;
55497866Sdes			return (-1);
55597866Sdes		}
55697866Sdes		total += wlen;
557106175Simp		while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
558106175Simp			wlen -= iov->iov_len;
559106175Simp			iov++;
560106175Simp			iovcnt--;
561106175Simp		}
562106175Simp		if (iovcnt > 0) {
563106175Simp			iov->iov_len -= wlen;
564106175Simp			iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;
565106175Simp		}
56697866Sdes	}
56797866Sdes	return (total);
56897866Sdes}
56997866Sdes
57098117Sdes
57197866Sdes/*
57297866Sdes * Write a line of text to a connection w/ timeout
57397866Sdes */
57462981Sdesint
57597856Sdes_fetch_putln(conn_t *conn, const char *str, size_t len)
57662981Sdes{
577106175Simp	struct iovec iov[2];
578106205Sdes	int ret;
57998748Sdes
58098748Sdes	DEBUG(fprintf(stderr, ">>> %s\n", str));
581106175Simp	iov[0].iov_base = __DECONST(char *, str);
582106175Simp	iov[0].iov_len = len;
583106175Simp	iov[1].iov_base = __DECONST(char *, ENDL);
584109967Sdes	iov[1].iov_len = sizeof(ENDL);
585106205Sdes	if (len == 0)
586106205Sdes		ret = _fetch_writev(conn, &iov[1], 1);
587106205Sdes	else
588106205Sdes		ret = _fetch_writev(conn, iov, 2);
589106205Sdes	if (ret == -1)
59090267Sdes		return (-1);
59190267Sdes	return (0);
59262981Sdes}
59362981Sdes
59462981Sdes
59597856Sdes/*
59697856Sdes * Close connection
59797856Sdes */
59897856Sdesint
59997856Sdes_fetch_close(conn_t *conn)
60097856Sdes{
60197856Sdes	int ret;
60297856Sdes
60398117Sdes	if (--conn->ref > 0)
60498117Sdes		return (0);
60597856Sdes	ret = close(conn->sd);
60697856Sdes	free(conn);
60797856Sdes	return (ret);
60897856Sdes}
60997856Sdes
61097856Sdes
61141989Sdes/*** Directory-related utility functions *************************************/
61241989Sdes
61341989Sdesint
61441989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len,
61590267Sdes    const char *name, struct url_stat *us)
61641989Sdes{
61790267Sdes	struct url_ent *tmp;
61841989Sdes
61990267Sdes	if (*p == NULL) {
62090268Sdes		*size = 0;
62190267Sdes		*len = 0;
62241989Sdes	}
62341989Sdes
62490267Sdes	if (*len >= *size - 1) {
625109967Sdes		tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
62690267Sdes		if (tmp == NULL) {
62790267Sdes			errno = ENOMEM;
62890267Sdes			_fetch_syserr();
62990267Sdes			return (-1);
63090267Sdes		}
63190268Sdes		*size = (*size * 2 + 1);
63290267Sdes		*p = tmp;
63390267Sdes	}
63441989Sdes
63590267Sdes	tmp = *p + *len;
63690267Sdes	snprintf(tmp->name, PATH_MAX, "%s", name);
637109967Sdes	bcopy(us, &tmp->stat, sizeof(*us));
63841989Sdes
63990267Sdes	(*len)++;
64090267Sdes	(++tmp)->name[0] = 0;
64190267Sdes
64290267Sdes	return (0);
64341989Sdes}
644109695Sdes
645109695Sdes
646109695Sdes/*** Authentication-related utility functions ********************************/
647109695Sdes
648109695Sdesstatic const char *
649109695Sdes_fetch_read_word(FILE *f)
650109695Sdes{
651109695Sdes	static char word[1024];
652109695Sdes
653109695Sdes	if (fscanf(f, " %1024s ", word) != 1)
654109695Sdes		return (NULL);
655109695Sdes	return (word);
656109695Sdes}
657109695Sdes
658109695Sdes/*
659109695Sdes * Get authentication data for a URL from .netrc
660109695Sdes */
661109695Sdesint
662109695Sdes_fetch_netrc_auth(struct url *url)
663109695Sdes{
664109695Sdes	char fn[PATH_MAX];
665109695Sdes	const char *word;
666109695Sdes	char *p;
667109695Sdes	FILE *f;
668109695Sdes
669109695Sdes	if ((p = getenv("NETRC")) != NULL) {
670109967Sdes		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
671109695Sdes			_fetch_info("$NETRC specifies a file name "
672109695Sdes			    "longer than PATH_MAX");
673109695Sdes			return (-1);
674109695Sdes		}
675109695Sdes	} else {
676109695Sdes		if ((p = getenv("HOME")) != NULL) {
677109695Sdes			struct passwd *pwd;
678109695Sdes
679109695Sdes			if ((pwd = getpwuid(getuid())) == NULL ||
680109695Sdes			    (p = pwd->pw_dir) == NULL)
681109695Sdes				return (-1);
682109695Sdes		}
683109967Sdes		if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
684109695Sdes			return (-1);
685109695Sdes	}
686109695Sdes
687109695Sdes	if ((f = fopen(fn, "r")) == NULL)
688109695Sdes		return (-1);
689109695Sdes	while ((word = _fetch_read_word(f)) != NULL) {
690109695Sdes		if (strcmp(word, "default") == 0) {
691109695Sdes			DEBUG(_fetch_info("Using default .netrc settings"));
692109695Sdes			break;
693109695Sdes		}
694109695Sdes		if (strcmp(word, "machine") == 0 &&
695109695Sdes		    (word = _fetch_read_word(f)) != NULL &&
696109695Sdes		    strcasecmp(word, url->host) == 0) {
697109695Sdes			DEBUG(_fetch_info("Using .netrc settings for %s", word));
698109695Sdes			break;
699109695Sdes		}
700109695Sdes	}
701109695Sdes	if (word == NULL)
702109695Sdes		goto ferr;
703109695Sdes	while ((word = _fetch_read_word(f)) != NULL) {
704109695Sdes		if (strcmp(word, "login") == 0) {
705109695Sdes			if ((word = _fetch_read_word(f)) == NULL)
706109695Sdes				goto ferr;
707109967Sdes			if (snprintf(url->user, sizeof(url->user),
708109960Sjwd				"%s", word) > (int)sizeof(url->user)) {
709109695Sdes				_fetch_info("login name in .netrc is too long");
710109695Sdes				url->user[0] = '\0';
711109695Sdes			}
712109695Sdes		} else if (strcmp(word, "password") == 0) {
713109695Sdes			if ((word = _fetch_read_word(f)) == NULL)
714109695Sdes				goto ferr;
715109967Sdes			if (snprintf(url->pwd, sizeof(url->pwd),
716109960Sjwd				"%s", word) > (int)sizeof(url->pwd)) {
717109695Sdes				_fetch_info("password in .netrc is too long");
718109695Sdes				url->pwd[0] = '\0';
719109695Sdes			}
720109695Sdes		} else if (strcmp(word, "account") == 0) {
721109695Sdes			if ((word = _fetch_read_word(f)) == NULL)
722109695Sdes				goto ferr;
723109695Sdes			/* XXX not supported! */
724109695Sdes		} else {
725109695Sdes			break;
726109695Sdes		}
727109695Sdes	}
728109695Sdes	fclose(f);
729109695Sdes	return (0);
730109695Sdes ferr:
731109695Sdes	fclose(f);
732109695Sdes	return (-1);
733109695Sdes}
734