common.c revision 135546
1290931Srodrigc/*- 2290931Srodrigc * Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav 3290931Srodrigc * All rights reserved. 4290931Srodrigc * 5290931Srodrigc * Redistribution and use in source and binary forms, with or without 6290931Srodrigc * modification, are permitted provided that the following conditions 7290931Srodrigc * are met: 8290931Srodrigc * 1. Redistributions of source code must retain the above copyright 9290931Srodrigc * notice, this list of conditions and the following disclaimer 10290931Srodrigc * in this position and unchanged. 11290931Srodrigc * 2. Redistributions in binary form must reproduce the above copyright 12290931Srodrigc * notice, this list of conditions and the following disclaimer in the 13290931Srodrigc * documentation and/or other materials provided with the distribution. 14290931Srodrigc * 3. The name of the author may not be used to endorse or promote products 15290931Srodrigc * derived from this software without specific prior written permission 16290931Srodrigc * 17290931Srodrigc * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18290931Srodrigc * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19290931Srodrigc * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20290931Srodrigc * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21290931Srodrigc * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22290931Srodrigc * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23290931Srodrigc * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24290931Srodrigc * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25290931Srodrigc * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26290931Srodrigc * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27290931Srodrigc */ 28290931Srodrigc 29290937Srodrigc#include <sys/cdefs.h> 30290931Srodrigc__FBSDID("$FreeBSD: head/lib/libfetch/common.c 135546 2004-09-21 18:35:21Z des $"); 31290931Srodrigc 32290931Srodrigc#include <sys/param.h> 33290931Srodrigc#include <sys/socket.h> 34290931Srodrigc#include <sys/time.h> 35290931Srodrigc#include <sys/uio.h> 36290931Srodrigc#include <netinet/in.h> 37290931Srodrigc 38290931Srodrigc#include <errno.h> 39290931Srodrigc#include <netdb.h> 40290931Srodrigc#include <pwd.h> 41290931Srodrigc#include <stdarg.h> 42290931Srodrigc#include <stdlib.h> 43290931Srodrigc#include <stdio.h> 44290931Srodrigc#include <string.h> 45290931Srodrigc#include <unistd.h> 46290931Srodrigc 47290931Srodrigc#include "fetch.h" 48290931Srodrigc#include "common.h" 49290931Srodrigc 50290931Srodrigc 51290931Srodrigc/*** Local data **************************************************************/ 52290931Srodrigc 53290931Srodrigc/* 54290931Srodrigc * Error messages for resolver errors 55290931Srodrigc */ 56290931Srodrigcstatic struct fetcherr _netdb_errlist[] = { 57290931Srodrigc#ifdef EAI_NODATA 58290931Srodrigc { EAI_NODATA, FETCH_RESOLV, "Host not found" }, 59290931Srodrigc#endif 60290931Srodrigc { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, 61290931Srodrigc { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, 62290931Srodrigc { EAI_NONAME, FETCH_RESOLV, "No address record" }, 63290931Srodrigc { -1, FETCH_UNKNOWN, "Unknown resolver error" } 64290931Srodrigc}; 65290931Srodrigc 66290931Srodrigc/* End-of-Line */ 67290931Srodrigcstatic const char ENDL[2] = "\r\n"; 68290931Srodrigc 69290931Srodrigc 70290931Srodrigc/*** Error-reporting functions ***********************************************/ 71290931Srodrigc 72290931Srodrigc/* 73290931Srodrigc * Map error code to string 74290931Srodrigc */ 75290931Srodrigcstatic struct fetcherr * 76290931Srodrigc_fetch_finderr(struct fetcherr *p, int e) 77290931Srodrigc{ 78290931Srodrigc while (p->num != -1 && p->num != e) 79290931Srodrigc p++; 80290931Srodrigc return (p); 81290931Srodrigc} 82290931Srodrigc 83290931Srodrigc/* 84290931Srodrigc * Set error code 85290931Srodrigc */ 86290931Srodrigcvoid 87290931Srodrigc_fetch_seterr(struct fetcherr *p, int e) 88290931Srodrigc{ 89290931Srodrigc p = _fetch_finderr(p, e); 90290931Srodrigc fetchLastErrCode = p->cat; 91290931Srodrigc snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); 92290931Srodrigc} 93290931Srodrigc 94290931Srodrigc/* 95290931Srodrigc * Set error code according to errno 96290931Srodrigc */ 97290931Srodrigcvoid 98290931Srodrigc_fetch_syserr(void) 99290931Srodrigc{ 100290931Srodrigc switch (errno) { 101290931Srodrigc case 0: 102290931Srodrigc fetchLastErrCode = FETCH_OK; 103290931Srodrigc break; 104290931Srodrigc case EPERM: 105290931Srodrigc case EACCES: 106290931Srodrigc case EROFS: 107290931Srodrigc case EAUTH: 108290931Srodrigc case ENEEDAUTH: 109290931Srodrigc fetchLastErrCode = FETCH_AUTH; 110290931Srodrigc break; 111290931Srodrigc case ENOENT: 112290931Srodrigc case EISDIR: /* XXX */ 113290931Srodrigc fetchLastErrCode = FETCH_UNAVAIL; 114290931Srodrigc break; 115290931Srodrigc case ENOMEM: 116290931Srodrigc fetchLastErrCode = FETCH_MEMORY; 117290931Srodrigc break; 118290931Srodrigc case EBUSY: 119290931Srodrigc case EAGAIN: 120290931Srodrigc fetchLastErrCode = FETCH_TEMP; 121290931Srodrigc break; 122290931Srodrigc case EEXIST: 123290931Srodrigc fetchLastErrCode = FETCH_EXISTS; 124290931Srodrigc break; 125290931Srodrigc case ENOSPC: 126290931Srodrigc fetchLastErrCode = FETCH_FULL; 127290931Srodrigc break; 128290931Srodrigc case EADDRINUSE: 129290931Srodrigc case EADDRNOTAVAIL: 130290931Srodrigc case ENETDOWN: 131290931Srodrigc case ENETUNREACH: 132290931Srodrigc case ENETRESET: 133290931Srodrigc case EHOSTUNREACH: 134290931Srodrigc fetchLastErrCode = FETCH_NETWORK; 135290931Srodrigc break; 136290931Srodrigc case ECONNABORTED: 137290931Srodrigc case ECONNRESET: 138290931Srodrigc fetchLastErrCode = FETCH_ABORT; 139290931Srodrigc break; 140290931Srodrigc case ETIMEDOUT: 141290931Srodrigc fetchLastErrCode = FETCH_TIMEOUT; 142290931Srodrigc break; 143290931Srodrigc case ECONNREFUSED: 144290931Srodrigc case EHOSTDOWN: 145290931Srodrigc fetchLastErrCode = FETCH_DOWN; 146290931Srodrigc break; 147330965Seadlerdefault: 148330965Seadler fetchLastErrCode = FETCH_UNKNOWN; 149330965Seadler } 150330965Seadler snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); 151330965Seadler} 152330965Seadler 153330965Seadler 154330965Seadler/* 155290931Srodrigc * Emit status message 156290931Srodrigc */ 157290931Srodrigcvoid 158290931Srodrigc_fetch_info(const char *fmt, ...) 159290931Srodrigc{ 160290931Srodrigc va_list ap; 161290931Srodrigc 162290931Srodrigc va_start(ap, fmt); 163290931Srodrigc vfprintf(stderr, fmt, ap); 164290931Srodrigc va_end(ap); 165290931Srodrigc fputc('\n', stderr); 166290931Srodrigc} 167290931Srodrigc 168290931Srodrigc 169290931Srodrigc/*** Network-related utility functions ***************************************/ 170290931Srodrigc 171290931Srodrigc/* 172290931Srodrigc * Return the default port for a scheme 173290931Srodrigc */ 174290931Srodrigcint 175290931Srodrigc_fetch_default_port(const char *scheme) 176290931Srodrigc{ 177290931Srodrigc struct servent *se; 178290931Srodrigc 179290931Srodrigc if ((se = getservbyname(scheme, "tcp")) != NULL) 180290931Srodrigc return (ntohs(se->s_port)); 181290931Srodrigc if (strcasecmp(scheme, SCHEME_FTP) == 0) 182290931Srodrigc return (FTP_DEFAULT_PORT); 183290931Srodrigc if (strcasecmp(scheme, SCHEME_HTTP) == 0) 184290931Srodrigc return (HTTP_DEFAULT_PORT); 185290931Srodrigc return (0); 186290931Srodrigc} 187290931Srodrigc 188290931Srodrigc/* 189290931Srodrigc * Return the default proxy port for a scheme 190290931Srodrigc */ 191290931Srodrigcint 192290931Srodrigc_fetch_default_proxy_port(const char *scheme) 193290931Srodrigc{ 194290931Srodrigc if (strcasecmp(scheme, SCHEME_FTP) == 0) 195290931Srodrigc return (FTP_DEFAULT_PROXY_PORT); 196290931Srodrigc if (strcasecmp(scheme, SCHEME_HTTP) == 0) 197290931Srodrigc return (HTTP_DEFAULT_PROXY_PORT); 198290931Srodrigc return (0); 199290931Srodrigc} 200290931Srodrigc 201290931Srodrigc 202290931Srodrigc/* 203290931Srodrigc * Create a connection for an existing descriptor. 204290931Srodrigc */ 205290931Srodrigcconn_t * 206290931Srodrigc_fetch_reopen(int sd) 207290931Srodrigc{ 208290931Srodrigc conn_t *conn; 209290931Srodrigc 210290931Srodrigc /* allocate and fill connection structure */ 211290931Srodrigc if ((conn = calloc(1, sizeof(*conn))) == NULL) 212290931Srodrigc return (NULL); 213290931Srodrigc conn->sd = sd; 214290931Srodrigc ++conn->ref; 215290931Srodrigc return (conn); 216290931Srodrigc} 217290931Srodrigc 218290931Srodrigc 219290931Srodrigc/* 220290931Srodrigc * Bump a connection's reference count. 221290931Srodrigc */ 222290931Srodrigcconn_t * 223290931Srodrigc_fetch_ref(conn_t *conn) 224290931Srodrigc{ 225290931Srodrigc 226290931Srodrigc ++conn->ref; 227290931Srodrigc return (conn); 228290931Srodrigc} 229290931Srodrigc 230290931Srodrigc 231290931Srodrigc/* 232290931Srodrigc * Bind a socket to a specific local address 233290931Srodrigc */ 234290931Srodrigcint 235290931Srodrigc_fetch_bind(int sd, int af, const char *addr) 236290931Srodrigc{ 237290931Srodrigc struct addrinfo hints, *res, *res0; 238290931Srodrigc int err; 239290931Srodrigc 240290931Srodrigc memset(&hints, 0, sizeof(hints)); 241290931Srodrigc hints.ai_family = af; 242290931Srodrigc hints.ai_socktype = SOCK_STREAM; 243290931Srodrigc hints.ai_protocol = 0; 244290931Srodrigc if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0) 245290931Srodrigc return (-1); 246290931Srodrigc for (res = res0; res; res = res->ai_next) 247290931Srodrigc if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) 248290931Srodrigc return (0); 249290931Srodrigc return (-1); 250290931Srodrigc} 251290931Srodrigc 252290931Srodrigc 253290931Srodrigc/* 254290931Srodrigc * Establish a TCP connection to the specified port on the specified host. 255290931Srodrigc */ 256290931Srodrigcconn_t * 257290931Srodrigc_fetch_connect(const char *host, int port, int af, int verbose) 258290931Srodrigc{ 259290931Srodrigc conn_t *conn; 260290931Srodrigc char pbuf[10]; 261290931Srodrigc const char *bindaddr; 262290931Srodrigc struct addrinfo hints, *res, *res0; 263290931Srodrigc int sd, err; 264290931Srodrigc 265290931Srodrigc DEBUG(fprintf(stderr, "---> %s:%d\n", host, port)); 266290931Srodrigc 267290931Srodrigc if (verbose) 268290931Srodrigc _fetch_info("looking up %s", host); 269290931Srodrigc 270290931Srodrigc /* look up host name and set up socket address structure */ 271290931Srodrigc snprintf(pbuf, sizeof(pbuf), "%d", port); 272290931Srodrigc memset(&hints, 0, sizeof(hints)); 273290931Srodrigc hints.ai_family = af; 274290931Srodrigc hints.ai_socktype = SOCK_STREAM; 275290931Srodrigc hints.ai_protocol = 0; 276290931Srodrigc if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) { 277290931Srodrigc _netdb_seterr(err); 278290931Srodrigc return (NULL); 279290931Srodrigc } 280290931Srodrigc bindaddr = getenv("FETCH_BIND_ADDRESS"); 281290931Srodrigc 282290931Srodrigc if (verbose) 283290931Srodrigc _fetch_info("connecting to %s:%d", host, port); 284290931Srodrigc 285290931Srodrigc /* try to connect */ 286290931Srodrigc for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) { 287290931Srodrigc if ((sd = socket(res->ai_family, res->ai_socktype, 288290931Srodrigc res->ai_protocol)) == -1) 289290931Srodrigc continue; 290290931Srodrigc if (bindaddr != NULL && *bindaddr != '\0' && 291290931Srodrigc _fetch_bind(sd, res->ai_family, bindaddr) != 0) { 292290931Srodrigc _fetch_info("failed to bind to '%s'", bindaddr); 293290931Srodrigc close(sd); 294290931Srodrigc continue; 295290931Srodrigc } 296290931Srodrigc if (connect(sd, res->ai_addr, res->ai_addrlen) == 0) 297290931Srodrigc break; 298290931Srodrigc close(sd); 299290931Srodrigc } 300290931Srodrigc freeaddrinfo(res0); 301290931Srodrigc if (sd == -1) { 302290931Srodrigc _fetch_syserr(); 303290931Srodrigc return (NULL); 304290931Srodrigc } 305290931Srodrigc 306290931Srodrigc if ((conn = _fetch_reopen(sd)) == NULL) { 307290931Srodrigc _fetch_syserr(); 308290931Srodrigc close(sd); 309290931Srodrigc } 310290931Srodrigc return (conn); 311290931Srodrigc} 312290931Srodrigc 313290931Srodrigc 314290931Srodrigc/* 315290931Srodrigc * Enable SSL on a connection. 316290931Srodrigc */ 317290931Srodrigcint 318290931Srodrigc_fetch_ssl(conn_t *conn, int verbose) 319290931Srodrigc{ 320290931Srodrigc 321290931Srodrigc#ifdef WITH_SSL 322290931Srodrigc /* Init the SSL library and context */ 323290931Srodrigc if (!SSL_library_init()){ 324290931Srodrigc fprintf(stderr, "SSL library init failed\n"); 325290931Srodrigc return (-1); 326290931Srodrigc } 327290931Srodrigc 328290931Srodrigc SSL_load_error_strings(); 329290931Srodrigc 330290931Srodrigc conn->ssl_meth = SSLv23_client_method(); 331290931Srodrigc conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth); 332290931Srodrigc SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY); 333290931Srodrigc 334290931Srodrigc conn->ssl = SSL_new(conn->ssl_ctx); 335290931Srodrigc if (conn->ssl == NULL){ 336290931Srodrigc fprintf(stderr, "SSL context creation failed\n"); 337290931Srodrigc return (-1); 338290931Srodrigc } 339290931Srodrigc SSL_set_fd(conn->ssl, conn->sd); 340290931Srodrigc if (SSL_connect(conn->ssl) == -1){ 341290931Srodrigc ERR_print_errors_fp(stderr); 342290931Srodrigc return (-1); 343290931Srodrigc } 344290931Srodrigc 345290931Srodrigc if (verbose) { 346290931Srodrigc X509_NAME *name; 347290931Srodrigc char *str; 348290931Srodrigc 349290931Srodrigc fprintf(stderr, "SSL connection established using %s\n", 350290931Srodrigc SSL_get_cipher(conn->ssl)); 351290931Srodrigc conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); 352290931Srodrigc name = X509_get_subject_name(conn->ssl_cert); 353290931Srodrigc str = X509_NAME_oneline(name, 0, 0); 354290931Srodrigc printf("Certificate subject: %s\n", str); 355290931Srodrigc free(str); 356290931Srodrigc name = X509_get_issuer_name(conn->ssl_cert); 357290931Srodrigc str = X509_NAME_oneline(name, 0, 0); 358290931Srodrigc printf("Certificate issuer: %s\n", str); 359290931Srodrigc free(str); 360290931Srodrigc } 361290931Srodrigc 362290931Srodrigc return (0); 363290931Srodrigc#else 364290931Srodrigc (void)conn; 365290931Srodrigc (void)verbose; 366290931Srodrigc fprintf(stderr, "SSL support disabled\n"); 367290931Srodrigc return (-1); 368290931Srodrigc#endif 369290931Srodrigc} 370290931Srodrigc 371290931Srodrigc 372290931Srodrigc/* 373290931Srodrigc * Read a character from a connection w/ timeout 374290931Srodrigc */ 375290931Srodrigcssize_t 376290931Srodrigc_fetch_read(conn_t *conn, char *buf, size_t len) 377290931Srodrigc{ 378290931Srodrigc struct timeval now, timeout, wait; 379290931Srodrigc fd_set readfds; 380290931Srodrigc ssize_t rlen, total; 381290931Srodrigc int r; 382290931Srodrigc 383290931Srodrigc if (fetchTimeout) { 384290931Srodrigc FD_ZERO(&readfds); 385290931Srodrigc gettimeofday(&timeout, NULL); 386290931Srodrigc timeout.tv_sec += fetchTimeout; 387290931Srodrigc } 388290931Srodrigc 389290931Srodrigc total = 0; 390290931Srodrigc while (len > 0) { 391290931Srodrigc while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) { 392290931Srodrigc FD_SET(conn->sd, &readfds); 393290931Srodrigc gettimeofday(&now, NULL); 394290931Srodrigc wait.tv_sec = timeout.tv_sec - now.tv_sec; 395290931Srodrigc wait.tv_usec = timeout.tv_usec - now.tv_usec; 396290931Srodrigc if (wait.tv_usec < 0) { 397290931Srodrigc wait.tv_usec += 1000000; 398290931Srodrigc wait.tv_sec--; 399290931Srodrigc } 400290931Srodrigc if (wait.tv_sec < 0) { 401290931Srodrigc errno = ETIMEDOUT; 402290931Srodrigc _fetch_syserr(); 403290931Srodrigc return (-1); 404290931Srodrigc } 405290931Srodrigc errno = 0; 406290931Srodrigc r = select(conn->sd + 1, &readfds, NULL, NULL, &wait); 407290931Srodrigc if (r == -1) { 408290931Srodrigc if (errno == EINTR && fetchRestartCalls) 409290931Srodrigc continue; 410290931Srodrigc _fetch_syserr(); 411290931Srodrigc return (-1); 412290931Srodrigc } 413290931Srodrigc } 414290931Srodrigc#ifdef WITH_SSL 415290931Srodrigc if (conn->ssl != NULL) 416290931Srodrigc rlen = SSL_read(conn->ssl, buf, len); 417290931Srodrigc else 418290931Srodrigc#endif 419290931Srodrigc rlen = read(conn->sd, buf, len); 420290931Srodrigc if (rlen == 0) 421290931Srodrigc break; 422290931Srodrigc if (rlen < 0) { 423290931Srodrigc if (errno == EINTR && fetchRestartCalls) 424290931Srodrigc continue; 425290931Srodrigc return (-1); 426290931Srodrigc } 427290931Srodrigc len -= rlen; 428290931Srodrigc buf += rlen; 429290931Srodrigc total += rlen; 430290931Srodrigc } 431290931Srodrigc return (total); 432290931Srodrigc} 433290931Srodrigc 434290931Srodrigc 435290931Srodrigc/* 436290931Srodrigc * Read a line of text from a connection w/ timeout 437290931Srodrigc */ 438290931Srodrigc#define MIN_BUF_SIZE 1024 439290931Srodrigc 440290931Srodrigcint 441290931Srodrigc_fetch_getln(conn_t *conn) 442290931Srodrigc{ 443290931Srodrigc char *tmp; 444290931Srodrigc size_t tmpsize; 445290931Srodrigc ssize_t len; 446290931Srodrigc char c; 447290931Srodrigc 448290931Srodrigc if (conn->buf == NULL) { 449290931Srodrigc if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) { 450290931Srodrigc errno = ENOMEM; 451290931Srodrigc return (-1); 452290931Srodrigc } 453290931Srodrigc conn->bufsize = MIN_BUF_SIZE; 454290931Srodrigc } 455290931Srodrigc 456290931Srodrigc conn->buf[0] = '\0'; 457290931Srodrigc conn->buflen = 0; 458290931Srodrigc 459290931Srodrigc do { 460290931Srodrigc len = _fetch_read(conn, &c, 1); 461290931Srodrigc if (len == -1) 462290931Srodrigc return (-1); 463290931Srodrigc if (len == 0) 464290931Srodrigc break; 465290931Srodrigc conn->buf[conn->buflen++] = c; 466290931Srodrigc if (conn->buflen == conn->bufsize) { 467290931Srodrigc tmp = conn->buf; 468290931Srodrigc tmpsize = conn->bufsize * 2 + 1; 469290931Srodrigc if ((tmp = realloc(tmp, tmpsize)) == NULL) { 470290931Srodrigc errno = ENOMEM; 471290931Srodrigc return (-1); 472290931Srodrigc } 473290931Srodrigc conn->buf = tmp; 474290931Srodrigc conn->bufsize = tmpsize; 475290931Srodrigc } 476290931Srodrigc } while (c != '\n'); 477290931Srodrigc 478290931Srodrigc conn->buf[conn->buflen] = '\0'; 479290931Srodrigc DEBUG(fprintf(stderr, "<<< %s", conn->buf)); 480290931Srodrigc return (0); 481290931Srodrigc} 482290931Srodrigc 483290931Srodrigc 484290931Srodrigc/* 485290931Srodrigc * Write to a connection w/ timeout 486290931Srodrigc */ 487290931Srodrigcssize_t 488290931Srodrigc_fetch_write(conn_t *conn, const char *buf, size_t len) 489290931Srodrigc{ 490290931Srodrigc struct iovec iov; 491290931Srodrigc 492290931Srodrigc iov.iov_base = __DECONST(char *, buf); 493290931Srodrigc iov.iov_len = len; 494290931Srodrigc return _fetch_writev(conn, &iov, 1); 495290931Srodrigc} 496290931Srodrigc 497290931Srodrigc/* 498290931Srodrigc * Write a vector to a connection w/ timeout 499290931Srodrigc * Note: can modify the iovec. 500290931Srodrigc */ 501290931Srodrigcssize_t 502290931Srodrigc_fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt) 503290931Srodrigc{ 504290931Srodrigc struct timeval now, timeout, wait; 505290931Srodrigc fd_set writefds; 506290931Srodrigc ssize_t wlen, total; 507290931Srodrigc int r; 508290931Srodrigc 509290931Srodrigc if (fetchTimeout) { 510290931Srodrigc FD_ZERO(&writefds); 511290931Srodrigc gettimeofday(&timeout, NULL); 512290931Srodrigc timeout.tv_sec += fetchTimeout; 513290931Srodrigc } 514290931Srodrigc 515290931Srodrigc total = 0; 516290931Srodrigc while (iovcnt > 0) { 517290931Srodrigc while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) { 518290931Srodrigc FD_SET(conn->sd, &writefds); 519290931Srodrigc gettimeofday(&now, NULL); 520290931Srodrigc wait.tv_sec = timeout.tv_sec - now.tv_sec; 521290931Srodrigc wait.tv_usec = timeout.tv_usec - now.tv_usec; 522290931Srodrigc if (wait.tv_usec < 0) { 523290931Srodrigc wait.tv_usec += 1000000; 524290931Srodrigc wait.tv_sec--; 525290931Srodrigc } 526290931Srodrigc if (wait.tv_sec < 0) { 527290931Srodrigc errno = ETIMEDOUT; 528290931Srodrigc _fetch_syserr(); 529290931Srodrigc return (-1); 530290931Srodrigc } 531290931Srodrigc errno = 0; 532290931Srodrigc r = select(conn->sd + 1, NULL, &writefds, NULL, &wait); 533290931Srodrigc if (r == -1) { 534290931Srodrigc if (errno == EINTR && fetchRestartCalls) 535290931Srodrigc continue; 536290931Srodrigc return (-1); 537290931Srodrigc } 538290931Srodrigc } 539290931Srodrigc errno = 0; 540290931Srodrigc#ifdef WITH_SSL 541290931Srodrigc if (conn->ssl != NULL) 542290931Srodrigc wlen = SSL_write(conn->ssl, 543290931Srodrigc iov->iov_base, iov->iov_len); 544290931Srodrigc else 545290931Srodrigc#endif 546290931Srodrigc wlen = writev(conn->sd, iov, iovcnt); 547290931Srodrigc if (wlen == 0) { 548290931Srodrigc /* we consider a short write a failure */ 549290931Srodrigc errno = EPIPE; 550290931Srodrigc _fetch_syserr(); 551290931Srodrigc return (-1); 552290931Srodrigc } 553290931Srodrigc if (wlen < 0) { 554290931Srodrigc if (errno == EINTR && fetchRestartCalls) 555290931Srodrigc continue; 556290931Srodrigc return (-1); 557290931Srodrigc } 558290931Srodrigc total += wlen; 559290931Srodrigc while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) { 560290931Srodrigc wlen -= iov->iov_len; 561290931Srodrigc iov++; 562290931Srodrigc iovcnt--; 563290931Srodrigc } 564290931Srodrigc if (iovcnt > 0) { 565290931Srodrigc iov->iov_len -= wlen; 566290931Srodrigc iov->iov_base = __DECONST(char *, iov->iov_base) + wlen; 567290931Srodrigc } 568290931Srodrigc } 569290931Srodrigc return (total); 570290931Srodrigc} 571290931Srodrigc 572290931Srodrigc 573290931Srodrigc/* 574290931Srodrigc * Write a line of text to a connection w/ timeout 575290931Srodrigc */ 576290931Srodrigcint 577290931Srodrigc_fetch_putln(conn_t *conn, const char *str, size_t len) 578290931Srodrigc{ 579290931Srodrigc struct iovec iov[2]; 580290931Srodrigc int ret; 581290931Srodrigc 582290931Srodrigc DEBUG(fprintf(stderr, ">>> %s\n", str)); 583290931Srodrigc iov[0].iov_base = __DECONST(char *, str); 584290931Srodrigc iov[0].iov_len = len; 585290931Srodrigc iov[1].iov_base = __DECONST(char *, ENDL); 586290931Srodrigc iov[1].iov_len = sizeof(ENDL); 587290931Srodrigc if (len == 0) 588290931Srodrigc ret = _fetch_writev(conn, &iov[1], 1); 589290931Srodrigc else 590290931Srodrigc ret = _fetch_writev(conn, iov, 2); 591290931Srodrigc if (ret == -1) 592290931Srodrigc return (-1); 593290931Srodrigc return (0); 594290931Srodrigc} 595290931Srodrigc 596290931Srodrigc 597290931Srodrigc/* 598290931Srodrigc * Close connection 599290931Srodrigc */ 600290931Srodrigcint 601290931Srodrigc_fetch_close(conn_t *conn) 602290931Srodrigc{ 603290931Srodrigc int ret; 604290931Srodrigc 605290931Srodrigc if (--conn->ref > 0) 606290931Srodrigc return (0); 607290931Srodrigc ret = close(conn->sd); 608290931Srodrigc free(conn); 609290931Srodrigc return (ret); 610290931Srodrigc} 611290931Srodrigc 612290931Srodrigc 613290931Srodrigc/*** Directory-related utility functions *************************************/ 614290931Srodrigc 615290931Srodrigcint 616290931Srodrigc_fetch_add_entry(struct url_ent **p, int *size, int *len, 617290931Srodrigc const char *name, struct url_stat *us) 618290931Srodrigc{ 619290931Srodrigc struct url_ent *tmp; 620290931Srodrigc 621290931Srodrigc if (*p == NULL) { 622290931Srodrigc *size = 0; 623290931Srodrigc *len = 0; 624290931Srodrigc } 625290931Srodrigc 626290931Srodrigc if (*len >= *size - 1) { 627290931Srodrigc tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p)); 628290931Srodrigc if (tmp == NULL) { 629290931Srodrigc errno = ENOMEM; 630290931Srodrigc _fetch_syserr(); 631290931Srodrigc return (-1); 632290931Srodrigc } 633290931Srodrigc *size = (*size * 2 + 1); 634290931Srodrigc *p = tmp; 635290931Srodrigc } 636290931Srodrigc 637290931Srodrigc tmp = *p + *len; 638290931Srodrigc snprintf(tmp->name, PATH_MAX, "%s", name); 639290931Srodrigc bcopy(us, &tmp->stat, sizeof(*us)); 640290931Srodrigc 641290931Srodrigc (*len)++; 642290931Srodrigc (++tmp)->name[0] = 0; 643290931Srodrigc 644290931Srodrigc return (0); 645290931Srodrigc} 646290931Srodrigc 647290931Srodrigc 648290931Srodrigc/*** Authentication-related utility functions ********************************/ 649290931Srodrigc 650290931Srodrigcstatic const char * 651290931Srodrigc_fetch_read_word(FILE *f) 652290931Srodrigc{ 653290931Srodrigc static char word[1024]; 654290931Srodrigc 655290931Srodrigc if (fscanf(f, " %1024s ", word) != 1) 656290931Srodrigc return (NULL); 657290931Srodrigc return (word); 658290931Srodrigc} 659290931Srodrigc 660290931Srodrigc/* 661290931Srodrigc * Get authentication data for a URL from .netrc 662290931Srodrigc */ 663290931Srodrigcint 664290931Srodrigc_fetch_netrc_auth(struct url *url) 665290931Srodrigc{ 666290931Srodrigc char fn[PATH_MAX]; 667290931Srodrigc const char *word; 668290931Srodrigc char *p; 669290931Srodrigc FILE *f; 670290931Srodrigc 671290931Srodrigc if ((p = getenv("NETRC")) != NULL) { 672290931Srodrigc if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) { 673290931Srodrigc _fetch_info("$NETRC specifies a file name " 674290931Srodrigc "longer than PATH_MAX"); 675290931Srodrigc return (-1); 676290931Srodrigc } 677290931Srodrigc } else { 678290931Srodrigc if ((p = getenv("HOME")) != NULL) { 679290931Srodrigc struct passwd *pwd; 680290931Srodrigc 681290931Srodrigc if ((pwd = getpwuid(getuid())) == NULL || 682290931Srodrigc (p = pwd->pw_dir) == NULL) 683290931Srodrigc return (-1); 684290931Srodrigc } 685290931Srodrigc if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn)) 686290931Srodrigc return (-1); 687290931Srodrigc } 688290931Srodrigc 689290931Srodrigc if ((f = fopen(fn, "r")) == NULL) 690290931Srodrigc return (-1); 691290931Srodrigc while ((word = _fetch_read_word(f)) != NULL) { 692290931Srodrigc if (strcmp(word, "default") == 0) { 693290931Srodrigc DEBUG(_fetch_info("Using default .netrc settings")); 694290931Srodrigc break; 695290931Srodrigc } 696290931Srodrigc if (strcmp(word, "machine") == 0 && 697290931Srodrigc (word = _fetch_read_word(f)) != NULL && 698290931Srodrigc strcasecmp(word, url->host) == 0) { 699290931Srodrigc DEBUG(_fetch_info("Using .netrc settings for %s", word)); 700290931Srodrigc break; 701290931Srodrigc } 702290931Srodrigc } 703290931Srodrigc if (word == NULL) 704290931Srodrigc goto ferr; 705290931Srodrigc while ((word = _fetch_read_word(f)) != NULL) { 706290931Srodrigc if (strcmp(word, "login") == 0) { 707290931Srodrigc if ((word = _fetch_read_word(f)) == NULL) 708290931Srodrigc goto ferr; 709290931Srodrigc if (snprintf(url->user, sizeof(url->user), 710290931Srodrigc "%s", word) > (int)sizeof(url->user)) { 711290931Srodrigc _fetch_info("login name in .netrc is too long"); 712290931Srodrigc url->user[0] = '\0'; 713290931Srodrigc } 714290931Srodrigc } else if (strcmp(word, "password") == 0) { 715290931Srodrigc if ((word = _fetch_read_word(f)) == NULL) 716290931Srodrigc goto ferr; 717290931Srodrigc if (snprintf(url->pwd, sizeof(url->pwd), 718290931Srodrigc "%s", word) > (int)sizeof(url->pwd)) { 719290931Srodrigc _fetch_info("password in .netrc is too long"); 720290931Srodrigc url->pwd[0] = '\0'; 721290931Srodrigc } 722290931Srodrigc } else if (strcmp(word, "account") == 0) { 723290931Srodrigc if ((word = _fetch_read_word(f)) == NULL) 724290931Srodrigc goto ferr; 725290931Srodrigc /* XXX not supported! */ 726290931Srodrigc } else { 727290931Srodrigc break; 728290931Srodrigc } 729290931Srodrigc } 730290931Srodrigc fclose(f); 731290931Srodrigc return (0); 732290931Srodrigc ferr: 733290931Srodrigc fclose(f); 734290931Srodrigc return (-1); 735290931Srodrigc} 736290931Srodrigc