common.c revision 90268
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 */ 2840939Sdes 2984203Sdillon#include <sys/cdefs.h> 3084203Sdillon__FBSDID("$FreeBSD: head/lib/libfetch/common.c 90268 2002-02-05 22:15:16Z des $"); 3184203Sdillon 3241862Sdes#include <sys/param.h> 3340939Sdes#include <sys/socket.h> 3455557Sdes#include <sys/time.h> 3562981Sdes#include <sys/uio.h> 3640939Sdes#include <netinet/in.h> 3740939Sdes 3840939Sdes#include <errno.h> 3940939Sdes#include <netdb.h> 4060924Sdes#include <stdarg.h> 4141862Sdes#include <stdlib.h> 4241862Sdes#include <stdio.h> 4340939Sdes#include <string.h> 4440939Sdes#include <unistd.h> 4540939Sdes 4640939Sdes#include "fetch.h" 4740939Sdes#include "common.h" 4840939Sdes 4940975Sdes 5040939Sdes/*** Local data **************************************************************/ 5140939Sdes 5240939Sdes/* 5340939Sdes * Error messages for resolver errors 5440939Sdes */ 5540939Sdesstatic struct fetcherr _netdb_errlist[] = { 5690267Sdes { EAI_NODATA, FETCH_RESOLV, "Host not found" }, 5790267Sdes { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, 5890267Sdes { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, 5990267Sdes { EAI_NONAME, FETCH_RESOLV, "No address record" }, 6090267Sdes { -1, FETCH_UNKNOWN, "Unknown resolver error" } 6140939Sdes}; 6240939Sdes 6362981Sdes/* End-of-Line */ 6475891Sarchiestatic const char ENDL[2] = "\r\n"; 6540939Sdes 6662981Sdes 6740939Sdes/*** Error-reporting functions ***********************************************/ 6840939Sdes 6940939Sdes/* 7040939Sdes * Map error code to string 7140939Sdes */ 7260924Sdesstatic struct fetcherr * 7340975Sdes_fetch_finderr(struct fetcherr *p, int e) 7440939Sdes{ 7590267Sdes while (p->num != -1 && p->num != e) 7690267Sdes p++; 7790267Sdes return (p); 7840939Sdes} 7940939Sdes 8040939Sdes/* 8140939Sdes * Set error code 8240939Sdes */ 8340939Sdesvoid 8440939Sdes_fetch_seterr(struct fetcherr *p, int e) 8540939Sdes{ 8690267Sdes p = _fetch_finderr(p, e); 8790267Sdes fetchLastErrCode = p->cat; 8890267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); 8940939Sdes} 9040939Sdes 9140939Sdes/* 9240939Sdes * Set error code according to errno 9340939Sdes */ 9440939Sdesvoid 9540939Sdes_fetch_syserr(void) 9640939Sdes{ 9790267Sdes switch (errno) { 9890267Sdes case 0: 9990267Sdes fetchLastErrCode = FETCH_OK; 10090267Sdes break; 10190267Sdes case EPERM: 10290267Sdes case EACCES: 10390267Sdes case EROFS: 10490267Sdes case EAUTH: 10590267Sdes case ENEEDAUTH: 10690267Sdes fetchLastErrCode = FETCH_AUTH; 10790267Sdes break; 10890267Sdes case ENOENT: 10990267Sdes case EISDIR: /* XXX */ 11090267Sdes fetchLastErrCode = FETCH_UNAVAIL; 11190267Sdes break; 11290267Sdes case ENOMEM: 11390267Sdes fetchLastErrCode = FETCH_MEMORY; 11490267Sdes break; 11590267Sdes case EBUSY: 11690267Sdes case EAGAIN: 11790267Sdes fetchLastErrCode = FETCH_TEMP; 11890267Sdes break; 11990267Sdes case EEXIST: 12090267Sdes fetchLastErrCode = FETCH_EXISTS; 12190267Sdes break; 12290267Sdes case ENOSPC: 12390267Sdes fetchLastErrCode = FETCH_FULL; 12490267Sdes break; 12590267Sdes case EADDRINUSE: 12690267Sdes case EADDRNOTAVAIL: 12790267Sdes case ENETDOWN: 12890267Sdes case ENETUNREACH: 12990267Sdes case ENETRESET: 13090267Sdes case EHOSTUNREACH: 13190267Sdes fetchLastErrCode = FETCH_NETWORK; 13290267Sdes break; 13390267Sdes case ECONNABORTED: 13490267Sdes case ECONNRESET: 13590267Sdes fetchLastErrCode = FETCH_ABORT; 13690267Sdes break; 13790267Sdes case ETIMEDOUT: 13890267Sdes fetchLastErrCode = FETCH_TIMEOUT; 13990267Sdes break; 14090267Sdes case ECONNREFUSED: 14190267Sdes case EHOSTDOWN: 14290267Sdes fetchLastErrCode = FETCH_DOWN; 14390267Sdes break; 14490267Sdesdefault: 14590267Sdes fetchLastErrCode = FETCH_UNKNOWN; 14690267Sdes } 14790267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); 14840939Sdes} 14940939Sdes 15040939Sdes 15141862Sdes/* 15241862Sdes * Emit status message 15341862Sdes */ 15460924Sdesvoid 15575891Sarchie_fetch_info(const char *fmt, ...) 15641862Sdes{ 15790267Sdes va_list ap; 15890267Sdes 15990267Sdes va_start(ap, fmt); 16090267Sdes vfprintf(stderr, fmt, ap); 16190267Sdes va_end(ap); 16290267Sdes fputc('\n', stderr); 16341862Sdes} 16441862Sdes 16541862Sdes 16640939Sdes/*** Network-related utility functions ***************************************/ 16740939Sdes 16840939Sdes/* 16968551Sdes * Return the default port for a scheme 17068551Sdes */ 17168551Sdesint 17275891Sarchie_fetch_default_port(const char *scheme) 17368551Sdes{ 17490267Sdes struct servent *se; 17568551Sdes 17690267Sdes if ((se = getservbyname(scheme, "tcp")) != NULL) 17790267Sdes return (ntohs(se->s_port)); 17890267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 17990267Sdes return (FTP_DEFAULT_PORT); 18090267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 18190267Sdes return (HTTP_DEFAULT_PORT); 18290267Sdes return (0); 18368551Sdes} 18468551Sdes 18568551Sdes/* 18668551Sdes * Return the default proxy port for a scheme 18768551Sdes */ 18868551Sdesint 18975891Sarchie_fetch_default_proxy_port(const char *scheme) 19068551Sdes{ 19190267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 19290267Sdes return (FTP_DEFAULT_PROXY_PORT); 19390267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 19490267Sdes return (HTTP_DEFAULT_PROXY_PORT); 19590267Sdes return (0); 19668551Sdes} 19768551Sdes 19868551Sdes/* 19940939Sdes * Establish a TCP connection to the specified port on the specified host. 20040939Sdes */ 20140939Sdesint 20275891Sarchie_fetch_connect(const char *host, int port, int af, int verbose) 20340939Sdes{ 20490267Sdes char pbuf[10]; 20590267Sdes struct addrinfo hints, *res, *res0; 20690267Sdes int sd, err; 20740939Sdes 20890267Sdes DEBUG(fprintf(stderr, "---> %s:%d\n", host, port)); 20941862Sdes 21090267Sdes if (verbose) 21190267Sdes _fetch_info("looking up %s", host); 21240939Sdes 21390267Sdes /* look up host name and set up socket address structure */ 21490267Sdes snprintf(pbuf, sizeof(pbuf), "%d", port); 21590267Sdes memset(&hints, 0, sizeof(hints)); 21690267Sdes hints.ai_family = af; 21790267Sdes hints.ai_socktype = SOCK_STREAM; 21890267Sdes hints.ai_protocol = 0; 21990267Sdes if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) { 22090267Sdes _netdb_seterr(err); 22190267Sdes return (-1); 22290267Sdes } 22390267Sdes 22490267Sdes if (verbose) 22590267Sdes _fetch_info("connecting to %s:%d", host, port); 22690267Sdes 22790267Sdes /* try to connect */ 22890267Sdes for (sd = -1, res = res0; res; res = res->ai_next) { 22990267Sdes if ((sd = socket(res->ai_family, res->ai_socktype, 23062981Sdes res->ai_protocol)) == -1) 23190267Sdes continue; 23290267Sdes if (connect(sd, res->ai_addr, res->ai_addrlen) != -1) 23390267Sdes break; 23490267Sdes close(sd); 23590267Sdes sd = -1; 23690267Sdes } 23790267Sdes freeaddrinfo(res0); 23890267Sdes if (sd == -1) { 23990267Sdes _fetch_syserr(); 24090267Sdes return (-1); 24190267Sdes } 24240939Sdes 24390267Sdes return (sd); 24440939Sdes} 24541989Sdes 24641989Sdes 24755557Sdes/* 24855557Sdes * Read a line of text from a socket w/ timeout 24955557Sdes */ 25055557Sdes#define MIN_BUF_SIZE 1024 25155557Sdes 25255557Sdesint 25355557Sdes_fetch_getln(int fd, char **buf, size_t *size, size_t *len) 25455557Sdes{ 25590267Sdes struct timeval now, timeout, wait; 25690267Sdes fd_set readfds; 25790267Sdes int r; 25890267Sdes char c; 25990267Sdes 26090267Sdes if (*buf == NULL) { 26190267Sdes if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) { 26290267Sdes errno = ENOMEM; 26390267Sdes return (-1); 26490267Sdes } 26590267Sdes *size = MIN_BUF_SIZE; 26655557Sdes } 26755557Sdes 26890267Sdes **buf = '\0'; 26990267Sdes *len = 0; 27055557Sdes 27155557Sdes if (fetchTimeout) { 27290267Sdes gettimeofday(&timeout, NULL); 27390267Sdes timeout.tv_sec += fetchTimeout; 27490267Sdes FD_ZERO(&readfds); 27555557Sdes } 27690267Sdes 27790267Sdes do { 27890267Sdes if (fetchTimeout) { 27990267Sdes FD_SET(fd, &readfds); 28090267Sdes gettimeofday(&now, NULL); 28190267Sdes wait.tv_sec = timeout.tv_sec - now.tv_sec; 28290267Sdes wait.tv_usec = timeout.tv_usec - now.tv_usec; 28390267Sdes if (wait.tv_usec < 0) { 28490267Sdes wait.tv_usec += 1000000; 28590267Sdes wait.tv_sec--; 28690267Sdes } 28790267Sdes if (wait.tv_sec < 0) { 28890267Sdes errno = ETIMEDOUT; 28990267Sdes return (-1); 29090267Sdes } 29190267Sdes r = select(fd+1, &readfds, NULL, NULL, &wait); 29290267Sdes if (r == -1) { 29390267Sdes if (errno == EINTR && fetchRestartCalls) 29490267Sdes continue; 29590267Sdes /* EBADF or EINVAL: shouldn't happen */ 29690267Sdes return (-1); 29790267Sdes } 29890267Sdes if (!FD_ISSET(fd, &readfds)) 29990267Sdes continue; 30090267Sdes } 30190267Sdes r = read(fd, &c, 1); 30290267Sdes if (r == 0) 30390267Sdes break; 30490267Sdes if (r == -1) { 30590267Sdes if (errno == EINTR && fetchRestartCalls) 30690267Sdes continue; 30790267Sdes /* any other error is bad news */ 30890267Sdes return (-1); 30990267Sdes } 31090267Sdes (*buf)[*len] = c; 31190267Sdes *len += 1; 31290267Sdes if (*len == *size) { 31390267Sdes char *tmp; 31490267Sdes 31590267Sdes if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) { 31690267Sdes errno = ENOMEM; 31790267Sdes return (-1); 31890267Sdes } 31990267Sdes *buf = tmp; 32090267Sdes *size = *size * 2 + 1; 32190267Sdes } 32290267Sdes } while (c != '\n'); 32390267Sdes 32490267Sdes DEBUG(fprintf(stderr, "<<< %.*s", (int)*len, *buf)); 32590267Sdes return (0); 32655557Sdes} 32755557Sdes 32855557Sdes 32962981Sdes/* 33062981Sdes * Write a line of text to a socket w/ timeout 33162981Sdes * XXX currently does not enforce timeout 33262981Sdes */ 33362981Sdesint 33475891Sarchie_fetch_putln(int fd, const char *str, size_t len) 33562981Sdes{ 33690267Sdes struct iovec iov[2]; 33790267Sdes ssize_t wlen; 33862981Sdes 33990267Sdes /* XXX should enforce timeout */ 34090267Sdes iov[0].iov_base = (char *)str; 34190267Sdes iov[0].iov_len = len; 34290267Sdes iov[1].iov_base = (char *)ENDL; 34390267Sdes iov[1].iov_len = sizeof ENDL; 34490267Sdes len += sizeof ENDL; 34590267Sdes wlen = writev(fd, iov, 2); 34690267Sdes if (wlen < 0 || (size_t)wlen != len) 34790267Sdes return (-1); 34890267Sdes DEBUG(fprintf(stderr, ">>> %s\n", str)); 34990267Sdes return (0); 35062981Sdes} 35162981Sdes 35262981Sdes 35341989Sdes/*** Directory-related utility functions *************************************/ 35441989Sdes 35541989Sdesint 35641989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len, 35790267Sdes const char *name, struct url_stat *us) 35841989Sdes{ 35990267Sdes struct url_ent *tmp; 36041989Sdes 36190267Sdes if (*p == NULL) { 36290268Sdes *size = 0; 36390267Sdes *len = 0; 36441989Sdes } 36541989Sdes 36690267Sdes if (*len >= *size - 1) { 36790268Sdes tmp = realloc(*p, (*size * 2 + 1) * sizeof **p); 36890267Sdes if (tmp == NULL) { 36990267Sdes errno = ENOMEM; 37090267Sdes _fetch_syserr(); 37190267Sdes return (-1); 37290267Sdes } 37390268Sdes *size = (*size * 2 + 1); 37490267Sdes *p = tmp; 37590267Sdes } 37641989Sdes 37790267Sdes tmp = *p + *len; 37890267Sdes snprintf(tmp->name, PATH_MAX, "%s", name); 37990267Sdes bcopy(us, &tmp->stat, sizeof *us); 38041989Sdes 38190267Sdes (*len)++; 38290267Sdes (++tmp)->name[0] = 0; 38390267Sdes 38490267Sdes return (0); 38541989Sdes} 386