common.c revision 60928
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 60928 2000-05-25 16:50:08Z des $
2940939Sdes */
3040939Sdes
3141862Sdes#include <sys/param.h>
3240939Sdes#include <sys/socket.h>
3355557Sdes#include <sys/time.h>
3440939Sdes#include <netinet/in.h>
3540939Sdes
3640939Sdes#include <errno.h>
3740939Sdes#include <netdb.h>
3860924Sdes#include <stdarg.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[] = {
5460737Sume    { EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
5560737Sume    { EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
5660737Sume    { EAI_FAIL,		FETCH_RESOLV,	"Non-recoverable resolver failure" },
5760737Sume    { EAI_NONAME,	FETCH_RESOLV,	"No address record" },
5840975Sdes    { -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
5940939Sdes};
6040939Sdes
6140939Sdes
6240939Sdes/*** Error-reporting functions ***********************************************/
6340939Sdes
6440939Sdes/*
6540939Sdes * Map error code to string
6640939Sdes */
6760924Sdesstatic struct fetcherr *
6840975Sdes_fetch_finderr(struct fetcherr *p, int e)
6940939Sdes{
7060924Sdes    while (p->num != -1 && p->num != e)
7160924Sdes	p++;
7260924Sdes    return p;
7340939Sdes}
7440939Sdes
7540939Sdes/*
7640939Sdes * Set error code
7740939Sdes */
7840939Sdesvoid
7940939Sdes_fetch_seterr(struct fetcherr *p, int e)
8040939Sdes{
8160924Sdes    p = _fetch_finderr(p, e);
8260924Sdes    fetchLastErrCode = p->cat;
8360924Sdes    snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
8440939Sdes}
8540939Sdes
8640939Sdes/*
8740939Sdes * Set error code according to errno
8840939Sdes */
8940939Sdesvoid
9040939Sdes_fetch_syserr(void)
9140939Sdes{
9241862Sdes    int e;
9341862Sdes    e = errno;
9440975Sdes
9540975Sdes    switch (errno) {
9640975Sdes    case 0:
9741862Sdes	fetchLastErrCode = FETCH_OK;
9840975Sdes	break;
9940975Sdes    case EPERM:
10040975Sdes    case EACCES:
10140975Sdes    case EROFS:
10240975Sdes    case EAUTH:
10340975Sdes    case ENEEDAUTH:
10441862Sdes	fetchLastErrCode = FETCH_AUTH;
10540975Sdes	break;
10640975Sdes    case ENOENT:
10740975Sdes    case EISDIR: /* XXX */
10841862Sdes	fetchLastErrCode = FETCH_UNAVAIL;
10940975Sdes	break;
11040975Sdes    case ENOMEM:
11141862Sdes	fetchLastErrCode = FETCH_MEMORY;
11240975Sdes	break;
11340975Sdes    case EBUSY:
11440975Sdes    case EAGAIN:
11541862Sdes	fetchLastErrCode = FETCH_TEMP;
11640975Sdes	break;
11740975Sdes    case EEXIST:
11841862Sdes	fetchLastErrCode = FETCH_EXISTS;
11940975Sdes	break;
12040975Sdes    case ENOSPC:
12141862Sdes	fetchLastErrCode = FETCH_FULL;
12240975Sdes	break;
12340975Sdes    case EADDRINUSE:
12440975Sdes    case EADDRNOTAVAIL:
12540975Sdes    case ENETDOWN:
12640975Sdes    case ENETUNREACH:
12740975Sdes    case ENETRESET:
12840975Sdes    case EHOSTUNREACH:
12941862Sdes	fetchLastErrCode = FETCH_NETWORK;
13040975Sdes	break;
13140975Sdes    case ECONNABORTED:
13240975Sdes    case ECONNRESET:
13341862Sdes	fetchLastErrCode = FETCH_ABORT;
13440975Sdes	break;
13540975Sdes    case ETIMEDOUT:
13641862Sdes	fetchLastErrCode = FETCH_TIMEOUT;
13740975Sdes	break;
13840975Sdes    case ECONNREFUSED:
13940975Sdes    case EHOSTDOWN:
14041862Sdes	fetchLastErrCode = FETCH_DOWN;
14140975Sdes	break;
14240975Sdes    default:
14341862Sdes	fetchLastErrCode = FETCH_UNKNOWN;
14440975Sdes    }
14560924Sdes    snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(e));
14640939Sdes}
14740939Sdes
14840939Sdes
14941862Sdes/*
15041862Sdes * Emit status message
15141862Sdes */
15260924Sdesvoid
15341862Sdes_fetch_info(char *fmt, ...)
15441862Sdes{
15541862Sdes    va_list ap;
15641862Sdes
15741862Sdes    va_start(ap, fmt);
15860924Sdes    vfprintf(stderr, fmt, ap);
15941862Sdes    va_end(ap);
16060928Sdes    fputc('\n', stderr);
16141862Sdes}
16241862Sdes
16341862Sdes
16440939Sdes/*** Network-related utility functions ***************************************/
16540939Sdes
16640939Sdes/*
16740939Sdes * Establish a TCP connection to the specified port on the specified host.
16840939Sdes */
16940939Sdesint
17060737Sume_fetch_connect(char *host, int port, int af, int verbose)
17140939Sdes{
17260737Sume    char pbuf[10];
17360737Sume    struct addrinfo hints, *res, *res0;
17460737Sume    int sd, err;
17540939Sdes
17640939Sdes#ifndef NDEBUG
17740939Sdes    fprintf(stderr, "\033[1m---> %s:%d\033[m\n", host, port);
17840939Sdes#endif
17941862Sdes
18041862Sdes    if (verbose)
18141862Sdes	_fetch_info("looking up %s", host);
18240939Sdes
18360737Sume    /* look up host name and set up socket address structure */
18460737Sume    snprintf(pbuf, sizeof(pbuf), "%d", port);
18560737Sume    memset(&hints, 0, sizeof(hints));
18660737Sume    hints.ai_family = af;
18760737Sume    hints.ai_socktype = SOCK_STREAM;
18860737Sume    hints.ai_protocol = 0;
18960737Sume    if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
19060737Sume	_netdb_seterr(err);
19140939Sdes	return -1;
19240939Sdes    }
19340939Sdes
19441862Sdes    if (verbose)
19541862Sdes	_fetch_info("connecting to %s:%d", host, port);
19641862Sdes
19740939Sdes    /* try to connect */
19860737Sume    sd = -1;
19960737Sume    for (res = res0; res; res = res->ai_next) {
20060737Sume	if ((sd = socket(res->ai_family, res->ai_socktype,
20160737Sume			 res->ai_protocol)) < 0)
20260737Sume	    continue;
20360737Sume	if (connect(sd, res->ai_addr, res->ai_addrlen) >= 0)
20460737Sume	    break;
20560737Sume	close(sd);
20660737Sume	sd = -1;
20740939Sdes    }
20860737Sume    if (sd < 0) {
20940939Sdes	_fetch_syserr();
21040939Sdes	return -1;
21140939Sdes    }
21240939Sdes
21340939Sdes    return sd;
21440939Sdes}
21541989Sdes
21641989Sdes
21755557Sdes/*
21855557Sdes * Read a line of text from a socket w/ timeout
21955557Sdes */
22055557Sdes#define MIN_BUF_SIZE 1024
22155557Sdes
22255557Sdesint
22355557Sdes_fetch_getln(int fd, char **buf, size_t *size, size_t *len)
22455557Sdes{
22555557Sdes    struct timeval now, timeout, wait;
22655557Sdes    fd_set readfds;
22755557Sdes    int r;
22855557Sdes    char c;
22955557Sdes
23055557Sdes    if (*buf == NULL) {
23155557Sdes	if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) {
23255557Sdes	    errno = ENOMEM;
23355557Sdes	    return -1;
23455557Sdes	}
23555557Sdes	*size = MIN_BUF_SIZE;
23655557Sdes    }
23755557Sdes
23855557Sdes    **buf = '\0';
23955557Sdes    *len = 0;
24055557Sdes
24155557Sdes    if (fetchTimeout) {
24255557Sdes	gettimeofday(&timeout, NULL);
24355557Sdes	timeout.tv_sec += fetchTimeout;
24455557Sdes	FD_ZERO(&readfds);
24555557Sdes    }
24655557Sdes
24755557Sdes    do {
24855557Sdes	if (fetchTimeout) {
24955557Sdes	    FD_SET(fd, &readfds);
25055557Sdes	    gettimeofday(&now, NULL);
25155557Sdes	    wait.tv_sec = timeout.tv_sec - now.tv_sec;
25255557Sdes	    wait.tv_usec = timeout.tv_usec - now.tv_usec;
25355557Sdes	    if (wait.tv_usec < 0) {
25455557Sdes		wait.tv_usec += 1000000;
25555557Sdes		wait.tv_sec--;
25655557Sdes	    }
25755557Sdes	    if (wait.tv_sec < 0) {
25855557Sdes		errno = ETIMEDOUT;
25955557Sdes		return -1;
26055557Sdes	    }
26155557Sdes	    r = select(fd+1, &readfds, NULL, NULL, &wait);
26255557Sdes	    if (r == -1) {
26355557Sdes		if (errno == EINTR)
26455557Sdes		    continue;
26555557Sdes		/* EBADF or EINVAL: shouldn't happen */
26655557Sdes		return -1;
26755557Sdes	    }
26855557Sdes	    if (!FD_ISSET(fd, &readfds))
26955557Sdes		continue;
27055557Sdes	}
27155557Sdes	r = read(fd, &c, 1);
27255557Sdes	if (r == 0)
27355557Sdes	    break;
27455557Sdes	if (r == -1) {
27555557Sdes	    if (errno == EINTR)
27655557Sdes		continue;
27755557Sdes	    /* any other error is bad news */
27855557Sdes	    return -1;
27955557Sdes	}
28055557Sdes	(*buf)[*len] = c;
28155557Sdes	*len += 1;
28255557Sdes	if (*len == *size) {
28355557Sdes	    char *tmp;
28455557Sdes
28555557Sdes	    if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) {
28655557Sdes		errno = ENOMEM;
28755557Sdes		return -1;
28855557Sdes	    }
28955557Sdes	    *buf = tmp;
29055557Sdes	    *size = *size * 2 + 1;
29155557Sdes	}
29255557Sdes    } while (c != '\n');
29355557Sdes
29455557Sdes    return 0;
29555557Sdes}
29655557Sdes
29755557Sdes
29841989Sdes/*** Directory-related utility functions *************************************/
29941989Sdes
30041989Sdesint
30141989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len,
30241989Sdes		 char *name, struct url_stat *stat)
30341989Sdes{
30441989Sdes    struct url_ent *tmp;
30541989Sdes
30641989Sdes    if (*p == NULL) {
30741989Sdes#define INITIAL_SIZE 8
30841989Sdes	if ((*p = malloc(INITIAL_SIZE * sizeof **p)) == NULL) {
30941989Sdes	    errno = ENOMEM;
31041989Sdes	    _fetch_syserr();
31141989Sdes	    return -1;
31241989Sdes	}
31341989Sdes	*size = INITIAL_SIZE;
31441989Sdes	*len = 0;
31541989Sdes#undef INITIAL_SIZE
31641989Sdes    }
31741989Sdes
31841989Sdes    if (*len >= *size - 1) {
31941989Sdes	tmp = realloc(*p, *size * 2 * sizeof **p);
32041989Sdes	if (tmp == NULL) {
32141989Sdes	    errno = ENOMEM;
32241989Sdes	    _fetch_syserr();
32341989Sdes	    return -1;
32441989Sdes	}
32541989Sdes	*size *= 2;
32641989Sdes	*p = tmp;
32741989Sdes    }
32841989Sdes
32941989Sdes    tmp = *p + *len;
33041989Sdes    snprintf(tmp->name, MAXPATHLEN, "%s", name);
33141989Sdes    bcopy(stat, &tmp->stat, sizeof *stat);
33241989Sdes
33341989Sdes    (*len)++;
33441989Sdes    (++tmp)->name[0] = 0;
33541989Sdes
33641989Sdes    return 0;
33741989Sdes}
338