common.c revision 55557
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 55557 2000-01-07 12:58:40Z des $
2940939Sdes */
3040939Sdes
3141862Sdes#include <sys/param.h>
3240939Sdes#include <sys/socket.h>
3355557Sdes#include <sys/time.h>
3440939Sdes#include <netinet/in.h>
3540939Sdes
3640975Sdes#include <com_err.h>
3740939Sdes#include <errno.h>
3840939Sdes#include <netdb.h>
3941862Sdes#include <stdlib.h>
4041862Sdes#include <stdio.h>
4140939Sdes#include <string.h>
4240939Sdes#include <unistd.h>
4340939Sdes
4440939Sdes#include "fetch.h"
4540939Sdes#include "common.h"
4640939Sdes
4740975Sdes
4840939Sdes/*** Local data **************************************************************/
4940939Sdes
5040939Sdes/*
5140939Sdes * Error messages for resolver errors
5240939Sdes */
5340939Sdesstatic struct fetcherr _netdb_errlist[] = {
5440975Sdes    { HOST_NOT_FOUND,	FETCH_RESOLV,	"Host not found" },
5541862Sdes    { TRY_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
5640975Sdes    { NO_RECOVERY,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
5740975Sdes    { NO_DATA,		FETCH_RESOLV,	"No address record" },
5840975Sdes    { -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
5940939Sdes};
6040939Sdes
6140975Sdesstatic int com_err_initialized;
6240939Sdes
6340939Sdes/*** Error-reporting functions ***********************************************/
6440939Sdes
6540939Sdes/*
6640975Sdes * Initialize the common error library
6740975Sdes */
6840975Sdesstatic void
6940975Sdes_fetch_init_com_err(void)
7040975Sdes{
7140975Sdes    initialize_ftch_error_table();
7240975Sdes    com_err_initialized = 1;
7340975Sdes}
7440975Sdes
7540975Sdes/*
7640939Sdes * Map error code to string
7740939Sdes */
7840975Sdesstatic int
7940975Sdes_fetch_finderr(struct fetcherr *p, int e)
8040939Sdes{
8140975Sdes    int i;
8240975Sdes    for (i = 0; p[i].num != -1; i++)
8340975Sdes	if (p[i].num == e)
8440975Sdes	    break;
8540975Sdes    return i;
8640939Sdes}
8740939Sdes
8840939Sdes/*
8940939Sdes * Set error code
9040939Sdes */
9140939Sdesvoid
9240939Sdes_fetch_seterr(struct fetcherr *p, int e)
9340939Sdes{
9440975Sdes    int n;
9540975Sdes
9640975Sdes    if (!com_err_initialized)
9740975Sdes	_fetch_init_com_err();
9840975Sdes
9940975Sdes    n = _fetch_finderr(p, e);
10041862Sdes    fetchLastErrCode = p[n].cat;
10141862Sdes    com_err("libfetch", fetchLastErrCode, "(%03d %s)", e, p[n].string);
10240939Sdes}
10340939Sdes
10440939Sdes/*
10540939Sdes * Set error code according to errno
10640939Sdes */
10740939Sdesvoid
10840939Sdes_fetch_syserr(void)
10940939Sdes{
11041862Sdes    int e;
11141862Sdes    e = errno;
11240975Sdes
11340975Sdes    if (!com_err_initialized)
11440975Sdes	_fetch_init_com_err();
11540975Sdes
11640975Sdes    switch (errno) {
11740975Sdes    case 0:
11841862Sdes	fetchLastErrCode = FETCH_OK;
11940975Sdes	break;
12040975Sdes    case EPERM:
12140975Sdes    case EACCES:
12240975Sdes    case EROFS:
12340975Sdes    case EAUTH:
12440975Sdes    case ENEEDAUTH:
12541862Sdes	fetchLastErrCode = FETCH_AUTH;
12640975Sdes	break;
12740975Sdes    case ENOENT:
12840975Sdes    case EISDIR: /* XXX */
12941862Sdes	fetchLastErrCode = FETCH_UNAVAIL;
13040975Sdes	break;
13140975Sdes    case ENOMEM:
13241862Sdes	fetchLastErrCode = FETCH_MEMORY;
13340975Sdes	break;
13440975Sdes    case EBUSY:
13540975Sdes    case EAGAIN:
13641862Sdes	fetchLastErrCode = FETCH_TEMP;
13740975Sdes	break;
13840975Sdes    case EEXIST:
13941862Sdes	fetchLastErrCode = FETCH_EXISTS;
14040975Sdes	break;
14140975Sdes    case ENOSPC:
14241862Sdes	fetchLastErrCode = FETCH_FULL;
14340975Sdes	break;
14440975Sdes    case EADDRINUSE:
14540975Sdes    case EADDRNOTAVAIL:
14640975Sdes    case ENETDOWN:
14740975Sdes    case ENETUNREACH:
14840975Sdes    case ENETRESET:
14940975Sdes    case EHOSTUNREACH:
15041862Sdes	fetchLastErrCode = FETCH_NETWORK;
15140975Sdes	break;
15240975Sdes    case ECONNABORTED:
15340975Sdes    case ECONNRESET:
15441862Sdes	fetchLastErrCode = FETCH_ABORT;
15540975Sdes	break;
15640975Sdes    case ETIMEDOUT:
15741862Sdes	fetchLastErrCode = FETCH_TIMEOUT;
15840975Sdes	break;
15940975Sdes    case ECONNREFUSED:
16040975Sdes    case EHOSTDOWN:
16141862Sdes	fetchLastErrCode = FETCH_DOWN;
16240975Sdes	break;
16340975Sdes    default:
16441862Sdes	fetchLastErrCode = FETCH_UNKNOWN;
16540975Sdes    }
16641862Sdes    com_err("libfetch", fetchLastErrCode, "(%03d %s)", e, strerror(e));
16740939Sdes}
16840939Sdes
16940939Sdes
17041862Sdes/*
17141862Sdes * Emit status message
17241862Sdes */
17341862Sdesint
17441862Sdes_fetch_info(char *fmt, ...)
17541862Sdes{
17641862Sdes    va_list ap;
17741862Sdes    char *s;
17841862Sdes
17941862Sdes    if (!com_err_initialized)
18041862Sdes	_fetch_init_com_err();
18141862Sdes
18241862Sdes    va_start(ap, fmt);
18341862Sdes    vasprintf(&s, fmt, ap);
18441862Sdes    va_end(ap);
18541862Sdes
18641862Sdes    if (s == NULL) {
18741862Sdes	com_err("libfetch", FETCH_MEMORY, "");
18841862Sdes	return -1;
18941862Sdes    } else {
19041862Sdes	com_err("libfetch", FETCH_VERBOSE, "%s", s);
19141862Sdes	free(s);
19241862Sdes	return 0;
19341862Sdes    }
19441862Sdes}
19541862Sdes
19641862Sdes
19740939Sdes/*** Network-related utility functions ***************************************/
19840939Sdes
19940939Sdes/*
20040939Sdes * Establish a TCP connection to the specified port on the specified host.
20140939Sdes */
20240939Sdesint
20341923Sdes_fetch_connect(char *host, int port, int verbose)
20440939Sdes{
20540939Sdes    struct sockaddr_in sin;
20640939Sdes    struct hostent *he;
20740939Sdes    int sd;
20840939Sdes
20940939Sdes#ifndef NDEBUG
21040939Sdes    fprintf(stderr, "\033[1m---> %s:%d\033[m\n", host, port);
21140939Sdes#endif
21241862Sdes
21341862Sdes    if (verbose)
21441862Sdes	_fetch_info("looking up %s", host);
21540939Sdes
21640939Sdes    /* look up host name */
21740939Sdes    if ((he = gethostbyname(host)) == NULL) {
21840939Sdes	_netdb_seterr(h_errno);
21940939Sdes	return -1;
22040939Sdes    }
22140939Sdes
22241862Sdes    if (verbose)
22341862Sdes	_fetch_info("connecting to %s:%d", host, port);
22441862Sdes
22540939Sdes    /* set up socket address structure */
22640939Sdes    bzero(&sin, sizeof(sin));
22740939Sdes    bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length);
22840939Sdes    sin.sin_family = he->h_addrtype;
22940939Sdes    sin.sin_port = htons(port);
23040939Sdes
23140939Sdes    /* try to connect */
23240939Sdes    if ((sd = socket(sin.sin_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
23340939Sdes	_fetch_syserr();
23440939Sdes	return -1;
23540939Sdes    }
23640939Sdes    if (connect(sd, (struct sockaddr *)&sin, sizeof sin) == -1) {
23740939Sdes	_fetch_syserr();
23840939Sdes	close(sd);
23940939Sdes	return -1;
24040939Sdes    }
24140939Sdes
24240939Sdes    return sd;
24340939Sdes}
24441989Sdes
24541989Sdes
24655557Sdes/*
24755557Sdes * Read a line of text from a socket w/ timeout
24855557Sdes */
24955557Sdes#define MIN_BUF_SIZE 1024
25055557Sdes
25155557Sdesint
25255557Sdes_fetch_getln(int fd, char **buf, size_t *size, size_t *len)
25355557Sdes{
25455557Sdes    struct timeval now, timeout, wait;
25555557Sdes    fd_set readfds;
25655557Sdes    int r;
25755557Sdes    char c;
25855557Sdes
25955557Sdes    if (*buf == NULL) {
26055557Sdes	if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) {
26155557Sdes	    errno = ENOMEM;
26255557Sdes	    return -1;
26355557Sdes	}
26455557Sdes	*size = MIN_BUF_SIZE;
26555557Sdes    }
26655557Sdes
26755557Sdes    **buf = '\0';
26855557Sdes    *len = 0;
26955557Sdes
27055557Sdes    if (fetchTimeout) {
27155557Sdes	gettimeofday(&timeout, NULL);
27255557Sdes	timeout.tv_sec += fetchTimeout;
27355557Sdes	FD_ZERO(&readfds);
27455557Sdes    }
27555557Sdes
27655557Sdes    do {
27755557Sdes	if (fetchTimeout) {
27855557Sdes	    FD_SET(fd, &readfds);
27955557Sdes	    gettimeofday(&now, NULL);
28055557Sdes	    wait.tv_sec = timeout.tv_sec - now.tv_sec;
28155557Sdes	    wait.tv_usec = timeout.tv_usec - now.tv_usec;
28255557Sdes	    if (wait.tv_usec < 0) {
28355557Sdes		wait.tv_usec += 1000000;
28455557Sdes		wait.tv_sec--;
28555557Sdes	    }
28655557Sdes	    if (wait.tv_sec < 0) {
28755557Sdes		errno = ETIMEDOUT;
28855557Sdes		return -1;
28955557Sdes	    }
29055557Sdes	    r = select(fd+1, &readfds, NULL, NULL, &wait);
29155557Sdes	    if (r == -1) {
29255557Sdes		if (errno == EINTR)
29355557Sdes		    continue;
29455557Sdes		/* EBADF or EINVAL: shouldn't happen */
29555557Sdes		return -1;
29655557Sdes	    }
29755557Sdes	    if (!FD_ISSET(fd, &readfds))
29855557Sdes		continue;
29955557Sdes	}
30055557Sdes	r = read(fd, &c, 1);
30155557Sdes	if (r == 0)
30255557Sdes	    break;
30355557Sdes	if (r == -1) {
30455557Sdes	    if (errno == EINTR)
30555557Sdes		continue;
30655557Sdes	    /* any other error is bad news */
30755557Sdes	    return -1;
30855557Sdes	}
30955557Sdes	(*buf)[*len] = c;
31055557Sdes	*len += 1;
31155557Sdes	if (*len == *size) {
31255557Sdes	    char *tmp;
31355557Sdes
31455557Sdes	    if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) {
31555557Sdes		errno = ENOMEM;
31655557Sdes		return -1;
31755557Sdes	    }
31855557Sdes	    *buf = tmp;
31955557Sdes	    *size = *size * 2 + 1;
32055557Sdes	}
32155557Sdes    } while (c != '\n');
32255557Sdes
32355557Sdes    return 0;
32455557Sdes}
32555557Sdes
32655557Sdes
32741989Sdes/*** Directory-related utility functions *************************************/
32841989Sdes
32941989Sdesint
33041989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len,
33141989Sdes		 char *name, struct url_stat *stat)
33241989Sdes{
33341989Sdes    struct url_ent *tmp;
33441989Sdes
33541989Sdes    if (*p == NULL) {
33641989Sdes#define INITIAL_SIZE 8
33741989Sdes	if ((*p = malloc(INITIAL_SIZE * sizeof **p)) == NULL) {
33841989Sdes	    errno = ENOMEM;
33941989Sdes	    _fetch_syserr();
34041989Sdes	    return -1;
34141989Sdes	}
34241989Sdes	*size = INITIAL_SIZE;
34341989Sdes	*len = 0;
34441989Sdes#undef INITIAL_SIZE
34541989Sdes    }
34641989Sdes
34741989Sdes    if (*len >= *size - 1) {
34841989Sdes	tmp = realloc(*p, *size * 2 * sizeof **p);
34941989Sdes	if (tmp == NULL) {
35041989Sdes	    errno = ENOMEM;
35141989Sdes	    _fetch_syserr();
35241989Sdes	    return -1;
35341989Sdes	}
35441989Sdes	*size *= 2;
35541989Sdes	*p = tmp;
35641989Sdes    }
35741989Sdes
35841989Sdes    tmp = *p + *len;
35941989Sdes    snprintf(tmp->name, MAXPATHLEN, "%s", name);
36041989Sdes    bcopy(stat, &tmp->stat, sizeof *stat);
36141989Sdes
36241989Sdes    (*len)++;
36341989Sdes    (++tmp)->name[0] = 0;
36441989Sdes
36541989Sdes    return 0;
36641989Sdes}
367