common.c revision 230307
140939Sdes/*- 2226537Sdes * Copyright (c) 1998-2011 Dag-Erling 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 230307 2012-01-18 15:13:21Z des $"); 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> 41210568Sdes#include <fcntl.h> 4240939Sdes#include <netdb.h> 43109695Sdes#include <pwd.h> 4460924Sdes#include <stdarg.h> 4541862Sdes#include <stdlib.h> 4641862Sdes#include <stdio.h> 4740939Sdes#include <string.h> 4840939Sdes#include <unistd.h> 4940939Sdes 5040939Sdes#include "fetch.h" 5140939Sdes#include "common.h" 5240939Sdes 5340975Sdes 5440939Sdes/*** Local data **************************************************************/ 5540939Sdes 5640939Sdes/* 5740939Sdes * Error messages for resolver errors 5840939Sdes */ 59174588Sdesstatic struct fetcherr netdb_errlist[] = { 60121423Sume#ifdef EAI_NODATA 6190267Sdes { EAI_NODATA, FETCH_RESOLV, "Host not found" }, 62121423Sume#endif 6390267Sdes { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, 6490267Sdes { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, 6590267Sdes { EAI_NONAME, FETCH_RESOLV, "No address record" }, 6690267Sdes { -1, FETCH_UNKNOWN, "Unknown resolver error" } 6740939Sdes}; 6840939Sdes 6962981Sdes/* End-of-Line */ 7075891Sarchiestatic const char ENDL[2] = "\r\n"; 7140939Sdes 7262981Sdes 7340939Sdes/*** Error-reporting functions ***********************************************/ 7440939Sdes 7540939Sdes/* 7640939Sdes * Map error code to string 7740939Sdes */ 7860924Sdesstatic struct fetcherr * 79174588Sdesfetch_finderr(struct fetcherr *p, int e) 8040939Sdes{ 8190267Sdes while (p->num != -1 && p->num != e) 8290267Sdes p++; 8390267Sdes return (p); 8440939Sdes} 8540939Sdes 8640939Sdes/* 8740939Sdes * Set error code 8840939Sdes */ 8940939Sdesvoid 90174588Sdesfetch_seterr(struct fetcherr *p, int e) 9140939Sdes{ 92174588Sdes p = fetch_finderr(p, e); 9390267Sdes fetchLastErrCode = p->cat; 9490267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); 9540939Sdes} 9640939Sdes 9740939Sdes/* 9840939Sdes * Set error code according to errno 9940939Sdes */ 10040939Sdesvoid 101174588Sdesfetch_syserr(void) 10240939Sdes{ 10390267Sdes switch (errno) { 10490267Sdes case 0: 10590267Sdes fetchLastErrCode = FETCH_OK; 10690267Sdes break; 10790267Sdes case EPERM: 10890267Sdes case EACCES: 10990267Sdes case EROFS: 11090267Sdes case EAUTH: 11190267Sdes case ENEEDAUTH: 11290267Sdes fetchLastErrCode = FETCH_AUTH; 11390267Sdes break; 11490267Sdes case ENOENT: 11590267Sdes case EISDIR: /* XXX */ 11690267Sdes fetchLastErrCode = FETCH_UNAVAIL; 11790267Sdes break; 11890267Sdes case ENOMEM: 11990267Sdes fetchLastErrCode = FETCH_MEMORY; 12090267Sdes break; 12190267Sdes case EBUSY: 12290267Sdes case EAGAIN: 12390267Sdes fetchLastErrCode = FETCH_TEMP; 12490267Sdes break; 12590267Sdes case EEXIST: 12690267Sdes fetchLastErrCode = FETCH_EXISTS; 12790267Sdes break; 12890267Sdes case ENOSPC: 12990267Sdes fetchLastErrCode = FETCH_FULL; 13090267Sdes break; 13190267Sdes case EADDRINUSE: 13290267Sdes case EADDRNOTAVAIL: 13390267Sdes case ENETDOWN: 13490267Sdes case ENETUNREACH: 13590267Sdes case ENETRESET: 13690267Sdes case EHOSTUNREACH: 13790267Sdes fetchLastErrCode = FETCH_NETWORK; 13890267Sdes break; 13990267Sdes case ECONNABORTED: 14090267Sdes case ECONNRESET: 14190267Sdes fetchLastErrCode = FETCH_ABORT; 14290267Sdes break; 14390267Sdes case ETIMEDOUT: 14490267Sdes fetchLastErrCode = FETCH_TIMEOUT; 14590267Sdes break; 14690267Sdes case ECONNREFUSED: 14790267Sdes case EHOSTDOWN: 14890267Sdes fetchLastErrCode = FETCH_DOWN; 14990267Sdes break; 15090267Sdesdefault: 15190267Sdes fetchLastErrCode = FETCH_UNKNOWN; 15290267Sdes } 15390267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); 15440939Sdes} 15540939Sdes 15640939Sdes 15741862Sdes/* 15841862Sdes * Emit status message 15941862Sdes */ 16060924Sdesvoid 161174588Sdesfetch_info(const char *fmt, ...) 16241862Sdes{ 16390267Sdes va_list ap; 16490267Sdes 16590267Sdes va_start(ap, fmt); 16690267Sdes vfprintf(stderr, fmt, ap); 16790267Sdes va_end(ap); 16890267Sdes fputc('\n', stderr); 16941862Sdes} 17041862Sdes 17141862Sdes 17240939Sdes/*** Network-related utility functions ***************************************/ 17340939Sdes 17440939Sdes/* 17568551Sdes * Return the default port for a scheme 17668551Sdes */ 17768551Sdesint 178174588Sdesfetch_default_port(const char *scheme) 17968551Sdes{ 18090267Sdes struct servent *se; 18168551Sdes 18290267Sdes if ((se = getservbyname(scheme, "tcp")) != NULL) 18390267Sdes return (ntohs(se->s_port)); 18490267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 18590267Sdes return (FTP_DEFAULT_PORT); 18690267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 18790267Sdes return (HTTP_DEFAULT_PORT); 18890267Sdes return (0); 18968551Sdes} 19068551Sdes 19168551Sdes/* 19268551Sdes * Return the default proxy port for a scheme 19368551Sdes */ 19468551Sdesint 195174588Sdesfetch_default_proxy_port(const char *scheme) 19668551Sdes{ 19790267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 19890267Sdes return (FTP_DEFAULT_PROXY_PORT); 19990267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 20090267Sdes return (HTTP_DEFAULT_PROXY_PORT); 20190267Sdes return (0); 20268551Sdes} 20368551Sdes 20498117Sdes 20568551Sdes/* 20697866Sdes * Create a connection for an existing descriptor. 20797866Sdes */ 20897866Sdesconn_t * 209174588Sdesfetch_reopen(int sd) 21097866Sdes{ 21197866Sdes conn_t *conn; 21297866Sdes 21397866Sdes /* allocate and fill connection structure */ 214109967Sdes if ((conn = calloc(1, sizeof(*conn))) == NULL) 21597866Sdes return (NULL); 216221830Sdes fcntl(sd, F_SETFD, FD_CLOEXEC); 21797866Sdes conn->sd = sd; 21898117Sdes ++conn->ref; 21997866Sdes return (conn); 22097866Sdes} 22197866Sdes 22297866Sdes 22397866Sdes/* 22498117Sdes * Bump a connection's reference count. 22598117Sdes */ 22698117Sdesconn_t * 227174588Sdesfetch_ref(conn_t *conn) 22898117Sdes{ 22998117Sdes 23098117Sdes ++conn->ref; 23198117Sdes return (conn); 23298117Sdes} 23398117Sdes 23498117Sdes 23598117Sdes/* 236111816Sdes * Bind a socket to a specific local address 237111816Sdes */ 238111816Sdesint 239174588Sdesfetch_bind(int sd, int af, const char *addr) 240111816Sdes{ 241111816Sdes struct addrinfo hints, *res, *res0; 242111816Sdes int err; 243111816Sdes 244111816Sdes memset(&hints, 0, sizeof(hints)); 245111816Sdes hints.ai_family = af; 246111816Sdes hints.ai_socktype = SOCK_STREAM; 247111816Sdes hints.ai_protocol = 0; 248111816Sdes if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0) 249111816Sdes return (-1); 250111816Sdes for (res = res0; res; res = res->ai_next) 251111816Sdes if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) 252111816Sdes return (0); 253111816Sdes return (-1); 254111816Sdes} 255111816Sdes 256111816Sdes 257111816Sdes/* 25840939Sdes * Establish a TCP connection to the specified port on the specified host. 25940939Sdes */ 26097856Sdesconn_t * 261174588Sdesfetch_connect(const char *host, int port, int af, int verbose) 26240939Sdes{ 26397856Sdes conn_t *conn; 26490267Sdes char pbuf[10]; 265111816Sdes const char *bindaddr; 26690267Sdes struct addrinfo hints, *res, *res0; 26790267Sdes int sd, err; 26840939Sdes 26990267Sdes DEBUG(fprintf(stderr, "---> %s:%d\n", host, port)); 27041862Sdes 27190267Sdes if (verbose) 272174588Sdes fetch_info("looking up %s", host); 27340939Sdes 27490267Sdes /* look up host name and set up socket address structure */ 27590267Sdes snprintf(pbuf, sizeof(pbuf), "%d", port); 27690267Sdes memset(&hints, 0, sizeof(hints)); 27790267Sdes hints.ai_family = af; 27890267Sdes hints.ai_socktype = SOCK_STREAM; 27990267Sdes hints.ai_protocol = 0; 28090267Sdes if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) { 281174588Sdes netdb_seterr(err); 28297856Sdes return (NULL); 28390267Sdes } 284111816Sdes bindaddr = getenv("FETCH_BIND_ADDRESS"); 28590267Sdes 28690267Sdes if (verbose) 287174588Sdes fetch_info("connecting to %s:%d", host, port); 28890267Sdes 28990267Sdes /* try to connect */ 290111816Sdes for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) { 29190267Sdes if ((sd = socket(res->ai_family, res->ai_socktype, 29262981Sdes res->ai_protocol)) == -1) 29390267Sdes continue; 294111816Sdes if (bindaddr != NULL && *bindaddr != '\0' && 295174588Sdes fetch_bind(sd, res->ai_family, bindaddr) != 0) { 296174588Sdes fetch_info("failed to bind to '%s'", bindaddr); 297111816Sdes close(sd); 298111816Sdes continue; 299111816Sdes } 300210568Sdes if (connect(sd, res->ai_addr, res->ai_addrlen) == 0 && 301210568Sdes fcntl(sd, F_SETFL, O_NONBLOCK) == 0) 30290267Sdes break; 30390267Sdes close(sd); 30490267Sdes } 30590267Sdes freeaddrinfo(res0); 30690267Sdes if (sd == -1) { 307174588Sdes fetch_syserr(); 30897856Sdes return (NULL); 30990267Sdes } 31040939Sdes 311174588Sdes if ((conn = fetch_reopen(sd)) == NULL) { 312174588Sdes fetch_syserr(); 31397856Sdes close(sd); 314103459Sfenner } 31597856Sdes return (conn); 31640939Sdes} 31741989Sdes 31841989Sdes 31955557Sdes/* 32097868Sdes * Enable SSL on a connection. 32197868Sdes */ 32297868Sdesint 323174588Sdesfetch_ssl(conn_t *conn, int verbose) 32497868Sdes{ 325214256Semaste#ifdef WITH_SSL 326210568Sdes int ret, ssl_err; 32797868Sdes 32897868Sdes /* Init the SSL library and context */ 32997868Sdes if (!SSL_library_init()){ 33097868Sdes fprintf(stderr, "SSL library init failed\n"); 33197868Sdes return (-1); 33297868Sdes } 33397868Sdes 33497868Sdes SSL_load_error_strings(); 33597868Sdes 33697868Sdes conn->ssl_meth = SSLv23_client_method(); 33797868Sdes conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth); 338108579Sdes SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY); 33997868Sdes 34097868Sdes conn->ssl = SSL_new(conn->ssl_ctx); 34197868Sdes if (conn->ssl == NULL){ 34297868Sdes fprintf(stderr, "SSL context creation failed\n"); 34397868Sdes return (-1); 34497868Sdes } 34597868Sdes SSL_set_fd(conn->ssl, conn->sd); 346210568Sdes while ((ret = SSL_connect(conn->ssl)) == -1) { 347210568Sdes ssl_err = SSL_get_error(conn->ssl, ret); 348210568Sdes if (ssl_err != SSL_ERROR_WANT_READ && 349210568Sdes ssl_err != SSL_ERROR_WANT_WRITE) { 350210568Sdes ERR_print_errors_fp(stderr); 351210568Sdes return (-1); 352210568Sdes } 35397868Sdes } 35497868Sdes 35597868Sdes if (verbose) { 35697868Sdes X509_NAME *name; 35797868Sdes char *str; 35897868Sdes 35997868Sdes fprintf(stderr, "SSL connection established using %s\n", 36097868Sdes SSL_get_cipher(conn->ssl)); 36197868Sdes conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); 36297868Sdes name = X509_get_subject_name(conn->ssl_cert); 36397868Sdes str = X509_NAME_oneline(name, 0, 0); 36497868Sdes printf("Certificate subject: %s\n", str); 36597868Sdes free(str); 36697868Sdes name = X509_get_issuer_name(conn->ssl_cert); 36797868Sdes str = X509_NAME_oneline(name, 0, 0); 36897868Sdes printf("Certificate issuer: %s\n", str); 36997868Sdes free(str); 37097868Sdes } 37197868Sdes 37297868Sdes return (0); 37397891Sdes#else 37497891Sdes (void)conn; 37597891Sdes (void)verbose; 37697891Sdes fprintf(stderr, "SSL support disabled\n"); 37797891Sdes return (-1); 37897891Sdes#endif 37997868Sdes} 38097868Sdes 381210568Sdes#define FETCH_READ_WAIT -2 382210568Sdes#define FETCH_READ_ERROR -1 383210568Sdes#define FETCH_READ_DONE 0 38498117Sdes 385210568Sdes#ifdef WITH_SSL 386210568Sdesstatic ssize_t 387210568Sdesfetch_ssl_read(SSL *ssl, char *buf, size_t len) 388210568Sdes{ 389210568Sdes ssize_t rlen; 390210568Sdes int ssl_err; 391210568Sdes 392210568Sdes rlen = SSL_read(ssl, buf, len); 393210568Sdes if (rlen < 0) { 394210568Sdes ssl_err = SSL_get_error(ssl, rlen); 395210568Sdes if (ssl_err == SSL_ERROR_WANT_READ || 396210568Sdes ssl_err == SSL_ERROR_WANT_WRITE) { 397210568Sdes return (FETCH_READ_WAIT); 398210568Sdes } else { 399210568Sdes ERR_print_errors_fp(stderr); 400210568Sdes return (FETCH_READ_ERROR); 401210568Sdes } 402210568Sdes } 403210568Sdes return (rlen); 404210568Sdes} 405210568Sdes#endif 406210568Sdes 407230307Sdes/* 408230307Sdes * Cache some data that was read from a socket but cannot be immediately 409230307Sdes * returned because of an interrupted system call. 410230307Sdes */ 411230307Sdesstatic int 412230307Sdesfetch_cache_data(conn_t *conn, char *src, size_t nbytes) 413230307Sdes{ 414230307Sdes char *tmp; 415230307Sdes 416230307Sdes if (conn->cache.size < nbytes) { 417230307Sdes tmp = realloc(conn->cache.buf, nbytes); 418230307Sdes if (tmp == NULL) { 419230307Sdes errno = ENOMEM; 420230307Sdes fetch_syserr(); 421230307Sdes return (-1); 422230307Sdes } 423230307Sdes conn->cache.buf = tmp; 424230307Sdes conn->cache.size = nbytes; 425230307Sdes } 426230307Sdes 427230307Sdes memcpy(conn->cache.buf, src, nbytes); 428230307Sdes conn->cache.len = nbytes; 429230307Sdes conn->cache.pos = 0; 430230307Sdes 431230307Sdes return (0); 432230307Sdes} 433230307Sdes 434230307Sdes 435210568Sdesstatic ssize_t 436210568Sdesfetch_socket_read(int sd, char *buf, size_t len) 437210568Sdes{ 438210568Sdes ssize_t rlen; 439210568Sdes 440210568Sdes rlen = read(sd, buf, len); 441210568Sdes if (rlen < 0) { 442210568Sdes if (errno == EAGAIN || (errno == EINTR && fetchRestartCalls)) 443210568Sdes return (FETCH_READ_WAIT); 444210568Sdes else 445210568Sdes return (FETCH_READ_ERROR); 446210568Sdes } 447210568Sdes return (rlen); 448210568Sdes} 449210568Sdes 45097868Sdes/* 45197866Sdes * Read a character from a connection w/ timeout 45255557Sdes */ 45397866Sdesssize_t 454174588Sdesfetch_read(conn_t *conn, char *buf, size_t len) 45555557Sdes{ 456177447Sdes struct timeval now, timeout, delta; 45790267Sdes fd_set readfds; 45897866Sdes ssize_t rlen, total; 45990267Sdes int r; 460230307Sdes char *start; 46190267Sdes 46255557Sdes if (fetchTimeout) { 46397866Sdes FD_ZERO(&readfds); 46490267Sdes gettimeofday(&timeout, NULL); 46590267Sdes timeout.tv_sec += fetchTimeout; 46655557Sdes } 46790267Sdes 46897866Sdes total = 0; 469230307Sdes start = buf; 470230307Sdes 471230307Sdes if (conn->cache.len > 0) { 472230307Sdes /* 473230307Sdes * The last invocation of fetch_read was interrupted by a 474230307Sdes * signal after some data had been read from the socket. Copy 475230307Sdes * the cached data into the supplied buffer before trying to 476230307Sdes * read from the socket again. 477230307Sdes */ 478230307Sdes total = (conn->cache.len < len) ? conn->cache.len : len; 479230307Sdes memcpy(buf, conn->cache.buf, total); 480230307Sdes 481230307Sdes conn->cache.len -= total; 482230307Sdes conn->cache.pos += total; 483230307Sdes len -= total; 484230307Sdes buf+= total; 485230307Sdes } 486230307Sdes 48797866Sdes while (len > 0) { 488210568Sdes /* 489210568Sdes * The socket is non-blocking. Instead of the canonical 490210568Sdes * select() -> read(), we do the following: 491210568Sdes * 492210568Sdes * 1) call read() or SSL_read(). 493210568Sdes * 2) if an error occurred, return -1. 494210568Sdes * 3) if we received data but we still expect more, 495210568Sdes * update our counters and loop. 496210568Sdes * 4) if read() or SSL_read() signaled EOF, return. 497210568Sdes * 5) if we did not receive any data but we're not at EOF, 498210568Sdes * call select(). 499210568Sdes * 500210568Sdes * In the SSL case, this is necessary because if we 501210568Sdes * receive a close notification, we have to call 502210568Sdes * SSL_read() one additional time after we've read 503210568Sdes * everything we received. 504210568Sdes * 505210568Sdes * In the non-SSL case, it may improve performance (very 506210568Sdes * slightly) when reading small amounts of data. 507210568Sdes */ 508210568Sdes#ifdef WITH_SSL 509210568Sdes if (conn->ssl != NULL) 510210568Sdes rlen = fetch_ssl_read(conn->ssl, buf, len); 511210568Sdes else 512210568Sdes#endif 513210568Sdes rlen = fetch_socket_read(conn->sd, buf, len); 514210568Sdes if (rlen == 0) { 515210568Sdes break; 516210568Sdes } else if (rlen > 0) { 517210568Sdes len -= rlen; 518210568Sdes buf += rlen; 519210568Sdes total += rlen; 520210568Sdes continue; 521210568Sdes } else if (rlen == FETCH_READ_ERROR) { 522230307Sdes if (errno == EINTR) 523230307Sdes fetch_cache_data(conn, start, total); 524210568Sdes return (-1); 525210568Sdes } 526210568Sdes // assert(rlen == FETCH_READ_WAIT); 52797866Sdes while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) { 52897856Sdes FD_SET(conn->sd, &readfds); 52990267Sdes gettimeofday(&now, NULL); 530177447Sdes delta.tv_sec = timeout.tv_sec - now.tv_sec; 531177447Sdes delta.tv_usec = timeout.tv_usec - now.tv_usec; 532177447Sdes if (delta.tv_usec < 0) { 533177447Sdes delta.tv_usec += 1000000; 534177447Sdes delta.tv_sec--; 53590267Sdes } 536177447Sdes if (delta.tv_sec < 0) { 537106186Sdes errno = ETIMEDOUT; 538174588Sdes fetch_syserr(); 539106186Sdes return (-1); 540106186Sdes } 54197866Sdes errno = 0; 542177447Sdes r = select(conn->sd + 1, &readfds, NULL, NULL, &delta); 54390267Sdes if (r == -1) { 544230307Sdes if (errno == EINTR) { 545230307Sdes if (fetchRestartCalls) 546230307Sdes continue; 547230307Sdes /* Save anything that was read. */ 548230307Sdes fetch_cache_data(conn, start, total); 549230307Sdes } 550174588Sdes fetch_syserr(); 55190267Sdes return (-1); 55290267Sdes } 55390267Sdes } 55497866Sdes } 55597866Sdes return (total); 55697866Sdes} 55797866Sdes 55898117Sdes 55997866Sdes/* 56097866Sdes * Read a line of text from a connection w/ timeout 56197866Sdes */ 56297866Sdes#define MIN_BUF_SIZE 1024 56397866Sdes 56497866Sdesint 565174588Sdesfetch_getln(conn_t *conn) 56697866Sdes{ 56797866Sdes char *tmp; 56897866Sdes size_t tmpsize; 569106186Sdes ssize_t len; 57097866Sdes char c; 57197866Sdes 57297866Sdes if (conn->buf == NULL) { 57397866Sdes if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) { 57497866Sdes errno = ENOMEM; 57597866Sdes return (-1); 57697866Sdes } 57797866Sdes conn->bufsize = MIN_BUF_SIZE; 57897866Sdes } 57997866Sdes 58097866Sdes conn->buf[0] = '\0'; 58197866Sdes conn->buflen = 0; 58297866Sdes 58397866Sdes do { 584174588Sdes len = fetch_read(conn, &c, 1); 585106186Sdes if (len == -1) 58697866Sdes return (-1); 587106186Sdes if (len == 0) 588106137Sobrien break; 58997856Sdes conn->buf[conn->buflen++] = c; 59097856Sdes if (conn->buflen == conn->bufsize) { 59197856Sdes tmp = conn->buf; 59297856Sdes tmpsize = conn->bufsize * 2 + 1; 59397856Sdes if ((tmp = realloc(tmp, tmpsize)) == NULL) { 59490267Sdes errno = ENOMEM; 59590267Sdes return (-1); 59690267Sdes } 59797856Sdes conn->buf = tmp; 59897856Sdes conn->bufsize = tmpsize; 59990267Sdes } 60090267Sdes } while (c != '\n'); 60190267Sdes 60297856Sdes conn->buf[conn->buflen] = '\0'; 60397856Sdes DEBUG(fprintf(stderr, "<<< %s", conn->buf)); 60490267Sdes return (0); 60555557Sdes} 60655557Sdes 60755557Sdes 60862981Sdes/* 60997866Sdes * Write to a connection w/ timeout 61062981Sdes */ 61197866Sdesssize_t 612174588Sdesfetch_write(conn_t *conn, const char *buf, size_t len) 61397866Sdes{ 614106175Simp struct iovec iov; 615106175Simp 616106175Simp iov.iov_base = __DECONST(char *, buf); 617106175Simp iov.iov_len = len; 618174588Sdes return fetch_writev(conn, &iov, 1); 619106175Simp} 620106175Simp 621106175Simp/* 622106175Simp * Write a vector to a connection w/ timeout 623106175Simp * Note: can modify the iovec. 624106175Simp */ 625106175Simpssize_t 626174588Sdesfetch_writev(conn_t *conn, struct iovec *iov, int iovcnt) 627106175Simp{ 628177447Sdes struct timeval now, timeout, delta; 62997866Sdes fd_set writefds; 63097866Sdes ssize_t wlen, total; 63197866Sdes int r; 63297866Sdes 63397866Sdes if (fetchTimeout) { 63497866Sdes FD_ZERO(&writefds); 63597866Sdes gettimeofday(&timeout, NULL); 63697866Sdes timeout.tv_sec += fetchTimeout; 63797866Sdes } 63897866Sdes 639106175Simp total = 0; 640106175Simp while (iovcnt > 0) { 64197866Sdes while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) { 64297866Sdes FD_SET(conn->sd, &writefds); 64397866Sdes gettimeofday(&now, NULL); 644177447Sdes delta.tv_sec = timeout.tv_sec - now.tv_sec; 645177447Sdes delta.tv_usec = timeout.tv_usec - now.tv_usec; 646177447Sdes if (delta.tv_usec < 0) { 647177447Sdes delta.tv_usec += 1000000; 648177447Sdes delta.tv_sec--; 64997866Sdes } 650177447Sdes if (delta.tv_sec < 0) { 65197866Sdes errno = ETIMEDOUT; 652174588Sdes fetch_syserr(); 65397866Sdes return (-1); 65497866Sdes } 65597866Sdes errno = 0; 656177447Sdes r = select(conn->sd + 1, NULL, &writefds, NULL, &delta); 65797866Sdes if (r == -1) { 65897866Sdes if (errno == EINTR && fetchRestartCalls) 65997866Sdes continue; 66097866Sdes return (-1); 66197866Sdes } 66297866Sdes } 66397866Sdes errno = 0; 66497891Sdes#ifdef WITH_SSL 66597866Sdes if (conn->ssl != NULL) 666106175Simp wlen = SSL_write(conn->ssl, 667106175Simp iov->iov_base, iov->iov_len); 66897866Sdes else 66997891Sdes#endif 670106175Simp wlen = writev(conn->sd, iov, iovcnt); 671106175Simp if (wlen == 0) { 67297866Sdes /* we consider a short write a failure */ 673210568Sdes /* XXX perhaps we shouldn't in the SSL case */ 674106175Simp errno = EPIPE; 675174588Sdes fetch_syserr(); 67697866Sdes return (-1); 677106175Simp } 67897866Sdes if (wlen < 0) { 67997866Sdes if (errno == EINTR && fetchRestartCalls) 68097866Sdes continue; 68197866Sdes return (-1); 68297866Sdes } 68397866Sdes total += wlen; 684106175Simp while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) { 685106175Simp wlen -= iov->iov_len; 686106175Simp iov++; 687106175Simp iovcnt--; 688106175Simp } 689106175Simp if (iovcnt > 0) { 690106175Simp iov->iov_len -= wlen; 691106175Simp iov->iov_base = __DECONST(char *, iov->iov_base) + wlen; 692106175Simp } 69397866Sdes } 69497866Sdes return (total); 69597866Sdes} 69697866Sdes 69798117Sdes 69897866Sdes/* 69997866Sdes * Write a line of text to a connection w/ timeout 70097866Sdes */ 70162981Sdesint 702174588Sdesfetch_putln(conn_t *conn, const char *str, size_t len) 70362981Sdes{ 704106175Simp struct iovec iov[2]; 705106205Sdes int ret; 70698748Sdes 70798748Sdes DEBUG(fprintf(stderr, ">>> %s\n", str)); 708106175Simp iov[0].iov_base = __DECONST(char *, str); 709106175Simp iov[0].iov_len = len; 710106175Simp iov[1].iov_base = __DECONST(char *, ENDL); 711109967Sdes iov[1].iov_len = sizeof(ENDL); 712106205Sdes if (len == 0) 713174588Sdes ret = fetch_writev(conn, &iov[1], 1); 714106205Sdes else 715174588Sdes ret = fetch_writev(conn, iov, 2); 716106205Sdes if (ret == -1) 71790267Sdes return (-1); 71890267Sdes return (0); 71962981Sdes} 72062981Sdes 72162981Sdes 72297856Sdes/* 72397856Sdes * Close connection 72497856Sdes */ 72597856Sdesint 726174588Sdesfetch_close(conn_t *conn) 72797856Sdes{ 72897856Sdes int ret; 72997856Sdes 73098117Sdes if (--conn->ref > 0) 73198117Sdes return (0); 73297856Sdes ret = close(conn->sd); 733230307Sdes free(conn->cache.buf); 734141970Sdes free(conn->buf); 73597856Sdes free(conn); 73697856Sdes return (ret); 73797856Sdes} 73897856Sdes 73997856Sdes 74041989Sdes/*** Directory-related utility functions *************************************/ 74141989Sdes 74241989Sdesint 743174588Sdesfetch_add_entry(struct url_ent **p, int *size, int *len, 74490267Sdes const char *name, struct url_stat *us) 74541989Sdes{ 74690267Sdes struct url_ent *tmp; 74741989Sdes 74890267Sdes if (*p == NULL) { 74990268Sdes *size = 0; 75090267Sdes *len = 0; 75141989Sdes } 75241989Sdes 75390267Sdes if (*len >= *size - 1) { 754109967Sdes tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p)); 75590267Sdes if (tmp == NULL) { 75690267Sdes errno = ENOMEM; 757174588Sdes fetch_syserr(); 75890267Sdes return (-1); 75990267Sdes } 76090268Sdes *size = (*size * 2 + 1); 76190267Sdes *p = tmp; 76290267Sdes } 76341989Sdes 76490267Sdes tmp = *p + *len; 76590267Sdes snprintf(tmp->name, PATH_MAX, "%s", name); 766176105Sdes memcpy(&tmp->stat, us, sizeof(*us)); 76741989Sdes 76890267Sdes (*len)++; 76990267Sdes (++tmp)->name[0] = 0; 77090267Sdes 77190267Sdes return (0); 77241989Sdes} 773109695Sdes 774109695Sdes 775109695Sdes/*** Authentication-related utility functions ********************************/ 776109695Sdes 777109695Sdesstatic const char * 778174588Sdesfetch_read_word(FILE *f) 779109695Sdes{ 780109695Sdes static char word[1024]; 781109695Sdes 782178234Scperciva if (fscanf(f, " %1023s ", word) != 1) 783109695Sdes return (NULL); 784109695Sdes return (word); 785109695Sdes} 786109695Sdes 787109695Sdes/* 788109695Sdes * Get authentication data for a URL from .netrc 789109695Sdes */ 790109695Sdesint 791174588Sdesfetch_netrc_auth(struct url *url) 792109695Sdes{ 793109695Sdes char fn[PATH_MAX]; 794109695Sdes const char *word; 795109695Sdes char *p; 796109695Sdes FILE *f; 797109695Sdes 798109695Sdes if ((p = getenv("NETRC")) != NULL) { 799109967Sdes if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) { 800174588Sdes fetch_info("$NETRC specifies a file name " 801109695Sdes "longer than PATH_MAX"); 802109695Sdes return (-1); 803109695Sdes } 804109695Sdes } else { 805109695Sdes if ((p = getenv("HOME")) != NULL) { 806109695Sdes struct passwd *pwd; 807109695Sdes 808109695Sdes if ((pwd = getpwuid(getuid())) == NULL || 809109695Sdes (p = pwd->pw_dir) == NULL) 810109695Sdes return (-1); 811109695Sdes } 812109967Sdes if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn)) 813109695Sdes return (-1); 814109695Sdes } 815109695Sdes 816109695Sdes if ((f = fopen(fn, "r")) == NULL) 817109695Sdes return (-1); 818174588Sdes while ((word = fetch_read_word(f)) != NULL) { 819109695Sdes if (strcmp(word, "default") == 0) { 820174588Sdes DEBUG(fetch_info("Using default .netrc settings")); 821109695Sdes break; 822109695Sdes } 823109695Sdes if (strcmp(word, "machine") == 0 && 824174588Sdes (word = fetch_read_word(f)) != NULL && 825109695Sdes strcasecmp(word, url->host) == 0) { 826174588Sdes DEBUG(fetch_info("Using .netrc settings for %s", word)); 827109695Sdes break; 828109695Sdes } 829109695Sdes } 830109695Sdes if (word == NULL) 831109695Sdes goto ferr; 832174588Sdes while ((word = fetch_read_word(f)) != NULL) { 833109695Sdes if (strcmp(word, "login") == 0) { 834174588Sdes if ((word = fetch_read_word(f)) == NULL) 835109695Sdes goto ferr; 836109967Sdes if (snprintf(url->user, sizeof(url->user), 837109960Sjwd "%s", word) > (int)sizeof(url->user)) { 838174588Sdes fetch_info("login name in .netrc is too long"); 839109695Sdes url->user[0] = '\0'; 840109695Sdes } 841109695Sdes } else if (strcmp(word, "password") == 0) { 842174588Sdes if ((word = fetch_read_word(f)) == NULL) 843109695Sdes goto ferr; 844109967Sdes if (snprintf(url->pwd, sizeof(url->pwd), 845109960Sjwd "%s", word) > (int)sizeof(url->pwd)) { 846174588Sdes fetch_info("password in .netrc is too long"); 847109695Sdes url->pwd[0] = '\0'; 848109695Sdes } 849109695Sdes } else if (strcmp(word, "account") == 0) { 850174588Sdes if ((word = fetch_read_word(f)) == NULL) 851109695Sdes goto ferr; 852109695Sdes /* XXX not supported! */ 853109695Sdes } else { 854109695Sdes break; 855109695Sdes } 856109695Sdes } 857109695Sdes fclose(f); 858109695Sdes return (0); 859109695Sdes ferr: 860109695Sdes fclose(f); 861109695Sdes return (-1); 862109695Sdes} 863174752Sdes 864174752Sdes/* 865174752Sdes * The no_proxy environment variable specifies a set of domains for 866174752Sdes * which the proxy should not be consulted; the contents is a comma-, 867174752Sdes * or space-separated list of domain names. A single asterisk will 868174752Sdes * override all proxy variables and no transactions will be proxied 869174752Sdes * (for compatability with lynx and curl, see the discussion at 870174752Sdes * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>). 871174752Sdes */ 872174752Sdesint 873174752Sdesfetch_no_proxy_match(const char *host) 874174752Sdes{ 875174752Sdes const char *no_proxy, *p, *q; 876174752Sdes size_t h_len, d_len; 877174752Sdes 878174752Sdes if ((no_proxy = getenv("NO_PROXY")) == NULL && 879174752Sdes (no_proxy = getenv("no_proxy")) == NULL) 880174752Sdes return (0); 881174752Sdes 882174752Sdes /* asterisk matches any hostname */ 883174752Sdes if (strcmp(no_proxy, "*") == 0) 884174752Sdes return (1); 885174752Sdes 886174752Sdes h_len = strlen(host); 887174752Sdes p = no_proxy; 888174752Sdes do { 889174752Sdes /* position p at the beginning of a domain suffix */ 890174761Sdes while (*p == ',' || isspace((unsigned char)*p)) 891174752Sdes p++; 892174752Sdes 893174752Sdes /* position q at the first separator character */ 894174752Sdes for (q = p; *q; ++q) 895174761Sdes if (*q == ',' || isspace((unsigned char)*q)) 896174752Sdes break; 897174752Sdes 898174752Sdes d_len = q - p; 899198339Sfabient if (d_len > 0 && h_len >= d_len && 900174752Sdes strncasecmp(host + h_len - d_len, 901174752Sdes p, d_len) == 0) { 902174752Sdes /* domain name matches */ 903174752Sdes return (1); 904174752Sdes } 905174752Sdes 906174752Sdes p = q + 1; 907174752Sdes } while (*q); 908174752Sdes 909174752Sdes return (0); 910174752Sdes} 911