xref: /dflybsd-src/lib/libfetch/common.c (revision 14c24f74315ca3fb26eaf73089bbc97e0c0e50fa)
1 /*-
2  * Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav
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  * $FreeBSD: src/lib/libfetch/common.c,v 1.56 2008/04/15 23:29:51 cperciva Exp $
29  */
30 
31 #include <sys/param.h>
32 #include <sys/socket.h>
33 #include <sys/time.h>
34 #include <sys/uio.h>
35 
36 #include <netinet/in.h>
37 
38 #include <ctype.h>
39 #include <errno.h>
40 #include <fcntl.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 	int opt = 1;
212 
213 	/* allocate and fill connection structure */
214 	if ((conn = calloc(1, sizeof(*conn))) == NULL)
215 		return (NULL);
216         fcntl(sd, F_SETFD, FD_CLOEXEC);
217         setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof opt);
218 	conn->sd = sd;
219 	++conn->ref;
220 	return (conn);
221 }
222 
223 
224 /*
225  * Bump a connection's reference count.
226  */
227 conn_t *
228 fetch_ref(conn_t *conn)
229 {
230 
231 	++conn->ref;
232 	return (conn);
233 }
234 
235 
236 /*
237  * Bind a socket to a specific local address
238  */
239 int
240 fetch_bind(int sd, int af, const char *addr)
241 {
242 	struct addrinfo hints, *res, *res0;
243 	int err;
244 
245 	memset(&hints, 0, sizeof(hints));
246 	hints.ai_family = af;
247 	hints.ai_socktype = SOCK_STREAM;
248 	hints.ai_protocol = 0;
249 	if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)
250 		return (-1);
251 	for (res = res0; res; res = res->ai_next)
252 		if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
253 			return (0);
254 	return (-1);
255 }
256 
257 
258 /*
259  * Establish a TCP connection to the specified port on the specified host.
260  */
261 conn_t *
262 fetch_connect(const char *host, int port, int af, int verbose)
263 {
264 	conn_t *conn;
265 	char pbuf[10];
266 	const char *bindaddr;
267 	struct addrinfo hints, *res, *res0;
268 	int sd, err;
269 
270 	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
271 
272 	if (verbose)
273 		fetch_info("looking up %s", host);
274 
275 	/* look up host name and set up socket address structure */
276 	snprintf(pbuf, sizeof(pbuf), "%d", port);
277 	memset(&hints, 0, sizeof(hints));
278 	hints.ai_family = af;
279 	hints.ai_socktype = SOCK_STREAM;
280 	hints.ai_protocol = 0;
281 	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
282 		netdb_seterr(err);
283 		return (NULL);
284 	}
285 	bindaddr = getenv("FETCH_BIND_ADDRESS");
286 
287 	if (verbose)
288 		fetch_info("connecting to %s:%d", host, port);
289 
290 	/* try to connect */
291 	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
292 		if ((sd = socket(res->ai_family, res->ai_socktype,
293 			 res->ai_protocol)) == -1)
294 			continue;
295 		if (bindaddr != NULL && *bindaddr != '\0' &&
296 		    fetch_bind(sd, res->ai_family, bindaddr) != 0) {
297 			fetch_info("failed to bind to '%s'", bindaddr);
298 			close(sd);
299 			continue;
300 		}
301 		if (connect(sd, res->ai_addr, res->ai_addrlen) == 0 &&
302                     fcntl(sd, F_SETFL, O_NONBLOCK) == 0)
303 			break;
304 		close(sd);
305 	}
306 	freeaddrinfo(res0);
307 	if (sd == -1) {
308 		fetch_syserr();
309 		return (NULL);
310 	}
311 
312 	if ((conn = fetch_reopen(sd)) == NULL) {
313 		fetch_syserr();
314 		close(sd);
315 	}
316 	return (conn);
317 }
318 
319 
320 /*
321  * Enable SSL on a connection.
322  */
323 int
324 fetch_ssl(conn_t *conn, int verbose)
325 {
326         int ret, ssl_err;
327 
328 #ifdef WITH_SSL
329 	/* Init the SSL library and context */
330 	if (!SSL_library_init()){
331 		fprintf(stderr, "SSL library init failed\n");
332 		return (-1);
333 	}
334 
335 	SSL_load_error_strings();
336 
337 	conn->ssl_meth = SSLv23_client_method();
338 	conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
339 	SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
340 
341 	conn->ssl = SSL_new(conn->ssl_ctx);
342 	if (conn->ssl == NULL){
343 		fprintf(stderr, "SSL context creation failed\n");
344 		return (-1);
345 	}
346 	SSL_set_fd(conn->ssl, conn->sd);
347 	while ((ret = SSL_connect(conn->ssl)) == -1) {
348 		ssl_err = SSL_get_error(conn->ssl, ret);
349 		if (ssl_err != SSL_ERROR_WANT_READ &&
350 		    ssl_err != SSL_ERROR_WANT_WRITE) {
351 			ERR_print_errors_fp(stderr);
352 			return (-1);
353 		}
354 	}
355 
356 	if (verbose) {
357 		X509_NAME *name;
358 		char *str;
359 
360 		fprintf(stderr, "SSL connection established using %s\n",
361 		    SSL_get_cipher(conn->ssl));
362 		conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
363 		name = X509_get_subject_name(conn->ssl_cert);
364 		str = X509_NAME_oneline(name, 0, 0);
365 		printf("Certificate subject: %s\n", str);
366 		free(str);
367 		name = X509_get_issuer_name(conn->ssl_cert);
368 		str = X509_NAME_oneline(name, 0, 0);
369 		printf("Certificate issuer: %s\n", str);
370 		free(str);
371 	}
372 
373 	return (0);
374 #else
375 	(void)conn;
376 	(void)verbose;
377 	fprintf(stderr, "SSL support disabled\n");
378 	return (-1);
379 #endif
380 }
381 
382 #define FETCH_READ_WAIT		-2
383 #define FETCH_READ_ERROR	-1
384 #define FETCH_READ_DONE		 0
385 
386 #ifdef WITH_SSL
387 static ssize_t
388 fetch_ssl_read(SSL *ssl, char *buf, size_t len)
389 {
390 	ssize_t rlen;
391 	int ssl_err;
392 
393 	rlen = SSL_read(ssl, buf, len);
394 	if (rlen < 0) {
395 		ssl_err = SSL_get_error(ssl, rlen);
396 		if (ssl_err == SSL_ERROR_WANT_READ ||
397 		    ssl_err == SSL_ERROR_WANT_WRITE) {
398 			return (FETCH_READ_WAIT);
399 		} else {
400 			ERR_print_errors_fp(stderr);
401 			return (FETCH_READ_ERROR);
402 		}
403 	}
404 	return (rlen);
405 }
406 #endif
407 
408 /*
409  * Cache some data that was read from a socket but cannot be immediately
410  * returned because of an interrupted system call.
411  */
412 static int
413 fetch_cache_data(conn_t *conn, char *src, size_t nbytes)
414 {
415 	char *tmp;
416 
417 	if (conn->cache.size < nbytes) {
418 		tmp = realloc(conn->cache.buf, nbytes);
419 		if (tmp == NULL) {
420 			fetch_syserr();
421 			return (-1);
422 		}
423 		conn->cache.buf = tmp;
424 		conn->cache.size = nbytes;
425 	}
426 
427 	memcpy(conn->cache.buf, src, nbytes);
428 	conn->cache.len = nbytes;
429 	conn->cache.pos = 0;
430 
431 	return (0);
432 }
433 
434 
435 static ssize_t
436 fetch_socket_read(int sd, char *buf, size_t len)
437 {
438 	ssize_t rlen;
439 
440 	rlen = read(sd, buf, len);
441 	if (rlen < 0) {
442 		if (errno == EAGAIN || (errno == EINTR && fetchRestartCalls))
443 			return (FETCH_READ_WAIT);
444 		else
445 			return (FETCH_READ_ERROR);
446 	}
447 	return (rlen);
448 }
449 
450 /*
451  * Read a character from a connection w/ timeout
452  */
453 ssize_t
454 fetch_read(conn_t *conn, char *buf, size_t len)
455 {
456 	struct timeval now, timeout, delta;
457 	fd_set readfds;
458 	ssize_t rlen, total;
459 	int r;
460 	char *start;
461 
462 	if (fetchTimeout) {
463 		FD_ZERO(&readfds);
464 		gettimeofday(&timeout, NULL);
465 		timeout.tv_sec += fetchTimeout;
466 	}
467 
468 	total = 0;
469 	start = buf;
470 
471 	if (conn->cache.len > 0) {
472 		/*
473 		 * The last invocation of fetch_read was interrupted by a
474 		 * signal after some data had been read from the socket. Copy
475 		 * the cached data into the supplied buffer before trying to
476 		 * read from the socket again.
477 		 */
478 		total = (conn->cache.len < len) ? conn->cache.len : len;
479 		memcpy(buf, conn->cache.buf, total);
480 
481 		conn->cache.len -= total;
482 		conn->cache.pos += total;
483 		len -= total;
484 		buf += total;
485 	}
486 
487 	while (len > 0) {
488 		/*
489 		 * The socket is non-blocking.  Instead of the canonical
490 		 * select() -> read(), we do the following:
491 		 *
492 		 * 1) call read() or SSL_read().
493 		 * 2) if an error occurred, return -1.
494 		 * 3) if we received data but we still expect more,
495 		 *    update our counters and loop.
496 		 * 4) if read() or SSL_read() signaled EOF, return.
497 		 * 5) if we did not receive any data but we're not at EOF,
498 		 *    call select().
499 		 *
500 		 * In the SSL case, this is necessary because if we
501 		 * receive a close notification, we have to call
502 		 * SSL_read() one additional time after we've read
503 		 * everything we received.
504 		 *
505 		 * In the non-SSL case, it may improve performance (very
506 		 * slightly) when reading small amounts of data.
507 		 */
508 #ifdef WITH_SSL
509 		if (conn->ssl != NULL)
510 			rlen = fetch_ssl_read(conn->ssl, buf, len);
511 		else
512 #endif
513 			rlen = fetch_socket_read(conn->sd, buf, len);
514 		if (rlen == 0) {
515 			break;
516 		} else if (rlen > 0) {
517 			len -= rlen;
518 			buf += rlen;
519 			total += rlen;
520 			continue;
521 		} else if (rlen == FETCH_READ_ERROR) {
522 			if (errno == EINTR)
523 				fetch_cache_data(conn, start, total);
524 			return (-1);
525 		}
526 		// assert(rlen == FETCH_READ_WAIT);
527 		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
528 			FD_SET(conn->sd, &readfds);
529 			gettimeofday(&now, NULL);
530 			delta.tv_sec = timeout.tv_sec - now.tv_sec;
531 			delta.tv_usec = timeout.tv_usec - now.tv_usec;
532 			if (delta.tv_usec < 0) {
533 				delta.tv_usec += 1000000;
534 				delta.tv_sec--;
535 			}
536 			if (delta.tv_sec < 0) {
537 				errno = ETIMEDOUT;
538 				fetch_syserr();
539 				return (-1);
540 			}
541 			errno = 0;
542 			r = select(conn->sd + 1, &readfds, NULL, NULL, &delta);
543 			if (r == -1) {
544 				if (errno == EINTR) {
545 					if (fetchRestartCalls)
546 						continue;
547 					/* Save anything that was read. */
548 					fetch_cache_data(conn, start, total);
549 				}
550 				fetch_syserr();
551 				return (-1);
552 			}
553 		}
554 	}
555 	return (total);
556 }
557 
558 
559 /*
560  * Read a line of text from a connection w/ timeout
561  */
562 #define MIN_BUF_SIZE 1024
563 
564 int
565 fetch_getln(conn_t *conn)
566 {
567 	char *tmp;
568 	size_t tmpsize;
569 	ssize_t len;
570 	char c;
571 
572 	if (conn->buf == NULL) {
573 		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
574 			errno = ENOMEM;
575 			return (-1);
576 		}
577 		conn->bufsize = MIN_BUF_SIZE;
578 	}
579 
580 	conn->buf[0] = '\0';
581 	conn->buflen = 0;
582 
583 	do {
584 		len = fetch_read(conn, &c, 1);
585 		if (len == -1)
586 			return (-1);
587 		if (len == 0)
588 			break;
589 		conn->buf[conn->buflen++] = c;
590 		if (conn->buflen == conn->bufsize) {
591 			tmp = conn->buf;
592 			tmpsize = conn->bufsize * 2 + 1;
593 			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
594 				errno = ENOMEM;
595 				return (-1);
596 			}
597 			conn->buf = tmp;
598 			conn->bufsize = tmpsize;
599 		}
600 	} while (c != '\n');
601 
602 	conn->buf[conn->buflen] = '\0';
603 	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
604 	return (0);
605 }
606 
607 
608 /*
609  * Write to a connection w/ timeout
610  */
611 ssize_t
612 fetch_write(conn_t *conn, const char *buf, size_t len)
613 {
614 	struct iovec iov;
615 
616 	iov.iov_base = __DECONST(char *, buf);
617 	iov.iov_len = len;
618 	return fetch_writev(conn, &iov, 1);
619 }
620 
621 /*
622  * Write a vector to a connection w/ timeout
623  * Note: can modify the iovec.
624  */
625 ssize_t
626 fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
627 {
628 	struct timeval now, timeout, delta;
629 	fd_set writefds;
630 	ssize_t wlen, total;
631 	int r;
632 
633 	if (fetchTimeout) {
634 		FD_ZERO(&writefds);
635 		gettimeofday(&timeout, NULL);
636 		timeout.tv_sec += fetchTimeout;
637 	}
638 
639 	total = 0;
640 	while (iovcnt > 0) {
641 		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
642 			FD_SET(conn->sd, &writefds);
643 			gettimeofday(&now, NULL);
644 			delta.tv_sec = timeout.tv_sec - now.tv_sec;
645 			delta.tv_usec = timeout.tv_usec - now.tv_usec;
646 			if (delta.tv_usec < 0) {
647 				delta.tv_usec += 1000000;
648 				delta.tv_sec--;
649 			}
650 			if (delta.tv_sec < 0) {
651 				errno = ETIMEDOUT;
652 				fetch_syserr();
653 				return (-1);
654 			}
655 			errno = 0;
656 			r = select(conn->sd + 1, NULL, &writefds, NULL, &delta);
657 			if (r == -1) {
658 				if (errno == EINTR && fetchRestartCalls)
659 					continue;
660 				return (-1);
661 			}
662 		}
663 		errno = 0;
664 #ifdef WITH_SSL
665 		if (conn->ssl != NULL)
666 			wlen = SSL_write(conn->ssl,
667 			    iov->iov_base, iov->iov_len);
668 		else
669 #endif
670 			wlen = writev(conn->sd, iov, iovcnt);
671 		if (wlen == 0) {
672 			/* we consider a short write a failure */
673 			/* XXX perhaps we shouldn't in the SSL case */
674 			errno = EPIPE;
675 			fetch_syserr();
676 			return (-1);
677 		}
678 		if (wlen < 0) {
679 			if (errno == EINTR && fetchRestartCalls)
680 				continue;
681 			return (-1);
682 		}
683 		total += wlen;
684 		while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
685 			wlen -= iov->iov_len;
686 			iov++;
687 			iovcnt--;
688 		}
689 		if (iovcnt > 0) {
690 			iov->iov_len -= wlen;
691 			iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;
692 		}
693 	}
694 	return (total);
695 }
696 
697 
698 /*
699  * Write a line of text to a connection w/ timeout
700  */
701 int
702 fetch_putln(conn_t *conn, const char *str, size_t len)
703 {
704 	struct iovec iov[2];
705 	int ret;
706 
707 	DEBUG(fprintf(stderr, ">>> %s\n", str));
708 	iov[0].iov_base = __DECONST(char *, str);
709 	iov[0].iov_len = len;
710 	iov[1].iov_base = __DECONST(char *, ENDL);
711 	iov[1].iov_len = sizeof(ENDL);
712 	if (len == 0)
713 		ret = fetch_writev(conn, &iov[1], 1);
714 	else
715 		ret = fetch_writev(conn, iov, 2);
716 	if (ret == -1)
717 		return (-1);
718 	return (0);
719 }
720 
721 
722 /*
723  * Close connection
724  */
725 int
726 fetch_close(conn_t *conn)
727 {
728 	int ret;
729 
730 	if (--conn->ref > 0)
731 		return (0);
732 	ret = close(conn->sd);
733 	free(conn->cache.buf);
734 	free(conn->buf);
735 	free(conn);
736 	return (ret);
737 }
738 
739 
740 /*** Directory-related utility functions *************************************/
741 
742 int
743 fetch_add_entry(struct url_ent **p, int *size, int *len,
744     const char *name, struct url_stat *us)
745 {
746 	struct url_ent *tmp;
747 
748 	if (*p == NULL) {
749 		*size = 0;
750 		*len = 0;
751 	}
752 
753 	if (*len >= *size - 1) {
754 		tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
755 		if (tmp == NULL) {
756 			errno = ENOMEM;
757 			fetch_syserr();
758 			return (-1);
759 		}
760 		*size = (*size * 2 + 1);
761 		*p = tmp;
762 	}
763 
764 	tmp = *p + *len;
765 	snprintf(tmp->name, PATH_MAX, "%s", name);
766 	memcpy(&tmp->stat, us, sizeof(*us));
767 
768 	(*len)++;
769 	(++tmp)->name[0] = 0;
770 
771 	return (0);
772 }
773 
774 
775 /*** Authentication-related utility functions ********************************/
776 
777 static const char *
778 fetch_read_word(FILE *f)
779 {
780 	static char word[1024];
781 
782 	if (fscanf(f, " %1023s ", word) != 1)
783 		return (NULL);
784 	return (word);
785 }
786 
787 /*
788  * Get authentication data for a URL from .netrc
789  */
790 int
791 fetch_netrc_auth(struct url *url)
792 {
793 	char fn[PATH_MAX];
794 	const char *word;
795 	char *p;
796 	FILE *f;
797 
798 	if ((p = getenv("NETRC")) != NULL) {
799 		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
800 			fetch_info("$NETRC specifies a file name "
801 			    "longer than PATH_MAX");
802 			return (-1);
803 		}
804 	} else {
805 		if ((p = getenv("HOME")) != NULL) {
806 			struct passwd *pwd;
807 
808 			if ((pwd = getpwuid(getuid())) == NULL ||
809 			    (p = pwd->pw_dir) == NULL)
810 				return (-1);
811 		}
812 		if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
813 			return (-1);
814 	}
815 
816 	if ((f = fopen(fn, "r")) == NULL)
817 		return (-1);
818 	while ((word = fetch_read_word(f)) != NULL) {
819 		if (strcmp(word, "default") == 0) {
820 			DEBUG(fetch_info("Using default .netrc settings"));
821 			break;
822 		}
823 		if (strcmp(word, "machine") == 0 &&
824 		    (word = fetch_read_word(f)) != NULL &&
825 		    strcasecmp(word, url->host) == 0) {
826 			DEBUG(fetch_info("Using .netrc settings for %s", word));
827 			break;
828 		}
829 	}
830 	if (word == NULL)
831 		goto ferr;
832 	while ((word = fetch_read_word(f)) != NULL) {
833 		if (strcmp(word, "login") == 0) {
834 			if ((word = fetch_read_word(f)) == NULL)
835 				goto ferr;
836 			if (snprintf(url->user, sizeof(url->user),
837 				"%s", word) > (int)sizeof(url->user)) {
838 				fetch_info("login name in .netrc is too long");
839 				url->user[0] = '\0';
840 			}
841 		} else if (strcmp(word, "password") == 0) {
842 			if ((word = fetch_read_word(f)) == NULL)
843 				goto ferr;
844 			if (snprintf(url->pwd, sizeof(url->pwd),
845 				"%s", word) > (int)sizeof(url->pwd)) {
846 				fetch_info("password in .netrc is too long");
847 				url->pwd[0] = '\0';
848 			}
849 		} else if (strcmp(word, "account") == 0) {
850 			if ((word = fetch_read_word(f)) == NULL)
851 				goto ferr;
852 			/* XXX not supported! */
853 		} else {
854 			break;
855 		}
856 	}
857 	fclose(f);
858 	return (0);
859  ferr:
860 	fclose(f);
861 	return (-1);
862 }
863 
864 /*
865  * The no_proxy environment variable specifies a set of domains for
866  * which the proxy should not be consulted; the contents is a comma-,
867  * or space-separated list of domain names.  A single asterisk will
868  * override all proxy variables and no transactions will be proxied
869  * (for compatability with lynx and curl, see the discussion at
870  * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
871  */
872 int
873 fetch_no_proxy_match(const char *host)
874 {
875 	const char *no_proxy, *p, *q;
876 	size_t h_len, d_len;
877 
878 	if ((no_proxy = getenv("NO_PROXY")) == NULL &&
879 	    (no_proxy = getenv("no_proxy")) == NULL)
880 		return (0);
881 
882 	/* asterisk matches any hostname */
883 	if (strcmp(no_proxy, "*") == 0)
884 		return (1);
885 
886 	h_len = strlen(host);
887 	p = no_proxy;
888 	do {
889 		/* position p at the beginning of a domain suffix */
890 		while (*p == ',' || isspace((unsigned char)*p))
891 			p++;
892 
893 		/* position q at the first separator character */
894 		for (q = p; *q; ++q)
895 			if (*q == ',' || isspace((unsigned char)*q))
896 				break;
897 
898 		d_len = q - p;
899 		if (d_len > 0 && h_len >= d_len &&
900 		    strncasecmp(host + h_len - d_len,
901 			p, d_len) == 0) {
902 			/* domain name matches */
903 			return (1);
904 		}
905 
906 		p = q + 1;
907 	} while (*q);
908 
909 	return (0);
910 }
911