common.c revision 75891
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 *
2850476Speter * $FreeBSD: head/lib/libfetch/common.c 75891 2001-04-24 00:06:21Z archie $
2940939Sdes */
3040939Sdes
3141862Sdes#include <sys/param.h>
3240939Sdes#include <sys/socket.h>
3355557Sdes#include <sys/time.h>
3462981Sdes#include <sys/uio.h>
3540939Sdes#include <netinet/in.h>
3640939Sdes
3740939Sdes#include <errno.h>
3840939Sdes#include <netdb.h>
3960924Sdes#include <stdarg.h>
4041862Sdes#include <stdlib.h>
4141862Sdes#include <stdio.h>
4240939Sdes#include <string.h>
4340939Sdes#include <unistd.h>
4440939Sdes
4540939Sdes#include "fetch.h"
4640939Sdes#include "common.h"
4740939Sdes
4840975Sdes
4940939Sdes/*** Local data **************************************************************/
5040939Sdes
5140939Sdes/*
5240939Sdes * Error messages for resolver errors
5340939Sdes */
5440939Sdesstatic struct fetcherr _netdb_errlist[] = {
5560737Sume    { EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
5660737Sume    { EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
5760737Sume    { EAI_FAIL,		FETCH_RESOLV,	"Non-recoverable resolver failure" },
5860737Sume    { EAI_NONAME,	FETCH_RESOLV,	"No address record" },
5940975Sdes    { -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
6040939Sdes};
6140939Sdes
6262981Sdes/* End-of-Line */
6375891Sarchiestatic const char ENDL[2] = "\r\n";
6440939Sdes
6562981Sdes
6640939Sdes/*** Error-reporting functions ***********************************************/
6740939Sdes
6840939Sdes/*
6940939Sdes * Map error code to string
7040939Sdes */
7160924Sdesstatic struct fetcherr *
7240975Sdes_fetch_finderr(struct fetcherr *p, int e)
7340939Sdes{
7460924Sdes    while (p->num != -1 && p->num != e)
7560924Sdes	p++;
7660924Sdes    return p;
7740939Sdes}
7840939Sdes
7940939Sdes/*
8040939Sdes * Set error code
8140939Sdes */
8240939Sdesvoid
8340939Sdes_fetch_seterr(struct fetcherr *p, int e)
8440939Sdes{
8560924Sdes    p = _fetch_finderr(p, e);
8660924Sdes    fetchLastErrCode = p->cat;
8760924Sdes    snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
8840939Sdes}
8940939Sdes
9040939Sdes/*
9140939Sdes * Set error code according to errno
9240939Sdes */
9340939Sdesvoid
9440939Sdes_fetch_syserr(void)
9540939Sdes{
9641862Sdes    int e;
9741862Sdes    e = errno;
9840975Sdes
9940975Sdes    switch (errno) {
10040975Sdes    case 0:
10141862Sdes	fetchLastErrCode = FETCH_OK;
10240975Sdes	break;
10340975Sdes    case EPERM:
10440975Sdes    case EACCES:
10540975Sdes    case EROFS:
10640975Sdes    case EAUTH:
10740975Sdes    case ENEEDAUTH:
10841862Sdes	fetchLastErrCode = FETCH_AUTH;
10940975Sdes	break;
11040975Sdes    case ENOENT:
11140975Sdes    case EISDIR: /* XXX */
11241862Sdes	fetchLastErrCode = FETCH_UNAVAIL;
11340975Sdes	break;
11440975Sdes    case ENOMEM:
11541862Sdes	fetchLastErrCode = FETCH_MEMORY;
11640975Sdes	break;
11740975Sdes    case EBUSY:
11840975Sdes    case EAGAIN:
11941862Sdes	fetchLastErrCode = FETCH_TEMP;
12040975Sdes	break;
12140975Sdes    case EEXIST:
12241862Sdes	fetchLastErrCode = FETCH_EXISTS;
12340975Sdes	break;
12440975Sdes    case ENOSPC:
12541862Sdes	fetchLastErrCode = FETCH_FULL;
12640975Sdes	break;
12740975Sdes    case EADDRINUSE:
12840975Sdes    case EADDRNOTAVAIL:
12940975Sdes    case ENETDOWN:
13040975Sdes    case ENETUNREACH:
13140975Sdes    case ENETRESET:
13240975Sdes    case EHOSTUNREACH:
13341862Sdes	fetchLastErrCode = FETCH_NETWORK;
13440975Sdes	break;
13540975Sdes    case ECONNABORTED:
13640975Sdes    case ECONNRESET:
13741862Sdes	fetchLastErrCode = FETCH_ABORT;
13840975Sdes	break;
13940975Sdes    case ETIMEDOUT:
14041862Sdes	fetchLastErrCode = FETCH_TIMEOUT;
14140975Sdes	break;
14240975Sdes    case ECONNREFUSED:
14340975Sdes    case EHOSTDOWN:
14441862Sdes	fetchLastErrCode = FETCH_DOWN;
14540975Sdes	break;
14640975Sdes    default:
14741862Sdes	fetchLastErrCode = FETCH_UNKNOWN;
14840975Sdes    }
14960924Sdes    snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(e));
15040939Sdes}
15140939Sdes
15240939Sdes
15341862Sdes/*
15441862Sdes * Emit status message
15541862Sdes */
15660924Sdesvoid
15775891Sarchie_fetch_info(const char *fmt, ...)
15841862Sdes{
15941862Sdes    va_list ap;
16041862Sdes
16141862Sdes    va_start(ap, fmt);
16260924Sdes    vfprintf(stderr, fmt, ap);
16341862Sdes    va_end(ap);
16460928Sdes    fputc('\n', stderr);
16541862Sdes}
16641862Sdes
16741862Sdes
16840939Sdes/*** Network-related utility functions ***************************************/
16940939Sdes
17040939Sdes/*
17168551Sdes * Return the default port for a scheme
17268551Sdes */
17368551Sdesint
17475891Sarchie_fetch_default_port(const char *scheme)
17568551Sdes{
17668551Sdes    struct servent *se;
17768551Sdes
17868551Sdes    if ((se = getservbyname(scheme, "tcp")) != NULL)
17968551Sdes	return ntohs(se->s_port);
18068551Sdes    if (strcasecmp(scheme, SCHEME_FTP) == 0)
18168551Sdes	return FTP_DEFAULT_PORT;
18268551Sdes    if (strcasecmp(scheme, SCHEME_HTTP) == 0)
18368551Sdes	return HTTP_DEFAULT_PORT;
18468551Sdes    return 0;
18568551Sdes}
18668551Sdes
18768551Sdes/*
18868551Sdes * Return the default proxy port for a scheme
18968551Sdes */
19068551Sdesint
19175891Sarchie_fetch_default_proxy_port(const char *scheme)
19268551Sdes{
19368551Sdes    if (strcasecmp(scheme, SCHEME_FTP) == 0)
19468551Sdes	return FTP_DEFAULT_PROXY_PORT;
19568551Sdes    if (strcasecmp(scheme, SCHEME_HTTP) == 0)
19668551Sdes	return HTTP_DEFAULT_PROXY_PORT;
19768551Sdes    return 0;
19868551Sdes}
19968551Sdes
20068551Sdes/*
20140939Sdes * Establish a TCP connection to the specified port on the specified host.
20240939Sdes */
20340939Sdesint
20475891Sarchie_fetch_connect(const char *host, int port, int af, int verbose)
20540939Sdes{
20660737Sume    char pbuf[10];
20760737Sume    struct addrinfo hints, *res, *res0;
20860737Sume    int sd, err;
20940939Sdes
21062964Sdes    DEBUG(fprintf(stderr, "\033[1m---> %s:%d\033[m\n", host, port));
21141862Sdes
21241862Sdes    if (verbose)
21341862Sdes	_fetch_info("looking up %s", host);
21440939Sdes
21560737Sume    /* look up host name and set up socket address structure */
21660737Sume    snprintf(pbuf, sizeof(pbuf), "%d", port);
21760737Sume    memset(&hints, 0, sizeof(hints));
21860737Sume    hints.ai_family = af;
21960737Sume    hints.ai_socktype = SOCK_STREAM;
22060737Sume    hints.ai_protocol = 0;
22160737Sume    if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
22260737Sume	_netdb_seterr(err);
22340939Sdes	return -1;
22440939Sdes    }
22540939Sdes
22641862Sdes    if (verbose)
22741862Sdes	_fetch_info("connecting to %s:%d", host, port);
22841862Sdes
22940939Sdes    /* try to connect */
23062981Sdes    for (sd = -1, res = res0; res; res = res->ai_next) {
23160737Sume	if ((sd = socket(res->ai_family, res->ai_socktype,
23262981Sdes			 res->ai_protocol)) == -1)
23360737Sume	    continue;
23462981Sdes	if (connect(sd, res->ai_addr, res->ai_addrlen) != -1)
23560737Sume	    break;
23660737Sume	close(sd);
23760737Sume	sd = -1;
23840939Sdes    }
23962911Sume    freeaddrinfo(res0);
24062981Sdes    if (sd == -1) {
24140939Sdes	_fetch_syserr();
24240939Sdes	return -1;
24340939Sdes    }
24440939Sdes
24540939Sdes    return sd;
24640939Sdes}
24741989Sdes
24841989Sdes
24955557Sdes/*
25055557Sdes * Read a line of text from a socket w/ timeout
25155557Sdes */
25255557Sdes#define MIN_BUF_SIZE 1024
25355557Sdes
25455557Sdesint
25555557Sdes_fetch_getln(int fd, char **buf, size_t *size, size_t *len)
25655557Sdes{
25755557Sdes    struct timeval now, timeout, wait;
25855557Sdes    fd_set readfds;
25955557Sdes    int r;
26055557Sdes    char c;
26155557Sdes
26255557Sdes    if (*buf == NULL) {
26355557Sdes	if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) {
26455557Sdes	    errno = ENOMEM;
26555557Sdes	    return -1;
26655557Sdes	}
26755557Sdes	*size = MIN_BUF_SIZE;
26855557Sdes    }
26955557Sdes
27055557Sdes    **buf = '\0';
27155557Sdes    *len = 0;
27255557Sdes
27355557Sdes    if (fetchTimeout) {
27455557Sdes	gettimeofday(&timeout, NULL);
27555557Sdes	timeout.tv_sec += fetchTimeout;
27655557Sdes	FD_ZERO(&readfds);
27755557Sdes    }
27855557Sdes
27955557Sdes    do {
28055557Sdes	if (fetchTimeout) {
28155557Sdes	    FD_SET(fd, &readfds);
28255557Sdes	    gettimeofday(&now, NULL);
28355557Sdes	    wait.tv_sec = timeout.tv_sec - now.tv_sec;
28455557Sdes	    wait.tv_usec = timeout.tv_usec - now.tv_usec;
28555557Sdes	    if (wait.tv_usec < 0) {
28655557Sdes		wait.tv_usec += 1000000;
28755557Sdes		wait.tv_sec--;
28855557Sdes	    }
28955557Sdes	    if (wait.tv_sec < 0) {
29055557Sdes		errno = ETIMEDOUT;
29155557Sdes		return -1;
29255557Sdes	    }
29355557Sdes	    r = select(fd+1, &readfds, NULL, NULL, &wait);
29455557Sdes	    if (r == -1) {
29563334Sdes		if (errno == EINTR && fetchRestartCalls)
29655557Sdes		    continue;
29755557Sdes		/* EBADF or EINVAL: shouldn't happen */
29855557Sdes		return -1;
29955557Sdes	    }
30055557Sdes	    if (!FD_ISSET(fd, &readfds))
30155557Sdes		continue;
30255557Sdes	}
30355557Sdes	r = read(fd, &c, 1);
30455557Sdes	if (r == 0)
30555557Sdes	    break;
30655557Sdes	if (r == -1) {
30763334Sdes	    if (errno == EINTR && fetchRestartCalls)
30855557Sdes		continue;
30955557Sdes	    /* any other error is bad news */
31055557Sdes	    return -1;
31155557Sdes	}
31255557Sdes	(*buf)[*len] = c;
31355557Sdes	*len += 1;
31455557Sdes	if (*len == *size) {
31555557Sdes	    char *tmp;
31655557Sdes
31755557Sdes	    if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) {
31855557Sdes		errno = ENOMEM;
31955557Sdes		return -1;
32055557Sdes	    }
32155557Sdes	    *buf = tmp;
32255557Sdes	    *size = *size * 2 + 1;
32355557Sdes	}
32455557Sdes    } while (c != '\n');
32555557Sdes
32662964Sdes    DEBUG(fprintf(stderr, "\033[1m<<< %.*s\033[m", (int)*len, *buf));
32755557Sdes    return 0;
32855557Sdes}
32955557Sdes
33055557Sdes
33162981Sdes/*
33262981Sdes * Write a line of text to a socket w/ timeout
33362981Sdes * XXX currently does not enforce timeout
33462981Sdes */
33562981Sdesint
33675891Sarchie_fetch_putln(int fd, const char *str, size_t len)
33762981Sdes{
33862981Sdes    struct iovec iov[2];
33962981Sdes    ssize_t wlen;
34062981Sdes
34162981Sdes    /* XXX should enforce timeout */
34275891Sarchie    iov[0].iov_base = (char *)str;
34362981Sdes    iov[0].iov_len = len;
34475891Sarchie    iov[1].iov_base = (char *)ENDL;
34562981Sdes    iov[1].iov_len = sizeof ENDL;
34662981Sdes    wlen = writev(fd, iov, 2);
34762981Sdes    DEBUG(fprintf(stderr, "\033[1m>>> %s\n\033[m", str));
34862981Sdes    return (wlen != len);
34962981Sdes}
35062981Sdes
35162981Sdes
35241989Sdes/*** Directory-related utility functions *************************************/
35341989Sdes
35441989Sdesint
35541989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len,
35675891Sarchie		 const char *name, struct url_stat *stat)
35741989Sdes{
35841989Sdes    struct url_ent *tmp;
35941989Sdes
36041989Sdes    if (*p == NULL) {
36141989Sdes#define INITIAL_SIZE 8
36241989Sdes	if ((*p = malloc(INITIAL_SIZE * sizeof **p)) == NULL) {
36341989Sdes	    errno = ENOMEM;
36441989Sdes	    _fetch_syserr();
36541989Sdes	    return -1;
36641989Sdes	}
36741989Sdes	*size = INITIAL_SIZE;
36841989Sdes	*len = 0;
36941989Sdes#undef INITIAL_SIZE
37041989Sdes    }
37141989Sdes
37241989Sdes    if (*len >= *size - 1) {
37341989Sdes	tmp = realloc(*p, *size * 2 * sizeof **p);
37441989Sdes	if (tmp == NULL) {
37541989Sdes	    errno = ENOMEM;
37641989Sdes	    _fetch_syserr();
37741989Sdes	    return -1;
37841989Sdes	}
37941989Sdes	*size *= 2;
38041989Sdes	*p = tmp;
38141989Sdes    }
38241989Sdes
38341989Sdes    tmp = *p + *len;
38441989Sdes    snprintf(tmp->name, MAXPATHLEN, "%s", name);
38541989Sdes    bcopy(stat, &tmp->stat, sizeof *stat);
38641989Sdes
38741989Sdes    (*len)++;
38841989Sdes    (++tmp)->name[0] = 0;
38941989Sdes
39041989Sdes    return 0;
39141989Sdes}
392