common.c revision 97866
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 97866 2002-06-05 12:19:08Z 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 3897866Sdes#include <ctype.h> /* XXX */ 3940939Sdes#include <errno.h> 4040939Sdes#include <netdb.h> 4160924Sdes#include <stdarg.h> 4241862Sdes#include <stdlib.h> 4341862Sdes#include <stdio.h> 4440939Sdes#include <string.h> 4540939Sdes#include <unistd.h> 4640939Sdes 4740939Sdes#include "fetch.h" 4840939Sdes#include "common.h" 4940939Sdes 5040975Sdes 5140939Sdes/*** Local data **************************************************************/ 5240939Sdes 5340939Sdes/* 5440939Sdes * Error messages for resolver errors 5540939Sdes */ 5640939Sdesstatic struct fetcherr _netdb_errlist[] = { 5790267Sdes { EAI_NODATA, FETCH_RESOLV, "Host not found" }, 5890267Sdes { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, 5990267Sdes { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, 6090267Sdes { EAI_NONAME, FETCH_RESOLV, "No address record" }, 6190267Sdes { -1, FETCH_UNKNOWN, "Unknown resolver error" } 6240939Sdes}; 6340939Sdes 6462981Sdes/* End-of-Line */ 6575891Sarchiestatic const char ENDL[2] = "\r\n"; 6640939Sdes 6762981Sdes 6840939Sdes/*** Error-reporting functions ***********************************************/ 6940939Sdes 7040939Sdes/* 7140939Sdes * Map error code to string 7240939Sdes */ 7360924Sdesstatic struct fetcherr * 7440975Sdes_fetch_finderr(struct fetcherr *p, int e) 7540939Sdes{ 7690267Sdes while (p->num != -1 && p->num != e) 7790267Sdes p++; 7890267Sdes return (p); 7940939Sdes} 8040939Sdes 8140939Sdes/* 8240939Sdes * Set error code 8340939Sdes */ 8440939Sdesvoid 8540939Sdes_fetch_seterr(struct fetcherr *p, int e) 8640939Sdes{ 8790267Sdes p = _fetch_finderr(p, e); 8890267Sdes fetchLastErrCode = p->cat; 8990267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); 9040939Sdes} 9140939Sdes 9240939Sdes/* 9340939Sdes * Set error code according to errno 9440939Sdes */ 9540939Sdesvoid 9640939Sdes_fetch_syserr(void) 9740939Sdes{ 9890267Sdes switch (errno) { 9990267Sdes case 0: 10090267Sdes fetchLastErrCode = FETCH_OK; 10190267Sdes break; 10290267Sdes case EPERM: 10390267Sdes case EACCES: 10490267Sdes case EROFS: 10590267Sdes case EAUTH: 10690267Sdes case ENEEDAUTH: 10790267Sdes fetchLastErrCode = FETCH_AUTH; 10890267Sdes break; 10990267Sdes case ENOENT: 11090267Sdes case EISDIR: /* XXX */ 11190267Sdes fetchLastErrCode = FETCH_UNAVAIL; 11290267Sdes break; 11390267Sdes case ENOMEM: 11490267Sdes fetchLastErrCode = FETCH_MEMORY; 11590267Sdes break; 11690267Sdes case EBUSY: 11790267Sdes case EAGAIN: 11890267Sdes fetchLastErrCode = FETCH_TEMP; 11990267Sdes break; 12090267Sdes case EEXIST: 12190267Sdes fetchLastErrCode = FETCH_EXISTS; 12290267Sdes break; 12390267Sdes case ENOSPC: 12490267Sdes fetchLastErrCode = FETCH_FULL; 12590267Sdes break; 12690267Sdes case EADDRINUSE: 12790267Sdes case EADDRNOTAVAIL: 12890267Sdes case ENETDOWN: 12990267Sdes case ENETUNREACH: 13090267Sdes case ENETRESET: 13190267Sdes case EHOSTUNREACH: 13290267Sdes fetchLastErrCode = FETCH_NETWORK; 13390267Sdes break; 13490267Sdes case ECONNABORTED: 13590267Sdes case ECONNRESET: 13690267Sdes fetchLastErrCode = FETCH_ABORT; 13790267Sdes break; 13890267Sdes case ETIMEDOUT: 13990267Sdes fetchLastErrCode = FETCH_TIMEOUT; 14090267Sdes break; 14190267Sdes case ECONNREFUSED: 14290267Sdes case EHOSTDOWN: 14390267Sdes fetchLastErrCode = FETCH_DOWN; 14490267Sdes break; 14590267Sdesdefault: 14690267Sdes fetchLastErrCode = FETCH_UNKNOWN; 14790267Sdes } 14890267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); 14940939Sdes} 15040939Sdes 15140939Sdes 15241862Sdes/* 15341862Sdes * Emit status message 15441862Sdes */ 15560924Sdesvoid 15675891Sarchie_fetch_info(const char *fmt, ...) 15741862Sdes{ 15890267Sdes va_list ap; 15990267Sdes 16090267Sdes va_start(ap, fmt); 16190267Sdes vfprintf(stderr, fmt, ap); 16290267Sdes va_end(ap); 16390267Sdes fputc('\n', stderr); 16441862Sdes} 16541862Sdes 16641862Sdes 16740939Sdes/*** Network-related utility functions ***************************************/ 16840939Sdes 16940939Sdes/* 17068551Sdes * Return the default port for a scheme 17168551Sdes */ 17268551Sdesint 17375891Sarchie_fetch_default_port(const char *scheme) 17468551Sdes{ 17590267Sdes struct servent *se; 17668551Sdes 17790267Sdes if ((se = getservbyname(scheme, "tcp")) != NULL) 17890267Sdes return (ntohs(se->s_port)); 17990267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 18090267Sdes return (FTP_DEFAULT_PORT); 18190267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 18290267Sdes return (HTTP_DEFAULT_PORT); 18390267Sdes return (0); 18468551Sdes} 18568551Sdes 18668551Sdes/* 18768551Sdes * Return the default proxy port for a scheme 18868551Sdes */ 18968551Sdesint 19075891Sarchie_fetch_default_proxy_port(const char *scheme) 19168551Sdes{ 19290267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 19390267Sdes return (FTP_DEFAULT_PROXY_PORT); 19490267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 19590267Sdes return (HTTP_DEFAULT_PROXY_PORT); 19690267Sdes return (0); 19768551Sdes} 19868551Sdes 19968551Sdes/* 20097866Sdes * Create a connection for an existing descriptor. 20197866Sdes */ 20297866Sdesconn_t * 20397866Sdes_fetch_reopen(int sd) 20497866Sdes{ 20597866Sdes conn_t *conn; 20697866Sdes 20797866Sdes /* allocate and fill connection structure */ 20897866Sdes if ((conn = calloc(1, sizeof *conn)) == NULL) 20997866Sdes return (NULL); 21097866Sdes conn->sd = sd; 21197866Sdes return (conn); 21297866Sdes} 21397866Sdes 21497866Sdes 21597866Sdes/* 21640939Sdes * Establish a TCP connection to the specified port on the specified host. 21740939Sdes */ 21897856Sdesconn_t * 21975891Sarchie_fetch_connect(const char *host, int port, int af, int verbose) 22040939Sdes{ 22197856Sdes conn_t *conn; 22290267Sdes char pbuf[10]; 22390267Sdes struct addrinfo hints, *res, *res0; 22490267Sdes int sd, err; 22540939Sdes 22690267Sdes DEBUG(fprintf(stderr, "---> %s:%d\n", host, port)); 22741862Sdes 22890267Sdes if (verbose) 22990267Sdes _fetch_info("looking up %s", host); 23040939Sdes 23190267Sdes /* look up host name and set up socket address structure */ 23290267Sdes snprintf(pbuf, sizeof(pbuf), "%d", port); 23390267Sdes memset(&hints, 0, sizeof(hints)); 23490267Sdes hints.ai_family = af; 23590267Sdes hints.ai_socktype = SOCK_STREAM; 23690267Sdes hints.ai_protocol = 0; 23790267Sdes if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) { 23890267Sdes _netdb_seterr(err); 23997856Sdes return (NULL); 24090267Sdes } 24190267Sdes 24290267Sdes if (verbose) 24390267Sdes _fetch_info("connecting to %s:%d", host, port); 24490267Sdes 24590267Sdes /* try to connect */ 24690267Sdes for (sd = -1, res = res0; res; res = res->ai_next) { 24790267Sdes if ((sd = socket(res->ai_family, res->ai_socktype, 24862981Sdes res->ai_protocol)) == -1) 24990267Sdes continue; 25090267Sdes if (connect(sd, res->ai_addr, res->ai_addrlen) != -1) 25190267Sdes break; 25290267Sdes close(sd); 25390267Sdes sd = -1; 25490267Sdes } 25590267Sdes freeaddrinfo(res0); 25690267Sdes if (sd == -1) { 25790267Sdes _fetch_syserr(); 25897856Sdes return (NULL); 25990267Sdes } 26040939Sdes 26197866Sdes if ((conn = _fetch_reopen(sd)) == NULL) 26297856Sdes close(sd); 26397856Sdes return (conn); 26440939Sdes} 26541989Sdes 26641989Sdes 26755557Sdes/* 26897866Sdes * Read a character from a connection w/ timeout 26955557Sdes */ 27097866Sdesssize_t 27197866Sdes_fetch_read(conn_t *conn, char *buf, size_t len) 27255557Sdes{ 27390267Sdes struct timeval now, timeout, wait; 27490267Sdes fd_set readfds; 27597866Sdes ssize_t rlen, total; 27690267Sdes int r; 27790267Sdes 27855557Sdes if (fetchTimeout) { 27997866Sdes FD_ZERO(&readfds); 28090267Sdes gettimeofday(&timeout, NULL); 28190267Sdes timeout.tv_sec += fetchTimeout; 28255557Sdes } 28390267Sdes 28497866Sdes total = 0; 28597866Sdes while (len > 0) { 28697866Sdes while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) { 28797856Sdes FD_SET(conn->sd, &readfds); 28890267Sdes gettimeofday(&now, NULL); 28990267Sdes wait.tv_sec = timeout.tv_sec - now.tv_sec; 29090267Sdes wait.tv_usec = timeout.tv_usec - now.tv_usec; 29190267Sdes if (wait.tv_usec < 0) { 29290267Sdes wait.tv_usec += 1000000; 29390267Sdes wait.tv_sec--; 29490267Sdes } 29597866Sdes if (wait.tv_sec < 0) 29697866Sdes return (rlen); 29797866Sdes errno = 0; 29897856Sdes r = select(conn->sd + 1, &readfds, NULL, NULL, &wait); 29990267Sdes if (r == -1) { 30090267Sdes if (errno == EINTR && fetchRestartCalls) 30190267Sdes continue; 30290267Sdes return (-1); 30390267Sdes } 30490267Sdes } 30597866Sdes if (conn->ssl != NULL) 30697866Sdes rlen = SSL_read(conn->ssl, buf, len); 30797866Sdes else 30897866Sdes rlen = read(conn->sd, buf, len); 30997866Sdes if (rlen == 0) 31090267Sdes break; 31197866Sdes if (rlen < 0) { 31290267Sdes if (errno == EINTR && fetchRestartCalls) 31390267Sdes continue; 31490267Sdes return (-1); 31590267Sdes } 31697866Sdes len -= rlen; 31797866Sdes buf += rlen; 31897866Sdes total += rlen; 31997866Sdes } 32097866Sdes return (total); 32197866Sdes} 32297866Sdes 32397866Sdes/* 32497866Sdes * Read a line of text from a connection w/ timeout 32597866Sdes */ 32697866Sdes#define MIN_BUF_SIZE 1024 32797866Sdes 32897866Sdesint 32997866Sdes_fetch_getln(conn_t *conn) 33097866Sdes{ 33197866Sdes char *tmp; 33297866Sdes size_t tmpsize; 33397866Sdes char c; 33497866Sdes 33597866Sdes if (conn->buf == NULL) { 33697866Sdes if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) { 33797866Sdes errno = ENOMEM; 33897866Sdes return (-1); 33997866Sdes } 34097866Sdes conn->bufsize = MIN_BUF_SIZE; 34197866Sdes } 34297866Sdes 34397866Sdes conn->buf[0] = '\0'; 34497866Sdes conn->buflen = 0; 34597866Sdes 34697866Sdes do { 34797866Sdes if (_fetch_read(conn, &c, 1) == -1) 34897866Sdes return (-1); 34997856Sdes conn->buf[conn->buflen++] = c; 35097856Sdes if (conn->buflen == conn->bufsize) { 35197856Sdes tmp = conn->buf; 35297856Sdes tmpsize = conn->bufsize * 2 + 1; 35397856Sdes if ((tmp = realloc(tmp, tmpsize)) == NULL) { 35490267Sdes errno = ENOMEM; 35590267Sdes return (-1); 35690267Sdes } 35797856Sdes conn->buf = tmp; 35897856Sdes conn->bufsize = tmpsize; 35990267Sdes } 36090267Sdes } while (c != '\n'); 36190267Sdes 36297856Sdes conn->buf[conn->buflen] = '\0'; 36397856Sdes DEBUG(fprintf(stderr, "<<< %s", conn->buf)); 36490267Sdes return (0); 36555557Sdes} 36655557Sdes 36755557Sdes 36862981Sdes/* 36997866Sdes * Write to a connection w/ timeout 37062981Sdes */ 37197866Sdesssize_t 37297866Sdes_fetch_write(conn_t *conn, const char *buf, size_t len) 37397866Sdes{ 37497866Sdes struct timeval now, timeout, wait; 37597866Sdes fd_set writefds; 37697866Sdes ssize_t wlen, total; 37797866Sdes int r; 37897866Sdes 37997866Sdes if (fetchTimeout) { 38097866Sdes FD_ZERO(&writefds); 38197866Sdes gettimeofday(&timeout, NULL); 38297866Sdes timeout.tv_sec += fetchTimeout; 38397866Sdes } 38497866Sdes 38597866Sdes while (len > 0) { 38697866Sdes while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) { 38797866Sdes FD_SET(conn->sd, &writefds); 38897866Sdes gettimeofday(&now, NULL); 38997866Sdes wait.tv_sec = timeout.tv_sec - now.tv_sec; 39097866Sdes wait.tv_usec = timeout.tv_usec - now.tv_usec; 39197866Sdes if (wait.tv_usec < 0) { 39297866Sdes wait.tv_usec += 1000000; 39397866Sdes wait.tv_sec--; 39497866Sdes } 39597866Sdes if (wait.tv_sec < 0) { 39697866Sdes errno = ETIMEDOUT; 39797866Sdes return (-1); 39897866Sdes } 39997866Sdes errno = 0; 40097866Sdes r = select(conn->sd + 1, NULL, &writefds, NULL, &wait); 40197866Sdes if (r == -1) { 40297866Sdes if (errno == EINTR && fetchRestartCalls) 40397866Sdes continue; 40497866Sdes return (-1); 40597866Sdes } 40697866Sdes } 40797866Sdes errno = 0; 40897866Sdes if (conn->ssl != NULL) 40997866Sdes wlen = SSL_write(conn->ssl, buf, len); 41097866Sdes else 41197866Sdes wlen = write(conn->sd, buf, len); 41297866Sdes if (wlen == 0) 41397866Sdes /* we consider a short write a failure */ 41497866Sdes return (-1); 41597866Sdes if (wlen < 0) { 41697866Sdes if (errno == EINTR && fetchRestartCalls) 41797866Sdes continue; 41897866Sdes return (-1); 41997866Sdes } 42097866Sdes len -= wlen; 42197866Sdes buf += wlen; 42297866Sdes total += wlen; 42397866Sdes } 42497866Sdes return (total); 42597866Sdes} 42697866Sdes 42797866Sdes/* 42897866Sdes * Write a line of text to a connection w/ timeout 42997866Sdes */ 43062981Sdesint 43197856Sdes_fetch_putln(conn_t *conn, const char *str, size_t len) 43262981Sdes{ 43397866Sdes if (_fetch_write(conn, str, len) == -1 || 43497866Sdes _fetch_write(conn, ENDL, sizeof ENDL) == -1) 43590267Sdes return (-1); 43690267Sdes return (0); 43762981Sdes} 43862981Sdes 43962981Sdes 44097856Sdes/* 44197856Sdes * Close connection 44297856Sdes */ 44397856Sdesint 44497856Sdes_fetch_close(conn_t *conn) 44597856Sdes{ 44697856Sdes int ret; 44797856Sdes 44897856Sdes ret = close(conn->sd); 44997856Sdes free(conn); 45097856Sdes return (ret); 45197856Sdes} 45297856Sdes 45397856Sdes 45441989Sdes/*** Directory-related utility functions *************************************/ 45541989Sdes 45641989Sdesint 45741989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len, 45890267Sdes const char *name, struct url_stat *us) 45941989Sdes{ 46090267Sdes struct url_ent *tmp; 46141989Sdes 46290267Sdes if (*p == NULL) { 46390268Sdes *size = 0; 46490267Sdes *len = 0; 46541989Sdes } 46641989Sdes 46790267Sdes if (*len >= *size - 1) { 46890268Sdes tmp = realloc(*p, (*size * 2 + 1) * sizeof **p); 46990267Sdes if (tmp == NULL) { 47090267Sdes errno = ENOMEM; 47190267Sdes _fetch_syserr(); 47290267Sdes return (-1); 47390267Sdes } 47490268Sdes *size = (*size * 2 + 1); 47590267Sdes *p = tmp; 47690267Sdes } 47741989Sdes 47890267Sdes tmp = *p + *len; 47990267Sdes snprintf(tmp->name, PATH_MAX, "%s", name); 48090267Sdes bcopy(us, &tmp->stat, sizeof *us); 48141989Sdes 48290267Sdes (*len)++; 48390267Sdes (++tmp)->name[0] = 0; 48490267Sdes 48590267Sdes return (0); 48641989Sdes} 487