common.c revision 210568
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 210568 2010-07-28 16:11:22Z des $");
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>
41210568Sdes#include <fcntl.h>
4240939Sdes#include <netdb.h>
43109695Sdes#include <pwd.h>
4460924Sdes#include <stdarg.h>
4541862Sdes#include <stdlib.h>
4641862Sdes#include <stdio.h>
4740939Sdes#include <string.h>
4840939Sdes#include <unistd.h>
4940939Sdes
5040939Sdes#include "fetch.h"
5140939Sdes#include "common.h"
5240939Sdes
5340975Sdes
5440939Sdes/*** Local data **************************************************************/
5540939Sdes
5640939Sdes/*
5740939Sdes * Error messages for resolver errors
5840939Sdes */
59174588Sdesstatic struct fetcherr netdb_errlist[] = {
60121423Sume#ifdef EAI_NODATA
6190267Sdes	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
62121423Sume#endif
6390267Sdes	{ EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
6490267Sdes	{ EAI_FAIL,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
6590267Sdes	{ EAI_NONAME,	FETCH_RESOLV,	"No address record" },
6690267Sdes	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
6740939Sdes};
6840939Sdes
6962981Sdes/* End-of-Line */
7075891Sarchiestatic const char ENDL[2] = "\r\n";
7140939Sdes
7262981Sdes
7340939Sdes/*** Error-reporting functions ***********************************************/
7440939Sdes
7540939Sdes/*
7640939Sdes * Map error code to string
7740939Sdes */
7860924Sdesstatic struct fetcherr *
79174588Sdesfetch_finderr(struct fetcherr *p, int e)
8040939Sdes{
8190267Sdes	while (p->num != -1 && p->num != e)
8290267Sdes		p++;
8390267Sdes	return (p);
8440939Sdes}
8540939Sdes
8640939Sdes/*
8740939Sdes * Set error code
8840939Sdes */
8940939Sdesvoid
90174588Sdesfetch_seterr(struct fetcherr *p, int e)
9140939Sdes{
92174588Sdes	p = fetch_finderr(p, e);
9390267Sdes	fetchLastErrCode = p->cat;
9490267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
9540939Sdes}
9640939Sdes
9740939Sdes/*
9840939Sdes * Set error code according to errno
9940939Sdes */
10040939Sdesvoid
101174588Sdesfetch_syserr(void)
10240939Sdes{
10390267Sdes	switch (errno) {
10490267Sdes	case 0:
10590267Sdes		fetchLastErrCode = FETCH_OK;
10690267Sdes		break;
10790267Sdes	case EPERM:
10890267Sdes	case EACCES:
10990267Sdes	case EROFS:
11090267Sdes	case EAUTH:
11190267Sdes	case ENEEDAUTH:
11290267Sdes		fetchLastErrCode = FETCH_AUTH;
11390267Sdes		break;
11490267Sdes	case ENOENT:
11590267Sdes	case EISDIR: /* XXX */
11690267Sdes		fetchLastErrCode = FETCH_UNAVAIL;
11790267Sdes		break;
11890267Sdes	case ENOMEM:
11990267Sdes		fetchLastErrCode = FETCH_MEMORY;
12090267Sdes		break;
12190267Sdes	case EBUSY:
12290267Sdes	case EAGAIN:
12390267Sdes		fetchLastErrCode = FETCH_TEMP;
12490267Sdes		break;
12590267Sdes	case EEXIST:
12690267Sdes		fetchLastErrCode = FETCH_EXISTS;
12790267Sdes		break;
12890267Sdes	case ENOSPC:
12990267Sdes		fetchLastErrCode = FETCH_FULL;
13090267Sdes		break;
13190267Sdes	case EADDRINUSE:
13290267Sdes	case EADDRNOTAVAIL:
13390267Sdes	case ENETDOWN:
13490267Sdes	case ENETUNREACH:
13590267Sdes	case ENETRESET:
13690267Sdes	case EHOSTUNREACH:
13790267Sdes		fetchLastErrCode = FETCH_NETWORK;
13890267Sdes		break;
13990267Sdes	case ECONNABORTED:
14090267Sdes	case ECONNRESET:
14190267Sdes		fetchLastErrCode = FETCH_ABORT;
14290267Sdes		break;
14390267Sdes	case ETIMEDOUT:
14490267Sdes		fetchLastErrCode = FETCH_TIMEOUT;
14590267Sdes		break;
14690267Sdes	case ECONNREFUSED:
14790267Sdes	case EHOSTDOWN:
14890267Sdes		fetchLastErrCode = FETCH_DOWN;
14990267Sdes		break;
15090267Sdesdefault:
15190267Sdes		fetchLastErrCode = FETCH_UNKNOWN;
15290267Sdes	}
15390267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
15440939Sdes}
15540939Sdes
15640939Sdes
15741862Sdes/*
15841862Sdes * Emit status message
15941862Sdes */
16060924Sdesvoid
161174588Sdesfetch_info(const char *fmt, ...)
16241862Sdes{
16390267Sdes	va_list ap;
16490267Sdes
16590267Sdes	va_start(ap, fmt);
16690267Sdes	vfprintf(stderr, fmt, ap);
16790267Sdes	va_end(ap);
16890267Sdes	fputc('\n', stderr);
16941862Sdes}
17041862Sdes
17141862Sdes
17240939Sdes/*** Network-related utility functions ***************************************/
17340939Sdes
17440939Sdes/*
17568551Sdes * Return the default port for a scheme
17668551Sdes */
17768551Sdesint
178174588Sdesfetch_default_port(const char *scheme)
17968551Sdes{
18090267Sdes	struct servent *se;
18168551Sdes
18290267Sdes	if ((se = getservbyname(scheme, "tcp")) != NULL)
18390267Sdes		return (ntohs(se->s_port));
18490267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
18590267Sdes		return (FTP_DEFAULT_PORT);
18690267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
18790267Sdes		return (HTTP_DEFAULT_PORT);
18890267Sdes	return (0);
18968551Sdes}
19068551Sdes
19168551Sdes/*
19268551Sdes * Return the default proxy port for a scheme
19368551Sdes */
19468551Sdesint
195174588Sdesfetch_default_proxy_port(const char *scheme)
19668551Sdes{
19790267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
19890267Sdes		return (FTP_DEFAULT_PROXY_PORT);
19990267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
20090267Sdes		return (HTTP_DEFAULT_PROXY_PORT);
20190267Sdes	return (0);
20268551Sdes}
20368551Sdes
20498117Sdes
20568551Sdes/*
20697866Sdes * Create a connection for an existing descriptor.
20797866Sdes */
20897866Sdesconn_t *
209174588Sdesfetch_reopen(int sd)
21097866Sdes{
21197866Sdes	conn_t *conn;
21297866Sdes
21397866Sdes	/* allocate and fill connection structure */
214109967Sdes	if ((conn = calloc(1, sizeof(*conn))) == NULL)
21597866Sdes		return (NULL);
21697866Sdes	conn->sd = sd;
21798117Sdes	++conn->ref;
21897866Sdes	return (conn);
21997866Sdes}
22097866Sdes
22197866Sdes
22297866Sdes/*
22398117Sdes * Bump a connection's reference count.
22498117Sdes */
22598117Sdesconn_t *
226174588Sdesfetch_ref(conn_t *conn)
22798117Sdes{
22898117Sdes
22998117Sdes	++conn->ref;
23098117Sdes	return (conn);
23198117Sdes}
23298117Sdes
23398117Sdes
23498117Sdes/*
235111816Sdes * Bind a socket to a specific local address
236111816Sdes */
237111816Sdesint
238174588Sdesfetch_bind(int sd, int af, const char *addr)
239111816Sdes{
240111816Sdes	struct addrinfo hints, *res, *res0;
241111816Sdes	int err;
242111816Sdes
243111816Sdes	memset(&hints, 0, sizeof(hints));
244111816Sdes	hints.ai_family = af;
245111816Sdes	hints.ai_socktype = SOCK_STREAM;
246111816Sdes	hints.ai_protocol = 0;
247111816Sdes	if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)
248111816Sdes		return (-1);
249111816Sdes	for (res = res0; res; res = res->ai_next)
250111816Sdes		if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
251111816Sdes			return (0);
252111816Sdes	return (-1);
253111816Sdes}
254111816Sdes
255111816Sdes
256111816Sdes/*
25740939Sdes * Establish a TCP connection to the specified port on the specified host.
25840939Sdes */
25997856Sdesconn_t *
260174588Sdesfetch_connect(const char *host, int port, int af, int verbose)
26140939Sdes{
26297856Sdes	conn_t *conn;
26390267Sdes	char pbuf[10];
264111816Sdes	const char *bindaddr;
26590267Sdes	struct addrinfo hints, *res, *res0;
26690267Sdes	int sd, err;
26740939Sdes
26890267Sdes	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
26941862Sdes
27090267Sdes	if (verbose)
271174588Sdes		fetch_info("looking up %s", host);
27240939Sdes
27390267Sdes	/* look up host name and set up socket address structure */
27490267Sdes	snprintf(pbuf, sizeof(pbuf), "%d", port);
27590267Sdes	memset(&hints, 0, sizeof(hints));
27690267Sdes	hints.ai_family = af;
27790267Sdes	hints.ai_socktype = SOCK_STREAM;
27890267Sdes	hints.ai_protocol = 0;
27990267Sdes	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
280174588Sdes		netdb_seterr(err);
28197856Sdes		return (NULL);
28290267Sdes	}
283111816Sdes	bindaddr = getenv("FETCH_BIND_ADDRESS");
28490267Sdes
28590267Sdes	if (verbose)
286174588Sdes		fetch_info("connecting to %s:%d", host, port);
28790267Sdes
28890267Sdes	/* try to connect */
289111816Sdes	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
29090267Sdes		if ((sd = socket(res->ai_family, res->ai_socktype,
29162981Sdes			 res->ai_protocol)) == -1)
29290267Sdes			continue;
293111816Sdes		if (bindaddr != NULL && *bindaddr != '\0' &&
294174588Sdes		    fetch_bind(sd, res->ai_family, bindaddr) != 0) {
295174588Sdes			fetch_info("failed to bind to '%s'", bindaddr);
296111816Sdes			close(sd);
297111816Sdes			continue;
298111816Sdes		}
299210568Sdes		if (connect(sd, res->ai_addr, res->ai_addrlen) == 0 &&
300210568Sdes		    fcntl(sd, F_SETFL, O_NONBLOCK) == 0)
30190267Sdes			break;
30290267Sdes		close(sd);
30390267Sdes	}
30490267Sdes	freeaddrinfo(res0);
30590267Sdes	if (sd == -1) {
306174588Sdes		fetch_syserr();
30797856Sdes		return (NULL);
30890267Sdes	}
30940939Sdes
310174588Sdes	if ((conn = fetch_reopen(sd)) == NULL) {
311174588Sdes		fetch_syserr();
31297856Sdes		close(sd);
313103459Sfenner	}
31497856Sdes	return (conn);
31540939Sdes}
31641989Sdes
31741989Sdes
31855557Sdes/*
31997868Sdes * Enable SSL on a connection.
32097868Sdes */
32197868Sdesint
322174588Sdesfetch_ssl(conn_t *conn, int verbose)
32397868Sdes{
324210568Sdes	int ret, ssl_err;
32597868Sdes
32697891Sdes#ifdef WITH_SSL
32797868Sdes	/* Init the SSL library and context */
32897868Sdes	if (!SSL_library_init()){
32997868Sdes		fprintf(stderr, "SSL library init failed\n");
33097868Sdes		return (-1);
33197868Sdes	}
33297868Sdes
33397868Sdes	SSL_load_error_strings();
33497868Sdes
33597868Sdes	conn->ssl_meth = SSLv23_client_method();
33697868Sdes	conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
337108579Sdes	SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
33897868Sdes
33997868Sdes	conn->ssl = SSL_new(conn->ssl_ctx);
34097868Sdes	if (conn->ssl == NULL){
34197868Sdes		fprintf(stderr, "SSL context creation failed\n");
34297868Sdes		return (-1);
34397868Sdes	}
34497868Sdes	SSL_set_fd(conn->ssl, conn->sd);
345210568Sdes	while ((ret = SSL_connect(conn->ssl)) == -1) {
346210568Sdes		ssl_err = SSL_get_error(conn->ssl, ret);
347210568Sdes		if (ssl_err != SSL_ERROR_WANT_READ &&
348210568Sdes		    ssl_err != SSL_ERROR_WANT_WRITE) {
349210568Sdes			ERR_print_errors_fp(stderr);
350210568Sdes			return (-1);
351210568Sdes		}
35297868Sdes	}
35397868Sdes
35497868Sdes	if (verbose) {
35597868Sdes		X509_NAME *name;
35697868Sdes		char *str;
35797868Sdes
35897868Sdes		fprintf(stderr, "SSL connection established using %s\n",
35997868Sdes		    SSL_get_cipher(conn->ssl));
36097868Sdes		conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
36197868Sdes		name = X509_get_subject_name(conn->ssl_cert);
36297868Sdes		str = X509_NAME_oneline(name, 0, 0);
36397868Sdes		printf("Certificate subject: %s\n", str);
36497868Sdes		free(str);
36597868Sdes		name = X509_get_issuer_name(conn->ssl_cert);
36697868Sdes		str = X509_NAME_oneline(name, 0, 0);
36797868Sdes		printf("Certificate issuer: %s\n", str);
36897868Sdes		free(str);
36997868Sdes	}
37097868Sdes
37197868Sdes	return (0);
37297891Sdes#else
37397891Sdes	(void)conn;
37497891Sdes	(void)verbose;
37597891Sdes	fprintf(stderr, "SSL support disabled\n");
37697891Sdes	return (-1);
37797891Sdes#endif
37897868Sdes}
37997868Sdes
380210568Sdes#define FETCH_READ_WAIT		-2
381210568Sdes#define FETCH_READ_ERROR	-1
382210568Sdes#define FETCH_READ_DONE		 0
38398117Sdes
384210568Sdes#ifdef WITH_SSL
385210568Sdesstatic ssize_t
386210568Sdesfetch_ssl_read(SSL *ssl, char *buf, size_t len)
387210568Sdes{
388210568Sdes	ssize_t rlen;
389210568Sdes	int ssl_err;
390210568Sdes
391210568Sdes	rlen = SSL_read(ssl, buf, len);
392210568Sdes	if (rlen < 0) {
393210568Sdes		ssl_err = SSL_get_error(ssl, rlen);
394210568Sdes		if (ssl_err == SSL_ERROR_WANT_READ ||
395210568Sdes		    ssl_err == SSL_ERROR_WANT_WRITE) {
396210568Sdes			return (FETCH_READ_WAIT);
397210568Sdes		} else {
398210568Sdes			ERR_print_errors_fp(stderr);
399210568Sdes			return (FETCH_READ_ERROR);
400210568Sdes		}
401210568Sdes	}
402210568Sdes	return (rlen);
403210568Sdes}
404210568Sdes#endif
405210568Sdes
406210568Sdesstatic ssize_t
407210568Sdesfetch_socket_read(int sd, char *buf, size_t len)
408210568Sdes{
409210568Sdes	ssize_t rlen;
410210568Sdes
411210568Sdes	rlen = read(sd, buf, len);
412210568Sdes	if (rlen < 0) {
413210568Sdes		if (errno == EAGAIN || (errno == EINTR && fetchRestartCalls))
414210568Sdes			return (FETCH_READ_WAIT);
415210568Sdes		else
416210568Sdes			return (FETCH_READ_ERROR);
417210568Sdes	}
418210568Sdes	return (rlen);
419210568Sdes}
420210568Sdes
42197868Sdes/*
42297866Sdes * Read a character from a connection w/ timeout
42355557Sdes */
42497866Sdesssize_t
425174588Sdesfetch_read(conn_t *conn, char *buf, size_t len)
42655557Sdes{
427177447Sdes	struct timeval now, timeout, delta;
42890267Sdes	fd_set readfds;
42997866Sdes	ssize_t rlen, total;
43090267Sdes	int r;
43190267Sdes
43255557Sdes	if (fetchTimeout) {
43397866Sdes		FD_ZERO(&readfds);
43490267Sdes		gettimeofday(&timeout, NULL);
43590267Sdes		timeout.tv_sec += fetchTimeout;
43655557Sdes	}
43790267Sdes
43897866Sdes	total = 0;
43997866Sdes	while (len > 0) {
440210568Sdes		/*
441210568Sdes		 * The socket is non-blocking.  Instead of the canonical
442210568Sdes		 * select() -> read(), we do the following:
443210568Sdes		 *
444210568Sdes		 * 1) call read() or SSL_read().
445210568Sdes		 * 2) if an error occurred, return -1.
446210568Sdes		 * 3) if we received data but we still expect more,
447210568Sdes		 *    update our counters and loop.
448210568Sdes		 * 4) if read() or SSL_read() signaled EOF, return.
449210568Sdes		 * 5) if we did not receive any data but we're not at EOF,
450210568Sdes		 *    call select().
451210568Sdes		 *
452210568Sdes		 * In the SSL case, this is necessary because if we
453210568Sdes		 * receive a close notification, we have to call
454210568Sdes		 * SSL_read() one additional time after we've read
455210568Sdes		 * everything we received.
456210568Sdes		 *
457210568Sdes		 * In the non-SSL case, it may improve performance (very
458210568Sdes		 * slightly) when reading small amounts of data.
459210568Sdes		 */
460210568Sdes#ifdef WITH_SSL
461210568Sdes		if (conn->ssl != NULL)
462210568Sdes			rlen = fetch_ssl_read(conn->ssl, buf, len);
463210568Sdes		else
464210568Sdes#endif
465210568Sdes			rlen = fetch_socket_read(conn->sd, buf, len);
466210568Sdes		if (rlen == 0) {
467210568Sdes			break;
468210568Sdes		} else if (rlen > 0) {
469210568Sdes			len -= rlen;
470210568Sdes			buf += rlen;
471210568Sdes			total += rlen;
472210568Sdes			continue;
473210568Sdes		} else if (rlen == FETCH_READ_ERROR) {
474210568Sdes			return (-1);
475210568Sdes		}
476210568Sdes		// assert(rlen == FETCH_READ_WAIT);
47797866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
47897856Sdes			FD_SET(conn->sd, &readfds);
47990267Sdes			gettimeofday(&now, NULL);
480177447Sdes			delta.tv_sec = timeout.tv_sec - now.tv_sec;
481177447Sdes			delta.tv_usec = timeout.tv_usec - now.tv_usec;
482177447Sdes			if (delta.tv_usec < 0) {
483177447Sdes				delta.tv_usec += 1000000;
484177447Sdes				delta.tv_sec--;
48590267Sdes			}
486177447Sdes			if (delta.tv_sec < 0) {
487106186Sdes				errno = ETIMEDOUT;
488174588Sdes				fetch_syserr();
489106186Sdes				return (-1);
490106186Sdes			}
49197866Sdes			errno = 0;
492177447Sdes			r = select(conn->sd + 1, &readfds, NULL, NULL, &delta);
49390267Sdes			if (r == -1) {
49490267Sdes				if (errno == EINTR && fetchRestartCalls)
49590267Sdes					continue;
496174588Sdes				fetch_syserr();
49790267Sdes				return (-1);
49890267Sdes			}
49990267Sdes		}
50097866Sdes	}
50197866Sdes	return (total);
50297866Sdes}
50397866Sdes
50498117Sdes
50597866Sdes/*
50697866Sdes * Read a line of text from a connection w/ timeout
50797866Sdes */
50897866Sdes#define MIN_BUF_SIZE 1024
50997866Sdes
51097866Sdesint
511174588Sdesfetch_getln(conn_t *conn)
51297866Sdes{
51397866Sdes	char *tmp;
51497866Sdes	size_t tmpsize;
515106186Sdes	ssize_t len;
51697866Sdes	char c;
51797866Sdes
51897866Sdes	if (conn->buf == NULL) {
51997866Sdes		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
52097866Sdes			errno = ENOMEM;
52197866Sdes			return (-1);
52297866Sdes		}
52397866Sdes		conn->bufsize = MIN_BUF_SIZE;
52497866Sdes	}
52597866Sdes
52697866Sdes	conn->buf[0] = '\0';
52797866Sdes	conn->buflen = 0;
52897866Sdes
52997866Sdes	do {
530174588Sdes		len = fetch_read(conn, &c, 1);
531106186Sdes		if (len == -1)
53297866Sdes			return (-1);
533106186Sdes		if (len == 0)
534106137Sobrien			break;
53597856Sdes		conn->buf[conn->buflen++] = c;
53697856Sdes		if (conn->buflen == conn->bufsize) {
53797856Sdes			tmp = conn->buf;
53897856Sdes			tmpsize = conn->bufsize * 2 + 1;
53997856Sdes			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
54090267Sdes				errno = ENOMEM;
54190267Sdes				return (-1);
54290267Sdes			}
54397856Sdes			conn->buf = tmp;
54497856Sdes			conn->bufsize = tmpsize;
54590267Sdes		}
54690267Sdes	} while (c != '\n');
54790267Sdes
54897856Sdes	conn->buf[conn->buflen] = '\0';
54997856Sdes	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
55090267Sdes	return (0);
55155557Sdes}
55255557Sdes
55355557Sdes
55462981Sdes/*
55597866Sdes * Write to a connection w/ timeout
55662981Sdes */
55797866Sdesssize_t
558174588Sdesfetch_write(conn_t *conn, const char *buf, size_t len)
55997866Sdes{
560106175Simp	struct iovec iov;
561106175Simp
562106175Simp	iov.iov_base = __DECONST(char *, buf);
563106175Simp	iov.iov_len = len;
564174588Sdes	return fetch_writev(conn, &iov, 1);
565106175Simp}
566106175Simp
567106175Simp/*
568106175Simp * Write a vector to a connection w/ timeout
569106175Simp * Note: can modify the iovec.
570106175Simp */
571106175Simpssize_t
572174588Sdesfetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
573106175Simp{
574177447Sdes	struct timeval now, timeout, delta;
57597866Sdes	fd_set writefds;
57697866Sdes	ssize_t wlen, total;
57797866Sdes	int r;
57897866Sdes
57997866Sdes	if (fetchTimeout) {
58097866Sdes		FD_ZERO(&writefds);
58197866Sdes		gettimeofday(&timeout, NULL);
58297866Sdes		timeout.tv_sec += fetchTimeout;
58397866Sdes	}
58497866Sdes
585106175Simp	total = 0;
586106175Simp	while (iovcnt > 0) {
58797866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
58897866Sdes			FD_SET(conn->sd, &writefds);
58997866Sdes			gettimeofday(&now, NULL);
590177447Sdes			delta.tv_sec = timeout.tv_sec - now.tv_sec;
591177447Sdes			delta.tv_usec = timeout.tv_usec - now.tv_usec;
592177447Sdes			if (delta.tv_usec < 0) {
593177447Sdes				delta.tv_usec += 1000000;
594177447Sdes				delta.tv_sec--;
59597866Sdes			}
596177447Sdes			if (delta.tv_sec < 0) {
59797866Sdes				errno = ETIMEDOUT;
598174588Sdes				fetch_syserr();
59997866Sdes				return (-1);
60097866Sdes			}
60197866Sdes			errno = 0;
602177447Sdes			r = select(conn->sd + 1, NULL, &writefds, NULL, &delta);
60397866Sdes			if (r == -1) {
60497866Sdes				if (errno == EINTR && fetchRestartCalls)
60597866Sdes					continue;
60697866Sdes				return (-1);
60797866Sdes			}
60897866Sdes		}
60997866Sdes		errno = 0;
61097891Sdes#ifdef WITH_SSL
61197866Sdes		if (conn->ssl != NULL)
612106175Simp			wlen = SSL_write(conn->ssl,
613106175Simp			    iov->iov_base, iov->iov_len);
61497866Sdes		else
61597891Sdes#endif
616106175Simp			wlen = writev(conn->sd, iov, iovcnt);
617106175Simp		if (wlen == 0) {
61897866Sdes			/* we consider a short write a failure */
619210568Sdes			/* XXX perhaps we shouldn't in the SSL case */
620106175Simp			errno = EPIPE;
621174588Sdes			fetch_syserr();
62297866Sdes			return (-1);
623106175Simp		}
62497866Sdes		if (wlen < 0) {
62597866Sdes			if (errno == EINTR && fetchRestartCalls)
62697866Sdes				continue;
62797866Sdes			return (-1);
62897866Sdes		}
62997866Sdes		total += wlen;
630106175Simp		while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
631106175Simp			wlen -= iov->iov_len;
632106175Simp			iov++;
633106175Simp			iovcnt--;
634106175Simp		}
635106175Simp		if (iovcnt > 0) {
636106175Simp			iov->iov_len -= wlen;
637106175Simp			iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;
638106175Simp		}
63997866Sdes	}
64097866Sdes	return (total);
64197866Sdes}
64297866Sdes
64398117Sdes
64497866Sdes/*
64597866Sdes * Write a line of text to a connection w/ timeout
64697866Sdes */
64762981Sdesint
648174588Sdesfetch_putln(conn_t *conn, const char *str, size_t len)
64962981Sdes{
650106175Simp	struct iovec iov[2];
651106205Sdes	int ret;
65298748Sdes
65398748Sdes	DEBUG(fprintf(stderr, ">>> %s\n", str));
654106175Simp	iov[0].iov_base = __DECONST(char *, str);
655106175Simp	iov[0].iov_len = len;
656106175Simp	iov[1].iov_base = __DECONST(char *, ENDL);
657109967Sdes	iov[1].iov_len = sizeof(ENDL);
658106205Sdes	if (len == 0)
659174588Sdes		ret = fetch_writev(conn, &iov[1], 1);
660106205Sdes	else
661174588Sdes		ret = fetch_writev(conn, iov, 2);
662106205Sdes	if (ret == -1)
66390267Sdes		return (-1);
66490267Sdes	return (0);
66562981Sdes}
66662981Sdes
66762981Sdes
66897856Sdes/*
66997856Sdes * Close connection
67097856Sdes */
67197856Sdesint
672174588Sdesfetch_close(conn_t *conn)
67397856Sdes{
67497856Sdes	int ret;
67597856Sdes
67698117Sdes	if (--conn->ref > 0)
67798117Sdes		return (0);
67897856Sdes	ret = close(conn->sd);
679141970Sdes	free(conn->buf);
68097856Sdes	free(conn);
68197856Sdes	return (ret);
68297856Sdes}
68397856Sdes
68497856Sdes
68541989Sdes/*** Directory-related utility functions *************************************/
68641989Sdes
68741989Sdesint
688174588Sdesfetch_add_entry(struct url_ent **p, int *size, int *len,
68990267Sdes    const char *name, struct url_stat *us)
69041989Sdes{
69190267Sdes	struct url_ent *tmp;
69241989Sdes
69390267Sdes	if (*p == NULL) {
69490268Sdes		*size = 0;
69590267Sdes		*len = 0;
69641989Sdes	}
69741989Sdes
69890267Sdes	if (*len >= *size - 1) {
699109967Sdes		tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
70090267Sdes		if (tmp == NULL) {
70190267Sdes			errno = ENOMEM;
702174588Sdes			fetch_syserr();
70390267Sdes			return (-1);
70490267Sdes		}
70590268Sdes		*size = (*size * 2 + 1);
70690267Sdes		*p = tmp;
70790267Sdes	}
70841989Sdes
70990267Sdes	tmp = *p + *len;
71090267Sdes	snprintf(tmp->name, PATH_MAX, "%s", name);
711176105Sdes	memcpy(&tmp->stat, us, sizeof(*us));
71241989Sdes
71390267Sdes	(*len)++;
71490267Sdes	(++tmp)->name[0] = 0;
71590267Sdes
71690267Sdes	return (0);
71741989Sdes}
718109695Sdes
719109695Sdes
720109695Sdes/*** Authentication-related utility functions ********************************/
721109695Sdes
722109695Sdesstatic const char *
723174588Sdesfetch_read_word(FILE *f)
724109695Sdes{
725109695Sdes	static char word[1024];
726109695Sdes
727178234Scperciva	if (fscanf(f, " %1023s ", word) != 1)
728109695Sdes		return (NULL);
729109695Sdes	return (word);
730109695Sdes}
731109695Sdes
732109695Sdes/*
733109695Sdes * Get authentication data for a URL from .netrc
734109695Sdes */
735109695Sdesint
736174588Sdesfetch_netrc_auth(struct url *url)
737109695Sdes{
738109695Sdes	char fn[PATH_MAX];
739109695Sdes	const char *word;
740109695Sdes	char *p;
741109695Sdes	FILE *f;
742109695Sdes
743109695Sdes	if ((p = getenv("NETRC")) != NULL) {
744109967Sdes		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
745174588Sdes			fetch_info("$NETRC specifies a file name "
746109695Sdes			    "longer than PATH_MAX");
747109695Sdes			return (-1);
748109695Sdes		}
749109695Sdes	} else {
750109695Sdes		if ((p = getenv("HOME")) != NULL) {
751109695Sdes			struct passwd *pwd;
752109695Sdes
753109695Sdes			if ((pwd = getpwuid(getuid())) == NULL ||
754109695Sdes			    (p = pwd->pw_dir) == NULL)
755109695Sdes				return (-1);
756109695Sdes		}
757109967Sdes		if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
758109695Sdes			return (-1);
759109695Sdes	}
760109695Sdes
761109695Sdes	if ((f = fopen(fn, "r")) == NULL)
762109695Sdes		return (-1);
763174588Sdes	while ((word = fetch_read_word(f)) != NULL) {
764109695Sdes		if (strcmp(word, "default") == 0) {
765174588Sdes			DEBUG(fetch_info("Using default .netrc settings"));
766109695Sdes			break;
767109695Sdes		}
768109695Sdes		if (strcmp(word, "machine") == 0 &&
769174588Sdes		    (word = fetch_read_word(f)) != NULL &&
770109695Sdes		    strcasecmp(word, url->host) == 0) {
771174588Sdes			DEBUG(fetch_info("Using .netrc settings for %s", word));
772109695Sdes			break;
773109695Sdes		}
774109695Sdes	}
775109695Sdes	if (word == NULL)
776109695Sdes		goto ferr;
777174588Sdes	while ((word = fetch_read_word(f)) != NULL) {
778109695Sdes		if (strcmp(word, "login") == 0) {
779174588Sdes			if ((word = fetch_read_word(f)) == NULL)
780109695Sdes				goto ferr;
781109967Sdes			if (snprintf(url->user, sizeof(url->user),
782109960Sjwd				"%s", word) > (int)sizeof(url->user)) {
783174588Sdes				fetch_info("login name in .netrc is too long");
784109695Sdes				url->user[0] = '\0';
785109695Sdes			}
786109695Sdes		} else if (strcmp(word, "password") == 0) {
787174588Sdes			if ((word = fetch_read_word(f)) == NULL)
788109695Sdes				goto ferr;
789109967Sdes			if (snprintf(url->pwd, sizeof(url->pwd),
790109960Sjwd				"%s", word) > (int)sizeof(url->pwd)) {
791174588Sdes				fetch_info("password in .netrc is too long");
792109695Sdes				url->pwd[0] = '\0';
793109695Sdes			}
794109695Sdes		} else if (strcmp(word, "account") == 0) {
795174588Sdes			if ((word = fetch_read_word(f)) == NULL)
796109695Sdes				goto ferr;
797109695Sdes			/* XXX not supported! */
798109695Sdes		} else {
799109695Sdes			break;
800109695Sdes		}
801109695Sdes	}
802109695Sdes	fclose(f);
803109695Sdes	return (0);
804109695Sdes ferr:
805109695Sdes	fclose(f);
806109695Sdes	return (-1);
807109695Sdes}
808174752Sdes
809174752Sdes/*
810174752Sdes * The no_proxy environment variable specifies a set of domains for
811174752Sdes * which the proxy should not be consulted; the contents is a comma-,
812174752Sdes * or space-separated list of domain names.  A single asterisk will
813174752Sdes * override all proxy variables and no transactions will be proxied
814174752Sdes * (for compatability with lynx and curl, see the discussion at
815174752Sdes * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
816174752Sdes */
817174752Sdesint
818174752Sdesfetch_no_proxy_match(const char *host)
819174752Sdes{
820174752Sdes	const char *no_proxy, *p, *q;
821174752Sdes	size_t h_len, d_len;
822174752Sdes
823174752Sdes	if ((no_proxy = getenv("NO_PROXY")) == NULL &&
824174752Sdes	    (no_proxy = getenv("no_proxy")) == NULL)
825174752Sdes		return (0);
826174752Sdes
827174752Sdes	/* asterisk matches any hostname */
828174752Sdes	if (strcmp(no_proxy, "*") == 0)
829174752Sdes		return (1);
830174752Sdes
831174752Sdes	h_len = strlen(host);
832174752Sdes	p = no_proxy;
833174752Sdes	do {
834174752Sdes		/* position p at the beginning of a domain suffix */
835174761Sdes		while (*p == ',' || isspace((unsigned char)*p))
836174752Sdes			p++;
837174752Sdes
838174752Sdes		/* position q at the first separator character */
839174752Sdes		for (q = p; *q; ++q)
840174761Sdes			if (*q == ',' || isspace((unsigned char)*q))
841174752Sdes				break;
842174752Sdes
843174752Sdes		d_len = q - p;
844198339Sfabient		if (d_len > 0 && h_len >= d_len &&
845174752Sdes		    strncasecmp(host + h_len - d_len,
846174752Sdes			p, d_len) == 0) {
847174752Sdes			/* domain name matches */
848174752Sdes			return (1);
849174752Sdes		}
850174752Sdes
851174752Sdes		p = q + 1;
852174752Sdes	} while (*q);
853174752Sdes
854174752Sdes	return (0);
855174752Sdes}
856