common.c revision 178234
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 178234 2008-04-15 23:29:51Z cperciva $"); 3184203Sdillon 3241862Sdes#include <sys/param.h> 3340939Sdes#include <sys/socket.h> 3455557Sdes#include <sys/time.h> 3562981Sdes#include <sys/uio.h> 36174752Sdes 3740939Sdes#include <netinet/in.h> 3840939Sdes 39174752Sdes#include <ctype.h> 4040939Sdes#include <errno.h> 4140939Sdes#include <netdb.h> 42109695Sdes#include <pwd.h> 4360924Sdes#include <stdarg.h> 4441862Sdes#include <stdlib.h> 4541862Sdes#include <stdio.h> 4640939Sdes#include <string.h> 4740939Sdes#include <unistd.h> 4840939Sdes 4940939Sdes#include "fetch.h" 5040939Sdes#include "common.h" 5140939Sdes 5240975Sdes 5340939Sdes/*** Local data **************************************************************/ 5440939Sdes 5540939Sdes/* 5640939Sdes * Error messages for resolver errors 5740939Sdes */ 58174588Sdesstatic struct fetcherr netdb_errlist[] = { 59121423Sume#ifdef EAI_NODATA 6090267Sdes { EAI_NODATA, FETCH_RESOLV, "Host not found" }, 61121423Sume#endif 6290267Sdes { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, 6390267Sdes { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, 6490267Sdes { EAI_NONAME, FETCH_RESOLV, "No address record" }, 6590267Sdes { -1, FETCH_UNKNOWN, "Unknown resolver error" } 6640939Sdes}; 6740939Sdes 6862981Sdes/* End-of-Line */ 6975891Sarchiestatic const char ENDL[2] = "\r\n"; 7040939Sdes 7162981Sdes 7240939Sdes/*** Error-reporting functions ***********************************************/ 7340939Sdes 7440939Sdes/* 7540939Sdes * Map error code to string 7640939Sdes */ 7760924Sdesstatic struct fetcherr * 78174588Sdesfetch_finderr(struct fetcherr *p, int e) 7940939Sdes{ 8090267Sdes while (p->num != -1 && p->num != e) 8190267Sdes p++; 8290267Sdes return (p); 8340939Sdes} 8440939Sdes 8540939Sdes/* 8640939Sdes * Set error code 8740939Sdes */ 8840939Sdesvoid 89174588Sdesfetch_seterr(struct fetcherr *p, int e) 9040939Sdes{ 91174588Sdes p = fetch_finderr(p, e); 9290267Sdes fetchLastErrCode = p->cat; 9390267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); 9440939Sdes} 9540939Sdes 9640939Sdes/* 9740939Sdes * Set error code according to errno 9840939Sdes */ 9940939Sdesvoid 100174588Sdesfetch_syserr(void) 10140939Sdes{ 10290267Sdes switch (errno) { 10390267Sdes case 0: 10490267Sdes fetchLastErrCode = FETCH_OK; 10590267Sdes break; 10690267Sdes case EPERM: 10790267Sdes case EACCES: 10890267Sdes case EROFS: 10990267Sdes case EAUTH: 11090267Sdes case ENEEDAUTH: 11190267Sdes fetchLastErrCode = FETCH_AUTH; 11290267Sdes break; 11390267Sdes case ENOENT: 11490267Sdes case EISDIR: /* XXX */ 11590267Sdes fetchLastErrCode = FETCH_UNAVAIL; 11690267Sdes break; 11790267Sdes case ENOMEM: 11890267Sdes fetchLastErrCode = FETCH_MEMORY; 11990267Sdes break; 12090267Sdes case EBUSY: 12190267Sdes case EAGAIN: 12290267Sdes fetchLastErrCode = FETCH_TEMP; 12390267Sdes break; 12490267Sdes case EEXIST: 12590267Sdes fetchLastErrCode = FETCH_EXISTS; 12690267Sdes break; 12790267Sdes case ENOSPC: 12890267Sdes fetchLastErrCode = FETCH_FULL; 12990267Sdes break; 13090267Sdes case EADDRINUSE: 13190267Sdes case EADDRNOTAVAIL: 13290267Sdes case ENETDOWN: 13390267Sdes case ENETUNREACH: 13490267Sdes case ENETRESET: 13590267Sdes case EHOSTUNREACH: 13690267Sdes fetchLastErrCode = FETCH_NETWORK; 13790267Sdes break; 13890267Sdes case ECONNABORTED: 13990267Sdes case ECONNRESET: 14090267Sdes fetchLastErrCode = FETCH_ABORT; 14190267Sdes break; 14290267Sdes case ETIMEDOUT: 14390267Sdes fetchLastErrCode = FETCH_TIMEOUT; 14490267Sdes break; 14590267Sdes case ECONNREFUSED: 14690267Sdes case EHOSTDOWN: 14790267Sdes fetchLastErrCode = FETCH_DOWN; 14890267Sdes break; 14990267Sdesdefault: 15090267Sdes fetchLastErrCode = FETCH_UNKNOWN; 15190267Sdes } 15290267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); 15340939Sdes} 15440939Sdes 15540939Sdes 15641862Sdes/* 15741862Sdes * Emit status message 15841862Sdes */ 15960924Sdesvoid 160174588Sdesfetch_info(const char *fmt, ...) 16141862Sdes{ 16290267Sdes va_list ap; 16390267Sdes 16490267Sdes va_start(ap, fmt); 16590267Sdes vfprintf(stderr, fmt, ap); 16690267Sdes va_end(ap); 16790267Sdes fputc('\n', stderr); 16841862Sdes} 16941862Sdes 17041862Sdes 17140939Sdes/*** Network-related utility functions ***************************************/ 17240939Sdes 17340939Sdes/* 17468551Sdes * Return the default port for a scheme 17568551Sdes */ 17668551Sdesint 177174588Sdesfetch_default_port(const char *scheme) 17868551Sdes{ 17990267Sdes struct servent *se; 18068551Sdes 18190267Sdes if ((se = getservbyname(scheme, "tcp")) != NULL) 18290267Sdes return (ntohs(se->s_port)); 18390267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 18490267Sdes return (FTP_DEFAULT_PORT); 18590267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 18690267Sdes return (HTTP_DEFAULT_PORT); 18790267Sdes return (0); 18868551Sdes} 18968551Sdes 19068551Sdes/* 19168551Sdes * Return the default proxy port for a scheme 19268551Sdes */ 19368551Sdesint 194174588Sdesfetch_default_proxy_port(const char *scheme) 19568551Sdes{ 19690267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 19790267Sdes return (FTP_DEFAULT_PROXY_PORT); 19890267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 19990267Sdes return (HTTP_DEFAULT_PROXY_PORT); 20090267Sdes return (0); 20168551Sdes} 20268551Sdes 20398117Sdes 20468551Sdes/* 20597866Sdes * Create a connection for an existing descriptor. 20697866Sdes */ 20797866Sdesconn_t * 208174588Sdesfetch_reopen(int sd) 20997866Sdes{ 21097866Sdes conn_t *conn; 21197866Sdes 21297866Sdes /* allocate and fill connection structure */ 213109967Sdes if ((conn = calloc(1, sizeof(*conn))) == NULL) 21497866Sdes return (NULL); 21597866Sdes conn->sd = sd; 21698117Sdes ++conn->ref; 21797866Sdes return (conn); 21897866Sdes} 21997866Sdes 22097866Sdes 22197866Sdes/* 22298117Sdes * Bump a connection's reference count. 22398117Sdes */ 22498117Sdesconn_t * 225174588Sdesfetch_ref(conn_t *conn) 22698117Sdes{ 22798117Sdes 22898117Sdes ++conn->ref; 22998117Sdes return (conn); 23098117Sdes} 23198117Sdes 23298117Sdes 23398117Sdes/* 234111816Sdes * Bind a socket to a specific local address 235111816Sdes */ 236111816Sdesint 237174588Sdesfetch_bind(int sd, int af, const char *addr) 238111816Sdes{ 239111816Sdes struct addrinfo hints, *res, *res0; 240111816Sdes int err; 241111816Sdes 242111816Sdes memset(&hints, 0, sizeof(hints)); 243111816Sdes hints.ai_family = af; 244111816Sdes hints.ai_socktype = SOCK_STREAM; 245111816Sdes hints.ai_protocol = 0; 246111816Sdes if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0) 247111816Sdes return (-1); 248111816Sdes for (res = res0; res; res = res->ai_next) 249111816Sdes if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) 250111816Sdes return (0); 251111816Sdes return (-1); 252111816Sdes} 253111816Sdes 254111816Sdes 255111816Sdes/* 25640939Sdes * Establish a TCP connection to the specified port on the specified host. 25740939Sdes */ 25897856Sdesconn_t * 259174588Sdesfetch_connect(const char *host, int port, int af, int verbose) 26040939Sdes{ 26197856Sdes conn_t *conn; 26290267Sdes char pbuf[10]; 263111816Sdes const char *bindaddr; 26490267Sdes struct addrinfo hints, *res, *res0; 26590267Sdes int sd, err; 26640939Sdes 26790267Sdes DEBUG(fprintf(stderr, "---> %s:%d\n", host, port)); 26841862Sdes 26990267Sdes if (verbose) 270174588Sdes fetch_info("looking up %s", host); 27140939Sdes 27290267Sdes /* look up host name and set up socket address structure */ 27390267Sdes snprintf(pbuf, sizeof(pbuf), "%d", port); 27490267Sdes memset(&hints, 0, sizeof(hints)); 27590267Sdes hints.ai_family = af; 27690267Sdes hints.ai_socktype = SOCK_STREAM; 27790267Sdes hints.ai_protocol = 0; 27890267Sdes if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) { 279174588Sdes netdb_seterr(err); 28097856Sdes return (NULL); 28190267Sdes } 282111816Sdes bindaddr = getenv("FETCH_BIND_ADDRESS"); 28390267Sdes 28490267Sdes if (verbose) 285174588Sdes fetch_info("connecting to %s:%d", host, port); 28690267Sdes 28790267Sdes /* try to connect */ 288111816Sdes for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) { 28990267Sdes if ((sd = socket(res->ai_family, res->ai_socktype, 29062981Sdes res->ai_protocol)) == -1) 29190267Sdes continue; 292111816Sdes if (bindaddr != NULL && *bindaddr != '\0' && 293174588Sdes fetch_bind(sd, res->ai_family, bindaddr) != 0) { 294174588Sdes fetch_info("failed to bind to '%s'", bindaddr); 295111816Sdes close(sd); 296111816Sdes continue; 297111816Sdes } 298111816Sdes if (connect(sd, res->ai_addr, res->ai_addrlen) == 0) 29990267Sdes break; 30090267Sdes close(sd); 30190267Sdes } 30290267Sdes freeaddrinfo(res0); 30390267Sdes if (sd == -1) { 304174588Sdes fetch_syserr(); 30597856Sdes return (NULL); 30690267Sdes } 30740939Sdes 308174588Sdes if ((conn = fetch_reopen(sd)) == NULL) { 309174588Sdes fetch_syserr(); 31097856Sdes close(sd); 311103459Sfenner } 31297856Sdes return (conn); 31340939Sdes} 31441989Sdes 31541989Sdes 31655557Sdes/* 31797868Sdes * Enable SSL on a connection. 31897868Sdes */ 31997868Sdesint 320174588Sdesfetch_ssl(conn_t *conn, int verbose) 32197868Sdes{ 32297868Sdes 32397891Sdes#ifdef WITH_SSL 32497868Sdes /* Init the SSL library and context */ 32597868Sdes if (!SSL_library_init()){ 32697868Sdes fprintf(stderr, "SSL library init failed\n"); 32797868Sdes return (-1); 32897868Sdes } 32997868Sdes 33097868Sdes SSL_load_error_strings(); 33197868Sdes 33297868Sdes conn->ssl_meth = SSLv23_client_method(); 33397868Sdes conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth); 334108579Sdes SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY); 33597868Sdes 33697868Sdes conn->ssl = SSL_new(conn->ssl_ctx); 33797868Sdes if (conn->ssl == NULL){ 33897868Sdes fprintf(stderr, "SSL context creation failed\n"); 33997868Sdes return (-1); 34097868Sdes } 34197868Sdes SSL_set_fd(conn->ssl, conn->sd); 34297868Sdes if (SSL_connect(conn->ssl) == -1){ 34397868Sdes ERR_print_errors_fp(stderr); 34497868Sdes return (-1); 34597868Sdes } 34697868Sdes 34797868Sdes if (verbose) { 34897868Sdes X509_NAME *name; 34997868Sdes char *str; 35097868Sdes 35197868Sdes fprintf(stderr, "SSL connection established using %s\n", 35297868Sdes SSL_get_cipher(conn->ssl)); 35397868Sdes conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); 35497868Sdes name = X509_get_subject_name(conn->ssl_cert); 35597868Sdes str = X509_NAME_oneline(name, 0, 0); 35697868Sdes printf("Certificate subject: %s\n", str); 35797868Sdes free(str); 35897868Sdes name = X509_get_issuer_name(conn->ssl_cert); 35997868Sdes str = X509_NAME_oneline(name, 0, 0); 36097868Sdes printf("Certificate issuer: %s\n", str); 36197868Sdes free(str); 36297868Sdes } 36397868Sdes 36497868Sdes return (0); 36597891Sdes#else 36697891Sdes (void)conn; 36797891Sdes (void)verbose; 36897891Sdes fprintf(stderr, "SSL support disabled\n"); 36997891Sdes return (-1); 37097891Sdes#endif 37197868Sdes} 37297868Sdes 37398117Sdes 37497868Sdes/* 37597866Sdes * Read a character from a connection w/ timeout 37655557Sdes */ 37797866Sdesssize_t 378174588Sdesfetch_read(conn_t *conn, char *buf, size_t len) 37955557Sdes{ 380177447Sdes struct timeval now, timeout, delta; 38190267Sdes fd_set readfds; 38297866Sdes ssize_t rlen, total; 38390267Sdes int r; 38490267Sdes 38555557Sdes if (fetchTimeout) { 38697866Sdes FD_ZERO(&readfds); 38790267Sdes gettimeofday(&timeout, NULL); 38890267Sdes timeout.tv_sec += fetchTimeout; 38955557Sdes } 39090267Sdes 39197866Sdes total = 0; 39297866Sdes while (len > 0) { 39397866Sdes while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) { 39497856Sdes FD_SET(conn->sd, &readfds); 39590267Sdes gettimeofday(&now, NULL); 396177447Sdes delta.tv_sec = timeout.tv_sec - now.tv_sec; 397177447Sdes delta.tv_usec = timeout.tv_usec - now.tv_usec; 398177447Sdes if (delta.tv_usec < 0) { 399177447Sdes delta.tv_usec += 1000000; 400177447Sdes delta.tv_sec--; 40190267Sdes } 402177447Sdes if (delta.tv_sec < 0) { 403106186Sdes errno = ETIMEDOUT; 404174588Sdes fetch_syserr(); 405106186Sdes return (-1); 406106186Sdes } 40797866Sdes errno = 0; 408177447Sdes r = select(conn->sd + 1, &readfds, NULL, NULL, &delta); 40990267Sdes if (r == -1) { 41090267Sdes if (errno == EINTR && fetchRestartCalls) 41190267Sdes continue; 412174588Sdes fetch_syserr(); 41390267Sdes return (-1); 41490267Sdes } 41590267Sdes } 41697891Sdes#ifdef WITH_SSL 41797866Sdes if (conn->ssl != NULL) 41897866Sdes rlen = SSL_read(conn->ssl, buf, len); 41997866Sdes else 42097891Sdes#endif 42197866Sdes rlen = read(conn->sd, buf, len); 422106049Sdes if (rlen == 0) 423106049Sdes break; 42497866Sdes if (rlen < 0) { 42590267Sdes if (errno == EINTR && fetchRestartCalls) 42690267Sdes continue; 42790267Sdes return (-1); 42890267Sdes } 42997866Sdes len -= rlen; 43097866Sdes buf += rlen; 43197866Sdes total += rlen; 43297866Sdes } 43397866Sdes return (total); 43497866Sdes} 43597866Sdes 43698117Sdes 43797866Sdes/* 43897866Sdes * Read a line of text from a connection w/ timeout 43997866Sdes */ 44097866Sdes#define MIN_BUF_SIZE 1024 44197866Sdes 44297866Sdesint 443174588Sdesfetch_getln(conn_t *conn) 44497866Sdes{ 44597866Sdes char *tmp; 44697866Sdes size_t tmpsize; 447106186Sdes ssize_t len; 44897866Sdes char c; 44997866Sdes 45097866Sdes if (conn->buf == NULL) { 45197866Sdes if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) { 45297866Sdes errno = ENOMEM; 45397866Sdes return (-1); 45497866Sdes } 45597866Sdes conn->bufsize = MIN_BUF_SIZE; 45697866Sdes } 45797866Sdes 45897866Sdes conn->buf[0] = '\0'; 45997866Sdes conn->buflen = 0; 46097866Sdes 46197866Sdes do { 462174588Sdes len = fetch_read(conn, &c, 1); 463106186Sdes if (len == -1) 46497866Sdes return (-1); 465106186Sdes if (len == 0) 466106137Sobrien break; 46797856Sdes conn->buf[conn->buflen++] = c; 46897856Sdes if (conn->buflen == conn->bufsize) { 46997856Sdes tmp = conn->buf; 47097856Sdes tmpsize = conn->bufsize * 2 + 1; 47197856Sdes if ((tmp = realloc(tmp, tmpsize)) == NULL) { 47290267Sdes errno = ENOMEM; 47390267Sdes return (-1); 47490267Sdes } 47597856Sdes conn->buf = tmp; 47697856Sdes conn->bufsize = tmpsize; 47790267Sdes } 47890267Sdes } while (c != '\n'); 47990267Sdes 48097856Sdes conn->buf[conn->buflen] = '\0'; 48197856Sdes DEBUG(fprintf(stderr, "<<< %s", conn->buf)); 48290267Sdes return (0); 48355557Sdes} 48455557Sdes 48555557Sdes 48662981Sdes/* 48797866Sdes * Write to a connection w/ timeout 48862981Sdes */ 48997866Sdesssize_t 490174588Sdesfetch_write(conn_t *conn, const char *buf, size_t len) 49197866Sdes{ 492106175Simp struct iovec iov; 493106175Simp 494106175Simp iov.iov_base = __DECONST(char *, buf); 495106175Simp iov.iov_len = len; 496174588Sdes return fetch_writev(conn, &iov, 1); 497106175Simp} 498106175Simp 499106175Simp/* 500106175Simp * Write a vector to a connection w/ timeout 501106175Simp * Note: can modify the iovec. 502106175Simp */ 503106175Simpssize_t 504174588Sdesfetch_writev(conn_t *conn, struct iovec *iov, int iovcnt) 505106175Simp{ 506177447Sdes struct timeval now, timeout, delta; 50797866Sdes fd_set writefds; 50897866Sdes ssize_t wlen, total; 50997866Sdes int r; 51097866Sdes 51197866Sdes if (fetchTimeout) { 51297866Sdes FD_ZERO(&writefds); 51397866Sdes gettimeofday(&timeout, NULL); 51497866Sdes timeout.tv_sec += fetchTimeout; 51597866Sdes } 51697866Sdes 517106175Simp total = 0; 518106175Simp while (iovcnt > 0) { 51997866Sdes while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) { 52097866Sdes FD_SET(conn->sd, &writefds); 52197866Sdes gettimeofday(&now, NULL); 522177447Sdes delta.tv_sec = timeout.tv_sec - now.tv_sec; 523177447Sdes delta.tv_usec = timeout.tv_usec - now.tv_usec; 524177447Sdes if (delta.tv_usec < 0) { 525177447Sdes delta.tv_usec += 1000000; 526177447Sdes delta.tv_sec--; 52797866Sdes } 528177447Sdes if (delta.tv_sec < 0) { 52997866Sdes errno = ETIMEDOUT; 530174588Sdes fetch_syserr(); 53197866Sdes return (-1); 53297866Sdes } 53397866Sdes errno = 0; 534177447Sdes r = select(conn->sd + 1, NULL, &writefds, NULL, &delta); 53597866Sdes if (r == -1) { 53697866Sdes if (errno == EINTR && fetchRestartCalls) 53797866Sdes continue; 53897866Sdes return (-1); 53997866Sdes } 54097866Sdes } 54197866Sdes errno = 0; 54297891Sdes#ifdef WITH_SSL 54397866Sdes if (conn->ssl != NULL) 544106175Simp wlen = SSL_write(conn->ssl, 545106175Simp iov->iov_base, iov->iov_len); 54697866Sdes else 54797891Sdes#endif 548106175Simp wlen = writev(conn->sd, iov, iovcnt); 549106175Simp if (wlen == 0) { 55097866Sdes /* we consider a short write a failure */ 551106175Simp errno = EPIPE; 552174588Sdes fetch_syserr(); 55397866Sdes return (-1); 554106175Simp } 55597866Sdes if (wlen < 0) { 55697866Sdes if (errno == EINTR && fetchRestartCalls) 55797866Sdes continue; 55897866Sdes return (-1); 55997866Sdes } 56097866Sdes total += wlen; 561106175Simp while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) { 562106175Simp wlen -= iov->iov_len; 563106175Simp iov++; 564106175Simp iovcnt--; 565106175Simp } 566106175Simp if (iovcnt > 0) { 567106175Simp iov->iov_len -= wlen; 568106175Simp iov->iov_base = __DECONST(char *, iov->iov_base) + wlen; 569106175Simp } 57097866Sdes } 57197866Sdes return (total); 57297866Sdes} 57397866Sdes 57498117Sdes 57597866Sdes/* 57697866Sdes * Write a line of text to a connection w/ timeout 57797866Sdes */ 57862981Sdesint 579174588Sdesfetch_putln(conn_t *conn, const char *str, size_t len) 58062981Sdes{ 581106175Simp struct iovec iov[2]; 582106205Sdes int ret; 58398748Sdes 58498748Sdes DEBUG(fprintf(stderr, ">>> %s\n", str)); 585106175Simp iov[0].iov_base = __DECONST(char *, str); 586106175Simp iov[0].iov_len = len; 587106175Simp iov[1].iov_base = __DECONST(char *, ENDL); 588109967Sdes iov[1].iov_len = sizeof(ENDL); 589106205Sdes if (len == 0) 590174588Sdes ret = fetch_writev(conn, &iov[1], 1); 591106205Sdes else 592174588Sdes ret = fetch_writev(conn, iov, 2); 593106205Sdes if (ret == -1) 59490267Sdes return (-1); 59590267Sdes return (0); 59662981Sdes} 59762981Sdes 59862981Sdes 59997856Sdes/* 60097856Sdes * Close connection 60197856Sdes */ 60297856Sdesint 603174588Sdesfetch_close(conn_t *conn) 60497856Sdes{ 60597856Sdes int ret; 60697856Sdes 60798117Sdes if (--conn->ref > 0) 60898117Sdes return (0); 60997856Sdes ret = close(conn->sd); 610141970Sdes free(conn->buf); 61197856Sdes free(conn); 61297856Sdes return (ret); 61397856Sdes} 61497856Sdes 61597856Sdes 61641989Sdes/*** Directory-related utility functions *************************************/ 61741989Sdes 61841989Sdesint 619174588Sdesfetch_add_entry(struct url_ent **p, int *size, int *len, 62090267Sdes const char *name, struct url_stat *us) 62141989Sdes{ 62290267Sdes struct url_ent *tmp; 62341989Sdes 62490267Sdes if (*p == NULL) { 62590268Sdes *size = 0; 62690267Sdes *len = 0; 62741989Sdes } 62841989Sdes 62990267Sdes if (*len >= *size - 1) { 630109967Sdes tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p)); 63190267Sdes if (tmp == NULL) { 63290267Sdes errno = ENOMEM; 633174588Sdes fetch_syserr(); 63490267Sdes return (-1); 63590267Sdes } 63690268Sdes *size = (*size * 2 + 1); 63790267Sdes *p = tmp; 63890267Sdes } 63941989Sdes 64090267Sdes tmp = *p + *len; 64190267Sdes snprintf(tmp->name, PATH_MAX, "%s", name); 642176105Sdes memcpy(&tmp->stat, us, sizeof(*us)); 64341989Sdes 64490267Sdes (*len)++; 64590267Sdes (++tmp)->name[0] = 0; 64690267Sdes 64790267Sdes return (0); 64841989Sdes} 649109695Sdes 650109695Sdes 651109695Sdes/*** Authentication-related utility functions ********************************/ 652109695Sdes 653109695Sdesstatic const char * 654174588Sdesfetch_read_word(FILE *f) 655109695Sdes{ 656109695Sdes static char word[1024]; 657109695Sdes 658178234Scperciva if (fscanf(f, " %1023s ", word) != 1) 659109695Sdes return (NULL); 660109695Sdes return (word); 661109695Sdes} 662109695Sdes 663109695Sdes/* 664109695Sdes * Get authentication data for a URL from .netrc 665109695Sdes */ 666109695Sdesint 667174588Sdesfetch_netrc_auth(struct url *url) 668109695Sdes{ 669109695Sdes char fn[PATH_MAX]; 670109695Sdes const char *word; 671109695Sdes char *p; 672109695Sdes FILE *f; 673109695Sdes 674109695Sdes if ((p = getenv("NETRC")) != NULL) { 675109967Sdes if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) { 676174588Sdes fetch_info("$NETRC specifies a file name " 677109695Sdes "longer than PATH_MAX"); 678109695Sdes return (-1); 679109695Sdes } 680109695Sdes } else { 681109695Sdes if ((p = getenv("HOME")) != NULL) { 682109695Sdes struct passwd *pwd; 683109695Sdes 684109695Sdes if ((pwd = getpwuid(getuid())) == NULL || 685109695Sdes (p = pwd->pw_dir) == NULL) 686109695Sdes return (-1); 687109695Sdes } 688109967Sdes if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn)) 689109695Sdes return (-1); 690109695Sdes } 691109695Sdes 692109695Sdes if ((f = fopen(fn, "r")) == NULL) 693109695Sdes return (-1); 694174588Sdes while ((word = fetch_read_word(f)) != NULL) { 695109695Sdes if (strcmp(word, "default") == 0) { 696174588Sdes DEBUG(fetch_info("Using default .netrc settings")); 697109695Sdes break; 698109695Sdes } 699109695Sdes if (strcmp(word, "machine") == 0 && 700174588Sdes (word = fetch_read_word(f)) != NULL && 701109695Sdes strcasecmp(word, url->host) == 0) { 702174588Sdes DEBUG(fetch_info("Using .netrc settings for %s", word)); 703109695Sdes break; 704109695Sdes } 705109695Sdes } 706109695Sdes if (word == NULL) 707109695Sdes goto ferr; 708174588Sdes while ((word = fetch_read_word(f)) != NULL) { 709109695Sdes if (strcmp(word, "login") == 0) { 710174588Sdes if ((word = fetch_read_word(f)) == NULL) 711109695Sdes goto ferr; 712109967Sdes if (snprintf(url->user, sizeof(url->user), 713109960Sjwd "%s", word) > (int)sizeof(url->user)) { 714174588Sdes fetch_info("login name in .netrc is too long"); 715109695Sdes url->user[0] = '\0'; 716109695Sdes } 717109695Sdes } else if (strcmp(word, "password") == 0) { 718174588Sdes if ((word = fetch_read_word(f)) == NULL) 719109695Sdes goto ferr; 720109967Sdes if (snprintf(url->pwd, sizeof(url->pwd), 721109960Sjwd "%s", word) > (int)sizeof(url->pwd)) { 722174588Sdes fetch_info("password in .netrc is too long"); 723109695Sdes url->pwd[0] = '\0'; 724109695Sdes } 725109695Sdes } else if (strcmp(word, "account") == 0) { 726174588Sdes if ((word = fetch_read_word(f)) == NULL) 727109695Sdes goto ferr; 728109695Sdes /* XXX not supported! */ 729109695Sdes } else { 730109695Sdes break; 731109695Sdes } 732109695Sdes } 733109695Sdes fclose(f); 734109695Sdes return (0); 735109695Sdes ferr: 736109695Sdes fclose(f); 737109695Sdes return (-1); 738109695Sdes} 739174752Sdes 740174752Sdes/* 741174752Sdes * The no_proxy environment variable specifies a set of domains for 742174752Sdes * which the proxy should not be consulted; the contents is a comma-, 743174752Sdes * or space-separated list of domain names. A single asterisk will 744174752Sdes * override all proxy variables and no transactions will be proxied 745174752Sdes * (for compatability with lynx and curl, see the discussion at 746174752Sdes * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>). 747174752Sdes */ 748174752Sdesint 749174752Sdesfetch_no_proxy_match(const char *host) 750174752Sdes{ 751174752Sdes const char *no_proxy, *p, *q; 752174752Sdes size_t h_len, d_len; 753174752Sdes 754174752Sdes if ((no_proxy = getenv("NO_PROXY")) == NULL && 755174752Sdes (no_proxy = getenv("no_proxy")) == NULL) 756174752Sdes return (0); 757174752Sdes 758174752Sdes /* asterisk matches any hostname */ 759174752Sdes if (strcmp(no_proxy, "*") == 0) 760174752Sdes return (1); 761174752Sdes 762174752Sdes h_len = strlen(host); 763174752Sdes p = no_proxy; 764174752Sdes do { 765174752Sdes /* position p at the beginning of a domain suffix */ 766174761Sdes while (*p == ',' || isspace((unsigned char)*p)) 767174752Sdes p++; 768174752Sdes 769174752Sdes /* position q at the first separator character */ 770174752Sdes for (q = p; *q; ++q) 771174761Sdes if (*q == ',' || isspace((unsigned char)*q)) 772174752Sdes break; 773174752Sdes 774174752Sdes d_len = q - p; 775174752Sdes if (d_len > 0 && h_len > d_len && 776174752Sdes strncasecmp(host + h_len - d_len, 777174752Sdes p, d_len) == 0) { 778174752Sdes /* domain name matches */ 779174752Sdes return (1); 780174752Sdes } 781174752Sdes 782174752Sdes p = q + 1; 783174752Sdes } while (*q); 784174752Sdes 785174752Sdes return (0); 786174752Sdes} 787