common.c revision 226537
1255736Sdavidch/*- 2255736Sdavidch * Copyright (c) 1998-2011 Dag-Erling Sm��rgrav 3255736Sdavidch * All rights reserved. 4255736Sdavidch * 5255736Sdavidch * Redistribution and use in source and binary forms, with or without 6255736Sdavidch * modification, are permitted provided that the following conditions 7255736Sdavidch * are met: 8255736Sdavidch * 1. Redistributions of source code must retain the above copyright 9255736Sdavidch * notice, this list of conditions and the following disclaimer 10255736Sdavidch * in this position and unchanged. 11255736Sdavidch * 2. Redistributions in binary form must reproduce the above copyright 12255736Sdavidch * notice, this list of conditions and the following disclaimer in the 13255736Sdavidch * documentation and/or other materials provided with the distribution. 14255736Sdavidch * 3. The name of the author may not be used to endorse or promote products 15255736Sdavidch * derived from this software without specific prior written permission 16255736Sdavidch * 17255736Sdavidch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18255736Sdavidch * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19255736Sdavidch * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20255736Sdavidch * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21255736Sdavidch * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22255736Sdavidch * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23255736Sdavidch * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24255736Sdavidch * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25255736Sdavidch * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26255736Sdavidch * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27255736Sdavidch */ 28255736Sdavidch 29255736Sdavidch#include <sys/cdefs.h> 30255736Sdavidch__FBSDID("$FreeBSD: head/lib/libfetch/common.c 226537 2011-10-19 11:43:51Z des $"); 31255736Sdavidch 32255736Sdavidch#include <sys/param.h> 33255736Sdavidch#include <sys/socket.h> 34255736Sdavidch#include <sys/time.h> 35255736Sdavidch#include <sys/uio.h> 36255736Sdavidch 37260415Sedavis#include <netinet/in.h> 38255736Sdavidch 39255736Sdavidch#include <ctype.h> 40255736Sdavidch#include <errno.h> 41255736Sdavidch#include <fcntl.h> 42255736Sdavidch#include <netdb.h> 43255736Sdavidch#include <pwd.h> 44255736Sdavidch#include <stdarg.h> 45255736Sdavidch#include <stdlib.h> 46255736Sdavidch#include <stdio.h> 47255736Sdavidch#include <string.h> 48255736Sdavidch#include <unistd.h> 49255736Sdavidch 50255736Sdavidch#include "fetch.h" 51255736Sdavidch#include "common.h" 52255736Sdavidch 53255736Sdavidch 54255736Sdavidch/*** Local data **************************************************************/ 55255736Sdavidch 56255736Sdavidch/* 57255736Sdavidch * Error messages for resolver errors 58255736Sdavidch */ 59255736Sdavidchstatic struct fetcherr netdb_errlist[] = { 60255736Sdavidch#ifdef EAI_NODATA 61255736Sdavidch { EAI_NODATA, FETCH_RESOLV, "Host not found" }, 62255736Sdavidch#endif 63255736Sdavidch { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, 64255736Sdavidch { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, 65255736Sdavidch { EAI_NONAME, FETCH_RESOLV, "No address record" }, 66255736Sdavidch { -1, FETCH_UNKNOWN, "Unknown resolver error" } 67255736Sdavidch}; 68255736Sdavidch 69255736Sdavidch/* End-of-Line */ 70255736Sdavidchstatic const char ENDL[2] = "\r\n"; 71255736Sdavidch 72255736Sdavidch 73255736Sdavidch/*** Error-reporting functions ***********************************************/ 74255736Sdavidch 75255736Sdavidch/* 76255736Sdavidch * Map error code to string 77255736Sdavidch */ 78255736Sdavidchstatic struct fetcherr * 79255736Sdavidchfetch_finderr(struct fetcherr *p, int e) 80255736Sdavidch{ 81255736Sdavidch while (p->num != -1 && p->num != e) 82255736Sdavidch p++; 83255736Sdavidch return (p); 84255736Sdavidch} 85255736Sdavidch 86255736Sdavidch/* 87255736Sdavidch * Set error code 88255736Sdavidch */ 89255736Sdavidchvoid 90255736Sdavidchfetch_seterr(struct fetcherr *p, int e) 91255736Sdavidch{ 92255736Sdavidch p = fetch_finderr(p, e); 93255736Sdavidch fetchLastErrCode = p->cat; 94255736Sdavidch snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); 95255736Sdavidch} 96255736Sdavidch 97255736Sdavidch/* 98255736Sdavidch * Set error code according to errno 99255736Sdavidch */ 100255736Sdavidchvoid 101255736Sdavidchfetch_syserr(void) 102255736Sdavidch{ 103255736Sdavidch switch (errno) { 104255736Sdavidch case 0: 105255736Sdavidch fetchLastErrCode = FETCH_OK; 106255736Sdavidch break; 107255736Sdavidch case EPERM: 108255736Sdavidch case EACCES: 109255736Sdavidch case EROFS: 110255736Sdavidch case EAUTH: 111255736Sdavidch case ENEEDAUTH: 112255736Sdavidch fetchLastErrCode = FETCH_AUTH; 113255736Sdavidch break; 114255736Sdavidch case ENOENT: 115255736Sdavidch case EISDIR: /* XXX */ 116255736Sdavidch fetchLastErrCode = FETCH_UNAVAIL; 117255736Sdavidch break; 118255736Sdavidch case ENOMEM: 119255736Sdavidch fetchLastErrCode = FETCH_MEMORY; 120255736Sdavidch break; 121255736Sdavidch case EBUSY: 122255736Sdavidch case EAGAIN: 123255736Sdavidch fetchLastErrCode = FETCH_TEMP; 124255736Sdavidch break; 125255736Sdavidch case EEXIST: 126255736Sdavidch fetchLastErrCode = FETCH_EXISTS; 127255736Sdavidch break; 128255736Sdavidch case ENOSPC: 129255736Sdavidch fetchLastErrCode = FETCH_FULL; 130255736Sdavidch break; 131255736Sdavidch case EADDRINUSE: 132255736Sdavidch case EADDRNOTAVAIL: 133255736Sdavidch case ENETDOWN: 134255736Sdavidch case ENETUNREACH: 135255736Sdavidch case ENETRESET: 136255736Sdavidch case EHOSTUNREACH: 137255736Sdavidch fetchLastErrCode = FETCH_NETWORK; 138255736Sdavidch break; 139255736Sdavidch case ECONNABORTED: 140255736Sdavidch case ECONNRESET: 141255736Sdavidch fetchLastErrCode = FETCH_ABORT; 142255736Sdavidch break; 143255736Sdavidch case ETIMEDOUT: 144255736Sdavidch fetchLastErrCode = FETCH_TIMEOUT; 145255736Sdavidch break; 146255736Sdavidch case ECONNREFUSED: 147255736Sdavidch case EHOSTDOWN: 148255736Sdavidch fetchLastErrCode = FETCH_DOWN; 149255736Sdavidch break; 150255736Sdavidchdefault: 151255736Sdavidch fetchLastErrCode = FETCH_UNKNOWN; 152255736Sdavidch } 153255736Sdavidch snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); 154255736Sdavidch} 155255736Sdavidch 156255736Sdavidch 157255736Sdavidch/* 158255736Sdavidch * Emit status message 159255736Sdavidch */ 160255736Sdavidchvoid 161255736Sdavidchfetch_info(const char *fmt, ...) 162255736Sdavidch{ 163255736Sdavidch va_list ap; 164255736Sdavidch 165255736Sdavidch va_start(ap, fmt); 166255736Sdavidch vfprintf(stderr, fmt, ap); 167255736Sdavidch va_end(ap); 168255736Sdavidch fputc('\n', stderr); 169255736Sdavidch} 170255736Sdavidch 171255736Sdavidch 172255736Sdavidch/*** Network-related utility functions ***************************************/ 173255736Sdavidch 174255736Sdavidch/* 175255736Sdavidch * Return the default port for a scheme 176255736Sdavidch */ 177255736Sdavidchint 178255736Sdavidchfetch_default_port(const char *scheme) 179255736Sdavidch{ 180255736Sdavidch struct servent *se; 181255736Sdavidch 182255736Sdavidch if ((se = getservbyname(scheme, "tcp")) != NULL) 183255736Sdavidch return (ntohs(se->s_port)); 184255736Sdavidch if (strcasecmp(scheme, SCHEME_FTP) == 0) 185255736Sdavidch return (FTP_DEFAULT_PORT); 186255736Sdavidch if (strcasecmp(scheme, SCHEME_HTTP) == 0) 187255736Sdavidch return (HTTP_DEFAULT_PORT); 188255736Sdavidch return (0); 189255736Sdavidch} 190255736Sdavidch 191255736Sdavidch/* 192255736Sdavidch * Return the default proxy port for a scheme 193255736Sdavidch */ 194255736Sdavidchint 195255736Sdavidchfetch_default_proxy_port(const char *scheme) 196255736Sdavidch{ 197255736Sdavidch if (strcasecmp(scheme, SCHEME_FTP) == 0) 198255736Sdavidch return (FTP_DEFAULT_PROXY_PORT); 199255736Sdavidch if (strcasecmp(scheme, SCHEME_HTTP) == 0) 200255736Sdavidch return (HTTP_DEFAULT_PROXY_PORT); 201255736Sdavidch return (0); 202255736Sdavidch} 203255736Sdavidch 204255736Sdavidch 205255736Sdavidch/* 206255736Sdavidch * Create a connection for an existing descriptor. 207255736Sdavidch */ 208255736Sdavidchconn_t * 209255736Sdavidchfetch_reopen(int sd) 210255736Sdavidch{ 211255736Sdavidch conn_t *conn; 212255736Sdavidch 213255736Sdavidch /* allocate and fill connection structure */ 214255736Sdavidch if ((conn = calloc(1, sizeof(*conn))) == NULL) 215255736Sdavidch return (NULL); 216255736Sdavidch fcntl(sd, F_SETFD, FD_CLOEXEC); 217255736Sdavidch conn->sd = sd; 218255736Sdavidch ++conn->ref; 219255736Sdavidch return (conn); 220255736Sdavidch} 221255736Sdavidch 222255736Sdavidch 223255736Sdavidch/* 224255736Sdavidch * Bump a connection's reference count. 225255736Sdavidch */ 226255736Sdavidchconn_t * 227255736Sdavidchfetch_ref(conn_t *conn) 228255736Sdavidch{ 229255736Sdavidch 230255736Sdavidch ++conn->ref; 231255736Sdavidch return (conn); 232255736Sdavidch} 233255736Sdavidch 234255736Sdavidch 235255736Sdavidch/* 236255736Sdavidch * Bind a socket to a specific local address 237255736Sdavidch */ 238255736Sdavidchint 239255736Sdavidchfetch_bind(int sd, int af, const char *addr) 240255736Sdavidch{ 241255736Sdavidch struct addrinfo hints, *res, *res0; 242255736Sdavidch int err; 243255736Sdavidch 244255736Sdavidch memset(&hints, 0, sizeof(hints)); 245255736Sdavidch hints.ai_family = af; 246255736Sdavidch hints.ai_socktype = SOCK_STREAM; 247255736Sdavidch hints.ai_protocol = 0; 248255736Sdavidch if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0) 249255736Sdavidch return (-1); 250255736Sdavidch for (res = res0; res; res = res->ai_next) 251255736Sdavidch if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) 252255736Sdavidch return (0); 253255736Sdavidch return (-1); 254255736Sdavidch} 255255736Sdavidch 256255736Sdavidch 257255736Sdavidch/* 258255736Sdavidch * Establish a TCP connection to the specified port on the specified host. 259255736Sdavidch */ 260255736Sdavidchconn_t * 261255736Sdavidchfetch_connect(const char *host, int port, int af, int verbose) 262255736Sdavidch{ 263255736Sdavidch conn_t *conn; 264255736Sdavidch char pbuf[10]; 265255736Sdavidch const char *bindaddr; 266255736Sdavidch struct addrinfo hints, *res, *res0; 267255736Sdavidch int sd, err; 268255736Sdavidch 269255736Sdavidch DEBUG(fprintf(stderr, "---> %s:%d\n", host, port)); 270255736Sdavidch 271255736Sdavidch if (verbose) 272255736Sdavidch fetch_info("looking up %s", host); 273255736Sdavidch 274255736Sdavidch /* look up host name and set up socket address structure */ 275255736Sdavidch snprintf(pbuf, sizeof(pbuf), "%d", port); 276255736Sdavidch memset(&hints, 0, sizeof(hints)); 277255736Sdavidch hints.ai_family = af; 278255736Sdavidch hints.ai_socktype = SOCK_STREAM; 279255736Sdavidch hints.ai_protocol = 0; 280255736Sdavidch if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) { 281255736Sdavidch netdb_seterr(err); 282255736Sdavidch return (NULL); 283255736Sdavidch } 284255736Sdavidch bindaddr = getenv("FETCH_BIND_ADDRESS"); 285255736Sdavidch 286255736Sdavidch if (verbose) 287255736Sdavidch fetch_info("connecting to %s:%d", host, port); 288255736Sdavidch 289255736Sdavidch /* try to connect */ 290255736Sdavidch for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) { 291255736Sdavidch if ((sd = socket(res->ai_family, res->ai_socktype, 292255736Sdavidch res->ai_protocol)) == -1) 293255736Sdavidch continue; 294255736Sdavidch if (bindaddr != NULL && *bindaddr != '\0' && 295255736Sdavidch fetch_bind(sd, res->ai_family, bindaddr) != 0) { 296255736Sdavidch fetch_info("failed to bind to '%s'", bindaddr); 297255736Sdavidch close(sd); 298255736Sdavidch continue; 299255736Sdavidch } 300255736Sdavidch if (connect(sd, res->ai_addr, res->ai_addrlen) == 0 && 301255736Sdavidch fcntl(sd, F_SETFL, O_NONBLOCK) == 0) 302255736Sdavidch break; 303255736Sdavidch close(sd); 304255736Sdavidch } 305255736Sdavidch freeaddrinfo(res0); 306258187Sedavis if (sd == -1) { 307258187Sedavis fetch_syserr(); 308258187Sedavis return (NULL); 309258187Sedavis } 310255736Sdavidch 311255736Sdavidch if ((conn = fetch_reopen(sd)) == NULL) { 312255736Sdavidch fetch_syserr(); 313255736Sdavidch close(sd); 314258187Sedavis } 315258187Sedavis return (conn); 316255736Sdavidch} 317255736Sdavidch 318255736Sdavidch 319255736Sdavidch/* 320258187Sedavis * Enable SSL on a connection. 321258187Sedavis */ 322255736Sdavidchint 323255736Sdavidchfetch_ssl(conn_t *conn, int verbose) 324258187Sedavis{ 325255736Sdavidch#ifdef WITH_SSL 326258187Sedavis int ret, ssl_err; 327258187Sedavis 328255736Sdavidch /* Init the SSL library and context */ 329255736Sdavidch if (!SSL_library_init()){ 330258187Sedavis fprintf(stderr, "SSL library init failed\n"); 331255736Sdavidch return (-1); 332258187Sedavis } 333258187Sedavis 334255736Sdavidch SSL_load_error_strings(); 335255736Sdavidch 336258187Sedavis conn->ssl_meth = SSLv23_client_method(); 337255736Sdavidch conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth); 338258187Sedavis SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY); 339258187Sedavis 340255736Sdavidch conn->ssl = SSL_new(conn->ssl_ctx); 341255736Sdavidch if (conn->ssl == NULL){ 342258187Sedavis fprintf(stderr, "SSL context creation failed\n"); 343255736Sdavidch return (-1); 344258187Sedavis } 345258187Sedavis SSL_set_fd(conn->ssl, conn->sd); 346255736Sdavidch while ((ret = SSL_connect(conn->ssl)) == -1) { 347255736Sdavidch ssl_err = SSL_get_error(conn->ssl, ret); 348258187Sedavis if (ssl_err != SSL_ERROR_WANT_READ && 349255736Sdavidch ssl_err != SSL_ERROR_WANT_WRITE) { 350258187Sedavis ERR_print_errors_fp(stderr); 351258187Sedavis return (-1); 352255736Sdavidch } 353255736Sdavidch } 354255736Sdavidch 355255736Sdavidch if (verbose) { 356258187Sedavis X509_NAME *name; 357258187Sedavis char *str; 358255736Sdavidch 359255736Sdavidch fprintf(stderr, "SSL connection established using %s\n", 360255736Sdavidch SSL_get_cipher(conn->ssl)); 361255736Sdavidch conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); 362258187Sedavis name = X509_get_subject_name(conn->ssl_cert); 363258187Sedavis str = X509_NAME_oneline(name, 0, 0); 364255736Sdavidch printf("Certificate subject: %s\n", str); 365255736Sdavidch free(str); 366255736Sdavidch name = X509_get_issuer_name(conn->ssl_cert); 367255736Sdavidch str = X509_NAME_oneline(name, 0, 0); 368258187Sedavis printf("Certificate issuer: %s\n", str); 369258187Sedavis free(str); 370255736Sdavidch } 371255736Sdavidch 372255736Sdavidch return (0); 373255736Sdavidch#else 374255736Sdavidch (void)conn; 375255736Sdavidch (void)verbose; 376255736Sdavidch fprintf(stderr, "SSL support disabled\n"); 377255736Sdavidch return (-1); 378255736Sdavidch#endif 379255736Sdavidch} 380255736Sdavidch 381255736Sdavidch#define FETCH_READ_WAIT -2 382255736Sdavidch#define FETCH_READ_ERROR -1 383255736Sdavidch#define FETCH_READ_DONE 0 384255736Sdavidch 385255736Sdavidch#ifdef WITH_SSL 386255736Sdavidchstatic ssize_t 387255736Sdavidchfetch_ssl_read(SSL *ssl, char *buf, size_t len) 388255736Sdavidch{ 389255736Sdavidch ssize_t rlen; 390255736Sdavidch int ssl_err; 391255736Sdavidch 392255736Sdavidch rlen = SSL_read(ssl, buf, len); 393255736Sdavidch if (rlen < 0) { 394255736Sdavidch ssl_err = SSL_get_error(ssl, rlen); 395255736Sdavidch if (ssl_err == SSL_ERROR_WANT_READ || 396255736Sdavidch ssl_err == SSL_ERROR_WANT_WRITE) { 397255736Sdavidch return (FETCH_READ_WAIT); 398255736Sdavidch } else { 399255736Sdavidch ERR_print_errors_fp(stderr); 400255736Sdavidch return (FETCH_READ_ERROR); 401255736Sdavidch } 402255736Sdavidch } 403255736Sdavidch return (rlen); 404255736Sdavidch} 405255736Sdavidch#endif 406255736Sdavidch 407255736Sdavidchstatic ssize_t 408255736Sdavidchfetch_socket_read(int sd, char *buf, size_t len) 409255736Sdavidch{ 410255736Sdavidch ssize_t rlen; 411255736Sdavidch 412255736Sdavidch rlen = read(sd, buf, len); 413255736Sdavidch if (rlen < 0) { 414255736Sdavidch if (errno == EAGAIN || (errno == EINTR && fetchRestartCalls)) 415255736Sdavidch return (FETCH_READ_WAIT); 416255736Sdavidch else 417255736Sdavidch return (FETCH_READ_ERROR); 418255736Sdavidch } 419255736Sdavidch return (rlen); 420255736Sdavidch} 421255736Sdavidch 422255736Sdavidch/* 423255736Sdavidch * Read a character from a connection w/ timeout 424255736Sdavidch */ 425255736Sdavidchssize_t 426255736Sdavidchfetch_read(conn_t *conn, char *buf, size_t len) 427255736Sdavidch{ 428255736Sdavidch struct timeval now, timeout, delta; 429255736Sdavidch fd_set readfds; 430255736Sdavidch ssize_t rlen, total; 431255736Sdavidch int r; 432255736Sdavidch 433255736Sdavidch if (fetchTimeout) { 434255736Sdavidch FD_ZERO(&readfds); 435255736Sdavidch gettimeofday(&timeout, NULL); 436255736Sdavidch timeout.tv_sec += fetchTimeout; 437255736Sdavidch } 438255736Sdavidch 439255736Sdavidch total = 0; 440255736Sdavidch while (len > 0) { 441255736Sdavidch /* 442255736Sdavidch * The socket is non-blocking. Instead of the canonical 443255736Sdavidch * select() -> read(), we do the following: 444255736Sdavidch * 445255736Sdavidch * 1) call read() or SSL_read(). 446255736Sdavidch * 2) if an error occurred, return -1. 447255736Sdavidch * 3) if we received data but we still expect more, 448255736Sdavidch * update our counters and loop. 449255736Sdavidch * 4) if read() or SSL_read() signaled EOF, return. 450255736Sdavidch * 5) if we did not receive any data but we're not at EOF, 451255736Sdavidch * call select(). 452255736Sdavidch * 453255736Sdavidch * In the SSL case, this is necessary because if we 454255736Sdavidch * receive a close notification, we have to call 455255736Sdavidch * SSL_read() one additional time after we've read 456255736Sdavidch * everything we received. 457255736Sdavidch * 458255736Sdavidch * In the non-SSL case, it may improve performance (very 459255736Sdavidch * slightly) when reading small amounts of data. 460255736Sdavidch */ 461255736Sdavidch#ifdef WITH_SSL 462255736Sdavidch if (conn->ssl != NULL) 463255736Sdavidch rlen = fetch_ssl_read(conn->ssl, buf, len); 464255736Sdavidch else 465255736Sdavidch#endif 466255736Sdavidch rlen = fetch_socket_read(conn->sd, buf, len); 467255736Sdavidch if (rlen == 0) { 468255736Sdavidch break; 469255736Sdavidch } else if (rlen > 0) { 470255736Sdavidch len -= rlen; 471255736Sdavidch buf += rlen; 472255736Sdavidch total += rlen; 473255736Sdavidch continue; 474255736Sdavidch } else if (rlen == FETCH_READ_ERROR) { 475255736Sdavidch return (-1); 476255736Sdavidch } 477255736Sdavidch // assert(rlen == FETCH_READ_WAIT); 478255736Sdavidch while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) { 479255736Sdavidch FD_SET(conn->sd, &readfds); 480255736Sdavidch gettimeofday(&now, NULL); 481255736Sdavidch delta.tv_sec = timeout.tv_sec - now.tv_sec; 482255736Sdavidch delta.tv_usec = timeout.tv_usec - now.tv_usec; 483255736Sdavidch if (delta.tv_usec < 0) { 484255736Sdavidch delta.tv_usec += 1000000; 485255736Sdavidch delta.tv_sec--; 486255736Sdavidch } 487255736Sdavidch if (delta.tv_sec < 0) { 488255736Sdavidch errno = ETIMEDOUT; 489255736Sdavidch fetch_syserr(); 490255736Sdavidch return (-1); 491255736Sdavidch } 492255736Sdavidch errno = 0; 493255736Sdavidch r = select(conn->sd + 1, &readfds, NULL, NULL, &delta); 494255736Sdavidch if (r == -1) { 495255736Sdavidch if (errno == EINTR && fetchRestartCalls) 496255736Sdavidch continue; 497255736Sdavidch fetch_syserr(); 498255736Sdavidch return (-1); 499255736Sdavidch } 500255736Sdavidch } 501255736Sdavidch } 502255736Sdavidch return (total); 503255736Sdavidch} 504255736Sdavidch 505255736Sdavidch 506255736Sdavidch/* 507255736Sdavidch * Read a line of text from a connection w/ timeout 508255736Sdavidch */ 509255736Sdavidch#define MIN_BUF_SIZE 1024 510255736Sdavidch 511255736Sdavidchint 512255736Sdavidchfetch_getln(conn_t *conn) 513255736Sdavidch{ 514255736Sdavidch char *tmp; 515255736Sdavidch size_t tmpsize; 516255736Sdavidch ssize_t len; 517255736Sdavidch char c; 518255736Sdavidch 519255736Sdavidch if (conn->buf == NULL) { 520255736Sdavidch if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) { 521255736Sdavidch errno = ENOMEM; 522255736Sdavidch return (-1); 523255736Sdavidch } 524255736Sdavidch conn->bufsize = MIN_BUF_SIZE; 525255736Sdavidch } 526255736Sdavidch 527255736Sdavidch conn->buf[0] = '\0'; 528255736Sdavidch conn->buflen = 0; 529255736Sdavidch 530255736Sdavidch do { 531255736Sdavidch len = fetch_read(conn, &c, 1); 532255736Sdavidch if (len == -1) 533255736Sdavidch return (-1); 534255736Sdavidch if (len == 0) 535255736Sdavidch break; 536255736Sdavidch conn->buf[conn->buflen++] = c; 537255736Sdavidch if (conn->buflen == conn->bufsize) { 538255736Sdavidch tmp = conn->buf; 539255736Sdavidch tmpsize = conn->bufsize * 2 + 1; 540255736Sdavidch if ((tmp = realloc(tmp, tmpsize)) == NULL) { 541255736Sdavidch errno = ENOMEM; 542255736Sdavidch return (-1); 543255736Sdavidch } 544255736Sdavidch conn->buf = tmp; 545255736Sdavidch conn->bufsize = tmpsize; 546255736Sdavidch } 547255736Sdavidch } while (c != '\n'); 548255736Sdavidch 549255736Sdavidch conn->buf[conn->buflen] = '\0'; 550255736Sdavidch DEBUG(fprintf(stderr, "<<< %s", conn->buf)); 551255736Sdavidch return (0); 552255736Sdavidch} 553255736Sdavidch 554255736Sdavidch 555255736Sdavidch/* 556255736Sdavidch * Write to a connection w/ timeout 557255736Sdavidch */ 558255736Sdavidchssize_t 559255736Sdavidchfetch_write(conn_t *conn, const char *buf, size_t len) 560255736Sdavidch{ 561255736Sdavidch struct iovec iov; 562255736Sdavidch 563255736Sdavidch iov.iov_base = __DECONST(char *, buf); 564255736Sdavidch iov.iov_len = len; 565255736Sdavidch return fetch_writev(conn, &iov, 1); 566255736Sdavidch} 567255736Sdavidch 568255736Sdavidch/* 569255736Sdavidch * Write a vector to a connection w/ timeout 570255736Sdavidch * Note: can modify the iovec. 571255736Sdavidch */ 572255736Sdavidchssize_t 573255736Sdavidchfetch_writev(conn_t *conn, struct iovec *iov, int iovcnt) 574255736Sdavidch{ 575255736Sdavidch struct timeval now, timeout, delta; 576255736Sdavidch fd_set writefds; 577255736Sdavidch ssize_t wlen, total; 578255736Sdavidch int r; 579255736Sdavidch 580255736Sdavidch if (fetchTimeout) { 581255736Sdavidch FD_ZERO(&writefds); 582255736Sdavidch gettimeofday(&timeout, NULL); 583255736Sdavidch timeout.tv_sec += fetchTimeout; 584255736Sdavidch } 585255736Sdavidch 586255736Sdavidch total = 0; 587255736Sdavidch while (iovcnt > 0) { 588255736Sdavidch while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) { 589255736Sdavidch FD_SET(conn->sd, &writefds); 590255736Sdavidch gettimeofday(&now, NULL); 591255736Sdavidch delta.tv_sec = timeout.tv_sec - now.tv_sec; 592255736Sdavidch delta.tv_usec = timeout.tv_usec - now.tv_usec; 593255736Sdavidch if (delta.tv_usec < 0) { 594255736Sdavidch delta.tv_usec += 1000000; 595255736Sdavidch delta.tv_sec--; 596255736Sdavidch } 597255736Sdavidch if (delta.tv_sec < 0) { 598255736Sdavidch errno = ETIMEDOUT; 599255736Sdavidch fetch_syserr(); 600255736Sdavidch return (-1); 601255736Sdavidch } 602255736Sdavidch errno = 0; 603255736Sdavidch r = select(conn->sd + 1, NULL, &writefds, NULL, &delta); 604255736Sdavidch if (r == -1) { 605255736Sdavidch if (errno == EINTR && fetchRestartCalls) 606255736Sdavidch continue; 607255736Sdavidch return (-1); 608255736Sdavidch } 609255736Sdavidch } 610255736Sdavidch errno = 0; 611255736Sdavidch#ifdef WITH_SSL 612255736Sdavidch if (conn->ssl != NULL) 613255736Sdavidch wlen = SSL_write(conn->ssl, 614255736Sdavidch iov->iov_base, iov->iov_len); 615255736Sdavidch else 616255736Sdavidch#endif 617255736Sdavidch wlen = writev(conn->sd, iov, iovcnt); 618255736Sdavidch if (wlen == 0) { 619255736Sdavidch /* we consider a short write a failure */ 620255736Sdavidch /* XXX perhaps we shouldn't in the SSL case */ 621255736Sdavidch errno = EPIPE; 622255736Sdavidch fetch_syserr(); 623255736Sdavidch return (-1); 624255736Sdavidch } 625255736Sdavidch if (wlen < 0) { 626255736Sdavidch if (errno == EINTR && fetchRestartCalls) 627255736Sdavidch continue; 628255736Sdavidch return (-1); 629255736Sdavidch } 630255736Sdavidch total += wlen; 631255736Sdavidch while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) { 632255736Sdavidch wlen -= iov->iov_len; 633255736Sdavidch iov++; 634255736Sdavidch iovcnt--; 635255736Sdavidch } 636255736Sdavidch if (iovcnt > 0) { 637255736Sdavidch iov->iov_len -= wlen; 638255736Sdavidch iov->iov_base = __DECONST(char *, iov->iov_base) + wlen; 639255736Sdavidch } 640255736Sdavidch } 641255736Sdavidch return (total); 642255736Sdavidch} 643255736Sdavidch 644255736Sdavidch 645255736Sdavidch/* 646255736Sdavidch * Write a line of text to a connection w/ timeout 647255736Sdavidch */ 648255736Sdavidchint 649255736Sdavidchfetch_putln(conn_t *conn, const char *str, size_t len) 650255736Sdavidch{ 651255736Sdavidch struct iovec iov[2]; 652255736Sdavidch int ret; 653255736Sdavidch 654255736Sdavidch DEBUG(fprintf(stderr, ">>> %s\n", str)); 655255736Sdavidch iov[0].iov_base = __DECONST(char *, str); 656255736Sdavidch iov[0].iov_len = len; 657255736Sdavidch iov[1].iov_base = __DECONST(char *, ENDL); 658255736Sdavidch iov[1].iov_len = sizeof(ENDL); 659255736Sdavidch if (len == 0) 660255736Sdavidch ret = fetch_writev(conn, &iov[1], 1); 661255736Sdavidch else 662255736Sdavidch ret = fetch_writev(conn, iov, 2); 663255736Sdavidch if (ret == -1) 664255736Sdavidch return (-1); 665255736Sdavidch return (0); 666255736Sdavidch} 667255736Sdavidch 668255736Sdavidch 669255736Sdavidch/* 670255736Sdavidch * Close connection 671255736Sdavidch */ 672255736Sdavidchint 673255736Sdavidchfetch_close(conn_t *conn) 674255736Sdavidch{ 675255736Sdavidch int ret; 676255736Sdavidch 677255736Sdavidch if (--conn->ref > 0) 678255736Sdavidch return (0); 679255736Sdavidch ret = close(conn->sd); 680255736Sdavidch free(conn->buf); 681255736Sdavidch free(conn); 682255736Sdavidch return (ret); 683255736Sdavidch} 684255736Sdavidch 685255736Sdavidch 686255736Sdavidch/*** Directory-related utility functions *************************************/ 687255736Sdavidch 688255736Sdavidchint 689255736Sdavidchfetch_add_entry(struct url_ent **p, int *size, int *len, 690255736Sdavidch const char *name, struct url_stat *us) 691255736Sdavidch{ 692255736Sdavidch struct url_ent *tmp; 693255736Sdavidch 694255736Sdavidch if (*p == NULL) { 695255736Sdavidch *size = 0; 696255736Sdavidch *len = 0; 697255736Sdavidch } 698255736Sdavidch 699255736Sdavidch if (*len >= *size - 1) { 700255736Sdavidch tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p)); 701255736Sdavidch if (tmp == NULL) { 702255736Sdavidch errno = ENOMEM; 703255736Sdavidch fetch_syserr(); 704255736Sdavidch return (-1); 705255736Sdavidch } 706255736Sdavidch *size = (*size * 2 + 1); 707255736Sdavidch *p = tmp; 708255736Sdavidch } 709255736Sdavidch 710255736Sdavidch tmp = *p + *len; 711255736Sdavidch snprintf(tmp->name, PATH_MAX, "%s", name); 712255736Sdavidch memcpy(&tmp->stat, us, sizeof(*us)); 713255736Sdavidch 714255736Sdavidch (*len)++; 715255736Sdavidch (++tmp)->name[0] = 0; 716255736Sdavidch 717255736Sdavidch return (0); 718255736Sdavidch} 719255736Sdavidch 720255736Sdavidch 721255736Sdavidch/*** Authentication-related utility functions ********************************/ 722255736Sdavidch 723255736Sdavidchstatic const char * 724255736Sdavidchfetch_read_word(FILE *f) 725255736Sdavidch{ 726255736Sdavidch static char word[1024]; 727255736Sdavidch 728255736Sdavidch if (fscanf(f, " %1023s ", word) != 1) 729255736Sdavidch return (NULL); 730255736Sdavidch return (word); 731255736Sdavidch} 732255736Sdavidch 733255736Sdavidch/* 734255736Sdavidch * Get authentication data for a URL from .netrc 735255736Sdavidch */ 736255736Sdavidchint 737255736Sdavidchfetch_netrc_auth(struct url *url) 738255736Sdavidch{ 739255736Sdavidch char fn[PATH_MAX]; 740255736Sdavidch const char *word; 741255736Sdavidch char *p; 742255736Sdavidch FILE *f; 743255736Sdavidch 744255736Sdavidch if ((p = getenv("NETRC")) != NULL) { 745255736Sdavidch if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) { 746255736Sdavidch fetch_info("$NETRC specifies a file name " 747255736Sdavidch "longer than PATH_MAX"); 748255736Sdavidch return (-1); 749255736Sdavidch } 750255736Sdavidch } else { 751255736Sdavidch if ((p = getenv("HOME")) != NULL) { 752255736Sdavidch struct passwd *pwd; 753255736Sdavidch 754255736Sdavidch if ((pwd = getpwuid(getuid())) == NULL || 755255736Sdavidch (p = pwd->pw_dir) == NULL) 756255736Sdavidch return (-1); 757255736Sdavidch } 758255736Sdavidch if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn)) 759255736Sdavidch return (-1); 760255736Sdavidch } 761255736Sdavidch 762255736Sdavidch if ((f = fopen(fn, "r")) == NULL) 763255736Sdavidch return (-1); 764255736Sdavidch while ((word = fetch_read_word(f)) != NULL) { 765255736Sdavidch if (strcmp(word, "default") == 0) { 766255736Sdavidch DEBUG(fetch_info("Using default .netrc settings")); 767255736Sdavidch break; 768255736Sdavidch } 769255736Sdavidch if (strcmp(word, "machine") == 0 && 770255736Sdavidch (word = fetch_read_word(f)) != NULL && 771255736Sdavidch strcasecmp(word, url->host) == 0) { 772255736Sdavidch DEBUG(fetch_info("Using .netrc settings for %s", word)); 773255736Sdavidch break; 774255736Sdavidch } 775255736Sdavidch } 776255736Sdavidch if (word == NULL) 777255736Sdavidch goto ferr; 778255736Sdavidch while ((word = fetch_read_word(f)) != NULL) { 779255736Sdavidch if (strcmp(word, "login") == 0) { 780255736Sdavidch if ((word = fetch_read_word(f)) == NULL) 781255736Sdavidch goto ferr; 782255736Sdavidch if (snprintf(url->user, sizeof(url->user), 783255736Sdavidch "%s", word) > (int)sizeof(url->user)) { 784255736Sdavidch fetch_info("login name in .netrc is too long"); 785255736Sdavidch url->user[0] = '\0'; 786255736Sdavidch } 787255736Sdavidch } else if (strcmp(word, "password") == 0) { 788255736Sdavidch if ((word = fetch_read_word(f)) == NULL) 789255736Sdavidch goto ferr; 790255736Sdavidch if (snprintf(url->pwd, sizeof(url->pwd), 791255736Sdavidch "%s", word) > (int)sizeof(url->pwd)) { 792255736Sdavidch fetch_info("password in .netrc is too long"); 793255736Sdavidch url->pwd[0] = '\0'; 794255736Sdavidch } 795255736Sdavidch } else if (strcmp(word, "account") == 0) { 796255736Sdavidch if ((word = fetch_read_word(f)) == NULL) 797255736Sdavidch goto ferr; 798255736Sdavidch /* XXX not supported! */ 799255736Sdavidch } else { 800255736Sdavidch break; 801255736Sdavidch } 802255736Sdavidch } 803255736Sdavidch fclose(f); 804255736Sdavidch return (0); 805255736Sdavidch ferr: 806255736Sdavidch fclose(f); 807255736Sdavidch return (-1); 808255736Sdavidch} 809255736Sdavidch 810255736Sdavidch/* 811255736Sdavidch * The no_proxy environment variable specifies a set of domains for 812255736Sdavidch * which the proxy should not be consulted; the contents is a comma-, 813255736Sdavidch * or space-separated list of domain names. A single asterisk will 814255736Sdavidch * override all proxy variables and no transactions will be proxied 815255736Sdavidch * (for compatability with lynx and curl, see the discussion at 816255736Sdavidch * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>). 817255736Sdavidch */ 818255736Sdavidchint 819255736Sdavidchfetch_no_proxy_match(const char *host) 820255736Sdavidch{ 821255736Sdavidch const char *no_proxy, *p, *q; 822255736Sdavidch size_t h_len, d_len; 823255736Sdavidch 824255736Sdavidch if ((no_proxy = getenv("NO_PROXY")) == NULL && 825255736Sdavidch (no_proxy = getenv("no_proxy")) == NULL) 826255736Sdavidch return (0); 827255736Sdavidch 828255736Sdavidch /* asterisk matches any hostname */ 829255736Sdavidch if (strcmp(no_proxy, "*") == 0) 830255736Sdavidch return (1); 831255736Sdavidch 832255736Sdavidch h_len = strlen(host); 833255736Sdavidch p = no_proxy; 834255736Sdavidch do { 835255736Sdavidch /* position p at the beginning of a domain suffix */ 836255736Sdavidch while (*p == ',' || isspace((unsigned char)*p)) 837255736Sdavidch p++; 838255736Sdavidch 839255736Sdavidch /* position q at the first separator character */ 840255736Sdavidch for (q = p; *q; ++q) 841255736Sdavidch if (*q == ',' || isspace((unsigned char)*q)) 842255736Sdavidch break; 843255736Sdavidch 844255736Sdavidch d_len = q - p; 845255736Sdavidch if (d_len > 0 && h_len >= d_len && 846255736Sdavidch strncasecmp(host + h_len - d_len, 847255736Sdavidch p, d_len) == 0) { 848255736Sdavidch /* domain name matches */ 849255736Sdavidch return (1); 850255736Sdavidch } 851255736Sdavidch 852255736Sdavidch p = q + 1; 853255736Sdavidch } while (*q); 854255736Sdavidch 855255736Sdavidch return (0); 856255736Sdavidch} 857255736Sdavidch