diff options
Diffstat (limited to 'lib/libfetch/common.c')
-rw-r--r-- | lib/libfetch/common.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/lib/libfetch/common.c b/lib/libfetch/common.c new file mode 100644 index 0000000..559c665 --- /dev/null +++ b/lib/libfetch/common.c @@ -0,0 +1,385 @@ +/*- + * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/uio.h> +#include <netinet/in.h> + +#include <errno.h> +#include <netdb.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "fetch.h" +#include "common.h" + + +/*** Local data **************************************************************/ + +/* + * Error messages for resolver errors + */ +static struct fetcherr _netdb_errlist[] = { + { EAI_NODATA, FETCH_RESOLV, "Host not found" }, + { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, + { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, + { EAI_NONAME, FETCH_RESOLV, "No address record" }, + { -1, FETCH_UNKNOWN, "Unknown resolver error" } +}; + +/* End-of-Line */ +static const char ENDL[2] = "\r\n"; + + +/*** Error-reporting functions ***********************************************/ + +/* + * Map error code to string + */ +static struct fetcherr * +_fetch_finderr(struct fetcherr *p, int e) +{ + while (p->num != -1 && p->num != e) + p++; + return (p); +} + +/* + * Set error code + */ +void +_fetch_seterr(struct fetcherr *p, int e) +{ + p = _fetch_finderr(p, e); + fetchLastErrCode = p->cat; + snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); +} + +/* + * Set error code according to errno + */ +void +_fetch_syserr(void) +{ + switch (errno) { + case 0: + fetchLastErrCode = FETCH_OK; + break; + case EPERM: + case EACCES: + case EROFS: + case EAUTH: + case ENEEDAUTH: + fetchLastErrCode = FETCH_AUTH; + break; + case ENOENT: + case EISDIR: /* XXX */ + fetchLastErrCode = FETCH_UNAVAIL; + break; + case ENOMEM: + fetchLastErrCode = FETCH_MEMORY; + break; + case EBUSY: + case EAGAIN: + fetchLastErrCode = FETCH_TEMP; + break; + case EEXIST: + fetchLastErrCode = FETCH_EXISTS; + break; + case ENOSPC: + fetchLastErrCode = FETCH_FULL; + break; + case EADDRINUSE: + case EADDRNOTAVAIL: + case ENETDOWN: + case ENETUNREACH: + case ENETRESET: + case EHOSTUNREACH: + fetchLastErrCode = FETCH_NETWORK; + break; + case ECONNABORTED: + case ECONNRESET: + fetchLastErrCode = FETCH_ABORT; + break; + case ETIMEDOUT: + fetchLastErrCode = FETCH_TIMEOUT; + break; + case ECONNREFUSED: + case EHOSTDOWN: + fetchLastErrCode = FETCH_DOWN; + break; +default: + fetchLastErrCode = FETCH_UNKNOWN; + } + snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); +} + + +/* + * Emit status message + */ +void +_fetch_info(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); +} + + +/*** Network-related utility functions ***************************************/ + +/* + * Return the default port for a scheme + */ +int +_fetch_default_port(const char *scheme) +{ + struct servent *se; + + if ((se = getservbyname(scheme, "tcp")) != NULL) + return (ntohs(se->s_port)); + if (strcasecmp(scheme, SCHEME_FTP) == 0) + return (FTP_DEFAULT_PORT); + if (strcasecmp(scheme, SCHEME_HTTP) == 0) + return (HTTP_DEFAULT_PORT); + return (0); +} + +/* + * Return the default proxy port for a scheme + */ +int +_fetch_default_proxy_port(const char *scheme) +{ + if (strcasecmp(scheme, SCHEME_FTP) == 0) + return (FTP_DEFAULT_PROXY_PORT); + if (strcasecmp(scheme, SCHEME_HTTP) == 0) + return (HTTP_DEFAULT_PROXY_PORT); + return (0); +} + +/* + * Establish a TCP connection to the specified port on the specified host. + */ +int +_fetch_connect(const char *host, int port, int af, int verbose) +{ + char pbuf[10]; + struct addrinfo hints, *res, *res0; + int sd, err; + + DEBUG(fprintf(stderr, "---> %s:%d\n", host, port)); + + if (verbose) + _fetch_info("looking up %s", host); + + /* look up host name and set up socket address structure */ + snprintf(pbuf, sizeof(pbuf), "%d", port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) { + _netdb_seterr(err); + return (-1); + } + + if (verbose) + _fetch_info("connecting to %s:%d", host, port); + + /* try to connect */ + for (sd = -1, res = res0; res; res = res->ai_next) { + if ((sd = socket(res->ai_family, res->ai_socktype, + res->ai_protocol)) == -1) + continue; + if (connect(sd, res->ai_addr, res->ai_addrlen) != -1) + break; + close(sd); + sd = -1; + } + freeaddrinfo(res0); + if (sd == -1) { + _fetch_syserr(); + return (-1); + } + + return (sd); +} + + +/* + * Read a line of text from a socket w/ timeout + */ +#define MIN_BUF_SIZE 1024 + +int +_fetch_getln(int fd, char **buf, size_t *size, size_t *len) +{ + struct timeval now, timeout, wait; + fd_set readfds; + int r; + char c; + + if (*buf == NULL) { + if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) { + errno = ENOMEM; + return (-1); + } + *size = MIN_BUF_SIZE; + } + + **buf = '\0'; + *len = 0; + + if (fetchTimeout) { + gettimeofday(&timeout, NULL); + timeout.tv_sec += fetchTimeout; + FD_ZERO(&readfds); + } + + do { + if (fetchTimeout) { + FD_SET(fd, &readfds); + gettimeofday(&now, NULL); + wait.tv_sec = timeout.tv_sec - now.tv_sec; + wait.tv_usec = timeout.tv_usec - now.tv_usec; + if (wait.tv_usec < 0) { + wait.tv_usec += 1000000; + wait.tv_sec--; + } + if (wait.tv_sec < 0) { + errno = ETIMEDOUT; + return (-1); + } + r = select(fd+1, &readfds, NULL, NULL, &wait); + if (r == -1) { + if (errno == EINTR && fetchRestartCalls) + continue; + /* EBADF or EINVAL: shouldn't happen */ + return (-1); + } + if (!FD_ISSET(fd, &readfds)) + continue; + } + r = read(fd, &c, 1); + if (r == 0) + break; + if (r == -1) { + if (errno == EINTR && fetchRestartCalls) + continue; + /* any other error is bad news */ + return (-1); + } + (*buf)[*len] = c; + *len += 1; + if (*len == *size) { + char *tmp; + + if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) { + errno = ENOMEM; + return (-1); + } + *buf = tmp; + *size = *size * 2 + 1; + } + } while (c != '\n'); + + DEBUG(fprintf(stderr, "<<< %.*s", (int)*len, *buf)); + return (0); +} + + +/* + * Write a line of text to a socket w/ timeout + * XXX currently does not enforce timeout + */ +int +_fetch_putln(int fd, const char *str, size_t len) +{ + struct iovec iov[2]; + ssize_t wlen; + + /* XXX should enforce timeout */ + iov[0].iov_base = (char *)str; + iov[0].iov_len = len; + iov[1].iov_base = (char *)ENDL; + iov[1].iov_len = sizeof ENDL; + len += sizeof ENDL; + wlen = writev(fd, iov, 2); + if (wlen < 0 || (size_t)wlen != len) + return (-1); + DEBUG(fprintf(stderr, ">>> %s\n", str)); + return (0); +} + + +/*** Directory-related utility functions *************************************/ + +int +_fetch_add_entry(struct url_ent **p, int *size, int *len, + const char *name, struct url_stat *us) +{ + struct url_ent *tmp; + + if (*p == NULL) { + *size = 0; + *len = 0; + } + + if (*len >= *size - 1) { + tmp = realloc(*p, (*size * 2 + 1) * sizeof **p); + if (tmp == NULL) { + errno = ENOMEM; + _fetch_syserr(); + return (-1); + } + *size = (*size * 2 + 1); + *p = tmp; + } + + tmp = *p + *len; + snprintf(tmp->name, PATH_MAX, "%s", name); + bcopy(us, &tmp->stat, sizeof *us); + + (*len)++; + (++tmp)->name[0] = 0; + + return (0); +} |