Home | History | Annotate | Line # | Download | only in libfetch
      1 /*-
      2  * Copyright (c) 1998-2004 Dag-Erling Codan Smrgrav
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer
     10  *    in this position and unchanged.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "free2net.h"
     30 
     31 #include <sys/cdefs.h>
     32 __FBSDID("$FreeBSD: src/lib/libfetch/common.c,v 1.48.4.2 2006/11/11 00:16:07 des Exp $");
     33 
     34 #include <sys/param.h>
     35 #include <sys/socket.h>
     36 #include <sys/time.h>
     37 #include <sys/uio.h>
     38 #include <netinet/in.h>
     39 
     40 #include <errno.h>
     41 #include <netdb.h>
     42 #include <pwd.h>
     43 #include <stdarg.h>
     44 #include <stdlib.h>
     45 #include <stdio.h>
     46 #include <string.h>
     47 #include <unistd.h>
     48 
     49 #include "fetch.h"
     50 #include "common.h"
     51 
     52 
     53 /*** Local data **************************************************************/
     54 
     55 /*
     56  * Error messages for resolver errors
     57  */
     58 static struct fetcherr _netdb_errlist[] = {
     59 #ifdef EAI_NODATA
     60 	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
     61 #endif
     62 	{ EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
     63 	{ EAI_FAIL,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
     64 	{ EAI_NONAME,	FETCH_RESOLV,	"No address record" },
     65 	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
     66 };
     67 
     68 /* End-of-Line */
     69 static const char ENDL[2] = "\r\n";
     70 
     71 
     72 /*** Error-reporting functions ***********************************************/
     73 
     74 /*
     75  * Map error code to string
     76  */
     77 static struct fetcherr *
     78 _fetch_finderr(struct fetcherr *p, int e)
     79 {
     80 	while (p->num != -1 && p->num != e)
     81 		p++;
     82 	return (p);
     83 }
     84 
     85 /*
     86  * Set error code
     87  */
     88 void
     89 _fetch_seterr(struct fetcherr *p, int e)
     90 {
     91 	p = _fetch_finderr(p, e);
     92 	fetchLastErrCode = p->cat;
     93 	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
     94 }
     95 
     96 /*
     97  * Set error code according to errno
     98  */
     99 void
    100 _fetch_syserr(void)
    101 {
    102 	switch (errno) {
    103 	case 0:
    104 		fetchLastErrCode = FETCH_OK;
    105 		break;
    106 	case EPERM:
    107 	case EACCES:
    108 	case EROFS:
    109 	case EAUTH:
    110 	case ENEEDAUTH:
    111 		fetchLastErrCode = FETCH_AUTH;
    112 		break;
    113 	case ENOENT:
    114 	case EISDIR: /* XXX */
    115 		fetchLastErrCode = FETCH_UNAVAIL;
    116 		break;
    117 	case ENOMEM:
    118 		fetchLastErrCode = FETCH_MEMORY;
    119 		break;
    120 	case EBUSY:
    121 	case EAGAIN:
    122 		fetchLastErrCode = FETCH_TEMP;
    123 		break;
    124 	case EEXIST:
    125 		fetchLastErrCode = FETCH_EXISTS;
    126 		break;
    127 	case ENOSPC:
    128 		fetchLastErrCode = FETCH_FULL;
    129 		break;
    130 	case EADDRINUSE:
    131 	case EADDRNOTAVAIL:
    132 	case ENETDOWN:
    133 	case ENETUNREACH:
    134 	case ENETRESET:
    135 	case EHOSTUNREACH:
    136 		fetchLastErrCode = FETCH_NETWORK;
    137 		break;
    138 	case ECONNABORTED:
    139 	case ECONNRESET:
    140 		fetchLastErrCode = FETCH_ABORT;
    141 		break;
    142 	case ETIMEDOUT:
    143 		fetchLastErrCode = FETCH_TIMEOUT;
    144 		break;
    145 	case ECONNREFUSED:
    146 	case EHOSTDOWN:
    147 		fetchLastErrCode = FETCH_DOWN;
    148 		break;
    149 default:
    150 		fetchLastErrCode = FETCH_UNKNOWN;
    151 	}
    152 	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
    153 }
    154 
    155 
    156 /*
    157  * Emit status message
    158  */
    159 void
    160 _fetch_info(const char *fmt, ...)
    161 {
    162 	va_list ap;
    163 
    164 	va_start(ap, fmt);
    165 	vfprintf(stderr, fmt, ap);
    166 	va_end(ap);
    167 	fputc('\n', stderr);
    168 }
    169 
    170 
    171 /*** Network-related utility functions ***************************************/
    172 
    173 /*
    174  * Return the default port for a scheme
    175  */
    176 int
    177 _fetch_default_port(const char *scheme)
    178 {
    179 	struct servent *se;
    180 
    181 	if ((se = getservbyname(scheme, "tcp")) != NULL)
    182 		return (ntohs(se->s_port));
    183 	if (strcasecmp(scheme, SCHEME_FTP) == 0)
    184 		return (FTP_DEFAULT_PORT);
    185 	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
    186 		return (HTTP_DEFAULT_PORT);
    187 	return (0);
    188 }
    189 
    190 /*
    191  * Return the default proxy port for a scheme
    192  */
    193 int
    194 _fetch_default_proxy_port(const char *scheme)
    195 {
    196 	if (strcasecmp(scheme, SCHEME_FTP) == 0)
    197 		return (FTP_DEFAULT_PROXY_PORT);
    198 	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
    199 		return (HTTP_DEFAULT_PROXY_PORT);
    200 	return (0);
    201 }
    202 
    203 
    204 /*
    205  * Create a connection for an existing descriptor.
    206  */
    207 conn_t *
    208 _fetch_reopen(int sd)
    209 {
    210 	conn_t *conn;
    211 
    212 	/* allocate and fill connection structure */
    213 	if ((conn = calloc(1, sizeof(*conn))) == NULL)
    214 		return (NULL);
    215 	conn->sd = sd;
    216 	++conn->ref;
    217 	return (conn);
    218 }
    219 
    220 
    221 /*
    222  * Bump a connection's reference count.
    223  */
    224 conn_t *
    225 _fetch_ref(conn_t *conn)
    226 {
    227 
    228 	++conn->ref;
    229 	return (conn);
    230 }
    231 
    232 
    233 /*
    234  * Bind a socket to a specific local address
    235  */
    236 int
    237 _fetch_bind(int sd, int af, const char *addr)
    238 {
    239 	struct addrinfo hints, *res, *res0;
    240 
    241 	memset(&hints, 0, sizeof(hints));
    242 	hints.ai_family = af;
    243 	hints.ai_socktype = SOCK_STREAM;
    244 	hints.ai_protocol = 0;
    245 	if (getaddrinfo(addr, NULL, &hints, &res0) != 0)
    246 		return (-1);
    247 	for (res = res0; res; res = res->ai_next)
    248 		if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
    249 			return (0);
    250 	return (-1);
    251 }
    252 
    253 
    254 /*
    255  * Establish a TCP connection to the specified port on the specified host.
    256  */
    257 conn_t *
    258 _fetch_connect(const char *host, int port, int af, int verbose)
    259 {
    260 	conn_t *conn;
    261 	char pbuf[10];
    262 	const char *bindaddr;
    263 	struct addrinfo hints, *res, *res0;
    264 	int sd, err;
    265 
    266 	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
    267 
    268 	if (verbose)
    269 		_fetch_info("looking up %s", host);
    270 
    271 	/* look up host name and set up socket address structure */
    272 	snprintf(pbuf, sizeof(pbuf), "%d", port);
    273 	memset(&hints, 0, sizeof(hints));
    274 	hints.ai_family = af;
    275 	hints.ai_socktype = SOCK_STREAM;
    276 	hints.ai_protocol = 0;
    277 	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
    278 		_netdb_seterr(err);
    279 		return (NULL);
    280 	}
    281 	bindaddr = getenv("FETCH_BIND_ADDRESS");
    282 
    283 	if (verbose)
    284 		_fetch_info("connecting to %s:%d", host, port);
    285 
    286 	/* try to connect */
    287 	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
    288 		if ((sd = socket(res->ai_family, res->ai_socktype,
    289 			 res->ai_protocol)) == -1)
    290 			continue;
    291 		if (bindaddr != NULL && *bindaddr != '\0' &&
    292 		    _fetch_bind(sd, res->ai_family, bindaddr) != 0) {
    293 			_fetch_info("failed to bind to '%s'", bindaddr);
    294 			close(sd);
    295 			continue;
    296 		}
    297 		if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
    298 			break;
    299 		close(sd);
    300 	}
    301 	freeaddrinfo(res0);
    302 	if (sd == -1) {
    303 		_fetch_syserr();
    304 		return (NULL);
    305 	}
    306 
    307 	if ((conn = _fetch_reopen(sd)) == NULL) {
    308 		_fetch_syserr();
    309 		close(sd);
    310 	}
    311 	return (conn);
    312 }
    313 
    314 
    315 /*
    316  * Enable SSL on a connection.
    317  */
    318 int
    319 _fetch_ssl(conn_t *conn, int verbose)
    320 {
    321 
    322 #ifdef WITH_SSL
    323 	/* Init the SSL library and context */
    324 	if (!SSL_library_init()){
    325 		fprintf(stderr, "SSL library init failed\n");
    326 		return (-1);
    327 	}
    328 
    329 	SSL_load_error_strings();
    330 
    331 	conn->ssl_meth = SSLv23_client_method();
    332 	conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
    333 	SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
    334 
    335 	conn->ssl = SSL_new(conn->ssl_ctx);
    336 	if (conn->ssl == NULL){
    337 		fprintf(stderr, "SSL context creation failed\n");
    338 		return (-1);
    339 	}
    340 	SSL_set_fd(conn->ssl, conn->sd);
    341 	if (SSL_connect(conn->ssl) == -1){
    342 		ERR_print_errors_fp(stderr);
    343 		return (-1);
    344 	}
    345 
    346 	if (verbose) {
    347 		X509_NAME *name;
    348 		char *str;
    349 
    350 		fprintf(stderr, "SSL connection established using %s\n",
    351 		    SSL_get_cipher(conn->ssl));
    352 		conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
    353 		name = X509_get_subject_name(conn->ssl_cert);
    354 		str = X509_NAME_oneline(name, 0, 0);
    355 		printf("Certificate subject: %s\n", str);
    356 		free(str);
    357 		name = X509_get_issuer_name(conn->ssl_cert);
    358 		str = X509_NAME_oneline(name, 0, 0);
    359 		printf("Certificate issuer: %s\n", str);
    360 		free(str);
    361 	}
    362 
    363 	return (0);
    364 #else
    365 	/* LINTED */
    366 	(void)conn;
    367 	/* LINTED */
    368 	(void)verbose;
    369 	fprintf(stderr, "SSL support disabled\n");
    370 	return (-1);
    371 #endif
    372 }
    373 
    374 
    375 /*
    376  * Read a character from a connection w/ timeout
    377  */
    378 ssize_t
    379 _fetch_read(conn_t *conn, char *buf, size_t len)
    380 {
    381 	struct timeval now, timeout, wait;
    382 	fd_set readfds;
    383 	ssize_t rlen, total;
    384 	int r;
    385 
    386 	if (fetchTimeout) {
    387 		FD_ZERO(&readfds);
    388 		gettimeofday(&timeout, NULL);
    389 		timeout.tv_sec += fetchTimeout;
    390 	}
    391 
    392 	total = 0;
    393 	while (len > 0) {
    394 		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
    395 			FD_SET(conn->sd, &readfds);
    396 			gettimeofday(&now, NULL);
    397 			wait.tv_sec = timeout.tv_sec - now.tv_sec;
    398 			wait.tv_usec = timeout.tv_usec - now.tv_usec;
    399 			if (wait.tv_usec < 0) {
    400 				wait.tv_usec += 1000000;
    401 				wait.tv_sec--;
    402 			}
    403 			if (wait.tv_sec < 0) {
    404 				errno = ETIMEDOUT;
    405 				_fetch_syserr();
    406 				return (-1);
    407 			}
    408 			errno = 0;
    409 			r = select(conn->sd + 1, &readfds, NULL, NULL, &wait);
    410 			if (r == -1) {
    411 				if (errno == EINTR && fetchRestartCalls)
    412 					continue;
    413 				_fetch_syserr();
    414 				return (-1);
    415 			}
    416 		}
    417 #ifdef WITH_SSL
    418 		if (conn->ssl != NULL)
    419 			rlen = SSL_read(conn->ssl, buf, len);
    420 		else
    421 #endif
    422 			rlen = read(conn->sd, buf, len);
    423 		if (rlen == 0)
    424 			break;
    425 		if (rlen < 0) {
    426 			if (errno == EINTR && fetchRestartCalls)
    427 				continue;
    428 			return (-1);
    429 		}
    430 		len -= rlen;
    431 		buf += rlen;
    432 		total += rlen;
    433 	}
    434 	return (total);
    435 }
    436 
    437 
    438 /*
    439  * Read a line of text from a connection w/ timeout
    440  */
    441 #define MIN_BUF_SIZE 1024
    442 
    443 int
    444 _fetch_getln(conn_t *conn)
    445 {
    446 	char *tmp;
    447 	size_t tmpsize;
    448 	ssize_t len;
    449 	char c;
    450 
    451 	if (conn->buf == NULL) {
    452 		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
    453 			errno = ENOMEM;
    454 			return (-1);
    455 		}
    456 		conn->bufsize = MIN_BUF_SIZE;
    457 	}
    458 
    459 	conn->buf[0] = '\0';
    460 	conn->buflen = 0;
    461 
    462 	do {
    463 		len = _fetch_read(conn, &c, 1);
    464 		if (len == -1)
    465 			return (-1);
    466 		if (len == 0)
    467 			break;
    468 		conn->buf[conn->buflen++] = c;
    469 		if (conn->buflen == conn->bufsize) {
    470 			tmp = conn->buf;
    471 			tmpsize = conn->bufsize * 2 + 1;
    472 			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
    473 				errno = ENOMEM;
    474 				return (-1);
    475 			}
    476 			conn->buf = tmp;
    477 			conn->bufsize = tmpsize;
    478 		}
    479 	} while (c != '\n');
    480 
    481 	conn->buf[conn->buflen] = '\0';
    482 	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
    483 	return (0);
    484 }
    485 
    486 
    487 /*
    488  * Write to a connection w/ timeout
    489  */
    490 ssize_t
    491 _fetch_write(conn_t *conn, const char *buf, size_t len)
    492 {
    493 	struct iovec iov;
    494 
    495 	iov.iov_base = __DECONST(char *, buf);
    496 	iov.iov_len = len;
    497 	return _fetch_writev(conn, &iov, 1);
    498 }
    499 
    500 /*
    501  * Write a vector to a connection w/ timeout
    502  * Note: can modify the iovec.
    503  */
    504 ssize_t
    505 _fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
    506 {
    507 	struct timeval now, timeout, wait;
    508 	fd_set writefds;
    509 	ssize_t wlen, total;
    510 	int r;
    511 
    512 	if (fetchTimeout) {
    513 		FD_ZERO(&writefds);
    514 		gettimeofday(&timeout, NULL);
    515 		timeout.tv_sec += fetchTimeout;
    516 	}
    517 
    518 	total = 0;
    519 	while (iovcnt > 0) {
    520 		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
    521 			FD_SET(conn->sd, &writefds);
    522 			gettimeofday(&now, NULL);
    523 			wait.tv_sec = timeout.tv_sec - now.tv_sec;
    524 			wait.tv_usec = timeout.tv_usec - now.tv_usec;
    525 			if (wait.tv_usec < 0) {
    526 				wait.tv_usec += 1000000;
    527 				wait.tv_sec--;
    528 			}
    529 			if (wait.tv_sec < 0) {
    530 				errno = ETIMEDOUT;
    531 				_fetch_syserr();
    532 				return (-1);
    533 			}
    534 			errno = 0;
    535 			r = select(conn->sd + 1, NULL, &writefds, NULL, &wait);
    536 			if (r == -1) {
    537 				if (errno == EINTR && fetchRestartCalls)
    538 					continue;
    539 				return (-1);
    540 			}
    541 		}
    542 		errno = 0;
    543 #ifdef WITH_SSL
    544 		if (conn->ssl != NULL)
    545 			wlen = SSL_write(conn->ssl,
    546 			    iov->iov_base, iov->iov_len);
    547 		else
    548 #endif
    549 			wlen = writev(conn->sd, iov, iovcnt);
    550 		if (wlen == 0) {
    551 			/* we consider a short write a failure */
    552 			errno = EPIPE;
    553 			_fetch_syserr();
    554 			return (-1);
    555 		}
    556 		if (wlen < 0) {
    557 			if (errno == EINTR && fetchRestartCalls)
    558 				continue;
    559 			return (-1);
    560 		}
    561 		total += wlen;
    562 		while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
    563 			wlen -= iov->iov_len;
    564 			iov++;
    565 			iovcnt--;
    566 		}
    567 		if (iovcnt > 0) {
    568 			iov->iov_len -= wlen;
    569 			iov->iov_base = (__DECONST(char *, iov->iov_base)) + wlen;
    570 		}
    571 	}
    572 	return (total);
    573 }
    574 
    575 
    576 /*
    577  * Write a line of text to a connection w/ timeout
    578  */
    579 int
    580 _fetch_putln(conn_t *conn, const char *str, size_t len)
    581 {
    582 	struct iovec iov[2];
    583 	int ret;
    584 
    585 	DEBUG(fprintf(stderr, ">>> %s\n", str));
    586 	iov[0].iov_base = __DECONST(char *, str);
    587 	iov[0].iov_len = len;
    588 	iov[1].iov_base = __DECONST(char *, ENDL);
    589 	iov[1].iov_len = sizeof(ENDL);
    590 	if (len == 0)
    591 		ret = _fetch_writev(conn, &iov[1], 1);
    592 	else
    593 		ret = _fetch_writev(conn, iov, 2);
    594 	if (ret == -1)
    595 		return (-1);
    596 	return (0);
    597 }
    598 
    599 
    600 /*
    601  * Close connection
    602  */
    603 int
    604 _fetch_close(conn_t *conn)
    605 {
    606 	int ret;
    607 
    608 	if (--conn->ref > 0)
    609 		return (0);
    610 	ret = close(conn->sd);
    611 	free(conn->buf);
    612 	free(conn);
    613 	return (ret);
    614 }
    615 
    616 
    617 /*** Directory-related utility functions *************************************/
    618 
    619 int
    620 _fetch_add_entry(struct url_ent **p, int *size, int *len,
    621     const char *name, struct url_stat *us)
    622 {
    623 	struct url_ent *tmp;
    624 
    625 	if (*p == NULL) {
    626 		*size = 0;
    627 		*len = 0;
    628 	}
    629 
    630 	if (*len >= *size - 1) {
    631 		tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
    632 		if (tmp == NULL) {
    633 			errno = ENOMEM;
    634 			_fetch_syserr();
    635 			return (-1);
    636 		}
    637 		*size = (*size * 2 + 1);
    638 		*p = tmp;
    639 	}
    640 
    641 	tmp = *p + *len;
    642 	snprintf(tmp->name, PATH_MAX, "%s", name);
    643 	bcopy(us, &tmp->stat, sizeof(*us));
    644 
    645 	(*len)++;
    646 	(++tmp)->name[0] = 0;
    647 
    648 	return (0);
    649 }
    650 
    651 
    652 /*** Authentication-related utility functions ********************************/
    653 
    654 static const char *
    655 _fetch_read_word(FILE *f)
    656 {
    657 	static char word[1024];
    658 
    659 	if (fscanf(f, " %1024s ", word) != 1)
    660 		return (NULL);
    661 	return (word);
    662 }
    663 
    664 /*
    665  * Get authentication data for a URL from .netrc
    666  */
    667 int
    668 _fetch_netrc_auth(struct url *url)
    669 {
    670 	char fn[PATH_MAX];
    671 	const char *word;
    672 	char *p;
    673 	FILE *f;
    674 
    675 	if ((p = getenv("NETRC")) != NULL) {
    676 		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
    677 			_fetch_info("$NETRC specifies a file name "
    678 			    "longer than PATH_MAX");
    679 			return (-1);
    680 		}
    681 	} else {
    682 		if ((p = getenv("HOME")) != NULL) {
    683 			struct passwd *pwd;
    684 
    685 			if ((pwd = getpwuid(getuid())) == NULL ||
    686 			    (p = pwd->pw_dir) == NULL)
    687 				return (-1);
    688 		}
    689 		if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
    690 			return (-1);
    691 	}
    692 
    693 	if ((f = fopen(fn, "r")) == NULL)
    694 		return (-1);
    695 	while ((word = _fetch_read_word(f)) != NULL) {
    696 		if (strcmp(word, "default") == 0) {
    697 			DEBUG(_fetch_info("Using default .netrc settings"));
    698 			break;
    699 		}
    700 		if (strcmp(word, "machine") == 0 &&
    701 		    (word = _fetch_read_word(f)) != NULL &&
    702 		    strcasecmp(word, url->host) == 0) {
    703 			DEBUG(_fetch_info("Using .netrc settings for %s", word));
    704 			break;
    705 		}
    706 	}
    707 	if (word == NULL)
    708 		goto ferr;
    709 	while ((word = _fetch_read_word(f)) != NULL) {
    710 		if (strcmp(word, "login") == 0) {
    711 			if ((word = _fetch_read_word(f)) == NULL)
    712 				goto ferr;
    713 			if (snprintf(url->user, sizeof(url->user),
    714 				"%s", word) > (int)sizeof(url->user)) {
    715 				_fetch_info("login name in .netrc is too long");
    716 				url->user[0] = '\0';
    717 			}
    718 		} else if (strcmp(word, "password") == 0) {
    719 			if ((word = _fetch_read_word(f)) == NULL)
    720 				goto ferr;
    721 			if (snprintf(url->pwd, sizeof(url->pwd),
    722 				"%s", word) > (int)sizeof(url->pwd)) {
    723 				_fetch_info("password in .netrc is too long");
    724 				url->pwd[0] = '\0';
    725 			}
    726 		} else if (strcmp(word, "account") == 0) {
    727 			if ((word = _fetch_read_word(f)) == NULL)
    728 				goto ferr;
    729 			/* XXX not supported! */
    730 		} else {
    731 			break;
    732 		}
    733 	}
    734 	fclose(f);
    735 	return (0);
    736  ferr:
    737 	fclose(f);
    738 	return (-1);
    739 }
    740