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