common.c revision 109967
140939Sdes/*- 240939Sdes * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav 340939Sdes * All rights reserved. 440939Sdes * 540939Sdes * Redistribution and use in source and binary forms, with or without 640939Sdes * modification, are permitted provided that the following conditions 740939Sdes * are met: 840939Sdes * 1. Redistributions of source code must retain the above copyright 940939Sdes * notice, this list of conditions and the following disclaimer 1040939Sdes * in this position and unchanged. 1140939Sdes * 2. Redistributions in binary form must reproduce the above copyright 1240939Sdes * notice, this list of conditions and the following disclaimer in the 1340939Sdes * documentation and/or other materials provided with the distribution. 1440939Sdes * 3. The name of the author may not be used to endorse or promote products 1540939Sdes * derived from this software without specific prior written permission 1640939Sdes * 1740939Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1840939Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1940939Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2040939Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2140939Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2240939Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2340939Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2440939Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2540939Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2640939Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2740939Sdes */ 2840939Sdes 2984203Sdillon#include <sys/cdefs.h> 3084203Sdillon__FBSDID("$FreeBSD: head/lib/libfetch/common.c 109967 2003-01-28 08:04:40Z des $"); 3184203Sdillon 3241862Sdes#include <sys/param.h> 3340939Sdes#include <sys/socket.h> 3455557Sdes#include <sys/time.h> 3562981Sdes#include <sys/uio.h> 3640939Sdes#include <netinet/in.h> 3740939Sdes 3840939Sdes#include <errno.h> 3940939Sdes#include <netdb.h> 40109695Sdes#include <pwd.h> 4160924Sdes#include <stdarg.h> 4241862Sdes#include <stdlib.h> 4341862Sdes#include <stdio.h> 4440939Sdes#include <string.h> 4540939Sdes#include <unistd.h> 4640939Sdes 4740939Sdes#include "fetch.h" 4840939Sdes#include "common.h" 4940939Sdes 5040975Sdes 5140939Sdes/*** Local data **************************************************************/ 5240939Sdes 5340939Sdes/* 5440939Sdes * Error messages for resolver errors 5540939Sdes */ 5640939Sdesstatic struct fetcherr _netdb_errlist[] = { 5790267Sdes { EAI_NODATA, FETCH_RESOLV, "Host not found" }, 5890267Sdes { EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" }, 5990267Sdes { EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" }, 6090267Sdes { EAI_NONAME, FETCH_RESOLV, "No address record" }, 6190267Sdes { -1, FETCH_UNKNOWN, "Unknown resolver error" } 6240939Sdes}; 6340939Sdes 6462981Sdes/* End-of-Line */ 6575891Sarchiestatic const char ENDL[2] = "\r\n"; 6640939Sdes 6762981Sdes 6840939Sdes/*** Error-reporting functions ***********************************************/ 6940939Sdes 7040939Sdes/* 7140939Sdes * Map error code to string 7240939Sdes */ 7360924Sdesstatic struct fetcherr * 7440975Sdes_fetch_finderr(struct fetcherr *p, int e) 7540939Sdes{ 7690267Sdes while (p->num != -1 && p->num != e) 7790267Sdes p++; 7890267Sdes return (p); 7940939Sdes} 8040939Sdes 8140939Sdes/* 8240939Sdes * Set error code 8340939Sdes */ 8440939Sdesvoid 8540939Sdes_fetch_seterr(struct fetcherr *p, int e) 8640939Sdes{ 8790267Sdes p = _fetch_finderr(p, e); 8890267Sdes fetchLastErrCode = p->cat; 8990267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string); 9040939Sdes} 9140939Sdes 9240939Sdes/* 9340939Sdes * Set error code according to errno 9440939Sdes */ 9540939Sdesvoid 9640939Sdes_fetch_syserr(void) 9740939Sdes{ 9890267Sdes switch (errno) { 9990267Sdes case 0: 10090267Sdes fetchLastErrCode = FETCH_OK; 10190267Sdes break; 10290267Sdes case EPERM: 10390267Sdes case EACCES: 10490267Sdes case EROFS: 10590267Sdes case EAUTH: 10690267Sdes case ENEEDAUTH: 10790267Sdes fetchLastErrCode = FETCH_AUTH; 10890267Sdes break; 10990267Sdes case ENOENT: 11090267Sdes case EISDIR: /* XXX */ 11190267Sdes fetchLastErrCode = FETCH_UNAVAIL; 11290267Sdes break; 11390267Sdes case ENOMEM: 11490267Sdes fetchLastErrCode = FETCH_MEMORY; 11590267Sdes break; 11690267Sdes case EBUSY: 11790267Sdes case EAGAIN: 11890267Sdes fetchLastErrCode = FETCH_TEMP; 11990267Sdes break; 12090267Sdes case EEXIST: 12190267Sdes fetchLastErrCode = FETCH_EXISTS; 12290267Sdes break; 12390267Sdes case ENOSPC: 12490267Sdes fetchLastErrCode = FETCH_FULL; 12590267Sdes break; 12690267Sdes case EADDRINUSE: 12790267Sdes case EADDRNOTAVAIL: 12890267Sdes case ENETDOWN: 12990267Sdes case ENETUNREACH: 13090267Sdes case ENETRESET: 13190267Sdes case EHOSTUNREACH: 13290267Sdes fetchLastErrCode = FETCH_NETWORK; 13390267Sdes break; 13490267Sdes case ECONNABORTED: 13590267Sdes case ECONNRESET: 13690267Sdes fetchLastErrCode = FETCH_ABORT; 13790267Sdes break; 13890267Sdes case ETIMEDOUT: 13990267Sdes fetchLastErrCode = FETCH_TIMEOUT; 14090267Sdes break; 14190267Sdes case ECONNREFUSED: 14290267Sdes case EHOSTDOWN: 14390267Sdes fetchLastErrCode = FETCH_DOWN; 14490267Sdes break; 14590267Sdesdefault: 14690267Sdes fetchLastErrCode = FETCH_UNKNOWN; 14790267Sdes } 14890267Sdes snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno)); 14940939Sdes} 15040939Sdes 15140939Sdes 15241862Sdes/* 15341862Sdes * Emit status message 15441862Sdes */ 15560924Sdesvoid 15675891Sarchie_fetch_info(const char *fmt, ...) 15741862Sdes{ 15890267Sdes va_list ap; 15990267Sdes 16090267Sdes va_start(ap, fmt); 16190267Sdes vfprintf(stderr, fmt, ap); 16290267Sdes va_end(ap); 16390267Sdes fputc('\n', stderr); 16441862Sdes} 16541862Sdes 16641862Sdes 16740939Sdes/*** Network-related utility functions ***************************************/ 16840939Sdes 16940939Sdes/* 17068551Sdes * Return the default port for a scheme 17168551Sdes */ 17268551Sdesint 17375891Sarchie_fetch_default_port(const char *scheme) 17468551Sdes{ 17590267Sdes struct servent *se; 17668551Sdes 17790267Sdes if ((se = getservbyname(scheme, "tcp")) != NULL) 17890267Sdes return (ntohs(se->s_port)); 17990267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 18090267Sdes return (FTP_DEFAULT_PORT); 18190267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 18290267Sdes return (HTTP_DEFAULT_PORT); 18390267Sdes return (0); 18468551Sdes} 18568551Sdes 18668551Sdes/* 18768551Sdes * Return the default proxy port for a scheme 18868551Sdes */ 18968551Sdesint 19075891Sarchie_fetch_default_proxy_port(const char *scheme) 19168551Sdes{ 19290267Sdes if (strcasecmp(scheme, SCHEME_FTP) == 0) 19390267Sdes return (FTP_DEFAULT_PROXY_PORT); 19490267Sdes if (strcasecmp(scheme, SCHEME_HTTP) == 0) 19590267Sdes return (HTTP_DEFAULT_PROXY_PORT); 19690267Sdes return (0); 19768551Sdes} 19868551Sdes 19998117Sdes 20068551Sdes/* 20197866Sdes * Create a connection for an existing descriptor. 20297866Sdes */ 20397866Sdesconn_t * 20497866Sdes_fetch_reopen(int sd) 20597866Sdes{ 20697866Sdes conn_t *conn; 20797866Sdes 20897866Sdes /* allocate and fill connection structure */ 209109967Sdes if ((conn = calloc(1, sizeof(*conn))) == NULL) 21097866Sdes return (NULL); 21197866Sdes conn->sd = sd; 21298117Sdes ++conn->ref; 21397866Sdes return (conn); 21497866Sdes} 21597866Sdes 21697866Sdes 21797866Sdes/* 21898117Sdes * Bump a connection's reference count. 21998117Sdes */ 22098117Sdesconn_t * 22198117Sdes_fetch_ref(conn_t *conn) 22298117Sdes{ 22398117Sdes 22498117Sdes ++conn->ref; 22598117Sdes return (conn); 22698117Sdes} 22798117Sdes 22898117Sdes 22998117Sdes/* 23040939Sdes * Establish a TCP connection to the specified port on the specified host. 23140939Sdes */ 23297856Sdesconn_t * 23375891Sarchie_fetch_connect(const char *host, int port, int af, int verbose) 23440939Sdes{ 23597856Sdes conn_t *conn; 23690267Sdes char pbuf[10]; 23790267Sdes struct addrinfo hints, *res, *res0; 23890267Sdes int sd, err; 23940939Sdes 24090267Sdes DEBUG(fprintf(stderr, "---> %s:%d\n", host, port)); 24141862Sdes 24290267Sdes if (verbose) 24390267Sdes _fetch_info("looking up %s", host); 24440939Sdes 24590267Sdes /* look up host name and set up socket address structure */ 24690267Sdes snprintf(pbuf, sizeof(pbuf), "%d", port); 24790267Sdes memset(&hints, 0, sizeof(hints)); 24890267Sdes hints.ai_family = af; 24990267Sdes hints.ai_socktype = SOCK_STREAM; 25090267Sdes hints.ai_protocol = 0; 25190267Sdes if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) { 25290267Sdes _netdb_seterr(err); 25397856Sdes return (NULL); 25490267Sdes } 25590267Sdes 25690267Sdes if (verbose) 25790267Sdes _fetch_info("connecting to %s:%d", host, port); 25890267Sdes 25990267Sdes /* try to connect */ 26090267Sdes for (sd = -1, res = res0; res; res = res->ai_next) { 26190267Sdes if ((sd = socket(res->ai_family, res->ai_socktype, 26262981Sdes res->ai_protocol)) == -1) 26390267Sdes continue; 26490267Sdes if (connect(sd, res->ai_addr, res->ai_addrlen) != -1) 26590267Sdes break; 26690267Sdes close(sd); 26790267Sdes sd = -1; 26890267Sdes } 26990267Sdes freeaddrinfo(res0); 27090267Sdes if (sd == -1) { 27190267Sdes _fetch_syserr(); 27297856Sdes return (NULL); 27390267Sdes } 27440939Sdes 275103459Sfenner if ((conn = _fetch_reopen(sd)) == NULL) { 276103459Sfenner _fetch_syserr(); 27797856Sdes close(sd); 278103459Sfenner } 27997856Sdes return (conn); 28040939Sdes} 28141989Sdes 28241989Sdes 28355557Sdes/* 28497868Sdes * Enable SSL on a connection. 28597868Sdes */ 28697868Sdesint 28797868Sdes_fetch_ssl(conn_t *conn, int verbose) 28897868Sdes{ 28997868Sdes 29097891Sdes#ifdef WITH_SSL 29197868Sdes /* Init the SSL library and context */ 29297868Sdes if (!SSL_library_init()){ 29397868Sdes fprintf(stderr, "SSL library init failed\n"); 29497868Sdes return (-1); 29597868Sdes } 29697868Sdes 29797868Sdes SSL_load_error_strings(); 29897868Sdes 29997868Sdes conn->ssl_meth = SSLv23_client_method(); 30097868Sdes conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth); 301108579Sdes SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY); 30297868Sdes 30397868Sdes conn->ssl = SSL_new(conn->ssl_ctx); 30497868Sdes if (conn->ssl == NULL){ 30597868Sdes fprintf(stderr, "SSL context creation failed\n"); 30697868Sdes return (-1); 30797868Sdes } 30897868Sdes SSL_set_fd(conn->ssl, conn->sd); 30997868Sdes if (SSL_connect(conn->ssl) == -1){ 31097868Sdes ERR_print_errors_fp(stderr); 31197868Sdes return (-1); 31297868Sdes } 31397868Sdes 31497868Sdes if (verbose) { 31597868Sdes X509_NAME *name; 31697868Sdes char *str; 31797868Sdes 31897868Sdes fprintf(stderr, "SSL connection established using %s\n", 31997868Sdes SSL_get_cipher(conn->ssl)); 32097868Sdes conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); 32197868Sdes name = X509_get_subject_name(conn->ssl_cert); 32297868Sdes str = X509_NAME_oneline(name, 0, 0); 32397868Sdes printf("Certificate subject: %s\n", str); 32497868Sdes free(str); 32597868Sdes name = X509_get_issuer_name(conn->ssl_cert); 32697868Sdes str = X509_NAME_oneline(name, 0, 0); 32797868Sdes printf("Certificate issuer: %s\n", str); 32897868Sdes free(str); 32997868Sdes } 33097868Sdes 33197868Sdes return (0); 33297891Sdes#else 33397891Sdes (void)conn; 33497891Sdes (void)verbose; 33597891Sdes fprintf(stderr, "SSL support disabled\n"); 33697891Sdes return (-1); 33797891Sdes#endif 33897868Sdes} 33997868Sdes 34098117Sdes 34197868Sdes/* 34297866Sdes * Read a character from a connection w/ timeout 34355557Sdes */ 34497866Sdesssize_t 34597866Sdes_fetch_read(conn_t *conn, char *buf, size_t len) 34655557Sdes{ 34790267Sdes struct timeval now, timeout, wait; 34890267Sdes fd_set readfds; 34997866Sdes ssize_t rlen, total; 35090267Sdes int r; 35190267Sdes 35255557Sdes if (fetchTimeout) { 35397866Sdes FD_ZERO(&readfds); 35490267Sdes gettimeofday(&timeout, NULL); 35590267Sdes timeout.tv_sec += fetchTimeout; 35655557Sdes } 35790267Sdes 35897866Sdes total = 0; 35997866Sdes while (len > 0) { 36097866Sdes while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) { 36197856Sdes FD_SET(conn->sd, &readfds); 36290267Sdes gettimeofday(&now, NULL); 36390267Sdes wait.tv_sec = timeout.tv_sec - now.tv_sec; 36490267Sdes wait.tv_usec = timeout.tv_usec - now.tv_usec; 36590267Sdes if (wait.tv_usec < 0) { 36690267Sdes wait.tv_usec += 1000000; 36790267Sdes wait.tv_sec--; 36890267Sdes } 369106186Sdes if (wait.tv_sec < 0) { 370106186Sdes errno = ETIMEDOUT; 371106186Sdes _fetch_syserr(); 372106186Sdes return (-1); 373106186Sdes } 37497866Sdes errno = 0; 37597856Sdes r = select(conn->sd + 1, &readfds, NULL, NULL, &wait); 37690267Sdes if (r == -1) { 37790267Sdes if (errno == EINTR && fetchRestartCalls) 37890267Sdes continue; 379106186Sdes _fetch_syserr(); 38090267Sdes return (-1); 38190267Sdes } 38290267Sdes } 38397891Sdes#ifdef WITH_SSL 38497866Sdes if (conn->ssl != NULL) 38597866Sdes rlen = SSL_read(conn->ssl, buf, len); 38697866Sdes else 38797891Sdes#endif 38897866Sdes rlen = read(conn->sd, buf, len); 389106049Sdes if (rlen == 0) 390106049Sdes break; 39197866Sdes if (rlen < 0) { 39290267Sdes if (errno == EINTR && fetchRestartCalls) 39390267Sdes continue; 39490267Sdes return (-1); 39590267Sdes } 39697866Sdes len -= rlen; 39797866Sdes buf += rlen; 39897866Sdes total += rlen; 39997866Sdes } 40097866Sdes return (total); 40197866Sdes} 40297866Sdes 40398117Sdes 40497866Sdes/* 40597866Sdes * Read a line of text from a connection w/ timeout 40697866Sdes */ 40797866Sdes#define MIN_BUF_SIZE 1024 40897866Sdes 40997866Sdesint 41097866Sdes_fetch_getln(conn_t *conn) 41197866Sdes{ 41297866Sdes char *tmp; 41397866Sdes size_t tmpsize; 414106186Sdes ssize_t len; 41597866Sdes char c; 41697866Sdes 41797866Sdes if (conn->buf == NULL) { 41897866Sdes if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) { 41997866Sdes errno = ENOMEM; 42097866Sdes return (-1); 42197866Sdes } 42297866Sdes conn->bufsize = MIN_BUF_SIZE; 42397866Sdes } 42497866Sdes 42597866Sdes conn->buf[0] = '\0'; 42697866Sdes conn->buflen = 0; 42797866Sdes 42897866Sdes do { 429106186Sdes len = _fetch_read(conn, &c, 1); 430106186Sdes if (len == -1) 43197866Sdes return (-1); 432106186Sdes if (len == 0) 433106137Sobrien break; 43497856Sdes conn->buf[conn->buflen++] = c; 43597856Sdes if (conn->buflen == conn->bufsize) { 43697856Sdes tmp = conn->buf; 43797856Sdes tmpsize = conn->bufsize * 2 + 1; 43897856Sdes if ((tmp = realloc(tmp, tmpsize)) == NULL) { 43990267Sdes errno = ENOMEM; 44090267Sdes return (-1); 44190267Sdes } 44297856Sdes conn->buf = tmp; 44397856Sdes conn->bufsize = tmpsize; 44490267Sdes } 44590267Sdes } while (c != '\n'); 44690267Sdes 44797856Sdes conn->buf[conn->buflen] = '\0'; 44897856Sdes DEBUG(fprintf(stderr, "<<< %s", conn->buf)); 44990267Sdes return (0); 45055557Sdes} 45155557Sdes 45255557Sdes 45362981Sdes/* 45497866Sdes * Write to a connection w/ timeout 45562981Sdes */ 45697866Sdesssize_t 45797866Sdes_fetch_write(conn_t *conn, const char *buf, size_t len) 45897866Sdes{ 459106175Simp struct iovec iov; 460106175Simp 461106175Simp iov.iov_base = __DECONST(char *, buf); 462106175Simp iov.iov_len = len; 463106175Simp return _fetch_writev(conn, &iov, 1); 464106175Simp} 465106175Simp 466106175Simp/* 467106175Simp * Write a vector to a connection w/ timeout 468106175Simp * Note: can modify the iovec. 469106175Simp */ 470106175Simpssize_t 471106175Simp_fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt) 472106175Simp{ 47397866Sdes struct timeval now, timeout, wait; 47497866Sdes fd_set writefds; 47597866Sdes ssize_t wlen, total; 47697866Sdes int r; 47797866Sdes 47897866Sdes if (fetchTimeout) { 47997866Sdes FD_ZERO(&writefds); 48097866Sdes gettimeofday(&timeout, NULL); 48197866Sdes timeout.tv_sec += fetchTimeout; 48297866Sdes } 48397866Sdes 484106175Simp total = 0; 485106175Simp while (iovcnt > 0) { 48697866Sdes while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) { 48797866Sdes FD_SET(conn->sd, &writefds); 48897866Sdes gettimeofday(&now, NULL); 48997866Sdes wait.tv_sec = timeout.tv_sec - now.tv_sec; 49097866Sdes wait.tv_usec = timeout.tv_usec - now.tv_usec; 49197866Sdes if (wait.tv_usec < 0) { 49297866Sdes wait.tv_usec += 1000000; 49397866Sdes wait.tv_sec--; 49497866Sdes } 49597866Sdes if (wait.tv_sec < 0) { 49697866Sdes errno = ETIMEDOUT; 497106186Sdes _fetch_syserr(); 49897866Sdes return (-1); 49997866Sdes } 50097866Sdes errno = 0; 50197866Sdes r = select(conn->sd + 1, NULL, &writefds, NULL, &wait); 50297866Sdes if (r == -1) { 50397866Sdes if (errno == EINTR && fetchRestartCalls) 50497866Sdes continue; 50597866Sdes return (-1); 50697866Sdes } 50797866Sdes } 50897866Sdes errno = 0; 50997891Sdes#ifdef WITH_SSL 51097866Sdes if (conn->ssl != NULL) 511106175Simp wlen = SSL_write(conn->ssl, 512106175Simp iov->iov_base, iov->iov_len); 51397866Sdes else 51497891Sdes#endif 515106175Simp wlen = writev(conn->sd, iov, iovcnt); 516106175Simp if (wlen == 0) { 51797866Sdes /* we consider a short write a failure */ 518106175Simp errno = EPIPE; 519106175Simp _fetch_syserr(); 52097866Sdes return (-1); 521106175Simp } 52297866Sdes if (wlen < 0) { 52397866Sdes if (errno == EINTR && fetchRestartCalls) 52497866Sdes continue; 52597866Sdes return (-1); 52697866Sdes } 52797866Sdes total += wlen; 528106175Simp while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) { 529106175Simp wlen -= iov->iov_len; 530106175Simp iov++; 531106175Simp iovcnt--; 532106175Simp } 533106175Simp if (iovcnt > 0) { 534106175Simp iov->iov_len -= wlen; 535106175Simp iov->iov_base = __DECONST(char *, iov->iov_base) + wlen; 536106175Simp } 53797866Sdes } 53897866Sdes return (total); 53997866Sdes} 54097866Sdes 54198117Sdes 54297866Sdes/* 54397866Sdes * Write a line of text to a connection w/ timeout 54497866Sdes */ 54562981Sdesint 54697856Sdes_fetch_putln(conn_t *conn, const char *str, size_t len) 54762981Sdes{ 548106175Simp struct iovec iov[2]; 549106205Sdes int ret; 55098748Sdes 55198748Sdes DEBUG(fprintf(stderr, ">>> %s\n", str)); 552106175Simp iov[0].iov_base = __DECONST(char *, str); 553106175Simp iov[0].iov_len = len; 554106175Simp iov[1].iov_base = __DECONST(char *, ENDL); 555109967Sdes iov[1].iov_len = sizeof(ENDL); 556106205Sdes if (len == 0) 557106205Sdes ret = _fetch_writev(conn, &iov[1], 1); 558106205Sdes else 559106205Sdes ret = _fetch_writev(conn, iov, 2); 560106205Sdes if (ret == -1) 56190267Sdes return (-1); 56290267Sdes return (0); 56362981Sdes} 56462981Sdes 56562981Sdes 56697856Sdes/* 56797856Sdes * Close connection 56897856Sdes */ 56997856Sdesint 57097856Sdes_fetch_close(conn_t *conn) 57197856Sdes{ 57297856Sdes int ret; 57397856Sdes 57498117Sdes if (--conn->ref > 0) 57598117Sdes return (0); 57697856Sdes ret = close(conn->sd); 57797856Sdes free(conn); 57897856Sdes return (ret); 57997856Sdes} 58097856Sdes 58197856Sdes 58241989Sdes/*** Directory-related utility functions *************************************/ 58341989Sdes 58441989Sdesint 58541989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len, 58690267Sdes const char *name, struct url_stat *us) 58741989Sdes{ 58890267Sdes struct url_ent *tmp; 58941989Sdes 59090267Sdes if (*p == NULL) { 59190268Sdes *size = 0; 59290267Sdes *len = 0; 59341989Sdes } 59441989Sdes 59590267Sdes if (*len >= *size - 1) { 596109967Sdes tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p)); 59790267Sdes if (tmp == NULL) { 59890267Sdes errno = ENOMEM; 59990267Sdes _fetch_syserr(); 60090267Sdes return (-1); 60190267Sdes } 60290268Sdes *size = (*size * 2 + 1); 60390267Sdes *p = tmp; 60490267Sdes } 60541989Sdes 60690267Sdes tmp = *p + *len; 60790267Sdes snprintf(tmp->name, PATH_MAX, "%s", name); 608109967Sdes bcopy(us, &tmp->stat, sizeof(*us)); 60941989Sdes 61090267Sdes (*len)++; 61190267Sdes (++tmp)->name[0] = 0; 61290267Sdes 61390267Sdes return (0); 61441989Sdes} 615109695Sdes 616109695Sdes 617109695Sdes/*** Authentication-related utility functions ********************************/ 618109695Sdes 619109695Sdesstatic const char * 620109695Sdes_fetch_read_word(FILE *f) 621109695Sdes{ 622109695Sdes static char word[1024]; 623109695Sdes 624109695Sdes if (fscanf(f, " %1024s ", word) != 1) 625109695Sdes return (NULL); 626109695Sdes return (word); 627109695Sdes} 628109695Sdes 629109695Sdes/* 630109695Sdes * Get authentication data for a URL from .netrc 631109695Sdes */ 632109695Sdesint 633109695Sdes_fetch_netrc_auth(struct url *url) 634109695Sdes{ 635109695Sdes char fn[PATH_MAX]; 636109695Sdes const char *word; 637109695Sdes char *p; 638109695Sdes FILE *f; 639109695Sdes 640109695Sdes if ((p = getenv("NETRC")) != NULL) { 641109967Sdes if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) { 642109695Sdes _fetch_info("$NETRC specifies a file name " 643109695Sdes "longer than PATH_MAX"); 644109695Sdes return (-1); 645109695Sdes } 646109695Sdes } else { 647109695Sdes if ((p = getenv("HOME")) != NULL) { 648109695Sdes struct passwd *pwd; 649109695Sdes 650109695Sdes if ((pwd = getpwuid(getuid())) == NULL || 651109695Sdes (p = pwd->pw_dir) == NULL) 652109695Sdes return (-1); 653109695Sdes } 654109967Sdes if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn)) 655109695Sdes return (-1); 656109695Sdes } 657109695Sdes 658109695Sdes if ((f = fopen(fn, "r")) == NULL) 659109695Sdes return (-1); 660109695Sdes while ((word = _fetch_read_word(f)) != NULL) { 661109695Sdes if (strcmp(word, "default") == 0) { 662109695Sdes DEBUG(_fetch_info("Using default .netrc settings")); 663109695Sdes break; 664109695Sdes } 665109695Sdes if (strcmp(word, "machine") == 0 && 666109695Sdes (word = _fetch_read_word(f)) != NULL && 667109695Sdes strcasecmp(word, url->host) == 0) { 668109695Sdes DEBUG(_fetch_info("Using .netrc settings for %s", word)); 669109695Sdes break; 670109695Sdes } 671109695Sdes } 672109695Sdes if (word == NULL) 673109695Sdes goto ferr; 674109695Sdes while ((word = _fetch_read_word(f)) != NULL) { 675109695Sdes if (strcmp(word, "login") == 0) { 676109695Sdes if ((word = _fetch_read_word(f)) == NULL) 677109695Sdes goto ferr; 678109967Sdes if (snprintf(url->user, sizeof(url->user), 679109960Sjwd "%s", word) > (int)sizeof(url->user)) { 680109695Sdes _fetch_info("login name in .netrc is too long"); 681109695Sdes url->user[0] = '\0'; 682109695Sdes } 683109695Sdes } else if (strcmp(word, "password") == 0) { 684109695Sdes if ((word = _fetch_read_word(f)) == NULL) 685109695Sdes goto ferr; 686109967Sdes if (snprintf(url->pwd, sizeof(url->pwd), 687109960Sjwd "%s", word) > (int)sizeof(url->pwd)) { 688109695Sdes _fetch_info("password in .netrc is too long"); 689109695Sdes url->pwd[0] = '\0'; 690109695Sdes } 691109695Sdes } else if (strcmp(word, "account") == 0) { 692109695Sdes if ((word = _fetch_read_word(f)) == NULL) 693109695Sdes goto ferr; 694109695Sdes /* XXX not supported! */ 695109695Sdes } else { 696109695Sdes break; 697109695Sdes } 698109695Sdes } 699109695Sdes fclose(f); 700109695Sdes return (0); 701109695Sdes ferr: 702109695Sdes fclose(f); 703109695Sdes return (-1); 704109695Sdes} 705