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