common.c revision 90268
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 90268 2002-02-05 22:15:16Z 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>
4060924Sdes#include <stdarg.h>
4141862Sdes#include <stdlib.h>
4241862Sdes#include <stdio.h>
4340939Sdes#include <string.h>
4440939Sdes#include <unistd.h>
4540939Sdes
4640939Sdes#include "fetch.h"
4740939Sdes#include "common.h"
4840939Sdes
4940975Sdes
5040939Sdes/*** Local data **************************************************************/
5140939Sdes
5240939Sdes/*
5340939Sdes * Error messages for resolver errors
5440939Sdes */
5540939Sdesstatic struct fetcherr _netdb_errlist[] = {
5690267Sdes	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
5790267Sdes	{ EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
5890267Sdes	{ EAI_FAIL,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
5990267Sdes	{ EAI_NONAME,	FETCH_RESOLV,	"No address record" },
6090267Sdes	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
6140939Sdes};
6240939Sdes
6362981Sdes/* End-of-Line */
6475891Sarchiestatic const char ENDL[2] = "\r\n";
6540939Sdes
6662981Sdes
6740939Sdes/*** Error-reporting functions ***********************************************/
6840939Sdes
6940939Sdes/*
7040939Sdes * Map error code to string
7140939Sdes */
7260924Sdesstatic struct fetcherr *
7340975Sdes_fetch_finderr(struct fetcherr *p, int e)
7440939Sdes{
7590267Sdes	while (p->num != -1 && p->num != e)
7690267Sdes		p++;
7790267Sdes	return (p);
7840939Sdes}
7940939Sdes
8040939Sdes/*
8140939Sdes * Set error code
8240939Sdes */
8340939Sdesvoid
8440939Sdes_fetch_seterr(struct fetcherr *p, int e)
8540939Sdes{
8690267Sdes	p = _fetch_finderr(p, e);
8790267Sdes	fetchLastErrCode = p->cat;
8890267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
8940939Sdes}
9040939Sdes
9140939Sdes/*
9240939Sdes * Set error code according to errno
9340939Sdes */
9440939Sdesvoid
9540939Sdes_fetch_syserr(void)
9640939Sdes{
9790267Sdes	switch (errno) {
9890267Sdes	case 0:
9990267Sdes		fetchLastErrCode = FETCH_OK;
10090267Sdes		break;
10190267Sdes	case EPERM:
10290267Sdes	case EACCES:
10390267Sdes	case EROFS:
10490267Sdes	case EAUTH:
10590267Sdes	case ENEEDAUTH:
10690267Sdes		fetchLastErrCode = FETCH_AUTH;
10790267Sdes		break;
10890267Sdes	case ENOENT:
10990267Sdes	case EISDIR: /* XXX */
11090267Sdes		fetchLastErrCode = FETCH_UNAVAIL;
11190267Sdes		break;
11290267Sdes	case ENOMEM:
11390267Sdes		fetchLastErrCode = FETCH_MEMORY;
11490267Sdes		break;
11590267Sdes	case EBUSY:
11690267Sdes	case EAGAIN:
11790267Sdes		fetchLastErrCode = FETCH_TEMP;
11890267Sdes		break;
11990267Sdes	case EEXIST:
12090267Sdes		fetchLastErrCode = FETCH_EXISTS;
12190267Sdes		break;
12290267Sdes	case ENOSPC:
12390267Sdes		fetchLastErrCode = FETCH_FULL;
12490267Sdes		break;
12590267Sdes	case EADDRINUSE:
12690267Sdes	case EADDRNOTAVAIL:
12790267Sdes	case ENETDOWN:
12890267Sdes	case ENETUNREACH:
12990267Sdes	case ENETRESET:
13090267Sdes	case EHOSTUNREACH:
13190267Sdes		fetchLastErrCode = FETCH_NETWORK;
13290267Sdes		break;
13390267Sdes	case ECONNABORTED:
13490267Sdes	case ECONNRESET:
13590267Sdes		fetchLastErrCode = FETCH_ABORT;
13690267Sdes		break;
13790267Sdes	case ETIMEDOUT:
13890267Sdes		fetchLastErrCode = FETCH_TIMEOUT;
13990267Sdes		break;
14090267Sdes	case ECONNREFUSED:
14190267Sdes	case EHOSTDOWN:
14290267Sdes		fetchLastErrCode = FETCH_DOWN;
14390267Sdes		break;
14490267Sdesdefault:
14590267Sdes		fetchLastErrCode = FETCH_UNKNOWN;
14690267Sdes	}
14790267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
14840939Sdes}
14940939Sdes
15040939Sdes
15141862Sdes/*
15241862Sdes * Emit status message
15341862Sdes */
15460924Sdesvoid
15575891Sarchie_fetch_info(const char *fmt, ...)
15641862Sdes{
15790267Sdes	va_list ap;
15890267Sdes
15990267Sdes	va_start(ap, fmt);
16090267Sdes	vfprintf(stderr, fmt, ap);
16190267Sdes	va_end(ap);
16290267Sdes	fputc('\n', stderr);
16341862Sdes}
16441862Sdes
16541862Sdes
16640939Sdes/*** Network-related utility functions ***************************************/
16740939Sdes
16840939Sdes/*
16968551Sdes * Return the default port for a scheme
17068551Sdes */
17168551Sdesint
17275891Sarchie_fetch_default_port(const char *scheme)
17368551Sdes{
17490267Sdes	struct servent *se;
17568551Sdes
17690267Sdes	if ((se = getservbyname(scheme, "tcp")) != NULL)
17790267Sdes		return (ntohs(se->s_port));
17890267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
17990267Sdes		return (FTP_DEFAULT_PORT);
18090267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
18190267Sdes		return (HTTP_DEFAULT_PORT);
18290267Sdes	return (0);
18368551Sdes}
18468551Sdes
18568551Sdes/*
18668551Sdes * Return the default proxy port for a scheme
18768551Sdes */
18868551Sdesint
18975891Sarchie_fetch_default_proxy_port(const char *scheme)
19068551Sdes{
19190267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
19290267Sdes		return (FTP_DEFAULT_PROXY_PORT);
19390267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
19490267Sdes		return (HTTP_DEFAULT_PROXY_PORT);
19590267Sdes	return (0);
19668551Sdes}
19768551Sdes
19868551Sdes/*
19940939Sdes * Establish a TCP connection to the specified port on the specified host.
20040939Sdes */
20140939Sdesint
20275891Sarchie_fetch_connect(const char *host, int port, int af, int verbose)
20340939Sdes{
20490267Sdes	char pbuf[10];
20590267Sdes	struct addrinfo hints, *res, *res0;
20690267Sdes	int sd, err;
20740939Sdes
20890267Sdes	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
20941862Sdes
21090267Sdes	if (verbose)
21190267Sdes		_fetch_info("looking up %s", host);
21240939Sdes
21390267Sdes	/* look up host name and set up socket address structure */
21490267Sdes	snprintf(pbuf, sizeof(pbuf), "%d", port);
21590267Sdes	memset(&hints, 0, sizeof(hints));
21690267Sdes	hints.ai_family = af;
21790267Sdes	hints.ai_socktype = SOCK_STREAM;
21890267Sdes	hints.ai_protocol = 0;
21990267Sdes	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
22090267Sdes		_netdb_seterr(err);
22190267Sdes		return (-1);
22290267Sdes	}
22390267Sdes
22490267Sdes	if (verbose)
22590267Sdes		_fetch_info("connecting to %s:%d", host, port);
22690267Sdes
22790267Sdes	/* try to connect */
22890267Sdes	for (sd = -1, res = res0; res; res = res->ai_next) {
22990267Sdes		if ((sd = socket(res->ai_family, res->ai_socktype,
23062981Sdes			 res->ai_protocol)) == -1)
23190267Sdes			continue;
23290267Sdes		if (connect(sd, res->ai_addr, res->ai_addrlen) != -1)
23390267Sdes			break;
23490267Sdes		close(sd);
23590267Sdes		sd = -1;
23690267Sdes	}
23790267Sdes	freeaddrinfo(res0);
23890267Sdes	if (sd == -1) {
23990267Sdes		_fetch_syserr();
24090267Sdes		return (-1);
24190267Sdes	}
24240939Sdes
24390267Sdes	return (sd);
24440939Sdes}
24541989Sdes
24641989Sdes
24755557Sdes/*
24855557Sdes * Read a line of text from a socket w/ timeout
24955557Sdes */
25055557Sdes#define MIN_BUF_SIZE 1024
25155557Sdes
25255557Sdesint
25355557Sdes_fetch_getln(int fd, char **buf, size_t *size, size_t *len)
25455557Sdes{
25590267Sdes	struct timeval now, timeout, wait;
25690267Sdes	fd_set readfds;
25790267Sdes	int r;
25890267Sdes	char c;
25990267Sdes
26090267Sdes	if (*buf == NULL) {
26190267Sdes		if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) {
26290267Sdes			errno = ENOMEM;
26390267Sdes			return (-1);
26490267Sdes		}
26590267Sdes		*size = MIN_BUF_SIZE;
26655557Sdes	}
26755557Sdes
26890267Sdes	**buf = '\0';
26990267Sdes	*len = 0;
27055557Sdes
27155557Sdes	if (fetchTimeout) {
27290267Sdes		gettimeofday(&timeout, NULL);
27390267Sdes		timeout.tv_sec += fetchTimeout;
27490267Sdes		FD_ZERO(&readfds);
27555557Sdes	}
27690267Sdes
27790267Sdes	do {
27890267Sdes		if (fetchTimeout) {
27990267Sdes			FD_SET(fd, &readfds);
28090267Sdes			gettimeofday(&now, NULL);
28190267Sdes			wait.tv_sec = timeout.tv_sec - now.tv_sec;
28290267Sdes			wait.tv_usec = timeout.tv_usec - now.tv_usec;
28390267Sdes			if (wait.tv_usec < 0) {
28490267Sdes				wait.tv_usec += 1000000;
28590267Sdes				wait.tv_sec--;
28690267Sdes			}
28790267Sdes			if (wait.tv_sec < 0) {
28890267Sdes				errno = ETIMEDOUT;
28990267Sdes				return (-1);
29090267Sdes			}
29190267Sdes			r = select(fd+1, &readfds, NULL, NULL, &wait);
29290267Sdes			if (r == -1) {
29390267Sdes				if (errno == EINTR && fetchRestartCalls)
29490267Sdes					continue;
29590267Sdes				/* EBADF or EINVAL: shouldn't happen */
29690267Sdes				return (-1);
29790267Sdes			}
29890267Sdes			if (!FD_ISSET(fd, &readfds))
29990267Sdes				continue;
30090267Sdes		}
30190267Sdes		r = read(fd, &c, 1);
30290267Sdes		if (r == 0)
30390267Sdes			break;
30490267Sdes		if (r == -1) {
30590267Sdes			if (errno == EINTR && fetchRestartCalls)
30690267Sdes				continue;
30790267Sdes			/* any other error is bad news */
30890267Sdes			return (-1);
30990267Sdes		}
31090267Sdes		(*buf)[*len] = c;
31190267Sdes		*len += 1;
31290267Sdes		if (*len == *size) {
31390267Sdes			char *tmp;
31490267Sdes
31590267Sdes			if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) {
31690267Sdes				errno = ENOMEM;
31790267Sdes				return (-1);
31890267Sdes			}
31990267Sdes			*buf = tmp;
32090267Sdes			*size = *size * 2 + 1;
32190267Sdes		}
32290267Sdes	} while (c != '\n');
32390267Sdes
32490267Sdes	DEBUG(fprintf(stderr, "<<< %.*s", (int)*len, *buf));
32590267Sdes	return (0);
32655557Sdes}
32755557Sdes
32855557Sdes
32962981Sdes/*
33062981Sdes * Write a line of text to a socket w/ timeout
33162981Sdes * XXX currently does not enforce timeout
33262981Sdes */
33362981Sdesint
33475891Sarchie_fetch_putln(int fd, const char *str, size_t len)
33562981Sdes{
33690267Sdes	struct iovec iov[2];
33790267Sdes	ssize_t wlen;
33862981Sdes
33990267Sdes	/* XXX should enforce timeout */
34090267Sdes	iov[0].iov_base = (char *)str;
34190267Sdes	iov[0].iov_len = len;
34290267Sdes	iov[1].iov_base = (char *)ENDL;
34390267Sdes	iov[1].iov_len = sizeof ENDL;
34490267Sdes	len += sizeof ENDL;
34590267Sdes	wlen = writev(fd, iov, 2);
34690267Sdes	if (wlen < 0 || (size_t)wlen != len)
34790267Sdes		return (-1);
34890267Sdes	DEBUG(fprintf(stderr, ">>> %s\n", str));
34990267Sdes	return (0);
35062981Sdes}
35162981Sdes
35262981Sdes
35341989Sdes/*** Directory-related utility functions *************************************/
35441989Sdes
35541989Sdesint
35641989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len,
35790267Sdes    const char *name, struct url_stat *us)
35841989Sdes{
35990267Sdes	struct url_ent *tmp;
36041989Sdes
36190267Sdes	if (*p == NULL) {
36290268Sdes		*size = 0;
36390267Sdes		*len = 0;
36441989Sdes	}
36541989Sdes
36690267Sdes	if (*len >= *size - 1) {
36790268Sdes		tmp = realloc(*p, (*size * 2 + 1) * sizeof **p);
36890267Sdes		if (tmp == NULL) {
36990267Sdes			errno = ENOMEM;
37090267Sdes			_fetch_syserr();
37190267Sdes			return (-1);
37290267Sdes		}
37390268Sdes		*size = (*size * 2 + 1);
37490267Sdes		*p = tmp;
37590267Sdes	}
37641989Sdes
37790267Sdes	tmp = *p + *len;
37890267Sdes	snprintf(tmp->name, PATH_MAX, "%s", name);
37990267Sdes	bcopy(us, &tmp->stat, sizeof *us);
38041989Sdes
38190267Sdes	(*len)++;
38290267Sdes	(++tmp)->name[0] = 0;
38390267Sdes
38490267Sdes	return (0);
38541989Sdes}
386