common.c revision 97866
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 97866 2002-06-05 12:19:08Z 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
3897866Sdes#include <ctype.h> /* XXX */
3940939Sdes#include <errno.h>
4040939Sdes#include <netdb.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
19968551Sdes/*
20097866Sdes * Create a connection for an existing descriptor.
20197866Sdes */
20297866Sdesconn_t *
20397866Sdes_fetch_reopen(int sd)
20497866Sdes{
20597866Sdes	conn_t *conn;
20697866Sdes
20797866Sdes	/* allocate and fill connection structure */
20897866Sdes	if ((conn = calloc(1, sizeof *conn)) == NULL)
20997866Sdes		return (NULL);
21097866Sdes	conn->sd = sd;
21197866Sdes	return (conn);
21297866Sdes}
21397866Sdes
21497866Sdes
21597866Sdes/*
21640939Sdes * Establish a TCP connection to the specified port on the specified host.
21740939Sdes */
21897856Sdesconn_t *
21975891Sarchie_fetch_connect(const char *host, int port, int af, int verbose)
22040939Sdes{
22197856Sdes	conn_t *conn;
22290267Sdes	char pbuf[10];
22390267Sdes	struct addrinfo hints, *res, *res0;
22490267Sdes	int sd, err;
22540939Sdes
22690267Sdes	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
22741862Sdes
22890267Sdes	if (verbose)
22990267Sdes		_fetch_info("looking up %s", host);
23040939Sdes
23190267Sdes	/* look up host name and set up socket address structure */
23290267Sdes	snprintf(pbuf, sizeof(pbuf), "%d", port);
23390267Sdes	memset(&hints, 0, sizeof(hints));
23490267Sdes	hints.ai_family = af;
23590267Sdes	hints.ai_socktype = SOCK_STREAM;
23690267Sdes	hints.ai_protocol = 0;
23790267Sdes	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
23890267Sdes		_netdb_seterr(err);
23997856Sdes		return (NULL);
24090267Sdes	}
24190267Sdes
24290267Sdes	if (verbose)
24390267Sdes		_fetch_info("connecting to %s:%d", host, port);
24490267Sdes
24590267Sdes	/* try to connect */
24690267Sdes	for (sd = -1, res = res0; res; res = res->ai_next) {
24790267Sdes		if ((sd = socket(res->ai_family, res->ai_socktype,
24862981Sdes			 res->ai_protocol)) == -1)
24990267Sdes			continue;
25090267Sdes		if (connect(sd, res->ai_addr, res->ai_addrlen) != -1)
25190267Sdes			break;
25290267Sdes		close(sd);
25390267Sdes		sd = -1;
25490267Sdes	}
25590267Sdes	freeaddrinfo(res0);
25690267Sdes	if (sd == -1) {
25790267Sdes		_fetch_syserr();
25897856Sdes		return (NULL);
25990267Sdes	}
26040939Sdes
26197866Sdes	if ((conn = _fetch_reopen(sd)) == NULL)
26297856Sdes		close(sd);
26397856Sdes	return (conn);
26440939Sdes}
26541989Sdes
26641989Sdes
26755557Sdes/*
26897866Sdes * Read a character from a connection w/ timeout
26955557Sdes */
27097866Sdesssize_t
27197866Sdes_fetch_read(conn_t *conn, char *buf, size_t len)
27255557Sdes{
27390267Sdes	struct timeval now, timeout, wait;
27490267Sdes	fd_set readfds;
27597866Sdes	ssize_t rlen, total;
27690267Sdes	int r;
27790267Sdes
27855557Sdes	if (fetchTimeout) {
27997866Sdes		FD_ZERO(&readfds);
28090267Sdes		gettimeofday(&timeout, NULL);
28190267Sdes		timeout.tv_sec += fetchTimeout;
28255557Sdes	}
28390267Sdes
28497866Sdes	total = 0;
28597866Sdes	while (len > 0) {
28697866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
28797856Sdes			FD_SET(conn->sd, &readfds);
28890267Sdes			gettimeofday(&now, NULL);
28990267Sdes			wait.tv_sec = timeout.tv_sec - now.tv_sec;
29090267Sdes			wait.tv_usec = timeout.tv_usec - now.tv_usec;
29190267Sdes			if (wait.tv_usec < 0) {
29290267Sdes				wait.tv_usec += 1000000;
29390267Sdes				wait.tv_sec--;
29490267Sdes			}
29597866Sdes			if (wait.tv_sec < 0)
29697866Sdes				return (rlen);
29797866Sdes			errno = 0;
29897856Sdes			r = select(conn->sd + 1, &readfds, NULL, NULL, &wait);
29990267Sdes			if (r == -1) {
30090267Sdes				if (errno == EINTR && fetchRestartCalls)
30190267Sdes					continue;
30290267Sdes				return (-1);
30390267Sdes			}
30490267Sdes		}
30597866Sdes		if (conn->ssl != NULL)
30697866Sdes			rlen = SSL_read(conn->ssl, buf, len);
30797866Sdes		else
30897866Sdes			rlen = read(conn->sd, buf, len);
30997866Sdes		if (rlen == 0)
31090267Sdes			break;
31197866Sdes		if (rlen < 0) {
31290267Sdes			if (errno == EINTR && fetchRestartCalls)
31390267Sdes				continue;
31490267Sdes			return (-1);
31590267Sdes		}
31697866Sdes		len -= rlen;
31797866Sdes		buf += rlen;
31897866Sdes		total += rlen;
31997866Sdes	}
32097866Sdes	return (total);
32197866Sdes}
32297866Sdes
32397866Sdes/*
32497866Sdes * Read a line of text from a connection w/ timeout
32597866Sdes */
32697866Sdes#define MIN_BUF_SIZE 1024
32797866Sdes
32897866Sdesint
32997866Sdes_fetch_getln(conn_t *conn)
33097866Sdes{
33197866Sdes	char *tmp;
33297866Sdes	size_t tmpsize;
33397866Sdes	char c;
33497866Sdes
33597866Sdes	if (conn->buf == NULL) {
33697866Sdes		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
33797866Sdes			errno = ENOMEM;
33897866Sdes			return (-1);
33997866Sdes		}
34097866Sdes		conn->bufsize = MIN_BUF_SIZE;
34197866Sdes	}
34297866Sdes
34397866Sdes	conn->buf[0] = '\0';
34497866Sdes	conn->buflen = 0;
34597866Sdes
34697866Sdes	do {
34797866Sdes		if (_fetch_read(conn, &c, 1) == -1)
34897866Sdes			return (-1);
34997856Sdes		conn->buf[conn->buflen++] = c;
35097856Sdes		if (conn->buflen == conn->bufsize) {
35197856Sdes			tmp = conn->buf;
35297856Sdes			tmpsize = conn->bufsize * 2 + 1;
35397856Sdes			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
35490267Sdes				errno = ENOMEM;
35590267Sdes				return (-1);
35690267Sdes			}
35797856Sdes			conn->buf = tmp;
35897856Sdes			conn->bufsize = tmpsize;
35990267Sdes		}
36090267Sdes	} while (c != '\n');
36190267Sdes
36297856Sdes	conn->buf[conn->buflen] = '\0';
36397856Sdes	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
36490267Sdes	return (0);
36555557Sdes}
36655557Sdes
36755557Sdes
36862981Sdes/*
36997866Sdes * Write to a connection w/ timeout
37062981Sdes */
37197866Sdesssize_t
37297866Sdes_fetch_write(conn_t *conn, const char *buf, size_t len)
37397866Sdes{
37497866Sdes	struct timeval now, timeout, wait;
37597866Sdes	fd_set writefds;
37697866Sdes	ssize_t wlen, total;
37797866Sdes	int r;
37897866Sdes
37997866Sdes	if (fetchTimeout) {
38097866Sdes		FD_ZERO(&writefds);
38197866Sdes		gettimeofday(&timeout, NULL);
38297866Sdes		timeout.tv_sec += fetchTimeout;
38397866Sdes	}
38497866Sdes
38597866Sdes	while (len > 0) {
38697866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
38797866Sdes			FD_SET(conn->sd, &writefds);
38897866Sdes			gettimeofday(&now, NULL);
38997866Sdes			wait.tv_sec = timeout.tv_sec - now.tv_sec;
39097866Sdes			wait.tv_usec = timeout.tv_usec - now.tv_usec;
39197866Sdes			if (wait.tv_usec < 0) {
39297866Sdes				wait.tv_usec += 1000000;
39397866Sdes				wait.tv_sec--;
39497866Sdes			}
39597866Sdes			if (wait.tv_sec < 0) {
39697866Sdes				errno = ETIMEDOUT;
39797866Sdes				return (-1);
39897866Sdes			}
39997866Sdes			errno = 0;
40097866Sdes			r = select(conn->sd + 1, NULL, &writefds, NULL, &wait);
40197866Sdes			if (r == -1) {
40297866Sdes				if (errno == EINTR && fetchRestartCalls)
40397866Sdes					continue;
40497866Sdes				return (-1);
40597866Sdes			}
40697866Sdes		}
40797866Sdes		errno = 0;
40897866Sdes		if (conn->ssl != NULL)
40997866Sdes			wlen = SSL_write(conn->ssl, buf, len);
41097866Sdes		else
41197866Sdes			wlen = write(conn->sd, buf, len);
41297866Sdes		if (wlen == 0)
41397866Sdes			/* we consider a short write a failure */
41497866Sdes			return (-1);
41597866Sdes		if (wlen < 0) {
41697866Sdes			if (errno == EINTR && fetchRestartCalls)
41797866Sdes				continue;
41897866Sdes			return (-1);
41997866Sdes		}
42097866Sdes		len -= wlen;
42197866Sdes		buf += wlen;
42297866Sdes		total += wlen;
42397866Sdes	}
42497866Sdes	return (total);
42597866Sdes}
42697866Sdes
42797866Sdes/*
42897866Sdes * Write a line of text to a connection w/ timeout
42997866Sdes */
43062981Sdesint
43197856Sdes_fetch_putln(conn_t *conn, const char *str, size_t len)
43262981Sdes{
43397866Sdes	if (_fetch_write(conn, str, len) == -1 ||
43497866Sdes	    _fetch_write(conn, ENDL, sizeof ENDL) == -1)
43590267Sdes		return (-1);
43690267Sdes	return (0);
43762981Sdes}
43862981Sdes
43962981Sdes
44097856Sdes/*
44197856Sdes * Close connection
44297856Sdes */
44397856Sdesint
44497856Sdes_fetch_close(conn_t *conn)
44597856Sdes{
44697856Sdes	int ret;
44797856Sdes
44897856Sdes	ret = close(conn->sd);
44997856Sdes	free(conn);
45097856Sdes	return (ret);
45197856Sdes}
45297856Sdes
45397856Sdes
45441989Sdes/*** Directory-related utility functions *************************************/
45541989Sdes
45641989Sdesint
45741989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len,
45890267Sdes    const char *name, struct url_stat *us)
45941989Sdes{
46090267Sdes	struct url_ent *tmp;
46141989Sdes
46290267Sdes	if (*p == NULL) {
46390268Sdes		*size = 0;
46490267Sdes		*len = 0;
46541989Sdes	}
46641989Sdes
46790267Sdes	if (*len >= *size - 1) {
46890268Sdes		tmp = realloc(*p, (*size * 2 + 1) * sizeof **p);
46990267Sdes		if (tmp == NULL) {
47090267Sdes			errno = ENOMEM;
47190267Sdes			_fetch_syserr();
47290267Sdes			return (-1);
47390267Sdes		}
47490268Sdes		*size = (*size * 2 + 1);
47590267Sdes		*p = tmp;
47690267Sdes	}
47741989Sdes
47890267Sdes	tmp = *p + *len;
47990267Sdes	snprintf(tmp->name, PATH_MAX, "%s", name);
48090267Sdes	bcopy(us, &tmp->stat, sizeof *us);
48141989Sdes
48290267Sdes	(*len)++;
48390267Sdes	(++tmp)->name[0] = 0;
48490267Sdes
48590267Sdes	return (0);
48641989Sdes}
487