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 "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 */
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 *
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 */
88void
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 */
99void
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;
149default:
150		fetchLastErrCode = FETCH_UNKNOWN;
151	}
152	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
153}
154
155
156/*
157 * Emit status message
158 */
159void
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 */
176int
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 */
193int
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 */
207conn_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 */
224conn_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 */
236int
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 */
257conn_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 */
318int
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 */
378ssize_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
443int
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 */
490ssize_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 */
504ssize_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 */
579int
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 */
603int
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
619int
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
654static 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 */
667int
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