common.c revision 62981
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 62981 2000-07-11 23:50:08Z des $
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 */
6362981Sdesstatic 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
15741862Sdes_fetch_info(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/*
17140939Sdes * Establish a TCP connection to the specified port on the specified host.
17240939Sdes */
17340939Sdesint
17460737Sume_fetch_connect(char *host, int port, int af, int verbose)
17540939Sdes{
17660737Sume    char pbuf[10];
17760737Sume    struct addrinfo hints, *res, *res0;
17860737Sume    int sd, err;
17940939Sdes
18062964Sdes    DEBUG(fprintf(stderr, "\033[1m---> %s:%d\033[m\n", host, port));
18141862Sdes
18241862Sdes    if (verbose)
18341862Sdes	_fetch_info("looking up %s", host);
18440939Sdes
18560737Sume    /* look up host name and set up socket address structure */
18660737Sume    snprintf(pbuf, sizeof(pbuf), "%d", port);
18760737Sume    memset(&hints, 0, sizeof(hints));
18860737Sume    hints.ai_family = af;
18960737Sume    hints.ai_socktype = SOCK_STREAM;
19060737Sume    hints.ai_protocol = 0;
19160737Sume    if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
19260737Sume	_netdb_seterr(err);
19340939Sdes	return -1;
19440939Sdes    }
19540939Sdes
19641862Sdes    if (verbose)
19741862Sdes	_fetch_info("connecting to %s:%d", host, port);
19841862Sdes
19940939Sdes    /* try to connect */
20062981Sdes    for (sd = -1, res = res0; res; res = res->ai_next) {
20160737Sume	if ((sd = socket(res->ai_family, res->ai_socktype,
20262981Sdes			 res->ai_protocol)) == -1)
20360737Sume	    continue;
20462981Sdes	if (connect(sd, res->ai_addr, res->ai_addrlen) != -1)
20560737Sume	    break;
20660737Sume	close(sd);
20760737Sume	sd = -1;
20840939Sdes    }
20962911Sume    freeaddrinfo(res0);
21062981Sdes    if (sd == -1) {
21140939Sdes	_fetch_syserr();
21240939Sdes	return -1;
21340939Sdes    }
21440939Sdes
21540939Sdes    return sd;
21640939Sdes}
21741989Sdes
21841989Sdes
21955557Sdes/*
22055557Sdes * Read a line of text from a socket w/ timeout
22155557Sdes */
22255557Sdes#define MIN_BUF_SIZE 1024
22355557Sdes
22455557Sdesint
22555557Sdes_fetch_getln(int fd, char **buf, size_t *size, size_t *len)
22655557Sdes{
22755557Sdes    struct timeval now, timeout, wait;
22855557Sdes    fd_set readfds;
22955557Sdes    int r;
23055557Sdes    char c;
23155557Sdes
23255557Sdes    if (*buf == NULL) {
23355557Sdes	if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) {
23455557Sdes	    errno = ENOMEM;
23555557Sdes	    return -1;
23655557Sdes	}
23755557Sdes	*size = MIN_BUF_SIZE;
23855557Sdes    }
23955557Sdes
24055557Sdes    **buf = '\0';
24155557Sdes    *len = 0;
24255557Sdes
24355557Sdes    if (fetchTimeout) {
24455557Sdes	gettimeofday(&timeout, NULL);
24555557Sdes	timeout.tv_sec += fetchTimeout;
24655557Sdes	FD_ZERO(&readfds);
24755557Sdes    }
24855557Sdes
24955557Sdes    do {
25055557Sdes	if (fetchTimeout) {
25155557Sdes	    FD_SET(fd, &readfds);
25255557Sdes	    gettimeofday(&now, NULL);
25355557Sdes	    wait.tv_sec = timeout.tv_sec - now.tv_sec;
25455557Sdes	    wait.tv_usec = timeout.tv_usec - now.tv_usec;
25555557Sdes	    if (wait.tv_usec < 0) {
25655557Sdes		wait.tv_usec += 1000000;
25755557Sdes		wait.tv_sec--;
25855557Sdes	    }
25955557Sdes	    if (wait.tv_sec < 0) {
26055557Sdes		errno = ETIMEDOUT;
26155557Sdes		return -1;
26255557Sdes	    }
26355557Sdes	    r = select(fd+1, &readfds, NULL, NULL, &wait);
26455557Sdes	    if (r == -1) {
26555557Sdes		if (errno == EINTR)
26655557Sdes		    continue;
26755557Sdes		/* EBADF or EINVAL: shouldn't happen */
26855557Sdes		return -1;
26955557Sdes	    }
27055557Sdes	    if (!FD_ISSET(fd, &readfds))
27155557Sdes		continue;
27255557Sdes	}
27355557Sdes	r = read(fd, &c, 1);
27455557Sdes	if (r == 0)
27555557Sdes	    break;
27655557Sdes	if (r == -1) {
27755557Sdes	    if (errno == EINTR)
27855557Sdes		continue;
27955557Sdes	    /* any other error is bad news */
28055557Sdes	    return -1;
28155557Sdes	}
28255557Sdes	(*buf)[*len] = c;
28355557Sdes	*len += 1;
28455557Sdes	if (*len == *size) {
28555557Sdes	    char *tmp;
28655557Sdes
28755557Sdes	    if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) {
28855557Sdes		errno = ENOMEM;
28955557Sdes		return -1;
29055557Sdes	    }
29155557Sdes	    *buf = tmp;
29255557Sdes	    *size = *size * 2 + 1;
29355557Sdes	}
29455557Sdes    } while (c != '\n');
29555557Sdes
29662964Sdes    DEBUG(fprintf(stderr, "\033[1m<<< %.*s\033[m", (int)*len, *buf));
29755557Sdes    return 0;
29855557Sdes}
29955557Sdes
30055557Sdes
30162981Sdes/*
30262981Sdes * Write a line of text to a socket w/ timeout
30362981Sdes * XXX currently does not enforce timeout
30462981Sdes */
30562981Sdesint
30662981Sdes_fetch_putln(int fd, char *str, size_t len)
30762981Sdes{
30862981Sdes    struct iovec iov[2];
30962981Sdes    ssize_t wlen;
31062981Sdes
31162981Sdes    /* XXX should enforce timeout */
31262981Sdes    iov[0].iov_base = str;
31362981Sdes    iov[0].iov_len = len;
31462981Sdes    iov[1].iov_base = ENDL;
31562981Sdes    iov[1].iov_len = sizeof ENDL;
31662981Sdes    wlen = writev(fd, iov, 2);
31762981Sdes    DEBUG(fprintf(stderr, "\033[1m>>> %s\n\033[m", str));
31862981Sdes    return (wlen != len);
31962981Sdes}
32062981Sdes
32162981Sdes
32241989Sdes/*** Directory-related utility functions *************************************/
32341989Sdes
32441989Sdesint
32541989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len,
32641989Sdes		 char *name, struct url_stat *stat)
32741989Sdes{
32841989Sdes    struct url_ent *tmp;
32941989Sdes
33041989Sdes    if (*p == NULL) {
33141989Sdes#define INITIAL_SIZE 8
33241989Sdes	if ((*p = malloc(INITIAL_SIZE * sizeof **p)) == NULL) {
33341989Sdes	    errno = ENOMEM;
33441989Sdes	    _fetch_syserr();
33541989Sdes	    return -1;
33641989Sdes	}
33741989Sdes	*size = INITIAL_SIZE;
33841989Sdes	*len = 0;
33941989Sdes#undef INITIAL_SIZE
34041989Sdes    }
34141989Sdes
34241989Sdes    if (*len >= *size - 1) {
34341989Sdes	tmp = realloc(*p, *size * 2 * sizeof **p);
34441989Sdes	if (tmp == NULL) {
34541989Sdes	    errno = ENOMEM;
34641989Sdes	    _fetch_syserr();
34741989Sdes	    return -1;
34841989Sdes	}
34941989Sdes	*size *= 2;
35041989Sdes	*p = tmp;
35141989Sdes    }
35241989Sdes
35341989Sdes    tmp = *p + *len;
35441989Sdes    snprintf(tmp->name, MAXPATHLEN, "%s", name);
35541989Sdes    bcopy(stat, &tmp->stat, sizeof *stat);
35641989Sdes
35741989Sdes    (*len)++;
35841989Sdes    (++tmp)->name[0] = 0;
35941989Sdes
36041989Sdes    return 0;
36141989Sdes}
362