common.c revision 141970
140939Sdes/*- 2135546Sdes * Copyright (c) 1998-2004 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 141970 2005-02-16 12:46:46Z 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> 40109695Sdes#include <pwd.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[] = { 57121423Sume#ifdef EAI_NODATA 5890267Sdes { EAI_NODATA, FETCH_RESOLV, "Host not found" }, 59121423Sume#endif 6090267Sdes { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, 6190267Sdes { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, 6290267Sdes { EAI_NONAME, FETCH_RESOLV, "No address record" }, 6390267Sdes { -1, FETCH_UNKNOWN, "Unknown resolver error" } 6440939Sdes}; 6540939Sdes 6662981Sdes/* End-of-Line */ 6775891Sarchiestatic const char ENDL[2] = "\r\n"; 6840939Sdes 6962981Sdes 7040939Sdes/*** Error-reporting functions ***********************************************/ 7140939Sdes 7240939Sdes/* 7340939Sdes * Map error code to string 7440939Sdes */ 7560924Sdesstatic struct fetcherr * 7640975Sdes_fetch_finderr(struct fetcherr *p, int e) 7740939Sdes{ 7890267Sdes while (p->num != -1 && p->num != e) 7990267Sdes p++; 8090267Sdes return (p); 8140939Sdes} 8240939Sdes 8340939Sdes/* 8440939Sdes * Set error code 8540939Sdes */ 8640939Sdesvoid 8740939Sdes_fetch_seterr(struct fetcherr *p, int e) 8840939Sdes{ 8990267Sdes p = _fetch_finderr(p, e); 9090267Sdes fetchLastErrCode = p->cat; 9190267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); 9240939Sdes} 9340939Sdes 9440939Sdes/* 9540939Sdes * Set error code according to errno 9640939Sdes */ 9740939Sdesvoid 9840939Sdes_fetch_syserr(void) 9940939Sdes{ 10090267Sdes switch (errno) { 10190267Sdes case 0: 10290267Sdes fetchLastErrCode = FETCH_OK; 10390267Sdes break; 10490267Sdes case EPERM: 10590267Sdes case EACCES: 10690267Sdes case EROFS: 10790267Sdes case EAUTH: 10890267Sdes case ENEEDAUTH: 10990267Sdes fetchLastErrCode = FETCH_AUTH; 11090267Sdes break; 11190267Sdes case ENOENT: 11290267Sdes case EISDIR: /* XXX */ 11390267Sdes fetchLastErrCode = FETCH_UNAVAIL; 11490267Sdes break; 11590267Sdes case ENOMEM: 11690267Sdes fetchLastErrCode = FETCH_MEMORY; 11790267Sdes break; 11890267Sdes case EBUSY: 11990267Sdes case EAGAIN: 12090267Sdes fetchLastErrCode = FETCH_TEMP; 12190267Sdes break; 12290267Sdes case EEXIST: 12390267Sdes fetchLastErrCode = FETCH_EXISTS; 12490267Sdes break; 12590267Sdes case ENOSPC: 12690267Sdes fetchLastErrCode = FETCH_FULL; 12790267Sdes break; 12890267Sdes case EADDRINUSE: 12990267Sdes case EADDRNOTAVAIL: 13090267Sdes case ENETDOWN: 13190267Sdes case ENETUNREACH: 13290267Sdes case ENETRESET: 13390267Sdes case EHOSTUNREACH: 13490267Sdes fetchLastErrCode = FETCH_NETWORK; 13590267Sdes break; 13690267Sdes case ECONNABORTED: 13790267Sdes case ECONNRESET: 13890267Sdes fetchLastErrCode = FETCH_ABORT; 13990267Sdes break; 14090267Sdes case ETIMEDOUT: 14190267Sdes fetchLastErrCode = FETCH_TIMEOUT; 14290267Sdes break; 14390267Sdes case ECONNREFUSED: 14490267Sdes case EHOSTDOWN: 14590267Sdes fetchLastErrCode = FETCH_DOWN; 14690267Sdes break; 14790267Sdesdefault: 14890267Sdes fetchLastErrCode = FETCH_UNKNOWN; 14990267Sdes } 15090267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); 15140939Sdes} 15240939Sdes 15340939Sdes 15441862Sdes/* 15541862Sdes * Emit status message 15641862Sdes */ 15760924Sdesvoid 15875891Sarchie_fetch_info(const char *fmt, ...) 15941862Sdes{ 16090267Sdes va_list ap; 16190267Sdes 16290267Sdes va_start(ap, fmt); 16390267Sdes vfprintf(stderr, fmt, ap); 16490267Sdes va_end(ap); 16590267Sdes fputc('\n', stderr); 16641862Sdes} 16741862Sdes 16841862Sdes 16940939Sdes/*** Network-related utility functions ***************************************/ 17040939Sdes 17140939Sdes/* 17268551Sdes * Return the default port for a scheme 17368551Sdes */ 17468551Sdesint 17575891Sarchie_fetch_default_port(const char *scheme) 17668551Sdes{ 17790267Sdes struct servent *se; 17868551Sdes 17990267Sdes if ((se = getservbyname(scheme, "tcp")) != NULL) 18090267Sdes return (ntohs(se->s_port)); 18190267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 18290267Sdes return (FTP_DEFAULT_PORT); 18390267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 18490267Sdes return (HTTP_DEFAULT_PORT); 18590267Sdes return (0); 18668551Sdes} 18768551Sdes 18868551Sdes/* 18968551Sdes * Return the default proxy port for a scheme 19068551Sdes */ 19168551Sdesint 19275891Sarchie_fetch_default_proxy_port(const char *scheme) 19368551Sdes{ 19490267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 19590267Sdes return (FTP_DEFAULT_PROXY_PORT); 19690267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 19790267Sdes return (HTTP_DEFAULT_PROXY_PORT); 19890267Sdes return (0); 19968551Sdes} 20068551Sdes 20198117Sdes 20268551Sdes/* 20397866Sdes * Create a connection for an existing descriptor. 20497866Sdes */ 20597866Sdesconn_t * 20697866Sdes_fetch_reopen(int sd) 20797866Sdes{ 20897866Sdes conn_t *conn; 20997866Sdes 21097866Sdes /* allocate and fill connection structure */ 211109967Sdes if ((conn = calloc(1, sizeof(*conn))) == NULL) 21297866Sdes return (NULL); 21397866Sdes conn->sd = sd; 21498117Sdes ++conn->ref; 21597866Sdes return (conn); 21697866Sdes} 21797866Sdes 21897866Sdes 21997866Sdes/* 22098117Sdes * Bump a connection's reference count. 22198117Sdes */ 22298117Sdesconn_t * 22398117Sdes_fetch_ref(conn_t *conn) 22498117Sdes{ 22598117Sdes 22698117Sdes ++conn->ref; 22798117Sdes return (conn); 22898117Sdes} 22998117Sdes 23098117Sdes 23198117Sdes/* 232111816Sdes * Bind a socket to a specific local address 233111816Sdes */ 234111816Sdesint 235111816Sdes_fetch_bind(int sd, int af, const char *addr) 236111816Sdes{ 237111816Sdes struct addrinfo hints, *res, *res0; 238111816Sdes int err; 239111816Sdes 240111816Sdes memset(&hints, 0, sizeof(hints)); 241111816Sdes hints.ai_family = af; 242111816Sdes hints.ai_socktype = SOCK_STREAM; 243111816Sdes hints.ai_protocol = 0; 244111816Sdes if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0) 245111816Sdes return (-1); 246111816Sdes for (res = res0; res; res = res->ai_next) 247111816Sdes if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) 248111816Sdes return (0); 249111816Sdes return (-1); 250111816Sdes} 251111816Sdes 252111816Sdes 253111816Sdes/* 25440939Sdes * Establish a TCP connection to the specified port on the specified host. 25540939Sdes */ 25697856Sdesconn_t * 25775891Sarchie_fetch_connect(const char *host, int port, int af, int verbose) 25840939Sdes{ 25997856Sdes conn_t *conn; 26090267Sdes char pbuf[10]; 261111816Sdes const char *bindaddr; 26290267Sdes struct addrinfo hints, *res, *res0; 26390267Sdes int sd, err; 26440939Sdes 26590267Sdes DEBUG(fprintf(stderr, "---> %s:%d\n", host, port)); 26641862Sdes 26790267Sdes if (verbose) 26890267Sdes _fetch_info("looking up %s", host); 26940939Sdes 27090267Sdes /* look up host name and set up socket address structure */ 27190267Sdes snprintf(pbuf, sizeof(pbuf), "%d", port); 27290267Sdes memset(&hints, 0, sizeof(hints)); 27390267Sdes hints.ai_family = af; 27490267Sdes hints.ai_socktype = SOCK_STREAM; 27590267Sdes hints.ai_protocol = 0; 27690267Sdes if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) { 27790267Sdes _netdb_seterr(err); 27897856Sdes return (NULL); 27990267Sdes } 280111816Sdes bindaddr = getenv("FETCH_BIND_ADDRESS"); 28190267Sdes 28290267Sdes if (verbose) 28390267Sdes _fetch_info("connecting to %s:%d", host, port); 28490267Sdes 28590267Sdes /* try to connect */ 286111816Sdes for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) { 28790267Sdes if ((sd = socket(res->ai_family, res->ai_socktype, 28862981Sdes res->ai_protocol)) == -1) 28990267Sdes continue; 290111816Sdes if (bindaddr != NULL && *bindaddr != '\0' && 291111816Sdes _fetch_bind(sd, res->ai_family, bindaddr) != 0) { 292111816Sdes _fetch_info("failed to bind to '%s'", bindaddr); 293111816Sdes close(sd); 294111816Sdes continue; 295111816Sdes } 296111816Sdes if (connect(sd, res->ai_addr, res->ai_addrlen) == 0) 29790267Sdes break; 29890267Sdes close(sd); 29990267Sdes } 30090267Sdes freeaddrinfo(res0); 30190267Sdes if (sd == -1) { 30290267Sdes _fetch_syserr(); 30397856Sdes return (NULL); 30490267Sdes } 30540939Sdes 306103459Sfenner if ((conn = _fetch_reopen(sd)) == NULL) { 307103459Sfenner _fetch_syserr(); 30897856Sdes close(sd); 309103459Sfenner } 31097856Sdes return (conn); 31140939Sdes} 31241989Sdes 31341989Sdes 31455557Sdes/* 31597868Sdes * Enable SSL on a connection. 31697868Sdes */ 31797868Sdesint 31897868Sdes_fetch_ssl(conn_t *conn, int verbose) 31997868Sdes{ 32097868Sdes 32197891Sdes#ifdef WITH_SSL 32297868Sdes /* Init the SSL library and context */ 32397868Sdes if (!SSL_library_init()){ 32497868Sdes fprintf(stderr, "SSL library init failed\n"); 32597868Sdes return (-1); 32697868Sdes } 32797868Sdes 32897868Sdes SSL_load_error_strings(); 32997868Sdes 33097868Sdes conn->ssl_meth = SSLv23_client_method(); 33197868Sdes conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth); 332108579Sdes SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY); 33397868Sdes 33497868Sdes conn->ssl = SSL_new(conn->ssl_ctx); 33597868Sdes if (conn->ssl == NULL){ 33697868Sdes fprintf(stderr, "SSL context creation failed\n"); 33797868Sdes return (-1); 33897868Sdes } 33997868Sdes SSL_set_fd(conn->ssl, conn->sd); 34097868Sdes if (SSL_connect(conn->ssl) == -1){ 34197868Sdes ERR_print_errors_fp(stderr); 34297868Sdes return (-1); 34397868Sdes } 34497868Sdes 34597868Sdes if (verbose) { 34697868Sdes X509_NAME *name; 34797868Sdes char *str; 34897868Sdes 34997868Sdes fprintf(stderr, "SSL connection established using %s\n", 35097868Sdes SSL_get_cipher(conn->ssl)); 35197868Sdes conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); 35297868Sdes name = X509_get_subject_name(conn->ssl_cert); 35397868Sdes str = X509_NAME_oneline(name, 0, 0); 35497868Sdes printf("Certificate subject: %s\n", str); 35597868Sdes free(str); 35697868Sdes name = X509_get_issuer_name(conn->ssl_cert); 35797868Sdes str = X509_NAME_oneline(name, 0, 0); 35897868Sdes printf("Certificate issuer: %s\n", str); 35997868Sdes free(str); 36097868Sdes } 36197868Sdes 36297868Sdes return (0); 36397891Sdes#else 36497891Sdes (void)conn; 36597891Sdes (void)verbose; 36697891Sdes fprintf(stderr, "SSL support disabled\n"); 36797891Sdes return (-1); 36897891Sdes#endif 36997868Sdes} 37097868Sdes 37198117Sdes 37297868Sdes/* 37397866Sdes * Read a character from a connection w/ timeout 37455557Sdes */ 37597866Sdesssize_t 37697866Sdes_fetch_read(conn_t *conn, char *buf, size_t len) 37755557Sdes{ 37890267Sdes struct timeval now, timeout, wait; 37990267Sdes fd_set readfds; 38097866Sdes ssize_t rlen, total; 38190267Sdes int r; 38290267Sdes 38355557Sdes if (fetchTimeout) { 38497866Sdes FD_ZERO(&readfds); 38590267Sdes gettimeofday(&timeout, NULL); 38690267Sdes timeout.tv_sec += fetchTimeout; 38755557Sdes } 38890267Sdes 38997866Sdes total = 0; 39097866Sdes while (len > 0) { 39197866Sdes while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) { 39297856Sdes FD_SET(conn->sd, &readfds); 39390267Sdes gettimeofday(&now, NULL); 39490267Sdes wait.tv_sec = timeout.tv_sec - now.tv_sec; 39590267Sdes wait.tv_usec = timeout.tv_usec - now.tv_usec; 39690267Sdes if (wait.tv_usec < 0) { 39790267Sdes wait.tv_usec += 1000000; 39890267Sdes wait.tv_sec--; 39990267Sdes } 400106186Sdes if (wait.tv_sec < 0) { 401106186Sdes errno = ETIMEDOUT; 402106186Sdes _fetch_syserr(); 403106186Sdes return (-1); 404106186Sdes } 40597866Sdes errno = 0; 40697856Sdes r = select(conn->sd + 1, &readfds, NULL, NULL, &wait); 40790267Sdes if (r == -1) { 40890267Sdes if (errno == EINTR && fetchRestartCalls) 40990267Sdes continue; 410106186Sdes _fetch_syserr(); 41190267Sdes return (-1); 41290267Sdes } 41390267Sdes } 41497891Sdes#ifdef WITH_SSL 41597866Sdes if (conn->ssl != NULL) 41697866Sdes rlen = SSL_read(conn->ssl, buf, len); 41797866Sdes else 41897891Sdes#endif 41997866Sdes rlen = read(conn->sd, buf, len); 420106049Sdes if (rlen == 0) 421106049Sdes break; 42297866Sdes if (rlen < 0) { 42390267Sdes if (errno == EINTR && fetchRestartCalls) 42490267Sdes continue; 42590267Sdes return (-1); 42690267Sdes } 42797866Sdes len -= rlen; 42897866Sdes buf += rlen; 42997866Sdes total += rlen; 43097866Sdes } 43197866Sdes return (total); 43297866Sdes} 43397866Sdes 43498117Sdes 43597866Sdes/* 43697866Sdes * Read a line of text from a connection w/ timeout 43797866Sdes */ 43897866Sdes#define MIN_BUF_SIZE 1024 43997866Sdes 44097866Sdesint 44197866Sdes_fetch_getln(conn_t *conn) 44297866Sdes{ 44397866Sdes char *tmp; 44497866Sdes size_t tmpsize; 445106186Sdes ssize_t len; 44697866Sdes char c; 44797866Sdes 44897866Sdes if (conn->buf == NULL) { 44997866Sdes if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) { 45097866Sdes errno = ENOMEM; 45197866Sdes return (-1); 45297866Sdes } 45397866Sdes conn->bufsize = MIN_BUF_SIZE; 45497866Sdes } 45597866Sdes 45697866Sdes conn->buf[0] = '\0'; 45797866Sdes conn->buflen = 0; 45897866Sdes 45997866Sdes do { 460106186Sdes len = _fetch_read(conn, &c, 1); 461106186Sdes if (len == -1) 46297866Sdes return (-1); 463106186Sdes if (len == 0) 464106137Sobrien break; 46597856Sdes conn->buf[conn->buflen++] = c; 46697856Sdes if (conn->buflen == conn->bufsize) { 46797856Sdes tmp = conn->buf; 46897856Sdes tmpsize = conn->bufsize * 2 + 1; 46997856Sdes if ((tmp = realloc(tmp, tmpsize)) == NULL) { 47090267Sdes errno = ENOMEM; 47190267Sdes return (-1); 47290267Sdes } 47397856Sdes conn->buf = tmp; 47497856Sdes conn->bufsize = tmpsize; 47590267Sdes } 47690267Sdes } while (c != '\n'); 47790267Sdes 47897856Sdes conn->buf[conn->buflen] = '\0'; 47997856Sdes DEBUG(fprintf(stderr, "<<< %s", conn->buf)); 48090267Sdes return (0); 48155557Sdes} 48255557Sdes 48355557Sdes 48462981Sdes/* 48597866Sdes * Write to a connection w/ timeout 48662981Sdes */ 48797866Sdesssize_t 48897866Sdes_fetch_write(conn_t *conn, const char *buf, size_t len) 48997866Sdes{ 490106175Simp struct iovec iov; 491106175Simp 492106175Simp iov.iov_base = __DECONST(char *, buf); 493106175Simp iov.iov_len = len; 494106175Simp return _fetch_writev(conn, &iov, 1); 495106175Simp} 496106175Simp 497106175Simp/* 498106175Simp * Write a vector to a connection w/ timeout 499106175Simp * Note: can modify the iovec. 500106175Simp */ 501106175Simpssize_t 502106175Simp_fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt) 503106175Simp{ 50497866Sdes struct timeval now, timeout, wait; 50597866Sdes fd_set writefds; 50697866Sdes ssize_t wlen, total; 50797866Sdes int r; 50897866Sdes 50997866Sdes if (fetchTimeout) { 51097866Sdes FD_ZERO(&writefds); 51197866Sdes gettimeofday(&timeout, NULL); 51297866Sdes timeout.tv_sec += fetchTimeout; 51397866Sdes } 51497866Sdes 515106175Simp total = 0; 516106175Simp while (iovcnt > 0) { 51797866Sdes while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) { 51897866Sdes FD_SET(conn->sd, &writefds); 51997866Sdes gettimeofday(&now, NULL); 52097866Sdes wait.tv_sec = timeout.tv_sec - now.tv_sec; 52197866Sdes wait.tv_usec = timeout.tv_usec - now.tv_usec; 52297866Sdes if (wait.tv_usec < 0) { 52397866Sdes wait.tv_usec += 1000000; 52497866Sdes wait.tv_sec--; 52597866Sdes } 52697866Sdes if (wait.tv_sec < 0) { 52797866Sdes errno = ETIMEDOUT; 528106186Sdes _fetch_syserr(); 52997866Sdes return (-1); 53097866Sdes } 53197866Sdes errno = 0; 53297866Sdes r = select(conn->sd + 1, NULL, &writefds, NULL, &wait); 53397866Sdes if (r == -1) { 53497866Sdes if (errno == EINTR && fetchRestartCalls) 53597866Sdes continue; 53697866Sdes return (-1); 53797866Sdes } 53897866Sdes } 53997866Sdes errno = 0; 54097891Sdes#ifdef WITH_SSL 54197866Sdes if (conn->ssl != NULL) 542106175Simp wlen = SSL_write(conn->ssl, 543106175Simp iov->iov_base, iov->iov_len); 54497866Sdes else 54597891Sdes#endif 546106175Simp wlen = writev(conn->sd, iov, iovcnt); 547106175Simp if (wlen == 0) { 54897866Sdes /* we consider a short write a failure */ 549106175Simp errno = EPIPE; 550106175Simp _fetch_syserr(); 55197866Sdes return (-1); 552106175Simp } 55397866Sdes if (wlen < 0) { 55497866Sdes if (errno == EINTR && fetchRestartCalls) 55597866Sdes continue; 55697866Sdes return (-1); 55797866Sdes } 55897866Sdes total += wlen; 559106175Simp while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) { 560106175Simp wlen -= iov->iov_len; 561106175Simp iov++; 562106175Simp iovcnt--; 563106175Simp } 564106175Simp if (iovcnt > 0) { 565106175Simp iov->iov_len -= wlen; 566106175Simp iov->iov_base = __DECONST(char *, iov->iov_base) + wlen; 567106175Simp } 56897866Sdes } 56997866Sdes return (total); 57097866Sdes} 57197866Sdes 57298117Sdes 57397866Sdes/* 57497866Sdes * Write a line of text to a connection w/ timeout 57597866Sdes */ 57662981Sdesint 57797856Sdes_fetch_putln(conn_t *conn, const char *str, size_t len) 57862981Sdes{ 579106175Simp struct iovec iov[2]; 580106205Sdes int ret; 58198748Sdes 58298748Sdes DEBUG(fprintf(stderr, ">>> %s\n", str)); 583106175Simp iov[0].iov_base = __DECONST(char *, str); 584106175Simp iov[0].iov_len = len; 585106175Simp iov[1].iov_base = __DECONST(char *, ENDL); 586109967Sdes iov[1].iov_len = sizeof(ENDL); 587106205Sdes if (len == 0) 588106205Sdes ret = _fetch_writev(conn, &iov[1], 1); 589106205Sdes else 590106205Sdes ret = _fetch_writev(conn, iov, 2); 591106205Sdes if (ret == -1) 59290267Sdes return (-1); 59390267Sdes return (0); 59462981Sdes} 59562981Sdes 59662981Sdes 59797856Sdes/* 59897856Sdes * Close connection 59997856Sdes */ 60097856Sdesint 60197856Sdes_fetch_close(conn_t *conn) 60297856Sdes{ 60397856Sdes int ret; 60497856Sdes 60598117Sdes if (--conn->ref > 0) 60698117Sdes return (0); 60797856Sdes ret = close(conn->sd); 608141970Sdes free(conn->buf); 60997856Sdes free(conn); 61097856Sdes return (ret); 61197856Sdes} 61297856Sdes 61397856Sdes 61441989Sdes/*** Directory-related utility functions *************************************/ 61541989Sdes 61641989Sdesint 61741989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len, 61890267Sdes const char *name, struct url_stat *us) 61941989Sdes{ 62090267Sdes struct url_ent *tmp; 62141989Sdes 62290267Sdes if (*p == NULL) { 62390268Sdes *size = 0; 62490267Sdes *len = 0; 62541989Sdes } 62641989Sdes 62790267Sdes if (*len >= *size - 1) { 628109967Sdes tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p)); 62990267Sdes if (tmp == NULL) { 63090267Sdes errno = ENOMEM; 63190267Sdes _fetch_syserr(); 63290267Sdes return (-1); 63390267Sdes } 63490268Sdes *size = (*size * 2 + 1); 63590267Sdes *p = tmp; 63690267Sdes } 63741989Sdes 63890267Sdes tmp = *p + *len; 63990267Sdes snprintf(tmp->name, PATH_MAX, "%s", name); 640109967Sdes bcopy(us, &tmp->stat, sizeof(*us)); 64141989Sdes 64290267Sdes (*len)++; 64390267Sdes (++tmp)->name[0] = 0; 64490267Sdes 64590267Sdes return (0); 64641989Sdes} 647109695Sdes 648109695Sdes 649109695Sdes/*** Authentication-related utility functions ********************************/ 650109695Sdes 651109695Sdesstatic const char * 652109695Sdes_fetch_read_word(FILE *f) 653109695Sdes{ 654109695Sdes static char word[1024]; 655109695Sdes 656109695Sdes if (fscanf(f, " %1024s ", word) != 1) 657109695Sdes return (NULL); 658109695Sdes return (word); 659109695Sdes} 660109695Sdes 661109695Sdes/* 662109695Sdes * Get authentication data for a URL from .netrc 663109695Sdes */ 664109695Sdesint 665109695Sdes_fetch_netrc_auth(struct url *url) 666109695Sdes{ 667109695Sdes char fn[PATH_MAX]; 668109695Sdes const char *word; 669109695Sdes char *p; 670109695Sdes FILE *f; 671109695Sdes 672109695Sdes if ((p = getenv("NETRC")) != NULL) { 673109967Sdes if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) { 674109695Sdes _fetch_info("$NETRC specifies a file name " 675109695Sdes "longer than PATH_MAX"); 676109695Sdes return (-1); 677109695Sdes } 678109695Sdes } else { 679109695Sdes if ((p = getenv("HOME")) != NULL) { 680109695Sdes struct passwd *pwd; 681109695Sdes 682109695Sdes if ((pwd = getpwuid(getuid())) == NULL || 683109695Sdes (p = pwd->pw_dir) == NULL) 684109695Sdes return (-1); 685109695Sdes } 686109967Sdes if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn)) 687109695Sdes return (-1); 688109695Sdes } 689109695Sdes 690109695Sdes if ((f = fopen(fn, "r")) == NULL) 691109695Sdes return (-1); 692109695Sdes while ((word = _fetch_read_word(f)) != NULL) { 693109695Sdes if (strcmp(word, "default") == 0) { 694109695Sdes DEBUG(_fetch_info("Using default .netrc settings")); 695109695Sdes break; 696109695Sdes } 697109695Sdes if (strcmp(word, "machine") == 0 && 698109695Sdes (word = _fetch_read_word(f)) != NULL && 699109695Sdes strcasecmp(word, url->host) == 0) { 700109695Sdes DEBUG(_fetch_info("Using .netrc settings for %s", word)); 701109695Sdes break; 702109695Sdes } 703109695Sdes } 704109695Sdes if (word == NULL) 705109695Sdes goto ferr; 706109695Sdes while ((word = _fetch_read_word(f)) != NULL) { 707109695Sdes if (strcmp(word, "login") == 0) { 708109695Sdes if ((word = _fetch_read_word(f)) == NULL) 709109695Sdes goto ferr; 710109967Sdes if (snprintf(url->user, sizeof(url->user), 711109960Sjwd "%s", word) > (int)sizeof(url->user)) { 712109695Sdes _fetch_info("login name in .netrc is too long"); 713109695Sdes url->user[0] = '\0'; 714109695Sdes } 715109695Sdes } else if (strcmp(word, "password") == 0) { 716109695Sdes if ((word = _fetch_read_word(f)) == NULL) 717109695Sdes goto ferr; 718109967Sdes if (snprintf(url->pwd, sizeof(url->pwd), 719109960Sjwd "%s", word) > (int)sizeof(url->pwd)) { 720109695Sdes _fetch_info("password in .netrc is too long"); 721109695Sdes url->pwd[0] = '\0'; 722109695Sdes } 723109695Sdes } else if (strcmp(word, "account") == 0) { 724109695Sdes if ((word = _fetch_read_word(f)) == NULL) 725109695Sdes goto ferr; 726109695Sdes /* XXX not supported! */ 727109695Sdes } else { 728109695Sdes break; 729109695Sdes } 730109695Sdes } 731109695Sdes fclose(f); 732109695Sdes return (0); 733109695Sdes ferr: 734109695Sdes fclose(f); 735109695Sdes return (-1); 736109695Sdes} 737