1842a95ccSDag-Erling Smørgrav /*- 2578153f1SDag-Erling Smørgrav * Copyright (c) 1998-2011 Dag-Erling Smørgrav 3842a95ccSDag-Erling Smørgrav * All rights reserved. 4842a95ccSDag-Erling Smørgrav * 5842a95ccSDag-Erling Smørgrav * Redistribution and use in source and binary forms, with or without 6842a95ccSDag-Erling Smørgrav * modification, are permitted provided that the following conditions 7842a95ccSDag-Erling Smørgrav * are met: 8842a95ccSDag-Erling Smørgrav * 1. Redistributions of source code must retain the above copyright 9842a95ccSDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer 10842a95ccSDag-Erling Smørgrav * in this position and unchanged. 11842a95ccSDag-Erling Smørgrav * 2. Redistributions in binary form must reproduce the above copyright 12842a95ccSDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer in the 13842a95ccSDag-Erling Smørgrav * documentation and/or other materials provided with the distribution. 14842a95ccSDag-Erling Smørgrav * 3. The name of the author may not be used to endorse or promote products 15842a95ccSDag-Erling Smørgrav * derived from this software without specific prior written permission 16842a95ccSDag-Erling Smørgrav * 17842a95ccSDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18842a95ccSDag-Erling Smørgrav * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19842a95ccSDag-Erling Smørgrav * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20842a95ccSDag-Erling Smørgrav * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21842a95ccSDag-Erling Smørgrav * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22842a95ccSDag-Erling Smørgrav * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23842a95ccSDag-Erling Smørgrav * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24842a95ccSDag-Erling Smørgrav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25842a95ccSDag-Erling Smørgrav * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26842a95ccSDag-Erling Smørgrav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27842a95ccSDag-Erling Smørgrav */ 28842a95ccSDag-Erling Smørgrav 29cecb889fSMatthew Dillon #include <sys/cdefs.h> 30cecb889fSMatthew Dillon __FBSDID("$FreeBSD$"); 31cecb889fSMatthew Dillon 320fba3a00SDag-Erling Smørgrav #include <sys/param.h> 33842a95ccSDag-Erling Smørgrav #include <sys/socket.h> 34fc6e9e65SDag-Erling Smørgrav #include <sys/time.h> 35a1763027SDag-Erling Smørgrav #include <sys/uio.h> 3662a2681cSDag-Erling Smørgrav 37842a95ccSDag-Erling Smørgrav #include <netinet/in.h> 38842a95ccSDag-Erling Smørgrav 3962a2681cSDag-Erling Smørgrav #include <ctype.h> 40842a95ccSDag-Erling Smørgrav #include <errno.h> 41caaffed8SDag-Erling Smørgrav #include <fcntl.h> 42842a95ccSDag-Erling Smørgrav #include <netdb.h> 4307350d12SDag-Erling Smørgrav #include <pwd.h> 44ba101983SDag-Erling Smørgrav #include <stdarg.h> 450fba3a00SDag-Erling Smørgrav #include <stdlib.h> 460fba3a00SDag-Erling Smørgrav #include <stdio.h> 47842a95ccSDag-Erling Smørgrav #include <string.h> 48842a95ccSDag-Erling Smørgrav #include <unistd.h> 49842a95ccSDag-Erling Smørgrav 50842a95ccSDag-Erling Smørgrav #include "fetch.h" 51842a95ccSDag-Erling Smørgrav #include "common.h" 52842a95ccSDag-Erling Smørgrav 53d8acd8dcSDag-Erling Smørgrav 54842a95ccSDag-Erling Smørgrav /*** Local data **************************************************************/ 55842a95ccSDag-Erling Smørgrav 56842a95ccSDag-Erling Smørgrav /* 57842a95ccSDag-Erling Smørgrav * Error messages for resolver errors 58842a95ccSDag-Erling Smørgrav */ 59a1b37df2SDag-Erling Smørgrav static struct fetcherr netdb_errlist[] = { 603d82ba43SHajimu UMEMOTO #ifdef EAI_NODATA 6128c645cfSHajimu UMEMOTO { EAI_NODATA, FETCH_RESOLV, "Host not found" }, 623d82ba43SHajimu UMEMOTO #endif 6328c645cfSHajimu UMEMOTO { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, 6428c645cfSHajimu UMEMOTO { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, 6528c645cfSHajimu UMEMOTO { EAI_NONAME, FETCH_RESOLV, "No address record" }, 66d8acd8dcSDag-Erling Smørgrav { -1, FETCH_UNKNOWN, "Unknown resolver error" } 67842a95ccSDag-Erling Smørgrav }; 68842a95ccSDag-Erling Smørgrav 69a1763027SDag-Erling Smørgrav /* End-of-Line */ 7038c7e4a6SArchie Cobbs static const char ENDL[2] = "\r\n"; 71a1763027SDag-Erling Smørgrav 72842a95ccSDag-Erling Smørgrav 73842a95ccSDag-Erling Smørgrav /*** Error-reporting functions ***********************************************/ 74842a95ccSDag-Erling Smørgrav 75842a95ccSDag-Erling Smørgrav /* 76842a95ccSDag-Erling Smørgrav * Map error code to string 77842a95ccSDag-Erling Smørgrav */ 78ba101983SDag-Erling Smørgrav static struct fetcherr * 79a1b37df2SDag-Erling Smørgrav fetch_finderr(struct fetcherr *p, int e) 80842a95ccSDag-Erling Smørgrav { 81ba101983SDag-Erling Smørgrav while (p->num != -1 && p->num != e) 82ba101983SDag-Erling Smørgrav p++; 83e19e6098SDag-Erling Smørgrav return (p); 84842a95ccSDag-Erling Smørgrav } 85842a95ccSDag-Erling Smørgrav 86842a95ccSDag-Erling Smørgrav /* 87842a95ccSDag-Erling Smørgrav * Set error code 88842a95ccSDag-Erling Smørgrav */ 89842a95ccSDag-Erling Smørgrav void 90a1b37df2SDag-Erling Smørgrav fetch_seterr(struct fetcherr *p, int e) 91842a95ccSDag-Erling Smørgrav { 92a1b37df2SDag-Erling Smørgrav p = fetch_finderr(p, e); 93ba101983SDag-Erling Smørgrav fetchLastErrCode = p->cat; 94ba101983SDag-Erling Smørgrav snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); 95842a95ccSDag-Erling Smørgrav } 96842a95ccSDag-Erling Smørgrav 97842a95ccSDag-Erling Smørgrav /* 98842a95ccSDag-Erling Smørgrav * Set error code according to errno 99842a95ccSDag-Erling Smørgrav */ 100842a95ccSDag-Erling Smørgrav void 101a1b37df2SDag-Erling Smørgrav fetch_syserr(void) 102842a95ccSDag-Erling Smørgrav { 103d8acd8dcSDag-Erling Smørgrav switch (errno) { 104d8acd8dcSDag-Erling Smørgrav case 0: 1050fba3a00SDag-Erling Smørgrav fetchLastErrCode = FETCH_OK; 106d8acd8dcSDag-Erling Smørgrav break; 107d8acd8dcSDag-Erling Smørgrav case EPERM: 108d8acd8dcSDag-Erling Smørgrav case EACCES: 109d8acd8dcSDag-Erling Smørgrav case EROFS: 110d8acd8dcSDag-Erling Smørgrav case EAUTH: 111d8acd8dcSDag-Erling Smørgrav case ENEEDAUTH: 1120fba3a00SDag-Erling Smørgrav fetchLastErrCode = FETCH_AUTH; 113d8acd8dcSDag-Erling Smørgrav break; 114d8acd8dcSDag-Erling Smørgrav case ENOENT: 115d8acd8dcSDag-Erling Smørgrav case EISDIR: /* XXX */ 1160fba3a00SDag-Erling Smørgrav fetchLastErrCode = FETCH_UNAVAIL; 117d8acd8dcSDag-Erling Smørgrav break; 118d8acd8dcSDag-Erling Smørgrav case ENOMEM: 1190fba3a00SDag-Erling Smørgrav fetchLastErrCode = FETCH_MEMORY; 120d8acd8dcSDag-Erling Smørgrav break; 121d8acd8dcSDag-Erling Smørgrav case EBUSY: 122d8acd8dcSDag-Erling Smørgrav case EAGAIN: 1230fba3a00SDag-Erling Smørgrav fetchLastErrCode = FETCH_TEMP; 124d8acd8dcSDag-Erling Smørgrav break; 125d8acd8dcSDag-Erling Smørgrav case EEXIST: 1260fba3a00SDag-Erling Smørgrav fetchLastErrCode = FETCH_EXISTS; 127d8acd8dcSDag-Erling Smørgrav break; 128d8acd8dcSDag-Erling Smørgrav case ENOSPC: 1290fba3a00SDag-Erling Smørgrav fetchLastErrCode = FETCH_FULL; 130d8acd8dcSDag-Erling Smørgrav break; 131d8acd8dcSDag-Erling Smørgrav case EADDRINUSE: 132d8acd8dcSDag-Erling Smørgrav case EADDRNOTAVAIL: 133d8acd8dcSDag-Erling Smørgrav case ENETDOWN: 134d8acd8dcSDag-Erling Smørgrav case ENETUNREACH: 135d8acd8dcSDag-Erling Smørgrav case ENETRESET: 136d8acd8dcSDag-Erling Smørgrav case EHOSTUNREACH: 1370fba3a00SDag-Erling Smørgrav fetchLastErrCode = FETCH_NETWORK; 138d8acd8dcSDag-Erling Smørgrav break; 139d8acd8dcSDag-Erling Smørgrav case ECONNABORTED: 140d8acd8dcSDag-Erling Smørgrav case ECONNRESET: 1410fba3a00SDag-Erling Smørgrav fetchLastErrCode = FETCH_ABORT; 142d8acd8dcSDag-Erling Smørgrav break; 143d8acd8dcSDag-Erling Smørgrav case ETIMEDOUT: 1440fba3a00SDag-Erling Smørgrav fetchLastErrCode = FETCH_TIMEOUT; 145d8acd8dcSDag-Erling Smørgrav break; 146d8acd8dcSDag-Erling Smørgrav case ECONNREFUSED: 147d8acd8dcSDag-Erling Smørgrav case EHOSTDOWN: 1480fba3a00SDag-Erling Smørgrav fetchLastErrCode = FETCH_DOWN; 149d8acd8dcSDag-Erling Smørgrav break; 150d8acd8dcSDag-Erling Smørgrav default: 1510fba3a00SDag-Erling Smørgrav fetchLastErrCode = FETCH_UNKNOWN; 152d8acd8dcSDag-Erling Smørgrav } 153e19e6098SDag-Erling Smørgrav snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); 1540fba3a00SDag-Erling Smørgrav } 1550fba3a00SDag-Erling Smørgrav 1560fba3a00SDag-Erling Smørgrav 1570fba3a00SDag-Erling Smørgrav /* 1580fba3a00SDag-Erling Smørgrav * Emit status message 1590fba3a00SDag-Erling Smørgrav */ 160ba101983SDag-Erling Smørgrav void 161a1b37df2SDag-Erling Smørgrav fetch_info(const char *fmt, ...) 1620fba3a00SDag-Erling Smørgrav { 1630fba3a00SDag-Erling Smørgrav va_list ap; 1640fba3a00SDag-Erling Smørgrav 1650fba3a00SDag-Erling Smørgrav va_start(ap, fmt); 166ba101983SDag-Erling Smørgrav vfprintf(stderr, fmt, ap); 1670fba3a00SDag-Erling Smørgrav va_end(ap); 168a68f8b58SDag-Erling Smørgrav fputc('\n', stderr); 169842a95ccSDag-Erling Smørgrav } 170842a95ccSDag-Erling Smørgrav 171842a95ccSDag-Erling Smørgrav 172842a95ccSDag-Erling Smørgrav /*** Network-related utility functions ***************************************/ 173842a95ccSDag-Erling Smørgrav 174842a95ccSDag-Erling Smørgrav /* 175e828ada7SDag-Erling Smørgrav * Return the default port for a scheme 176e828ada7SDag-Erling Smørgrav */ 177e828ada7SDag-Erling Smørgrav int 178a1b37df2SDag-Erling Smørgrav fetch_default_port(const char *scheme) 179e828ada7SDag-Erling Smørgrav { 180e828ada7SDag-Erling Smørgrav struct servent *se; 181e828ada7SDag-Erling Smørgrav 182e828ada7SDag-Erling Smørgrav if ((se = getservbyname(scheme, "tcp")) != NULL) 183e19e6098SDag-Erling Smørgrav return (ntohs(se->s_port)); 184e828ada7SDag-Erling Smørgrav if (strcasecmp(scheme, SCHEME_FTP) == 0) 185e19e6098SDag-Erling Smørgrav return (FTP_DEFAULT_PORT); 186e828ada7SDag-Erling Smørgrav if (strcasecmp(scheme, SCHEME_HTTP) == 0) 187e19e6098SDag-Erling Smørgrav return (HTTP_DEFAULT_PORT); 188e19e6098SDag-Erling Smørgrav return (0); 189e828ada7SDag-Erling Smørgrav } 190e828ada7SDag-Erling Smørgrav 191e828ada7SDag-Erling Smørgrav /* 192e828ada7SDag-Erling Smørgrav * Return the default proxy port for a scheme 193e828ada7SDag-Erling Smørgrav */ 194e828ada7SDag-Erling Smørgrav int 195a1b37df2SDag-Erling Smørgrav fetch_default_proxy_port(const char *scheme) 196e828ada7SDag-Erling Smørgrav { 197e828ada7SDag-Erling Smørgrav if (strcasecmp(scheme, SCHEME_FTP) == 0) 198e19e6098SDag-Erling Smørgrav return (FTP_DEFAULT_PROXY_PORT); 199e828ada7SDag-Erling Smørgrav if (strcasecmp(scheme, SCHEME_HTTP) == 0) 200e19e6098SDag-Erling Smørgrav return (HTTP_DEFAULT_PROXY_PORT); 201e19e6098SDag-Erling Smørgrav return (0); 202e828ada7SDag-Erling Smørgrav } 203e828ada7SDag-Erling Smørgrav 204f606d589SDag-Erling Smørgrav 205e828ada7SDag-Erling Smørgrav /* 2069601e333SDag-Erling Smørgrav * Create a connection for an existing descriptor. 2079601e333SDag-Erling Smørgrav */ 2089601e333SDag-Erling Smørgrav conn_t * 209a1b37df2SDag-Erling Smørgrav fetch_reopen(int sd) 2109601e333SDag-Erling Smørgrav { 2119601e333SDag-Erling Smørgrav conn_t *conn; 2129601e333SDag-Erling Smørgrav 2139601e333SDag-Erling Smørgrav /* allocate and fill connection structure */ 214930105c1SDag-Erling Smørgrav if ((conn = calloc(1, sizeof(*conn))) == NULL) 2159601e333SDag-Erling Smørgrav return (NULL); 21615b68c63SDag-Erling Smørgrav fcntl(sd, F_SETFD, FD_CLOEXEC); 2179601e333SDag-Erling Smørgrav conn->sd = sd; 218f606d589SDag-Erling Smørgrav ++conn->ref; 219f606d589SDag-Erling Smørgrav return (conn); 220f606d589SDag-Erling Smørgrav } 221f606d589SDag-Erling Smørgrav 222f606d589SDag-Erling Smørgrav 223f606d589SDag-Erling Smørgrav /* 224f606d589SDag-Erling Smørgrav * Bump a connection's reference count. 225f606d589SDag-Erling Smørgrav */ 226f606d589SDag-Erling Smørgrav conn_t * 227a1b37df2SDag-Erling Smørgrav fetch_ref(conn_t *conn) 228f606d589SDag-Erling Smørgrav { 229f606d589SDag-Erling Smørgrav 230f606d589SDag-Erling Smørgrav ++conn->ref; 2319601e333SDag-Erling Smørgrav return (conn); 2329601e333SDag-Erling Smørgrav } 2339601e333SDag-Erling Smørgrav 2349601e333SDag-Erling Smørgrav 2359601e333SDag-Erling Smørgrav /* 236c42cb9d9SDag-Erling Smørgrav * Bind a socket to a specific local address 237c42cb9d9SDag-Erling Smørgrav */ 238c42cb9d9SDag-Erling Smørgrav int 239a1b37df2SDag-Erling Smørgrav fetch_bind(int sd, int af, const char *addr) 240c42cb9d9SDag-Erling Smørgrav { 241c42cb9d9SDag-Erling Smørgrav struct addrinfo hints, *res, *res0; 242c42cb9d9SDag-Erling Smørgrav int err; 243c42cb9d9SDag-Erling Smørgrav 244c42cb9d9SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 245c42cb9d9SDag-Erling Smørgrav hints.ai_family = af; 246c42cb9d9SDag-Erling Smørgrav hints.ai_socktype = SOCK_STREAM; 247c42cb9d9SDag-Erling Smørgrav hints.ai_protocol = 0; 248c42cb9d9SDag-Erling Smørgrav if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0) 249c42cb9d9SDag-Erling Smørgrav return (-1); 250c42cb9d9SDag-Erling Smørgrav for (res = res0; res; res = res->ai_next) 251c42cb9d9SDag-Erling Smørgrav if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) 252c42cb9d9SDag-Erling Smørgrav return (0); 253c42cb9d9SDag-Erling Smørgrav return (-1); 254c42cb9d9SDag-Erling Smørgrav } 255c42cb9d9SDag-Erling Smørgrav 256c42cb9d9SDag-Erling Smørgrav 257c42cb9d9SDag-Erling Smørgrav /* 258842a95ccSDag-Erling Smørgrav * Establish a TCP connection to the specified port on the specified host. 259842a95ccSDag-Erling Smørgrav */ 260dea29ca1SDag-Erling Smørgrav conn_t * 261a1b37df2SDag-Erling Smørgrav fetch_connect(const char *host, int port, int af, int verbose) 262842a95ccSDag-Erling Smørgrav { 263dea29ca1SDag-Erling Smørgrav conn_t *conn; 26428c645cfSHajimu UMEMOTO char pbuf[10]; 265c42cb9d9SDag-Erling Smørgrav const char *bindaddr; 26628c645cfSHajimu UMEMOTO struct addrinfo hints, *res, *res0; 26728c645cfSHajimu UMEMOTO int sd, err; 268842a95ccSDag-Erling Smørgrav 269f67efa37SDag-Erling Smørgrav DEBUG(fprintf(stderr, "---> %s:%d\n", host, port)); 270842a95ccSDag-Erling Smørgrav 2710fba3a00SDag-Erling Smørgrav if (verbose) 272a1b37df2SDag-Erling Smørgrav fetch_info("looking up %s", host); 2730fba3a00SDag-Erling Smørgrav 27428c645cfSHajimu UMEMOTO /* look up host name and set up socket address structure */ 27528c645cfSHajimu UMEMOTO snprintf(pbuf, sizeof(pbuf), "%d", port); 27628c645cfSHajimu UMEMOTO memset(&hints, 0, sizeof(hints)); 27728c645cfSHajimu UMEMOTO hints.ai_family = af; 27828c645cfSHajimu UMEMOTO hints.ai_socktype = SOCK_STREAM; 27928c645cfSHajimu UMEMOTO hints.ai_protocol = 0; 28028c645cfSHajimu UMEMOTO if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) { 281a1b37df2SDag-Erling Smørgrav netdb_seterr(err); 282dea29ca1SDag-Erling Smørgrav return (NULL); 283842a95ccSDag-Erling Smørgrav } 284c42cb9d9SDag-Erling Smørgrav bindaddr = getenv("FETCH_BIND_ADDRESS"); 285842a95ccSDag-Erling Smørgrav 2860fba3a00SDag-Erling Smørgrav if (verbose) 287a1b37df2SDag-Erling Smørgrav fetch_info("connecting to %s:%d", host, port); 2880fba3a00SDag-Erling Smørgrav 289842a95ccSDag-Erling Smørgrav /* try to connect */ 290c42cb9d9SDag-Erling Smørgrav for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) { 29128c645cfSHajimu UMEMOTO if ((sd = socket(res->ai_family, res->ai_socktype, 292a1763027SDag-Erling Smørgrav res->ai_protocol)) == -1) 29328c645cfSHajimu UMEMOTO continue; 294c42cb9d9SDag-Erling Smørgrav if (bindaddr != NULL && *bindaddr != '\0' && 295a1b37df2SDag-Erling Smørgrav fetch_bind(sd, res->ai_family, bindaddr) != 0) { 296a1b37df2SDag-Erling Smørgrav fetch_info("failed to bind to '%s'", bindaddr); 297c42cb9d9SDag-Erling Smørgrav close(sd); 298c42cb9d9SDag-Erling Smørgrav continue; 299c42cb9d9SDag-Erling Smørgrav } 300caaffed8SDag-Erling Smørgrav if (connect(sd, res->ai_addr, res->ai_addrlen) == 0 && 301caaffed8SDag-Erling Smørgrav fcntl(sd, F_SETFL, O_NONBLOCK) == 0) 30228c645cfSHajimu UMEMOTO break; 303842a95ccSDag-Erling Smørgrav close(sd); 30428c645cfSHajimu UMEMOTO } 3057487ef62SHajimu UMEMOTO freeaddrinfo(res0); 306a1763027SDag-Erling Smørgrav if (sd == -1) { 307a1b37df2SDag-Erling Smørgrav fetch_syserr(); 308dea29ca1SDag-Erling Smørgrav return (NULL); 309842a95ccSDag-Erling Smørgrav } 310842a95ccSDag-Erling Smørgrav 311a1b37df2SDag-Erling Smørgrav if ((conn = fetch_reopen(sd)) == NULL) { 312a1b37df2SDag-Erling Smørgrav fetch_syserr(); 313dea29ca1SDag-Erling Smørgrav close(sd); 31440cfbfd5SBill Fenner } 315dea29ca1SDag-Erling Smørgrav return (conn); 316842a95ccSDag-Erling Smørgrav } 317ce71b736SDag-Erling Smørgrav 318ce71b736SDag-Erling Smørgrav 319fc6e9e65SDag-Erling Smørgrav /* 320111e2510SDag-Erling Smørgrav * Enable SSL on a connection. 321111e2510SDag-Erling Smørgrav */ 322111e2510SDag-Erling Smørgrav int 323a1b37df2SDag-Erling Smørgrav fetch_ssl(conn_t *conn, int verbose) 324111e2510SDag-Erling Smørgrav { 325a9d0c849SEd Maste #ifdef WITH_SSL 326caaffed8SDag-Erling Smørgrav int ret, ssl_err; 327111e2510SDag-Erling Smørgrav 328111e2510SDag-Erling Smørgrav /* Init the SSL library and context */ 329111e2510SDag-Erling Smørgrav if (!SSL_library_init()){ 330111e2510SDag-Erling Smørgrav fprintf(stderr, "SSL library init failed\n"); 331111e2510SDag-Erling Smørgrav return (-1); 332111e2510SDag-Erling Smørgrav } 333111e2510SDag-Erling Smørgrav 334111e2510SDag-Erling Smørgrav SSL_load_error_strings(); 335111e2510SDag-Erling Smørgrav 336111e2510SDag-Erling Smørgrav conn->ssl_meth = SSLv23_client_method(); 337111e2510SDag-Erling Smørgrav conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth); 338f8020ddeSDag-Erling Smørgrav SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY); 339111e2510SDag-Erling Smørgrav 340111e2510SDag-Erling Smørgrav conn->ssl = SSL_new(conn->ssl_ctx); 341111e2510SDag-Erling Smørgrav if (conn->ssl == NULL){ 342111e2510SDag-Erling Smørgrav fprintf(stderr, "SSL context creation failed\n"); 343111e2510SDag-Erling Smørgrav return (-1); 344111e2510SDag-Erling Smørgrav } 345111e2510SDag-Erling Smørgrav SSL_set_fd(conn->ssl, conn->sd); 346caaffed8SDag-Erling Smørgrav while ((ret = SSL_connect(conn->ssl)) == -1) { 347caaffed8SDag-Erling Smørgrav ssl_err = SSL_get_error(conn->ssl, ret); 348caaffed8SDag-Erling Smørgrav if (ssl_err != SSL_ERROR_WANT_READ && 349caaffed8SDag-Erling Smørgrav ssl_err != SSL_ERROR_WANT_WRITE) { 350111e2510SDag-Erling Smørgrav ERR_print_errors_fp(stderr); 351111e2510SDag-Erling Smørgrav return (-1); 352111e2510SDag-Erling Smørgrav } 353caaffed8SDag-Erling Smørgrav } 354111e2510SDag-Erling Smørgrav 355111e2510SDag-Erling Smørgrav if (verbose) { 356111e2510SDag-Erling Smørgrav X509_NAME *name; 357111e2510SDag-Erling Smørgrav char *str; 358111e2510SDag-Erling Smørgrav 359111e2510SDag-Erling Smørgrav fprintf(stderr, "SSL connection established using %s\n", 360111e2510SDag-Erling Smørgrav SSL_get_cipher(conn->ssl)); 361111e2510SDag-Erling Smørgrav conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); 362111e2510SDag-Erling Smørgrav name = X509_get_subject_name(conn->ssl_cert); 363111e2510SDag-Erling Smørgrav str = X509_NAME_oneline(name, 0, 0); 364111e2510SDag-Erling Smørgrav printf("Certificate subject: %s\n", str); 365111e2510SDag-Erling Smørgrav free(str); 366111e2510SDag-Erling Smørgrav name = X509_get_issuer_name(conn->ssl_cert); 367111e2510SDag-Erling Smørgrav str = X509_NAME_oneline(name, 0, 0); 368111e2510SDag-Erling Smørgrav printf("Certificate issuer: %s\n", str); 369111e2510SDag-Erling Smørgrav free(str); 370111e2510SDag-Erling Smørgrav } 371111e2510SDag-Erling Smørgrav 372111e2510SDag-Erling Smørgrav return (0); 3733070f6cbSDag-Erling Smørgrav #else 3743070f6cbSDag-Erling Smørgrav (void)conn; 3753070f6cbSDag-Erling Smørgrav (void)verbose; 3763070f6cbSDag-Erling Smørgrav fprintf(stderr, "SSL support disabled\n"); 3773070f6cbSDag-Erling Smørgrav return (-1); 3783070f6cbSDag-Erling Smørgrav #endif 379111e2510SDag-Erling Smørgrav } 380111e2510SDag-Erling Smørgrav 381caaffed8SDag-Erling Smørgrav #define FETCH_READ_WAIT -2 382caaffed8SDag-Erling Smørgrav #define FETCH_READ_ERROR -1 383caaffed8SDag-Erling Smørgrav #define FETCH_READ_DONE 0 384caaffed8SDag-Erling Smørgrav 385caaffed8SDag-Erling Smørgrav #ifdef WITH_SSL 386caaffed8SDag-Erling Smørgrav static ssize_t 387caaffed8SDag-Erling Smørgrav fetch_ssl_read(SSL *ssl, char *buf, size_t len) 388caaffed8SDag-Erling Smørgrav { 389caaffed8SDag-Erling Smørgrav ssize_t rlen; 390caaffed8SDag-Erling Smørgrav int ssl_err; 391caaffed8SDag-Erling Smørgrav 392caaffed8SDag-Erling Smørgrav rlen = SSL_read(ssl, buf, len); 393caaffed8SDag-Erling Smørgrav if (rlen < 0) { 394caaffed8SDag-Erling Smørgrav ssl_err = SSL_get_error(ssl, rlen); 395caaffed8SDag-Erling Smørgrav if (ssl_err == SSL_ERROR_WANT_READ || 396caaffed8SDag-Erling Smørgrav ssl_err == SSL_ERROR_WANT_WRITE) { 397caaffed8SDag-Erling Smørgrav return (FETCH_READ_WAIT); 398caaffed8SDag-Erling Smørgrav } else { 399caaffed8SDag-Erling Smørgrav ERR_print_errors_fp(stderr); 400caaffed8SDag-Erling Smørgrav return (FETCH_READ_ERROR); 401caaffed8SDag-Erling Smørgrav } 402caaffed8SDag-Erling Smørgrav } 403caaffed8SDag-Erling Smørgrav return (rlen); 404caaffed8SDag-Erling Smørgrav } 405caaffed8SDag-Erling Smørgrav #endif 406caaffed8SDag-Erling Smørgrav 407*2a7daafeSDag-Erling Smørgrav /* 408*2a7daafeSDag-Erling Smørgrav * Cache some data that was read from a socket but cannot be immediately 409*2a7daafeSDag-Erling Smørgrav * returned because of an interrupted system call. 410*2a7daafeSDag-Erling Smørgrav */ 411*2a7daafeSDag-Erling Smørgrav static int 412*2a7daafeSDag-Erling Smørgrav fetch_cache_data(conn_t *conn, char *src, size_t nbytes) 413*2a7daafeSDag-Erling Smørgrav { 414*2a7daafeSDag-Erling Smørgrav char *tmp; 415*2a7daafeSDag-Erling Smørgrav 416*2a7daafeSDag-Erling Smørgrav if (conn->cache.size < nbytes) { 417*2a7daafeSDag-Erling Smørgrav tmp = realloc(conn->cache.buf, nbytes); 418*2a7daafeSDag-Erling Smørgrav if (tmp == NULL) { 419*2a7daafeSDag-Erling Smørgrav errno = ENOMEM; 420*2a7daafeSDag-Erling Smørgrav fetch_syserr(); 421*2a7daafeSDag-Erling Smørgrav return (-1); 422*2a7daafeSDag-Erling Smørgrav } 423*2a7daafeSDag-Erling Smørgrav conn->cache.buf = tmp; 424*2a7daafeSDag-Erling Smørgrav conn->cache.size = nbytes; 425*2a7daafeSDag-Erling Smørgrav } 426*2a7daafeSDag-Erling Smørgrav 427*2a7daafeSDag-Erling Smørgrav memcpy(conn->cache.buf, src, nbytes); 428*2a7daafeSDag-Erling Smørgrav conn->cache.len = nbytes; 429*2a7daafeSDag-Erling Smørgrav conn->cache.pos = 0; 430*2a7daafeSDag-Erling Smørgrav 431*2a7daafeSDag-Erling Smørgrav return (0); 432*2a7daafeSDag-Erling Smørgrav } 433*2a7daafeSDag-Erling Smørgrav 434*2a7daafeSDag-Erling Smørgrav 435caaffed8SDag-Erling Smørgrav static ssize_t 436caaffed8SDag-Erling Smørgrav fetch_socket_read(int sd, char *buf, size_t len) 437caaffed8SDag-Erling Smørgrav { 438caaffed8SDag-Erling Smørgrav ssize_t rlen; 439caaffed8SDag-Erling Smørgrav 440caaffed8SDag-Erling Smørgrav rlen = read(sd, buf, len); 441caaffed8SDag-Erling Smørgrav if (rlen < 0) { 442caaffed8SDag-Erling Smørgrav if (errno == EAGAIN || (errno == EINTR && fetchRestartCalls)) 443caaffed8SDag-Erling Smørgrav return (FETCH_READ_WAIT); 444caaffed8SDag-Erling Smørgrav else 445caaffed8SDag-Erling Smørgrav return (FETCH_READ_ERROR); 446caaffed8SDag-Erling Smørgrav } 447caaffed8SDag-Erling Smørgrav return (rlen); 448caaffed8SDag-Erling Smørgrav } 449f606d589SDag-Erling Smørgrav 450111e2510SDag-Erling Smørgrav /* 4519601e333SDag-Erling Smørgrav * Read a character from a connection w/ timeout 4529601e333SDag-Erling Smørgrav */ 4539601e333SDag-Erling Smørgrav ssize_t 454a1b37df2SDag-Erling Smørgrav fetch_read(conn_t *conn, char *buf, size_t len) 4559601e333SDag-Erling Smørgrav { 4565092cf05SDag-Erling Smørgrav struct timeval now, timeout, delta; 4579601e333SDag-Erling Smørgrav fd_set readfds; 4589601e333SDag-Erling Smørgrav ssize_t rlen, total; 4599601e333SDag-Erling Smørgrav int r; 460*2a7daafeSDag-Erling Smørgrav char *start; 4619601e333SDag-Erling Smørgrav 4629601e333SDag-Erling Smørgrav if (fetchTimeout) { 4639601e333SDag-Erling Smørgrav FD_ZERO(&readfds); 4649601e333SDag-Erling Smørgrav gettimeofday(&timeout, NULL); 4659601e333SDag-Erling Smørgrav timeout.tv_sec += fetchTimeout; 4669601e333SDag-Erling Smørgrav } 4679601e333SDag-Erling Smørgrav 4689601e333SDag-Erling Smørgrav total = 0; 469*2a7daafeSDag-Erling Smørgrav start = buf; 470*2a7daafeSDag-Erling Smørgrav 471*2a7daafeSDag-Erling Smørgrav if (conn->cache.len > 0) { 472*2a7daafeSDag-Erling Smørgrav /* 473*2a7daafeSDag-Erling Smørgrav * The last invocation of fetch_read was interrupted by a 474*2a7daafeSDag-Erling Smørgrav * signal after some data had been read from the socket. Copy 475*2a7daafeSDag-Erling Smørgrav * the cached data into the supplied buffer before trying to 476*2a7daafeSDag-Erling Smørgrav * read from the socket again. 477*2a7daafeSDag-Erling Smørgrav */ 478*2a7daafeSDag-Erling Smørgrav total = (conn->cache.len < len) ? conn->cache.len : len; 479*2a7daafeSDag-Erling Smørgrav memcpy(buf, conn->cache.buf, total); 480*2a7daafeSDag-Erling Smørgrav 481*2a7daafeSDag-Erling Smørgrav conn->cache.len -= total; 482*2a7daafeSDag-Erling Smørgrav conn->cache.pos += total; 483*2a7daafeSDag-Erling Smørgrav len -= total; 484*2a7daafeSDag-Erling Smørgrav buf+= total; 485*2a7daafeSDag-Erling Smørgrav } 486*2a7daafeSDag-Erling Smørgrav 4879601e333SDag-Erling Smørgrav while (len > 0) { 488caaffed8SDag-Erling Smørgrav /* 489caaffed8SDag-Erling Smørgrav * The socket is non-blocking. Instead of the canonical 490caaffed8SDag-Erling Smørgrav * select() -> read(), we do the following: 491caaffed8SDag-Erling Smørgrav * 492caaffed8SDag-Erling Smørgrav * 1) call read() or SSL_read(). 493caaffed8SDag-Erling Smørgrav * 2) if an error occurred, return -1. 494caaffed8SDag-Erling Smørgrav * 3) if we received data but we still expect more, 495caaffed8SDag-Erling Smørgrav * update our counters and loop. 496caaffed8SDag-Erling Smørgrav * 4) if read() or SSL_read() signaled EOF, return. 497caaffed8SDag-Erling Smørgrav * 5) if we did not receive any data but we're not at EOF, 498caaffed8SDag-Erling Smørgrav * call select(). 499caaffed8SDag-Erling Smørgrav * 500caaffed8SDag-Erling Smørgrav * In the SSL case, this is necessary because if we 501caaffed8SDag-Erling Smørgrav * receive a close notification, we have to call 502caaffed8SDag-Erling Smørgrav * SSL_read() one additional time after we've read 503caaffed8SDag-Erling Smørgrav * everything we received. 504caaffed8SDag-Erling Smørgrav * 505caaffed8SDag-Erling Smørgrav * In the non-SSL case, it may improve performance (very 506caaffed8SDag-Erling Smørgrav * slightly) when reading small amounts of data. 507caaffed8SDag-Erling Smørgrav */ 508caaffed8SDag-Erling Smørgrav #ifdef WITH_SSL 509caaffed8SDag-Erling Smørgrav if (conn->ssl != NULL) 510caaffed8SDag-Erling Smørgrav rlen = fetch_ssl_read(conn->ssl, buf, len); 511caaffed8SDag-Erling Smørgrav else 512caaffed8SDag-Erling Smørgrav #endif 513caaffed8SDag-Erling Smørgrav rlen = fetch_socket_read(conn->sd, buf, len); 514caaffed8SDag-Erling Smørgrav if (rlen == 0) { 515caaffed8SDag-Erling Smørgrav break; 516caaffed8SDag-Erling Smørgrav } else if (rlen > 0) { 517caaffed8SDag-Erling Smørgrav len -= rlen; 518caaffed8SDag-Erling Smørgrav buf += rlen; 519caaffed8SDag-Erling Smørgrav total += rlen; 520caaffed8SDag-Erling Smørgrav continue; 521caaffed8SDag-Erling Smørgrav } else if (rlen == FETCH_READ_ERROR) { 522*2a7daafeSDag-Erling Smørgrav if (errno == EINTR) 523*2a7daafeSDag-Erling Smørgrav fetch_cache_data(conn, start, total); 524caaffed8SDag-Erling Smørgrav return (-1); 525caaffed8SDag-Erling Smørgrav } 526caaffed8SDag-Erling Smørgrav // assert(rlen == FETCH_READ_WAIT); 5279601e333SDag-Erling Smørgrav while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) { 5289601e333SDag-Erling Smørgrav FD_SET(conn->sd, &readfds); 5299601e333SDag-Erling Smørgrav gettimeofday(&now, NULL); 5305092cf05SDag-Erling Smørgrav delta.tv_sec = timeout.tv_sec - now.tv_sec; 5315092cf05SDag-Erling Smørgrav delta.tv_usec = timeout.tv_usec - now.tv_usec; 5325092cf05SDag-Erling Smørgrav if (delta.tv_usec < 0) { 5335092cf05SDag-Erling Smørgrav delta.tv_usec += 1000000; 5345092cf05SDag-Erling Smørgrav delta.tv_sec--; 5359601e333SDag-Erling Smørgrav } 5365092cf05SDag-Erling Smørgrav if (delta.tv_sec < 0) { 537bb13d0afSDag-Erling Smørgrav errno = ETIMEDOUT; 538a1b37df2SDag-Erling Smørgrav fetch_syserr(); 539bb13d0afSDag-Erling Smørgrav return (-1); 540bb13d0afSDag-Erling Smørgrav } 5419601e333SDag-Erling Smørgrav errno = 0; 5425092cf05SDag-Erling Smørgrav r = select(conn->sd + 1, &readfds, NULL, NULL, &delta); 5439601e333SDag-Erling Smørgrav if (r == -1) { 544*2a7daafeSDag-Erling Smørgrav if (errno == EINTR) { 545*2a7daafeSDag-Erling Smørgrav if (fetchRestartCalls) 5469601e333SDag-Erling Smørgrav continue; 547*2a7daafeSDag-Erling Smørgrav /* Save anything that was read. */ 548*2a7daafeSDag-Erling Smørgrav fetch_cache_data(conn, start, total); 549*2a7daafeSDag-Erling Smørgrav } 550a1b37df2SDag-Erling Smørgrav fetch_syserr(); 5519601e333SDag-Erling Smørgrav return (-1); 5529601e333SDag-Erling Smørgrav } 5539601e333SDag-Erling Smørgrav } 5549601e333SDag-Erling Smørgrav } 5559601e333SDag-Erling Smørgrav return (total); 5569601e333SDag-Erling Smørgrav } 5579601e333SDag-Erling Smørgrav 558f606d589SDag-Erling Smørgrav 5599601e333SDag-Erling Smørgrav /* 5609601e333SDag-Erling Smørgrav * Read a line of text from a connection w/ timeout 561fc6e9e65SDag-Erling Smørgrav */ 562fc6e9e65SDag-Erling Smørgrav #define MIN_BUF_SIZE 1024 563fc6e9e65SDag-Erling Smørgrav 564fc6e9e65SDag-Erling Smørgrav int 565a1b37df2SDag-Erling Smørgrav fetch_getln(conn_t *conn) 566fc6e9e65SDag-Erling Smørgrav { 5679601e333SDag-Erling Smørgrav char *tmp; 5689601e333SDag-Erling Smørgrav size_t tmpsize; 569bb13d0afSDag-Erling Smørgrav ssize_t len; 570fc6e9e65SDag-Erling Smørgrav char c; 571fc6e9e65SDag-Erling Smørgrav 572dea29ca1SDag-Erling Smørgrav if (conn->buf == NULL) { 573dea29ca1SDag-Erling Smørgrav if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) { 574fc6e9e65SDag-Erling Smørgrav errno = ENOMEM; 575e19e6098SDag-Erling Smørgrav return (-1); 576fc6e9e65SDag-Erling Smørgrav } 577dea29ca1SDag-Erling Smørgrav conn->bufsize = MIN_BUF_SIZE; 578fc6e9e65SDag-Erling Smørgrav } 579fc6e9e65SDag-Erling Smørgrav 580dea29ca1SDag-Erling Smørgrav conn->buf[0] = '\0'; 581dea29ca1SDag-Erling Smørgrav conn->buflen = 0; 582fc6e9e65SDag-Erling Smørgrav 583fc6e9e65SDag-Erling Smørgrav do { 584a1b37df2SDag-Erling Smørgrav len = fetch_read(conn, &c, 1); 585bb13d0afSDag-Erling Smørgrav if (len == -1) 586e19e6098SDag-Erling Smørgrav return (-1); 587bb13d0afSDag-Erling Smørgrav if (len == 0) 588b68fbebdSDavid E. O'Brien break; 589dea29ca1SDag-Erling Smørgrav conn->buf[conn->buflen++] = c; 590dea29ca1SDag-Erling Smørgrav if (conn->buflen == conn->bufsize) { 591dea29ca1SDag-Erling Smørgrav tmp = conn->buf; 592dea29ca1SDag-Erling Smørgrav tmpsize = conn->bufsize * 2 + 1; 593dea29ca1SDag-Erling Smørgrav if ((tmp = realloc(tmp, tmpsize)) == NULL) { 594fc6e9e65SDag-Erling Smørgrav errno = ENOMEM; 595e19e6098SDag-Erling Smørgrav return (-1); 596fc6e9e65SDag-Erling Smørgrav } 597dea29ca1SDag-Erling Smørgrav conn->buf = tmp; 598dea29ca1SDag-Erling Smørgrav conn->bufsize = tmpsize; 599fc6e9e65SDag-Erling Smørgrav } 600fc6e9e65SDag-Erling Smørgrav } while (c != '\n'); 601fc6e9e65SDag-Erling Smørgrav 602dea29ca1SDag-Erling Smørgrav conn->buf[conn->buflen] = '\0'; 603dea29ca1SDag-Erling Smørgrav DEBUG(fprintf(stderr, "<<< %s", conn->buf)); 604e19e6098SDag-Erling Smørgrav return (0); 605fc6e9e65SDag-Erling Smørgrav } 606fc6e9e65SDag-Erling Smørgrav 607fc6e9e65SDag-Erling Smørgrav 608a1763027SDag-Erling Smørgrav /* 6099601e333SDag-Erling Smørgrav * Write to a connection w/ timeout 6109601e333SDag-Erling Smørgrav */ 6119601e333SDag-Erling Smørgrav ssize_t 612a1b37df2SDag-Erling Smørgrav fetch_write(conn_t *conn, const char *buf, size_t len) 6139601e333SDag-Erling Smørgrav { 614a4a37038SWarner Losh struct iovec iov; 615a4a37038SWarner Losh 616a4a37038SWarner Losh iov.iov_base = __DECONST(char *, buf); 617a4a37038SWarner Losh iov.iov_len = len; 618a1b37df2SDag-Erling Smørgrav return fetch_writev(conn, &iov, 1); 619a4a37038SWarner Losh } 620a4a37038SWarner Losh 621a4a37038SWarner Losh /* 622a4a37038SWarner Losh * Write a vector to a connection w/ timeout 623a4a37038SWarner Losh * Note: can modify the iovec. 624a4a37038SWarner Losh */ 625a4a37038SWarner Losh ssize_t 626a1b37df2SDag-Erling Smørgrav fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt) 627a4a37038SWarner Losh { 6285092cf05SDag-Erling Smørgrav struct timeval now, timeout, delta; 6299601e333SDag-Erling Smørgrav fd_set writefds; 6309601e333SDag-Erling Smørgrav ssize_t wlen, total; 6319601e333SDag-Erling Smørgrav int r; 6329601e333SDag-Erling Smørgrav 6339601e333SDag-Erling Smørgrav if (fetchTimeout) { 6349601e333SDag-Erling Smørgrav FD_ZERO(&writefds); 6359601e333SDag-Erling Smørgrav gettimeofday(&timeout, NULL); 6369601e333SDag-Erling Smørgrav timeout.tv_sec += fetchTimeout; 6379601e333SDag-Erling Smørgrav } 6389601e333SDag-Erling Smørgrav 639a4a37038SWarner Losh total = 0; 640a4a37038SWarner Losh while (iovcnt > 0) { 6419601e333SDag-Erling Smørgrav while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) { 6429601e333SDag-Erling Smørgrav FD_SET(conn->sd, &writefds); 6439601e333SDag-Erling Smørgrav gettimeofday(&now, NULL); 6445092cf05SDag-Erling Smørgrav delta.tv_sec = timeout.tv_sec - now.tv_sec; 6455092cf05SDag-Erling Smørgrav delta.tv_usec = timeout.tv_usec - now.tv_usec; 6465092cf05SDag-Erling Smørgrav if (delta.tv_usec < 0) { 6475092cf05SDag-Erling Smørgrav delta.tv_usec += 1000000; 6485092cf05SDag-Erling Smørgrav delta.tv_sec--; 6499601e333SDag-Erling Smørgrav } 6505092cf05SDag-Erling Smørgrav if (delta.tv_sec < 0) { 6519601e333SDag-Erling Smørgrav errno = ETIMEDOUT; 652a1b37df2SDag-Erling Smørgrav fetch_syserr(); 6539601e333SDag-Erling Smørgrav return (-1); 6549601e333SDag-Erling Smørgrav } 6559601e333SDag-Erling Smørgrav errno = 0; 6565092cf05SDag-Erling Smørgrav r = select(conn->sd + 1, NULL, &writefds, NULL, &delta); 6579601e333SDag-Erling Smørgrav if (r == -1) { 6589601e333SDag-Erling Smørgrav if (errno == EINTR && fetchRestartCalls) 6599601e333SDag-Erling Smørgrav continue; 6609601e333SDag-Erling Smørgrav return (-1); 6619601e333SDag-Erling Smørgrav } 6629601e333SDag-Erling Smørgrav } 6639601e333SDag-Erling Smørgrav errno = 0; 6643070f6cbSDag-Erling Smørgrav #ifdef WITH_SSL 6659601e333SDag-Erling Smørgrav if (conn->ssl != NULL) 666a4a37038SWarner Losh wlen = SSL_write(conn->ssl, 667a4a37038SWarner Losh iov->iov_base, iov->iov_len); 6689601e333SDag-Erling Smørgrav else 6693070f6cbSDag-Erling Smørgrav #endif 670a4a37038SWarner Losh wlen = writev(conn->sd, iov, iovcnt); 671a4a37038SWarner Losh if (wlen == 0) { 6729601e333SDag-Erling Smørgrav /* we consider a short write a failure */ 673caaffed8SDag-Erling Smørgrav /* XXX perhaps we shouldn't in the SSL case */ 674a4a37038SWarner Losh errno = EPIPE; 675a1b37df2SDag-Erling Smørgrav fetch_syserr(); 6769601e333SDag-Erling Smørgrav return (-1); 677a4a37038SWarner Losh } 6789601e333SDag-Erling Smørgrav if (wlen < 0) { 6799601e333SDag-Erling Smørgrav if (errno == EINTR && fetchRestartCalls) 6809601e333SDag-Erling Smørgrav continue; 6819601e333SDag-Erling Smørgrav return (-1); 6829601e333SDag-Erling Smørgrav } 6839601e333SDag-Erling Smørgrav total += wlen; 684a4a37038SWarner Losh while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) { 685a4a37038SWarner Losh wlen -= iov->iov_len; 686a4a37038SWarner Losh iov++; 687a4a37038SWarner Losh iovcnt--; 688a4a37038SWarner Losh } 689a4a37038SWarner Losh if (iovcnt > 0) { 690a4a37038SWarner Losh iov->iov_len -= wlen; 691a4a37038SWarner Losh iov->iov_base = __DECONST(char *, iov->iov_base) + wlen; 692a4a37038SWarner Losh } 6939601e333SDag-Erling Smørgrav } 6949601e333SDag-Erling Smørgrav return (total); 6959601e333SDag-Erling Smørgrav } 6969601e333SDag-Erling Smørgrav 697f606d589SDag-Erling Smørgrav 6989601e333SDag-Erling Smørgrav /* 6999601e333SDag-Erling Smørgrav * Write a line of text to a connection w/ timeout 700a1763027SDag-Erling Smørgrav */ 701a1763027SDag-Erling Smørgrav int 702a1b37df2SDag-Erling Smørgrav fetch_putln(conn_t *conn, const char *str, size_t len) 703a1763027SDag-Erling Smørgrav { 704a4a37038SWarner Losh struct iovec iov[2]; 7057504527eSDag-Erling Smørgrav int ret; 70666ffb8a3SDag-Erling Smørgrav 70766ffb8a3SDag-Erling Smørgrav DEBUG(fprintf(stderr, ">>> %s\n", str)); 708a4a37038SWarner Losh iov[0].iov_base = __DECONST(char *, str); 709a4a37038SWarner Losh iov[0].iov_len = len; 710a4a37038SWarner Losh iov[1].iov_base = __DECONST(char *, ENDL); 711930105c1SDag-Erling Smørgrav iov[1].iov_len = sizeof(ENDL); 7127504527eSDag-Erling Smørgrav if (len == 0) 713a1b37df2SDag-Erling Smørgrav ret = fetch_writev(conn, &iov[1], 1); 7147504527eSDag-Erling Smørgrav else 715a1b37df2SDag-Erling Smørgrav ret = fetch_writev(conn, iov, 2); 7167504527eSDag-Erling Smørgrav if (ret == -1) 717e19e6098SDag-Erling Smørgrav return (-1); 718e19e6098SDag-Erling Smørgrav return (0); 719a1763027SDag-Erling Smørgrav } 720a1763027SDag-Erling Smørgrav 721a1763027SDag-Erling Smørgrav 722dea29ca1SDag-Erling Smørgrav /* 723dea29ca1SDag-Erling Smørgrav * Close connection 724dea29ca1SDag-Erling Smørgrav */ 725dea29ca1SDag-Erling Smørgrav int 726a1b37df2SDag-Erling Smørgrav fetch_close(conn_t *conn) 727dea29ca1SDag-Erling Smørgrav { 728dea29ca1SDag-Erling Smørgrav int ret; 729dea29ca1SDag-Erling Smørgrav 730f606d589SDag-Erling Smørgrav if (--conn->ref > 0) 731f606d589SDag-Erling Smørgrav return (0); 732dea29ca1SDag-Erling Smørgrav ret = close(conn->sd); 733*2a7daafeSDag-Erling Smørgrav free(conn->cache.buf); 73455cf7be1SDag-Erling Smørgrav free(conn->buf); 735dea29ca1SDag-Erling Smørgrav free(conn); 736dea29ca1SDag-Erling Smørgrav return (ret); 737dea29ca1SDag-Erling Smørgrav } 738dea29ca1SDag-Erling Smørgrav 739dea29ca1SDag-Erling Smørgrav 740ce71b736SDag-Erling Smørgrav /*** Directory-related utility functions *************************************/ 741ce71b736SDag-Erling Smørgrav 742ce71b736SDag-Erling Smørgrav int 743a1b37df2SDag-Erling Smørgrav fetch_add_entry(struct url_ent **p, int *size, int *len, 744f573a5fcSDag-Erling Smørgrav const char *name, struct url_stat *us) 745ce71b736SDag-Erling Smørgrav { 746ce71b736SDag-Erling Smørgrav struct url_ent *tmp; 747ce71b736SDag-Erling Smørgrav 748ce71b736SDag-Erling Smørgrav if (*p == NULL) { 7495a51c23bSDag-Erling Smørgrav *size = 0; 750ce71b736SDag-Erling Smørgrav *len = 0; 751ce71b736SDag-Erling Smørgrav } 752ce71b736SDag-Erling Smørgrav 753ce71b736SDag-Erling Smørgrav if (*len >= *size - 1) { 754930105c1SDag-Erling Smørgrav tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p)); 755ce71b736SDag-Erling Smørgrav if (tmp == NULL) { 756ce71b736SDag-Erling Smørgrav errno = ENOMEM; 757a1b37df2SDag-Erling Smørgrav fetch_syserr(); 758e19e6098SDag-Erling Smørgrav return (-1); 759ce71b736SDag-Erling Smørgrav } 7605a51c23bSDag-Erling Smørgrav *size = (*size * 2 + 1); 761ce71b736SDag-Erling Smørgrav *p = tmp; 762ce71b736SDag-Erling Smørgrav } 763ce71b736SDag-Erling Smørgrav 764ce71b736SDag-Erling Smørgrav tmp = *p + *len; 7652b26f942SDag-Erling Smørgrav snprintf(tmp->name, PATH_MAX, "%s", name); 766340b079bSDag-Erling Smørgrav memcpy(&tmp->stat, us, sizeof(*us)); 767ce71b736SDag-Erling Smørgrav 768ce71b736SDag-Erling Smørgrav (*len)++; 769ce71b736SDag-Erling Smørgrav (++tmp)->name[0] = 0; 770ce71b736SDag-Erling Smørgrav 771e19e6098SDag-Erling Smørgrav return (0); 772ce71b736SDag-Erling Smørgrav } 77307350d12SDag-Erling Smørgrav 77407350d12SDag-Erling Smørgrav 77507350d12SDag-Erling Smørgrav /*** Authentication-related utility functions ********************************/ 77607350d12SDag-Erling Smørgrav 77707350d12SDag-Erling Smørgrav static const char * 778a1b37df2SDag-Erling Smørgrav fetch_read_word(FILE *f) 77907350d12SDag-Erling Smørgrav { 78007350d12SDag-Erling Smørgrav static char word[1024]; 78107350d12SDag-Erling Smørgrav 782fc2841a9SColin Percival if (fscanf(f, " %1023s ", word) != 1) 78307350d12SDag-Erling Smørgrav return (NULL); 78407350d12SDag-Erling Smørgrav return (word); 78507350d12SDag-Erling Smørgrav } 78607350d12SDag-Erling Smørgrav 78707350d12SDag-Erling Smørgrav /* 78807350d12SDag-Erling Smørgrav * Get authentication data for a URL from .netrc 78907350d12SDag-Erling Smørgrav */ 79007350d12SDag-Erling Smørgrav int 791a1b37df2SDag-Erling Smørgrav fetch_netrc_auth(struct url *url) 79207350d12SDag-Erling Smørgrav { 79307350d12SDag-Erling Smørgrav char fn[PATH_MAX]; 79407350d12SDag-Erling Smørgrav const char *word; 79507350d12SDag-Erling Smørgrav char *p; 79607350d12SDag-Erling Smørgrav FILE *f; 79707350d12SDag-Erling Smørgrav 79807350d12SDag-Erling Smørgrav if ((p = getenv("NETRC")) != NULL) { 799930105c1SDag-Erling Smørgrav if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) { 800a1b37df2SDag-Erling Smørgrav fetch_info("$NETRC specifies a file name " 80107350d12SDag-Erling Smørgrav "longer than PATH_MAX"); 80207350d12SDag-Erling Smørgrav return (-1); 80307350d12SDag-Erling Smørgrav } 80407350d12SDag-Erling Smørgrav } else { 80507350d12SDag-Erling Smørgrav if ((p = getenv("HOME")) != NULL) { 80607350d12SDag-Erling Smørgrav struct passwd *pwd; 80707350d12SDag-Erling Smørgrav 80807350d12SDag-Erling Smørgrav if ((pwd = getpwuid(getuid())) == NULL || 80907350d12SDag-Erling Smørgrav (p = pwd->pw_dir) == NULL) 81007350d12SDag-Erling Smørgrav return (-1); 81107350d12SDag-Erling Smørgrav } 812930105c1SDag-Erling Smørgrav if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn)) 81307350d12SDag-Erling Smørgrav return (-1); 81407350d12SDag-Erling Smørgrav } 81507350d12SDag-Erling Smørgrav 81607350d12SDag-Erling Smørgrav if ((f = fopen(fn, "r")) == NULL) 81707350d12SDag-Erling Smørgrav return (-1); 818a1b37df2SDag-Erling Smørgrav while ((word = fetch_read_word(f)) != NULL) { 81907350d12SDag-Erling Smørgrav if (strcmp(word, "default") == 0) { 820a1b37df2SDag-Erling Smørgrav DEBUG(fetch_info("Using default .netrc settings")); 82107350d12SDag-Erling Smørgrav break; 82207350d12SDag-Erling Smørgrav } 82307350d12SDag-Erling Smørgrav if (strcmp(word, "machine") == 0 && 824a1b37df2SDag-Erling Smørgrav (word = fetch_read_word(f)) != NULL && 82507350d12SDag-Erling Smørgrav strcasecmp(word, url->host) == 0) { 826a1b37df2SDag-Erling Smørgrav DEBUG(fetch_info("Using .netrc settings for %s", word)); 82707350d12SDag-Erling Smørgrav break; 82807350d12SDag-Erling Smørgrav } 82907350d12SDag-Erling Smørgrav } 83007350d12SDag-Erling Smørgrav if (word == NULL) 83107350d12SDag-Erling Smørgrav goto ferr; 832a1b37df2SDag-Erling Smørgrav while ((word = fetch_read_word(f)) != NULL) { 83307350d12SDag-Erling Smørgrav if (strcmp(word, "login") == 0) { 834a1b37df2SDag-Erling Smørgrav if ((word = fetch_read_word(f)) == NULL) 83507350d12SDag-Erling Smørgrav goto ferr; 836930105c1SDag-Erling Smørgrav if (snprintf(url->user, sizeof(url->user), 8379015b953SJohn W. De Boskey "%s", word) > (int)sizeof(url->user)) { 838a1b37df2SDag-Erling Smørgrav fetch_info("login name in .netrc is too long"); 83907350d12SDag-Erling Smørgrav url->user[0] = '\0'; 84007350d12SDag-Erling Smørgrav } 84107350d12SDag-Erling Smørgrav } else if (strcmp(word, "password") == 0) { 842a1b37df2SDag-Erling Smørgrav if ((word = fetch_read_word(f)) == NULL) 84307350d12SDag-Erling Smørgrav goto ferr; 844930105c1SDag-Erling Smørgrav if (snprintf(url->pwd, sizeof(url->pwd), 8459015b953SJohn W. De Boskey "%s", word) > (int)sizeof(url->pwd)) { 846a1b37df2SDag-Erling Smørgrav fetch_info("password in .netrc is too long"); 84707350d12SDag-Erling Smørgrav url->pwd[0] = '\0'; 84807350d12SDag-Erling Smørgrav } 84907350d12SDag-Erling Smørgrav } else if (strcmp(word, "account") == 0) { 850a1b37df2SDag-Erling Smørgrav if ((word = fetch_read_word(f)) == NULL) 85107350d12SDag-Erling Smørgrav goto ferr; 85207350d12SDag-Erling Smørgrav /* XXX not supported! */ 85307350d12SDag-Erling Smørgrav } else { 85407350d12SDag-Erling Smørgrav break; 85507350d12SDag-Erling Smørgrav } 85607350d12SDag-Erling Smørgrav } 85707350d12SDag-Erling Smørgrav fclose(f); 85807350d12SDag-Erling Smørgrav return (0); 85907350d12SDag-Erling Smørgrav ferr: 86007350d12SDag-Erling Smørgrav fclose(f); 86107350d12SDag-Erling Smørgrav return (-1); 86207350d12SDag-Erling Smørgrav } 86362a2681cSDag-Erling Smørgrav 86462a2681cSDag-Erling Smørgrav /* 86562a2681cSDag-Erling Smørgrav * The no_proxy environment variable specifies a set of domains for 86662a2681cSDag-Erling Smørgrav * which the proxy should not be consulted; the contents is a comma-, 86762a2681cSDag-Erling Smørgrav * or space-separated list of domain names. A single asterisk will 86862a2681cSDag-Erling Smørgrav * override all proxy variables and no transactions will be proxied 86962a2681cSDag-Erling Smørgrav * (for compatability with lynx and curl, see the discussion at 87062a2681cSDag-Erling Smørgrav * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>). 87162a2681cSDag-Erling Smørgrav */ 87262a2681cSDag-Erling Smørgrav int 87362a2681cSDag-Erling Smørgrav fetch_no_proxy_match(const char *host) 87462a2681cSDag-Erling Smørgrav { 87562a2681cSDag-Erling Smørgrav const char *no_proxy, *p, *q; 87662a2681cSDag-Erling Smørgrav size_t h_len, d_len; 87762a2681cSDag-Erling Smørgrav 87862a2681cSDag-Erling Smørgrav if ((no_proxy = getenv("NO_PROXY")) == NULL && 87962a2681cSDag-Erling Smørgrav (no_proxy = getenv("no_proxy")) == NULL) 88062a2681cSDag-Erling Smørgrav return (0); 88162a2681cSDag-Erling Smørgrav 88262a2681cSDag-Erling Smørgrav /* asterisk matches any hostname */ 88362a2681cSDag-Erling Smørgrav if (strcmp(no_proxy, "*") == 0) 88462a2681cSDag-Erling Smørgrav return (1); 88562a2681cSDag-Erling Smørgrav 88662a2681cSDag-Erling Smørgrav h_len = strlen(host); 88762a2681cSDag-Erling Smørgrav p = no_proxy; 88862a2681cSDag-Erling Smørgrav do { 88962a2681cSDag-Erling Smørgrav /* position p at the beginning of a domain suffix */ 890facd9827SDag-Erling Smørgrav while (*p == ',' || isspace((unsigned char)*p)) 89162a2681cSDag-Erling Smørgrav p++; 89262a2681cSDag-Erling Smørgrav 89362a2681cSDag-Erling Smørgrav /* position q at the first separator character */ 89462a2681cSDag-Erling Smørgrav for (q = p; *q; ++q) 895facd9827SDag-Erling Smørgrav if (*q == ',' || isspace((unsigned char)*q)) 89662a2681cSDag-Erling Smørgrav break; 89762a2681cSDag-Erling Smørgrav 89862a2681cSDag-Erling Smørgrav d_len = q - p; 899c0d2581bSFabien Thomas if (d_len > 0 && h_len >= d_len && 90062a2681cSDag-Erling Smørgrav strncasecmp(host + h_len - d_len, 90162a2681cSDag-Erling Smørgrav p, d_len) == 0) { 90262a2681cSDag-Erling Smørgrav /* domain name matches */ 90362a2681cSDag-Erling Smørgrav return (1); 90462a2681cSDag-Erling Smørgrav } 90562a2681cSDag-Erling Smørgrav 90662a2681cSDag-Erling Smørgrav p = q + 1; 90762a2681cSDag-Erling Smørgrav } while (*q); 90862a2681cSDag-Erling Smørgrav 90962a2681cSDag-Erling Smørgrav return (0); 91062a2681cSDag-Erling Smørgrav } 911