common.c revision 60737
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 60737 2000-05-20 18:23:51Z ume $ 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[] = { 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 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 20360737Sume_fetch_connect(char *host, int port, int af, int verbose) 20440939Sdes{ 20560737Sume char pbuf[10]; 20660737Sume struct addrinfo hints, *res, *res0; 20760737Sume int sd, err; 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 21660737Sume /* look up host name and set up socket address structure */ 21760737Sume snprintf(pbuf, sizeof(pbuf), "%d", port); 21860737Sume memset(&hints, 0, sizeof(hints)); 21960737Sume hints.ai_family = af; 22060737Sume hints.ai_socktype = SOCK_STREAM; 22160737Sume hints.ai_protocol = 0; 22260737Sume if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) { 22360737Sume _netdb_seterr(err); 22440939Sdes return -1; 22540939Sdes } 22640939Sdes 22741862Sdes if (verbose) 22841862Sdes _fetch_info("connecting to %s:%d", host, port); 22941862Sdes 23040939Sdes /* try to connect */ 23160737Sume sd = -1; 23260737Sume for (res = res0; res; res = res->ai_next) { 23360737Sume if ((sd = socket(res->ai_family, res->ai_socktype, 23460737Sume res->ai_protocol)) < 0) 23560737Sume continue; 23660737Sume if (connect(sd, res->ai_addr, res->ai_addrlen) >= 0) 23760737Sume break; 23860737Sume close(sd); 23960737Sume sd = -1; 24040939Sdes } 24160737Sume if (sd < 0) { 24240939Sdes _fetch_syserr(); 24340939Sdes return -1; 24440939Sdes } 24540939Sdes 24640939Sdes return sd; 24740939Sdes} 24841989Sdes 24941989Sdes 25055557Sdes/* 25155557Sdes * Read a line of text from a socket w/ timeout 25255557Sdes */ 25355557Sdes#define MIN_BUF_SIZE 1024 25455557Sdes 25555557Sdesint 25655557Sdes_fetch_getln(int fd, char **buf, size_t *size, size_t *len) 25755557Sdes{ 25855557Sdes struct timeval now, timeout, wait; 25955557Sdes fd_set readfds; 26055557Sdes int r; 26155557Sdes char c; 26255557Sdes 26355557Sdes if (*buf == NULL) { 26455557Sdes if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) { 26555557Sdes errno = ENOMEM; 26655557Sdes return -1; 26755557Sdes } 26855557Sdes *size = MIN_BUF_SIZE; 26955557Sdes } 27055557Sdes 27155557Sdes **buf = '\0'; 27255557Sdes *len = 0; 27355557Sdes 27455557Sdes if (fetchTimeout) { 27555557Sdes gettimeofday(&timeout, NULL); 27655557Sdes timeout.tv_sec += fetchTimeout; 27755557Sdes FD_ZERO(&readfds); 27855557Sdes } 27955557Sdes 28055557Sdes do { 28155557Sdes if (fetchTimeout) { 28255557Sdes FD_SET(fd, &readfds); 28355557Sdes gettimeofday(&now, NULL); 28455557Sdes wait.tv_sec = timeout.tv_sec - now.tv_sec; 28555557Sdes wait.tv_usec = timeout.tv_usec - now.tv_usec; 28655557Sdes if (wait.tv_usec < 0) { 28755557Sdes wait.tv_usec += 1000000; 28855557Sdes wait.tv_sec--; 28955557Sdes } 29055557Sdes if (wait.tv_sec < 0) { 29155557Sdes errno = ETIMEDOUT; 29255557Sdes return -1; 29355557Sdes } 29455557Sdes r = select(fd+1, &readfds, NULL, NULL, &wait); 29555557Sdes if (r == -1) { 29655557Sdes if (errno == EINTR) 29755557Sdes continue; 29855557Sdes /* EBADF or EINVAL: shouldn't happen */ 29955557Sdes return -1; 30055557Sdes } 30155557Sdes if (!FD_ISSET(fd, &readfds)) 30255557Sdes continue; 30355557Sdes } 30455557Sdes r = read(fd, &c, 1); 30555557Sdes if (r == 0) 30655557Sdes break; 30755557Sdes if (r == -1) { 30855557Sdes if (errno == EINTR) 30955557Sdes continue; 31055557Sdes /* any other error is bad news */ 31155557Sdes return -1; 31255557Sdes } 31355557Sdes (*buf)[*len] = c; 31455557Sdes *len += 1; 31555557Sdes if (*len == *size) { 31655557Sdes char *tmp; 31755557Sdes 31855557Sdes if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) { 31955557Sdes errno = ENOMEM; 32055557Sdes return -1; 32155557Sdes } 32255557Sdes *buf = tmp; 32355557Sdes *size = *size * 2 + 1; 32455557Sdes } 32555557Sdes } while (c != '\n'); 32655557Sdes 32755557Sdes return 0; 32855557Sdes} 32955557Sdes 33055557Sdes 33141989Sdes/*** Directory-related utility functions *************************************/ 33241989Sdes 33341989Sdesint 33441989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len, 33541989Sdes char *name, struct url_stat *stat) 33641989Sdes{ 33741989Sdes struct url_ent *tmp; 33841989Sdes 33941989Sdes if (*p == NULL) { 34041989Sdes#define INITIAL_SIZE 8 34141989Sdes if ((*p = malloc(INITIAL_SIZE * sizeof **p)) == NULL) { 34241989Sdes errno = ENOMEM; 34341989Sdes _fetch_syserr(); 34441989Sdes return -1; 34541989Sdes } 34641989Sdes *size = INITIAL_SIZE; 34741989Sdes *len = 0; 34841989Sdes#undef INITIAL_SIZE 34941989Sdes } 35041989Sdes 35141989Sdes if (*len >= *size - 1) { 35241989Sdes tmp = realloc(*p, *size * 2 * sizeof **p); 35341989Sdes if (tmp == NULL) { 35441989Sdes errno = ENOMEM; 35541989Sdes _fetch_syserr(); 35641989Sdes return -1; 35741989Sdes } 35841989Sdes *size *= 2; 35941989Sdes *p = tmp; 36041989Sdes } 36141989Sdes 36241989Sdes tmp = *p + *len; 36341989Sdes snprintf(tmp->name, MAXPATHLEN, "%s", name); 36441989Sdes bcopy(stat, &tmp->stat, sizeof *stat); 36541989Sdes 36641989Sdes (*len)++; 36741989Sdes (++tmp)->name[0] = 0; 36841989Sdes 36941989Sdes return 0; 37041989Sdes} 371