xref: /netbsd-src/external/bsd/fetch/dist/libfetch/common.c (revision 7f21db1c0118155e0dd40b75182e30c589d9f63e)
1 /*	$NetBSD: common.c,v 1.1.1.8 2010/01/30 21:26:09 joerg Exp $	*/
2 /*-
3  * Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav
4  * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer
12  *    in this position and unchanged.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * $FreeBSD: common.c,v 1.53 2007/12/19 00:26:36 des Exp $
31  */
32 
33 #if HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 #ifndef NETBSD
37 #include <nbcompat.h>
38 #endif
39 
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/time.h>
43 #include <sys/uio.h>
44 
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 
48 #include <ctype.h>
49 #include <errno.h>
50 #if defined(HAVE_INTTYPES_H) || defined(NETBSD)
51 #include <inttypes.h>
52 #endif
53 #ifndef NETBSD
54 #include <nbcompat/netdb.h>
55 #else
56 #include <netdb.h>
57 #endif
58 #include <pwd.h>
59 #include <stdarg.h>
60 #include <stdlib.h>
61 #include <stdio.h>
62 #include <string.h>
63 #include <unistd.h>
64 
65 #ifndef MSG_NOSIGNAL
66 #include <signal.h>
67 #endif
68 
69 #include "fetch.h"
70 #include "common.h"
71 
72 /*** Local data **************************************************************/
73 
74 /*
75  * Error messages for resolver errors
76  */
77 static struct fetcherr netdb_errlist[] = {
78 #ifdef EAI_NODATA
79 	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
80 #endif
81 	{ EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
82 	{ EAI_FAIL,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
83 	{ EAI_NONAME,	FETCH_RESOLV,	"No address record" },
84 	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
85 };
86 
87 /*** Error-reporting functions ***********************************************/
88 
89 /*
90  * Map error code to string
91  */
92 static struct fetcherr *
93 fetch_finderr(struct fetcherr *p, int e)
94 {
95 	while (p->num != -1 && p->num != e)
96 		p++;
97 	return (p);
98 }
99 
100 /*
101  * Set error code
102  */
103 void
104 fetch_seterr(struct fetcherr *p, int e)
105 {
106 	p = fetch_finderr(p, e);
107 	fetchLastErrCode = p->cat;
108 	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
109 }
110 
111 /*
112  * Set error code according to errno
113  */
114 void
115 fetch_syserr(void)
116 {
117 	switch (errno) {
118 	case 0:
119 		fetchLastErrCode = FETCH_OK;
120 		break;
121 	case EPERM:
122 	case EACCES:
123 	case EROFS:
124 #ifdef EAUTH
125 	case EAUTH:
126 #endif
127 #ifdef ENEEDAUTH
128 	case ENEEDAUTH:
129 #endif
130 		fetchLastErrCode = FETCH_AUTH;
131 		break;
132 	case ENOENT:
133 	case EISDIR: /* XXX */
134 		fetchLastErrCode = FETCH_UNAVAIL;
135 		break;
136 	case ENOMEM:
137 		fetchLastErrCode = FETCH_MEMORY;
138 		break;
139 	case EBUSY:
140 	case EAGAIN:
141 		fetchLastErrCode = FETCH_TEMP;
142 		break;
143 	case EEXIST:
144 		fetchLastErrCode = FETCH_EXISTS;
145 		break;
146 	case ENOSPC:
147 		fetchLastErrCode = FETCH_FULL;
148 		break;
149 	case EADDRINUSE:
150 	case EADDRNOTAVAIL:
151 	case ENETDOWN:
152 	case ENETUNREACH:
153 	case ENETRESET:
154 	case EHOSTUNREACH:
155 		fetchLastErrCode = FETCH_NETWORK;
156 		break;
157 	case ECONNABORTED:
158 	case ECONNRESET:
159 		fetchLastErrCode = FETCH_ABORT;
160 		break;
161 	case ETIMEDOUT:
162 		fetchLastErrCode = FETCH_TIMEOUT;
163 		break;
164 	case ECONNREFUSED:
165 	case EHOSTDOWN:
166 		fetchLastErrCode = FETCH_DOWN;
167 		break;
168 default:
169 		fetchLastErrCode = FETCH_UNKNOWN;
170 	}
171 	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
172 }
173 
174 
175 /*
176  * Emit status message
177  */
178 void
179 fetch_info(const char *fmt, ...)
180 {
181 	va_list ap;
182 
183 	va_start(ap, fmt);
184 	vfprintf(stderr, fmt, ap);
185 	va_end(ap);
186 	fputc('\n', stderr);
187 }
188 
189 
190 /*** Network-related utility functions ***************************************/
191 
192 /*
193  * Return the default port for a scheme
194  */
195 int
196 fetch_default_port(const char *scheme)
197 {
198 	struct servent *se;
199 
200 	if ((se = getservbyname(scheme, "tcp")) != NULL)
201 		return (ntohs(se->s_port));
202 	if (strcasecmp(scheme, SCHEME_FTP) == 0)
203 		return (FTP_DEFAULT_PORT);
204 	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
205 		return (HTTP_DEFAULT_PORT);
206 	return (0);
207 }
208 
209 /*
210  * Return the default proxy port for a scheme
211  */
212 int
213 fetch_default_proxy_port(const char *scheme)
214 {
215 	if (strcasecmp(scheme, SCHEME_FTP) == 0)
216 		return (FTP_DEFAULT_PROXY_PORT);
217 	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
218 		return (HTTP_DEFAULT_PROXY_PORT);
219 	return (0);
220 }
221 
222 
223 /*
224  * Create a connection for an existing descriptor.
225  */
226 conn_t *
227 fetch_reopen(int sd)
228 {
229 	conn_t *conn;
230 
231 	/* allocate and fill connection structure */
232 	if ((conn = calloc(1, sizeof(*conn))) == NULL)
233 		return (NULL);
234 	conn->cache_url = NULL;
235 	conn->next_buf = NULL;
236 	conn->next_len = 0;
237 	conn->sd = sd;
238 	return (conn);
239 }
240 
241 
242 /*
243  * Bind a socket to a specific local address
244  */
245 int
246 fetch_bind(int sd, int af, const char *addr)
247 {
248 	struct addrinfo hints, *res, *res0;
249 
250 	memset(&hints, 0, sizeof(hints));
251 	hints.ai_family = af;
252 	hints.ai_socktype = SOCK_STREAM;
253 	hints.ai_protocol = 0;
254 	if (getaddrinfo(addr, NULL, &hints, &res0))
255 		return (-1);
256 	for (res = res0; res; res = res->ai_next) {
257 		if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
258 			return (0);
259 	}
260 	return (-1);
261 }
262 
263 
264 /*
265  * Establish a TCP connection to the specified port on the specified host.
266  */
267 conn_t *
268 fetch_connect(struct url *url, int af, int verbose)
269 {
270 	conn_t *conn;
271 	char pbuf[10];
272 	const char *bindaddr;
273 	struct addrinfo hints, *res, *res0;
274 	int sd, error;
275 
276 	if (verbose)
277 		fetch_info("looking up %s", url->host);
278 
279 	/* look up host name and set up socket address structure */
280 	snprintf(pbuf, sizeof(pbuf), "%d", url->port);
281 	memset(&hints, 0, sizeof(hints));
282 	hints.ai_family = af;
283 	hints.ai_socktype = SOCK_STREAM;
284 	hints.ai_protocol = 0;
285 	if ((error = getaddrinfo(url->host, pbuf, &hints, &res0)) != 0) {
286 		netdb_seterr(error);
287 		return (NULL);
288 	}
289 	bindaddr = getenv("FETCH_BIND_ADDRESS");
290 
291 	if (verbose)
292 		fetch_info("connecting to %s:%d", url->host, url->port);
293 
294 	/* try to connect */
295 	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
296 		if ((sd = socket(res->ai_family, res->ai_socktype,
297 			 res->ai_protocol)) == -1)
298 			continue;
299 		if (bindaddr != NULL && *bindaddr != '\0' &&
300 		    fetch_bind(sd, res->ai_family, bindaddr) != 0) {
301 			fetch_info("failed to bind to '%s'", bindaddr);
302 			close(sd);
303 			continue;
304 		}
305 		if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
306 			break;
307 		close(sd);
308 	}
309 	freeaddrinfo(res0);
310 	if (sd == -1) {
311 		fetch_syserr();
312 		return (NULL);
313 	}
314 
315 	if ((conn = fetch_reopen(sd)) == NULL) {
316 		fetch_syserr();
317 		close(sd);
318 	}
319 	conn->cache_url = fetchCopyURL(url);
320 	conn->cache_af = af;
321 	return (conn);
322 }
323 
324 static conn_t *connection_cache;
325 static int cache_global_limit = 0;
326 static int cache_per_host_limit = 0;
327 
328 /*
329  * Initialise cache with the given limits.
330  */
331 void
332 fetchConnectionCacheInit(int global_limit, int per_host_limit)
333 {
334 
335 	if (global_limit < 0)
336 		cache_global_limit = INT_MAX;
337 	else if (per_host_limit > global_limit)
338 		cache_global_limit = per_host_limit;
339 	else
340 		cache_global_limit = global_limit;
341 	if (per_host_limit < 0)
342 		cache_per_host_limit = INT_MAX;
343 	else
344 		cache_per_host_limit = per_host_limit;
345 }
346 
347 /*
348  * Flush cache and free all associated resources.
349  */
350 void
351 fetchConnectionCacheClose(void)
352 {
353 	conn_t *conn;
354 
355 	while ((conn = connection_cache) != NULL) {
356 		connection_cache = conn->next_cached;
357 		(*conn->cache_close)(conn);
358 	}
359 }
360 
361 /*
362  * Check connection cache for an existing entry matching
363  * protocol/host/port/user/password/family.
364  */
365 conn_t *
366 fetch_cache_get(const struct url *url, int af)
367 {
368 	conn_t *conn, *last_conn = NULL;
369 
370 	for (conn = connection_cache; conn; conn = conn->next_cached) {
371 		if (conn->cache_url->port == url->port &&
372 		    strcmp(conn->cache_url->scheme, url->scheme) == 0 &&
373 		    strcmp(conn->cache_url->host, url->host) == 0 &&
374 		    strcmp(conn->cache_url->user, url->user) == 0 &&
375 		    strcmp(conn->cache_url->pwd, url->pwd) == 0 &&
376 		    (conn->cache_af == AF_UNSPEC || af == AF_UNSPEC ||
377 		     conn->cache_af == af)) {
378 			if (last_conn != NULL)
379 				last_conn->next_cached = conn->next_cached;
380 			else
381 				connection_cache = conn->next_cached;
382 			return conn;
383 		}
384 	}
385 
386 	return NULL;
387 }
388 
389 /*
390  * Put the connection back into the cache for reuse.
391  * If the connection is freed due to LRU or if the cache
392  * is explicitly closed, the given callback is called.
393  */
394 void
395 fetch_cache_put(conn_t *conn, int (*closecb)(conn_t *))
396 {
397 	conn_t *iter, *last;
398 	int global_count, host_count;
399 
400 	if (conn->cache_url == NULL || cache_global_limit == 0) {
401 		(*closecb)(conn);
402 		return;
403 	}
404 
405 	global_count = host_count = 0;
406 	last = NULL;
407 	for (iter = connection_cache; iter;
408 	    last = iter, iter = iter->next_cached) {
409 		++global_count;
410 		if (strcmp(conn->cache_url->host, iter->cache_url->host) == 0)
411 			++host_count;
412 		if (global_count < cache_global_limit &&
413 		    host_count < cache_per_host_limit)
414 			continue;
415 		--global_count;
416 		if (last != NULL)
417 			last->next_cached = iter->next_cached;
418 		else
419 			connection_cache = iter->next_cached;
420 		(*iter->cache_close)(iter);
421 	}
422 
423 	conn->cache_close = closecb;
424 	conn->next_cached = connection_cache;
425 	connection_cache = conn;
426 }
427 
428 /*
429  * Enable SSL on a connection.
430  */
431 int
432 fetch_ssl(conn_t *conn, int verbose)
433 {
434 
435 #ifdef WITH_SSL
436 	/* Init the SSL library and context */
437 	if (!SSL_library_init()){
438 		fprintf(stderr, "SSL library init failed\n");
439 		return (-1);
440 	}
441 
442 	SSL_load_error_strings();
443 
444 	conn->ssl_meth = SSLv23_client_method();
445 	conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
446 	SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
447 
448 	conn->ssl = SSL_new(conn->ssl_ctx);
449 	if (conn->ssl == NULL){
450 		fprintf(stderr, "SSL context creation failed\n");
451 		return (-1);
452 	}
453 	SSL_set_fd(conn->ssl, conn->sd);
454 	if (SSL_connect(conn->ssl) == -1){
455 		ERR_print_errors_fp(stderr);
456 		return (-1);
457 	}
458 
459 	if (verbose) {
460 		X509_NAME *name;
461 		char *str;
462 
463 		fprintf(stderr, "SSL connection established using %s\n",
464 		    SSL_get_cipher(conn->ssl));
465 		conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
466 		name = X509_get_subject_name(conn->ssl_cert);
467 		str = X509_NAME_oneline(name, 0, 0);
468 		printf("Certificate subject: %s\n", str);
469 		free(str);
470 		name = X509_get_issuer_name(conn->ssl_cert);
471 		str = X509_NAME_oneline(name, 0, 0);
472 		printf("Certificate issuer: %s\n", str);
473 		free(str);
474 	}
475 
476 	return (0);
477 #else
478 	(void)conn;
479 	(void)verbose;
480 	fprintf(stderr, "SSL support disabled\n");
481 	return (-1);
482 #endif
483 }
484 
485 
486 /*
487  * Read a character from a connection w/ timeout
488  */
489 ssize_t
490 fetch_read(conn_t *conn, char *buf, size_t len)
491 {
492 	struct timeval now, timeout, waittv;
493 	fd_set readfds;
494 	ssize_t rlen;
495 	int r;
496 
497 	if (len == 0)
498 		return 0;
499 
500 	if (conn->next_len != 0) {
501 		if (conn->next_len < len)
502 			len = conn->next_len;
503 		memmove(buf, conn->next_buf, len);
504 		conn->next_len -= len;
505 		conn->next_buf += len;
506 		return len;
507 	}
508 
509 	if (fetchTimeout) {
510 		FD_ZERO(&readfds);
511 		gettimeofday(&timeout, NULL);
512 		timeout.tv_sec += fetchTimeout;
513 	}
514 
515 	for (;;) {
516 		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
517 			FD_SET(conn->sd, &readfds);
518 			gettimeofday(&now, NULL);
519 			waittv.tv_sec = timeout.tv_sec - now.tv_sec;
520 			waittv.tv_usec = timeout.tv_usec - now.tv_usec;
521 			if (waittv.tv_usec < 0) {
522 				waittv.tv_usec += 1000000;
523 				waittv.tv_sec--;
524 			}
525 			if (waittv.tv_sec < 0) {
526 				errno = ETIMEDOUT;
527 				fetch_syserr();
528 				return (-1);
529 			}
530 			errno = 0;
531 			r = select(conn->sd + 1, &readfds, NULL, NULL, &waittv);
532 			if (r == -1) {
533 				if (errno == EINTR && fetchRestartCalls)
534 					continue;
535 				fetch_syserr();
536 				return (-1);
537 			}
538 		}
539 #ifdef WITH_SSL
540 		if (conn->ssl != NULL)
541 			rlen = SSL_read(conn->ssl, buf, len);
542 		else
543 #endif
544 			rlen = read(conn->sd, buf, len);
545 		if (rlen >= 0)
546 			break;
547 
548 		if (errno != EINTR || !fetchRestartCalls)
549 			return (-1);
550 	}
551 	return (rlen);
552 }
553 
554 
555 /*
556  * Read a line of text from a connection w/ timeout
557  */
558 #define MIN_BUF_SIZE 1024
559 
560 int
561 fetch_getln(conn_t *conn)
562 {
563 	char *tmp, *next;
564 	size_t tmpsize;
565 	ssize_t len;
566 
567 	if (conn->buf == NULL) {
568 		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
569 			errno = ENOMEM;
570 			return (-1);
571 		}
572 		conn->bufsize = MIN_BUF_SIZE;
573 	}
574 
575 	conn->buflen = 0;
576 	next = NULL;
577 
578 	do {
579 		/*
580 		 * conn->bufsize != conn->buflen at this point,
581 		 * so the buffer can be NUL-terminated below for
582 		 * the case of len == 0.
583 		 */
584 		len = fetch_read(conn, conn->buf + conn->buflen,
585 		    conn->bufsize - conn->buflen);
586 		if (len == -1)
587 			return (-1);
588 		if (len == 0)
589 			break;
590 		next = memchr(conn->buf + conn->buflen, '\n', len);
591 		conn->buflen += len;
592 		if (conn->buflen == conn->bufsize && next == NULL) {
593 			tmp = conn->buf;
594 			tmpsize = conn->bufsize * 2;
595 			if (tmpsize < conn->bufsize) {
596 				errno = ENOMEM;
597 				return (-1);
598 			}
599 			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
600 				errno = ENOMEM;
601 				return (-1);
602 			}
603 			conn->buf = tmp;
604 			conn->bufsize = tmpsize;
605 		}
606 	} while (next == NULL);
607 
608 	if (next != NULL) {
609 		*next = '\0';
610 		conn->next_buf = next + 1;
611 		conn->next_len = conn->buflen - (conn->next_buf - conn->buf);
612 		conn->buflen = next - conn->buf;
613 	} else {
614 		conn->buf[conn->buflen] = '\0';
615 		conn->next_len = 0;
616 	}
617 	return (0);
618 }
619 
620 /*
621  * Write a vector to a connection w/ timeout
622  * Note: can modify the iovec.
623  */
624 ssize_t
625 fetch_write(conn_t *conn, const void *buf, size_t len)
626 {
627 	struct timeval now, timeout, waittv;
628 	fd_set writefds;
629 	ssize_t wlen, total;
630 	int r;
631 #ifndef MSG_NOSIGNAL
632 	static int killed_sigpipe;
633 #endif
634 
635 #ifndef MSG_NOSIGNAL
636 	if (!killed_sigpipe) {
637 		signal(SIGPIPE, SIG_IGN);
638 		killed_sigpipe = 1;
639 	}
640 #endif
641 
642 
643 	if (fetchTimeout) {
644 		FD_ZERO(&writefds);
645 		gettimeofday(&timeout, NULL);
646 		timeout.tv_sec += fetchTimeout;
647 	}
648 
649 	total = 0;
650 	while (len) {
651 		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
652 			FD_SET(conn->sd, &writefds);
653 			gettimeofday(&now, NULL);
654 			waittv.tv_sec = timeout.tv_sec - now.tv_sec;
655 			waittv.tv_usec = timeout.tv_usec - now.tv_usec;
656 			if (waittv.tv_usec < 0) {
657 				waittv.tv_usec += 1000000;
658 				waittv.tv_sec--;
659 			}
660 			if (waittv.tv_sec < 0) {
661 				errno = ETIMEDOUT;
662 				fetch_syserr();
663 				return (-1);
664 			}
665 			errno = 0;
666 			r = select(conn->sd + 1, NULL, &writefds, NULL, &waittv);
667 			if (r == -1) {
668 				if (errno == EINTR && fetchRestartCalls)
669 					continue;
670 				return (-1);
671 			}
672 		}
673 		errno = 0;
674 #ifdef WITH_SSL
675 		if (conn->ssl != NULL)
676 			wlen = SSL_write(conn->ssl, buf, len);
677 		else
678 #endif
679 #ifndef MSG_NOSIGNAL
680 			wlen = send(conn->sd, buf, len, 0);
681 #else
682 			wlen = send(conn->sd, buf, len, MSG_NOSIGNAL);
683 #endif
684 		if (wlen == 0) {
685 			/* we consider a short write a failure */
686 			errno = EPIPE;
687 			fetch_syserr();
688 			return (-1);
689 		}
690 		if (wlen < 0) {
691 			if (errno == EINTR && fetchRestartCalls)
692 				continue;
693 			return (-1);
694 		}
695 		total += wlen;
696 		buf = (const char *)buf + wlen;
697 		len -= wlen;
698 	}
699 	return (total);
700 }
701 
702 
703 /*
704  * Close connection
705  */
706 int
707 fetch_close(conn_t *conn)
708 {
709 	int ret;
710 
711 	ret = close(conn->sd);
712 	if (conn->cache_url)
713 		fetchFreeURL(conn->cache_url);
714 	free(conn->buf);
715 	free(conn);
716 	return (ret);
717 }
718 
719 
720 /*** Directory-related utility functions *************************************/
721 
722 int
723 fetch_add_entry(struct url_list *ue, struct url *base, const char *name,
724     int pre_quoted)
725 {
726 	struct url *tmp;
727 	char *tmp_name;
728 	size_t base_doc_len, name_len, i;
729 	unsigned char c;
730 
731 	if (strchr(name, '/') != NULL ||
732 	    strcmp(name, "..") == 0 ||
733 	    strcmp(name, ".") == 0)
734 		return 0;
735 
736 	if (strcmp(base->doc, "/") == 0)
737 		base_doc_len = 0;
738 	else
739 		base_doc_len = strlen(base->doc);
740 
741 	name_len = 1;
742 	for (i = 0; name[i] != '\0'; ++i) {
743 		if ((!pre_quoted && name[i] == '%') ||
744 		    !fetch_urlpath_safe(name[i]))
745 			name_len += 3;
746 		else
747 			++name_len;
748 	}
749 
750 	tmp_name = malloc( base_doc_len + name_len + 1);
751 	if (tmp_name == NULL) {
752 		errno = ENOMEM;
753 		fetch_syserr();
754 		return (-1);
755 	}
756 
757 	if (ue->length + 1 >= ue->alloc_size) {
758 		tmp = realloc(ue->urls, (ue->alloc_size * 2 + 1) * sizeof(*tmp));
759 		if (tmp == NULL) {
760 			free(tmp_name);
761 			errno = ENOMEM;
762 			fetch_syserr();
763 			return (-1);
764 		}
765 		ue->alloc_size = ue->alloc_size * 2 + 1;
766 		ue->urls = tmp;
767 	}
768 
769 	tmp = ue->urls + ue->length;
770 	strcpy(tmp->scheme, base->scheme);
771 	strcpy(tmp->user, base->user);
772 	strcpy(tmp->pwd, base->pwd);
773 	strcpy(tmp->host, base->host);
774 	tmp->port = base->port;
775 	tmp->doc = tmp_name;
776 	memcpy(tmp->doc, base->doc, base_doc_len);
777 	tmp->doc[base_doc_len] = '/';
778 
779 	for (i = base_doc_len + 1; *name != '\0'; ++name) {
780 		if ((!pre_quoted && *name == '%') ||
781 		    !fetch_urlpath_safe(*name)) {
782 			tmp->doc[i++] = '%';
783 			c = (unsigned char)*name / 16;
784 			if (c < 10)
785 				tmp->doc[i++] = '0' + c;
786 			else
787 				tmp->doc[i++] = 'a' - 10 + c;
788 			c = (unsigned char)*name % 16;
789 			if (c < 10)
790 				tmp->doc[i++] = '0' + c;
791 			else
792 				tmp->doc[i++] = 'a' - 10 + c;
793 		} else {
794 			tmp->doc[i++] = *name;
795 		}
796 	}
797 	tmp->doc[i] = '\0';
798 
799 	tmp->offset = 0;
800 	tmp->length = 0;
801 	tmp->last_modified = -1;
802 
803 	++ue->length;
804 
805 	return (0);
806 }
807 
808 void
809 fetchInitURLList(struct url_list *ue)
810 {
811 	ue->length = ue->alloc_size = 0;
812 	ue->urls = NULL;
813 }
814 
815 int
816 fetchAppendURLList(struct url_list *dst, const struct url_list *src)
817 {
818 	size_t i, j, len;
819 
820 	len = dst->length + src->length;
821 	if (len > dst->alloc_size) {
822 		struct url *tmp;
823 
824 		tmp = realloc(dst->urls, len * sizeof(*tmp));
825 		if (tmp == NULL) {
826 			errno = ENOMEM;
827 			fetch_syserr();
828 			return (-1);
829 		}
830 		dst->alloc_size = len;
831 		dst->urls = tmp;
832 	}
833 
834 	for (i = 0, j = dst->length; i < src->length; ++i, ++j) {
835 		dst->urls[j] = src->urls[i];
836 		dst->urls[j].doc = strdup(src->urls[i].doc);
837 		if (dst->urls[j].doc == NULL) {
838 			while (i-- > 0)
839 				free(dst->urls[j].doc);
840 			fetch_syserr();
841 			return -1;
842 		}
843 	}
844 	dst->length = len;
845 
846 	return 0;
847 }
848 
849 void
850 fetchFreeURLList(struct url_list *ue)
851 {
852 	size_t i;
853 
854 	for (i = 0; i < ue->length; ++i)
855 		free(ue->urls[i].doc);
856 	free(ue->urls);
857 	ue->length = ue->alloc_size = 0;
858 }
859 
860 
861 /*** Authentication-related utility functions ********************************/
862 
863 static const char *
864 fetch_read_word(FILE *f)
865 {
866 	static char word[1024];
867 
868 	if (fscanf(f, " %1023s ", word) != 1)
869 		return (NULL);
870 	return (word);
871 }
872 
873 /*
874  * Get authentication data for a URL from .netrc
875  */
876 int
877 fetch_netrc_auth(struct url *url)
878 {
879 	char fn[PATH_MAX];
880 	const char *word;
881 	char *p;
882 	FILE *f;
883 
884 	if ((p = getenv("NETRC")) != NULL) {
885 		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
886 			fetch_info("$NETRC specifies a file name "
887 			    "longer than PATH_MAX");
888 			return (-1);
889 		}
890 	} else {
891 		if ((p = getenv("HOME")) != NULL) {
892 			struct passwd *pwd;
893 
894 			if ((pwd = getpwuid(getuid())) == NULL ||
895 			    (p = pwd->pw_dir) == NULL)
896 				return (-1);
897 		}
898 		if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
899 			return (-1);
900 	}
901 
902 	if ((f = fopen(fn, "r")) == NULL)
903 		return (-1);
904 	while ((word = fetch_read_word(f)) != NULL) {
905 		if (strcmp(word, "default") == 0)
906 			break;
907 		if (strcmp(word, "machine") == 0 &&
908 		    (word = fetch_read_word(f)) != NULL &&
909 		    strcasecmp(word, url->host) == 0) {
910 			break;
911 		}
912 	}
913 	if (word == NULL)
914 		goto ferr;
915 	while ((word = fetch_read_word(f)) != NULL) {
916 		if (strcmp(word, "login") == 0) {
917 			if ((word = fetch_read_word(f)) == NULL)
918 				goto ferr;
919 			if (snprintf(url->user, sizeof(url->user),
920 				"%s", word) > (int)sizeof(url->user)) {
921 				fetch_info("login name in .netrc is too long");
922 				url->user[0] = '\0';
923 			}
924 		} else if (strcmp(word, "password") == 0) {
925 			if ((word = fetch_read_word(f)) == NULL)
926 				goto ferr;
927 			if (snprintf(url->pwd, sizeof(url->pwd),
928 				"%s", word) > (int)sizeof(url->pwd)) {
929 				fetch_info("password in .netrc is too long");
930 				url->pwd[0] = '\0';
931 			}
932 		} else if (strcmp(word, "account") == 0) {
933 			if ((word = fetch_read_word(f)) == NULL)
934 				goto ferr;
935 			/* XXX not supported! */
936 		} else {
937 			break;
938 		}
939 	}
940 	fclose(f);
941 	return (0);
942  ferr:
943 	fclose(f);
944 	return (-1);
945 }
946 
947 /*
948  * The no_proxy environment variable specifies a set of domains for
949  * which the proxy should not be consulted; the contents is a comma-,
950  * or space-separated list of domain names.  A single asterisk will
951  * override all proxy variables and no transactions will be proxied
952  * (for compatability with lynx and curl, see the discussion at
953  * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
954  */
955 int
956 fetch_no_proxy_match(const char *host)
957 {
958 	const char *no_proxy, *p, *q;
959 	size_t h_len, d_len;
960 
961 	if ((no_proxy = getenv("NO_PROXY")) == NULL &&
962 	    (no_proxy = getenv("no_proxy")) == NULL)
963 		return (0);
964 
965 	/* asterisk matches any hostname */
966 	if (strcmp(no_proxy, "*") == 0)
967 		return (1);
968 
969 	h_len = strlen(host);
970 	p = no_proxy;
971 	do {
972 		/* position p at the beginning of a domain suffix */
973 		while (*p == ',' || isspace((unsigned char)*p))
974 			p++;
975 
976 		/* position q at the first separator character */
977 		for (q = p; *q; ++q)
978 			if (*q == ',' || isspace((unsigned char)*q))
979 				break;
980 
981 		d_len = q - p;
982 		if (d_len > 0 && h_len > d_len &&
983 		    strncasecmp(host + h_len - d_len,
984 			p, d_len) == 0) {
985 			/* domain name matches */
986 			return (1);
987 		}
988 
989 		p = q + 1;
990 	} while (*q);
991 
992 	return (0);
993 }
994 
995 struct fetchIO {
996 	void *io_cookie;
997 	ssize_t (*io_read)(void *, void *, size_t);
998 	ssize_t (*io_write)(void *, const void *, size_t);
999 	void (*io_close)(void *);
1000 };
1001 
1002 void
1003 fetchIO_close(fetchIO *f)
1004 {
1005 	if (f->io_close != NULL)
1006 		(*f->io_close)(f->io_cookie);
1007 
1008 	free(f);
1009 }
1010 
1011 fetchIO *
1012 fetchIO_unopen(void *io_cookie, ssize_t (*io_read)(void *, void *, size_t),
1013     ssize_t (*io_write)(void *, const void *, size_t),
1014     void (*io_close)(void *))
1015 {
1016 	fetchIO *f;
1017 
1018 	f = malloc(sizeof(*f));
1019 	if (f == NULL)
1020 		return f;
1021 
1022 	f->io_cookie = io_cookie;
1023 	f->io_read = io_read;
1024 	f->io_write = io_write;
1025 	f->io_close = io_close;
1026 
1027 	return f;
1028 }
1029 
1030 ssize_t
1031 fetchIO_read(fetchIO *f, void *buf, size_t len)
1032 {
1033 	if (f->io_read == NULL)
1034 		return EBADF;
1035 	return (*f->io_read)(f->io_cookie, buf, len);
1036 }
1037 
1038 ssize_t
1039 fetchIO_write(fetchIO *f, const void *buf, size_t len)
1040 {
1041 	if (f->io_read == NULL)
1042 		return EBADF;
1043 	return (*f->io_write)(f->io_cookie, buf, len);
1044 }
1045