common.c revision 177447
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
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/lib/libfetch/common.c 177447 2008-03-20 09:55:27Z des $");
31
32#include <sys/param.h>
33#include <sys/socket.h>
34#include <sys/time.h>
35#include <sys/uio.h>
36
37#include <netinet/in.h>
38
39#include <ctype.h>
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 */
58static 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 */
69static const char ENDL[2] = "\r\n";
70
71
72/*** Error-reporting functions ***********************************************/
73
74/*
75 * Map error code to string
76 */
77static struct fetcherr *
78fetch_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 */
88void
89fetch_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 */
99void
100fetch_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;
149default:
150		fetchLastErrCode = FETCH_UNKNOWN;
151	}
152	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
153}
154
155
156/*
157 * Emit status message
158 */
159void
160fetch_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 */
176int
177fetch_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 */
193int
194fetch_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 */
207conn_t *
208fetch_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 */
224conn_t *
225fetch_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 */
236int
237fetch_bind(int sd, int af, const char *addr)
238{
239	struct addrinfo hints, *res, *res0;
240	int err;
241
242	memset(&hints, 0, sizeof(hints));
243	hints.ai_family = af;
244	hints.ai_socktype = SOCK_STREAM;
245	hints.ai_protocol = 0;
246	if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)
247		return (-1);
248	for (res = res0; res; res = res->ai_next)
249		if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
250			return (0);
251	return (-1);
252}
253
254
255/*
256 * Establish a TCP connection to the specified port on the specified host.
257 */
258conn_t *
259fetch_connect(const char *host, int port, int af, int verbose)
260{
261	conn_t *conn;
262	char pbuf[10];
263	const char *bindaddr;
264	struct addrinfo hints, *res, *res0;
265	int sd, err;
266
267	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
268
269	if (verbose)
270		fetch_info("looking up %s", host);
271
272	/* look up host name and set up socket address structure */
273	snprintf(pbuf, sizeof(pbuf), "%d", port);
274	memset(&hints, 0, sizeof(hints));
275	hints.ai_family = af;
276	hints.ai_socktype = SOCK_STREAM;
277	hints.ai_protocol = 0;
278	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
279		netdb_seterr(err);
280		return (NULL);
281	}
282	bindaddr = getenv("FETCH_BIND_ADDRESS");
283
284	if (verbose)
285		fetch_info("connecting to %s:%d", host, port);
286
287	/* try to connect */
288	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
289		if ((sd = socket(res->ai_family, res->ai_socktype,
290			 res->ai_protocol)) == -1)
291			continue;
292		if (bindaddr != NULL && *bindaddr != '\0' &&
293		    fetch_bind(sd, res->ai_family, bindaddr) != 0) {
294			fetch_info("failed to bind to '%s'", bindaddr);
295			close(sd);
296			continue;
297		}
298		if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
299			break;
300		close(sd);
301	}
302	freeaddrinfo(res0);
303	if (sd == -1) {
304		fetch_syserr();
305		return (NULL);
306	}
307
308	if ((conn = fetch_reopen(sd)) == NULL) {
309		fetch_syserr();
310		close(sd);
311	}
312	return (conn);
313}
314
315
316/*
317 * Enable SSL on a connection.
318 */
319int
320fetch_ssl(conn_t *conn, int verbose)
321{
322
323#ifdef WITH_SSL
324	/* Init the SSL library and context */
325	if (!SSL_library_init()){
326		fprintf(stderr, "SSL library init failed\n");
327		return (-1);
328	}
329
330	SSL_load_error_strings();
331
332	conn->ssl_meth = SSLv23_client_method();
333	conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
334	SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
335
336	conn->ssl = SSL_new(conn->ssl_ctx);
337	if (conn->ssl == NULL){
338		fprintf(stderr, "SSL context creation failed\n");
339		return (-1);
340	}
341	SSL_set_fd(conn->ssl, conn->sd);
342	if (SSL_connect(conn->ssl) == -1){
343		ERR_print_errors_fp(stderr);
344		return (-1);
345	}
346
347	if (verbose) {
348		X509_NAME *name;
349		char *str;
350
351		fprintf(stderr, "SSL connection established using %s\n",
352		    SSL_get_cipher(conn->ssl));
353		conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
354		name = X509_get_subject_name(conn->ssl_cert);
355		str = X509_NAME_oneline(name, 0, 0);
356		printf("Certificate subject: %s\n", str);
357		free(str);
358		name = X509_get_issuer_name(conn->ssl_cert);
359		str = X509_NAME_oneline(name, 0, 0);
360		printf("Certificate issuer: %s\n", str);
361		free(str);
362	}
363
364	return (0);
365#else
366	(void)conn;
367	(void)verbose;
368	fprintf(stderr, "SSL support disabled\n");
369	return (-1);
370#endif
371}
372
373
374/*
375 * Read a character from a connection w/ timeout
376 */
377ssize_t
378fetch_read(conn_t *conn, char *buf, size_t len)
379{
380	struct timeval now, timeout, delta;
381	fd_set readfds;
382	ssize_t rlen, total;
383	int r;
384
385	if (fetchTimeout) {
386		FD_ZERO(&readfds);
387		gettimeofday(&timeout, NULL);
388		timeout.tv_sec += fetchTimeout;
389	}
390
391	total = 0;
392	while (len > 0) {
393		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
394			FD_SET(conn->sd, &readfds);
395			gettimeofday(&now, NULL);
396			delta.tv_sec = timeout.tv_sec - now.tv_sec;
397			delta.tv_usec = timeout.tv_usec - now.tv_usec;
398			if (delta.tv_usec < 0) {
399				delta.tv_usec += 1000000;
400				delta.tv_sec--;
401			}
402			if (delta.tv_sec < 0) {
403				errno = ETIMEDOUT;
404				fetch_syserr();
405				return (-1);
406			}
407			errno = 0;
408			r = select(conn->sd + 1, &readfds, NULL, NULL, &delta);
409			if (r == -1) {
410				if (errno == EINTR && fetchRestartCalls)
411					continue;
412				fetch_syserr();
413				return (-1);
414			}
415		}
416#ifdef WITH_SSL
417		if (conn->ssl != NULL)
418			rlen = SSL_read(conn->ssl, buf, len);
419		else
420#endif
421			rlen = read(conn->sd, buf, len);
422		if (rlen == 0)
423			break;
424		if (rlen < 0) {
425			if (errno == EINTR && fetchRestartCalls)
426				continue;
427			return (-1);
428		}
429		len -= rlen;
430		buf += rlen;
431		total += rlen;
432	}
433	return (total);
434}
435
436
437/*
438 * Read a line of text from a connection w/ timeout
439 */
440#define MIN_BUF_SIZE 1024
441
442int
443fetch_getln(conn_t *conn)
444{
445	char *tmp;
446	size_t tmpsize;
447	ssize_t len;
448	char c;
449
450	if (conn->buf == NULL) {
451		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
452			errno = ENOMEM;
453			return (-1);
454		}
455		conn->bufsize = MIN_BUF_SIZE;
456	}
457
458	conn->buf[0] = '\0';
459	conn->buflen = 0;
460
461	do {
462		len = fetch_read(conn, &c, 1);
463		if (len == -1)
464			return (-1);
465		if (len == 0)
466			break;
467		conn->buf[conn->buflen++] = c;
468		if (conn->buflen == conn->bufsize) {
469			tmp = conn->buf;
470			tmpsize = conn->bufsize * 2 + 1;
471			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
472				errno = ENOMEM;
473				return (-1);
474			}
475			conn->buf = tmp;
476			conn->bufsize = tmpsize;
477		}
478	} while (c != '\n');
479
480	conn->buf[conn->buflen] = '\0';
481	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
482	return (0);
483}
484
485
486/*
487 * Write to a connection w/ timeout
488 */
489ssize_t
490fetch_write(conn_t *conn, const char *buf, size_t len)
491{
492	struct iovec iov;
493
494	iov.iov_base = __DECONST(char *, buf);
495	iov.iov_len = len;
496	return fetch_writev(conn, &iov, 1);
497}
498
499/*
500 * Write a vector to a connection w/ timeout
501 * Note: can modify the iovec.
502 */
503ssize_t
504fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
505{
506	struct timeval now, timeout, delta;
507	fd_set writefds;
508	ssize_t wlen, total;
509	int r;
510
511	if (fetchTimeout) {
512		FD_ZERO(&writefds);
513		gettimeofday(&timeout, NULL);
514		timeout.tv_sec += fetchTimeout;
515	}
516
517	total = 0;
518	while (iovcnt > 0) {
519		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
520			FD_SET(conn->sd, &writefds);
521			gettimeofday(&now, NULL);
522			delta.tv_sec = timeout.tv_sec - now.tv_sec;
523			delta.tv_usec = timeout.tv_usec - now.tv_usec;
524			if (delta.tv_usec < 0) {
525				delta.tv_usec += 1000000;
526				delta.tv_sec--;
527			}
528			if (delta.tv_sec < 0) {
529				errno = ETIMEDOUT;
530				fetch_syserr();
531				return (-1);
532			}
533			errno = 0;
534			r = select(conn->sd + 1, NULL, &writefds, NULL, &delta);
535			if (r == -1) {
536				if (errno == EINTR && fetchRestartCalls)
537					continue;
538				return (-1);
539			}
540		}
541		errno = 0;
542#ifdef WITH_SSL
543		if (conn->ssl != NULL)
544			wlen = SSL_write(conn->ssl,
545			    iov->iov_base, iov->iov_len);
546		else
547#endif
548			wlen = writev(conn->sd, iov, iovcnt);
549		if (wlen == 0) {
550			/* we consider a short write a failure */
551			errno = EPIPE;
552			fetch_syserr();
553			return (-1);
554		}
555		if (wlen < 0) {
556			if (errno == EINTR && fetchRestartCalls)
557				continue;
558			return (-1);
559		}
560		total += wlen;
561		while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
562			wlen -= iov->iov_len;
563			iov++;
564			iovcnt--;
565		}
566		if (iovcnt > 0) {
567			iov->iov_len -= wlen;
568			iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;
569		}
570	}
571	return (total);
572}
573
574
575/*
576 * Write a line of text to a connection w/ timeout
577 */
578int
579fetch_putln(conn_t *conn, const char *str, size_t len)
580{
581	struct iovec iov[2];
582	int ret;
583
584	DEBUG(fprintf(stderr, ">>> %s\n", str));
585	iov[0].iov_base = __DECONST(char *, str);
586	iov[0].iov_len = len;
587	iov[1].iov_base = __DECONST(char *, ENDL);
588	iov[1].iov_len = sizeof(ENDL);
589	if (len == 0)
590		ret = fetch_writev(conn, &iov[1], 1);
591	else
592		ret = fetch_writev(conn, iov, 2);
593	if (ret == -1)
594		return (-1);
595	return (0);
596}
597
598
599/*
600 * Close connection
601 */
602int
603fetch_close(conn_t *conn)
604{
605	int ret;
606
607	if (--conn->ref > 0)
608		return (0);
609	ret = close(conn->sd);
610	free(conn->buf);
611	free(conn);
612	return (ret);
613}
614
615
616/*** Directory-related utility functions *************************************/
617
618int
619fetch_add_entry(struct url_ent **p, int *size, int *len,
620    const char *name, struct url_stat *us)
621{
622	struct url_ent *tmp;
623
624	if (*p == NULL) {
625		*size = 0;
626		*len = 0;
627	}
628
629	if (*len >= *size - 1) {
630		tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
631		if (tmp == NULL) {
632			errno = ENOMEM;
633			fetch_syserr();
634			return (-1);
635		}
636		*size = (*size * 2 + 1);
637		*p = tmp;
638	}
639
640	tmp = *p + *len;
641	snprintf(tmp->name, PATH_MAX, "%s", name);
642	memcpy(&tmp->stat, us, sizeof(*us));
643
644	(*len)++;
645	(++tmp)->name[0] = 0;
646
647	return (0);
648}
649
650
651/*** Authentication-related utility functions ********************************/
652
653static const char *
654fetch_read_word(FILE *f)
655{
656	static char word[1024];
657
658	if (fscanf(f, " %1024s ", word) != 1)
659		return (NULL);
660	return (word);
661}
662
663/*
664 * Get authentication data for a URL from .netrc
665 */
666int
667fetch_netrc_auth(struct url *url)
668{
669	char fn[PATH_MAX];
670	const char *word;
671	char *p;
672	FILE *f;
673
674	if ((p = getenv("NETRC")) != NULL) {
675		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
676			fetch_info("$NETRC specifies a file name "
677			    "longer than PATH_MAX");
678			return (-1);
679		}
680	} else {
681		if ((p = getenv("HOME")) != NULL) {
682			struct passwd *pwd;
683
684			if ((pwd = getpwuid(getuid())) == NULL ||
685			    (p = pwd->pw_dir) == NULL)
686				return (-1);
687		}
688		if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
689			return (-1);
690	}
691
692	if ((f = fopen(fn, "r")) == NULL)
693		return (-1);
694	while ((word = fetch_read_word(f)) != NULL) {
695		if (strcmp(word, "default") == 0) {
696			DEBUG(fetch_info("Using default .netrc settings"));
697			break;
698		}
699		if (strcmp(word, "machine") == 0 &&
700		    (word = fetch_read_word(f)) != NULL &&
701		    strcasecmp(word, url->host) == 0) {
702			DEBUG(fetch_info("Using .netrc settings for %s", word));
703			break;
704		}
705	}
706	if (word == NULL)
707		goto ferr;
708	while ((word = fetch_read_word(f)) != NULL) {
709		if (strcmp(word, "login") == 0) {
710			if ((word = fetch_read_word(f)) == NULL)
711				goto ferr;
712			if (snprintf(url->user, sizeof(url->user),
713				"%s", word) > (int)sizeof(url->user)) {
714				fetch_info("login name in .netrc is too long");
715				url->user[0] = '\0';
716			}
717		} else if (strcmp(word, "password") == 0) {
718			if ((word = fetch_read_word(f)) == NULL)
719				goto ferr;
720			if (snprintf(url->pwd, sizeof(url->pwd),
721				"%s", word) > (int)sizeof(url->pwd)) {
722				fetch_info("password in .netrc is too long");
723				url->pwd[0] = '\0';
724			}
725		} else if (strcmp(word, "account") == 0) {
726			if ((word = fetch_read_word(f)) == NULL)
727				goto ferr;
728			/* XXX not supported! */
729		} else {
730			break;
731		}
732	}
733	fclose(f);
734	return (0);
735 ferr:
736	fclose(f);
737	return (-1);
738}
739
740/*
741 * The no_proxy environment variable specifies a set of domains for
742 * which the proxy should not be consulted; the contents is a comma-,
743 * or space-separated list of domain names.  A single asterisk will
744 * override all proxy variables and no transactions will be proxied
745 * (for compatability with lynx and curl, see the discussion at
746 * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
747 */
748int
749fetch_no_proxy_match(const char *host)
750{
751	const char *no_proxy, *p, *q;
752	size_t h_len, d_len;
753
754	if ((no_proxy = getenv("NO_PROXY")) == NULL &&
755	    (no_proxy = getenv("no_proxy")) == NULL)
756		return (0);
757
758	/* asterisk matches any hostname */
759	if (strcmp(no_proxy, "*") == 0)
760		return (1);
761
762	h_len = strlen(host);
763	p = no_proxy;
764	do {
765		/* position p at the beginning of a domain suffix */
766		while (*p == ',' || isspace((unsigned char)*p))
767			p++;
768
769		/* position q at the first separator character */
770		for (q = p; *q; ++q)
771			if (*q == ',' || isspace((unsigned char)*q))
772				break;
773
774		d_len = q - p;
775		if (d_len > 0 && h_len > d_len &&
776		    strncasecmp(host + h_len - d_len,
777			p, d_len) == 0) {
778			/* domain name matches */
779			return (1);
780		}
781
782		p = q + 1;
783	} while (*q);
784
785	return (0);
786}
787