common.c revision 97866
1/*-
2 * Copyright (c) 1998 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 97866 2002-06-05 12:19:08Z des $");
31
32#include <sys/param.h>
33#include <sys/socket.h>
34#include <sys/time.h>
35#include <sys/uio.h>
36#include <netinet/in.h>
37
38#include <ctype.h> /* XXX */
39#include <errno.h>
40#include <netdb.h>
41#include <stdarg.h>
42#include <stdlib.h>
43#include <stdio.h>
44#include <string.h>
45#include <unistd.h>
46
47#include "fetch.h"
48#include "common.h"
49
50
51/*** Local data **************************************************************/
52
53/*
54 * Error messages for resolver errors
55 */
56static struct fetcherr _netdb_errlist[] = {
57	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
58	{ EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
59	{ EAI_FAIL,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
60	{ EAI_NONAME,	FETCH_RESOLV,	"No address record" },
61	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
62};
63
64/* End-of-Line */
65static const char ENDL[2] = "\r\n";
66
67
68/*** Error-reporting functions ***********************************************/
69
70/*
71 * Map error code to string
72 */
73static struct fetcherr *
74_fetch_finderr(struct fetcherr *p, int e)
75{
76	while (p->num != -1 && p->num != e)
77		p++;
78	return (p);
79}
80
81/*
82 * Set error code
83 */
84void
85_fetch_seterr(struct fetcherr *p, int e)
86{
87	p = _fetch_finderr(p, e);
88	fetchLastErrCode = p->cat;
89	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
90}
91
92/*
93 * Set error code according to errno
94 */
95void
96_fetch_syserr(void)
97{
98	switch (errno) {
99	case 0:
100		fetchLastErrCode = FETCH_OK;
101		break;
102	case EPERM:
103	case EACCES:
104	case EROFS:
105	case EAUTH:
106	case ENEEDAUTH:
107		fetchLastErrCode = FETCH_AUTH;
108		break;
109	case ENOENT:
110	case EISDIR: /* XXX */
111		fetchLastErrCode = FETCH_UNAVAIL;
112		break;
113	case ENOMEM:
114		fetchLastErrCode = FETCH_MEMORY;
115		break;
116	case EBUSY:
117	case EAGAIN:
118		fetchLastErrCode = FETCH_TEMP;
119		break;
120	case EEXIST:
121		fetchLastErrCode = FETCH_EXISTS;
122		break;
123	case ENOSPC:
124		fetchLastErrCode = FETCH_FULL;
125		break;
126	case EADDRINUSE:
127	case EADDRNOTAVAIL:
128	case ENETDOWN:
129	case ENETUNREACH:
130	case ENETRESET:
131	case EHOSTUNREACH:
132		fetchLastErrCode = FETCH_NETWORK;
133		break;
134	case ECONNABORTED:
135	case ECONNRESET:
136		fetchLastErrCode = FETCH_ABORT;
137		break;
138	case ETIMEDOUT:
139		fetchLastErrCode = FETCH_TIMEOUT;
140		break;
141	case ECONNREFUSED:
142	case EHOSTDOWN:
143		fetchLastErrCode = FETCH_DOWN;
144		break;
145default:
146		fetchLastErrCode = FETCH_UNKNOWN;
147	}
148	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
149}
150
151
152/*
153 * Emit status message
154 */
155void
156_fetch_info(const char *fmt, ...)
157{
158	va_list ap;
159
160	va_start(ap, fmt);
161	vfprintf(stderr, fmt, ap);
162	va_end(ap);
163	fputc('\n', stderr);
164}
165
166
167/*** Network-related utility functions ***************************************/
168
169/*
170 * Return the default port for a scheme
171 */
172int
173_fetch_default_port(const char *scheme)
174{
175	struct servent *se;
176
177	if ((se = getservbyname(scheme, "tcp")) != NULL)
178		return (ntohs(se->s_port));
179	if (strcasecmp(scheme, SCHEME_FTP) == 0)
180		return (FTP_DEFAULT_PORT);
181	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
182		return (HTTP_DEFAULT_PORT);
183	return (0);
184}
185
186/*
187 * Return the default proxy port for a scheme
188 */
189int
190_fetch_default_proxy_port(const char *scheme)
191{
192	if (strcasecmp(scheme, SCHEME_FTP) == 0)
193		return (FTP_DEFAULT_PROXY_PORT);
194	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
195		return (HTTP_DEFAULT_PROXY_PORT);
196	return (0);
197}
198
199/*
200 * Create a connection for an existing descriptor.
201 */
202conn_t *
203_fetch_reopen(int sd)
204{
205	conn_t *conn;
206
207	/* allocate and fill connection structure */
208	if ((conn = calloc(1, sizeof *conn)) == NULL)
209		return (NULL);
210	conn->sd = sd;
211	return (conn);
212}
213
214
215/*
216 * Establish a TCP connection to the specified port on the specified host.
217 */
218conn_t *
219_fetch_connect(const char *host, int port, int af, int verbose)
220{
221	conn_t *conn;
222	char pbuf[10];
223	struct addrinfo hints, *res, *res0;
224	int sd, err;
225
226	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
227
228	if (verbose)
229		_fetch_info("looking up %s", host);
230
231	/* look up host name and set up socket address structure */
232	snprintf(pbuf, sizeof(pbuf), "%d", port);
233	memset(&hints, 0, sizeof(hints));
234	hints.ai_family = af;
235	hints.ai_socktype = SOCK_STREAM;
236	hints.ai_protocol = 0;
237	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
238		_netdb_seterr(err);
239		return (NULL);
240	}
241
242	if (verbose)
243		_fetch_info("connecting to %s:%d", host, port);
244
245	/* try to connect */
246	for (sd = -1, res = res0; res; res = res->ai_next) {
247		if ((sd = socket(res->ai_family, res->ai_socktype,
248			 res->ai_protocol)) == -1)
249			continue;
250		if (connect(sd, res->ai_addr, res->ai_addrlen) != -1)
251			break;
252		close(sd);
253		sd = -1;
254	}
255	freeaddrinfo(res0);
256	if (sd == -1) {
257		_fetch_syserr();
258		return (NULL);
259	}
260
261	if ((conn = _fetch_reopen(sd)) == NULL)
262		close(sd);
263	return (conn);
264}
265
266
267/*
268 * Read a character from a connection w/ timeout
269 */
270ssize_t
271_fetch_read(conn_t *conn, char *buf, size_t len)
272{
273	struct timeval now, timeout, wait;
274	fd_set readfds;
275	ssize_t rlen, total;
276	int r;
277
278	if (fetchTimeout) {
279		FD_ZERO(&readfds);
280		gettimeofday(&timeout, NULL);
281		timeout.tv_sec += fetchTimeout;
282	}
283
284	total = 0;
285	while (len > 0) {
286		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
287			FD_SET(conn->sd, &readfds);
288			gettimeofday(&now, NULL);
289			wait.tv_sec = timeout.tv_sec - now.tv_sec;
290			wait.tv_usec = timeout.tv_usec - now.tv_usec;
291			if (wait.tv_usec < 0) {
292				wait.tv_usec += 1000000;
293				wait.tv_sec--;
294			}
295			if (wait.tv_sec < 0)
296				return (rlen);
297			errno = 0;
298			r = select(conn->sd + 1, &readfds, NULL, NULL, &wait);
299			if (r == -1) {
300				if (errno == EINTR && fetchRestartCalls)
301					continue;
302				return (-1);
303			}
304		}
305		if (conn->ssl != NULL)
306			rlen = SSL_read(conn->ssl, buf, len);
307		else
308			rlen = read(conn->sd, buf, len);
309		if (rlen == 0)
310			break;
311		if (rlen < 0) {
312			if (errno == EINTR && fetchRestartCalls)
313				continue;
314			return (-1);
315		}
316		len -= rlen;
317		buf += rlen;
318		total += rlen;
319	}
320	return (total);
321}
322
323/*
324 * Read a line of text from a connection w/ timeout
325 */
326#define MIN_BUF_SIZE 1024
327
328int
329_fetch_getln(conn_t *conn)
330{
331	char *tmp;
332	size_t tmpsize;
333	char c;
334
335	if (conn->buf == NULL) {
336		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
337			errno = ENOMEM;
338			return (-1);
339		}
340		conn->bufsize = MIN_BUF_SIZE;
341	}
342
343	conn->buf[0] = '\0';
344	conn->buflen = 0;
345
346	do {
347		if (_fetch_read(conn, &c, 1) == -1)
348			return (-1);
349		conn->buf[conn->buflen++] = c;
350		if (conn->buflen == conn->bufsize) {
351			tmp = conn->buf;
352			tmpsize = conn->bufsize * 2 + 1;
353			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
354				errno = ENOMEM;
355				return (-1);
356			}
357			conn->buf = tmp;
358			conn->bufsize = tmpsize;
359		}
360	} while (c != '\n');
361
362	conn->buf[conn->buflen] = '\0';
363	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
364	return (0);
365}
366
367
368/*
369 * Write to a connection w/ timeout
370 */
371ssize_t
372_fetch_write(conn_t *conn, const char *buf, size_t len)
373{
374	struct timeval now, timeout, wait;
375	fd_set writefds;
376	ssize_t wlen, total;
377	int r;
378
379	if (fetchTimeout) {
380		FD_ZERO(&writefds);
381		gettimeofday(&timeout, NULL);
382		timeout.tv_sec += fetchTimeout;
383	}
384
385	while (len > 0) {
386		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
387			FD_SET(conn->sd, &writefds);
388			gettimeofday(&now, NULL);
389			wait.tv_sec = timeout.tv_sec - now.tv_sec;
390			wait.tv_usec = timeout.tv_usec - now.tv_usec;
391			if (wait.tv_usec < 0) {
392				wait.tv_usec += 1000000;
393				wait.tv_sec--;
394			}
395			if (wait.tv_sec < 0) {
396				errno = ETIMEDOUT;
397				return (-1);
398			}
399			errno = 0;
400			r = select(conn->sd + 1, NULL, &writefds, NULL, &wait);
401			if (r == -1) {
402				if (errno == EINTR && fetchRestartCalls)
403					continue;
404				return (-1);
405			}
406		}
407		errno = 0;
408		if (conn->ssl != NULL)
409			wlen = SSL_write(conn->ssl, buf, len);
410		else
411			wlen = write(conn->sd, buf, len);
412		if (wlen == 0)
413			/* we consider a short write a failure */
414			return (-1);
415		if (wlen < 0) {
416			if (errno == EINTR && fetchRestartCalls)
417				continue;
418			return (-1);
419		}
420		len -= wlen;
421		buf += wlen;
422		total += wlen;
423	}
424	return (total);
425}
426
427/*
428 * Write a line of text to a connection w/ timeout
429 */
430int
431_fetch_putln(conn_t *conn, const char *str, size_t len)
432{
433	if (_fetch_write(conn, str, len) == -1 ||
434	    _fetch_write(conn, ENDL, sizeof ENDL) == -1)
435		return (-1);
436	return (0);
437}
438
439
440/*
441 * Close connection
442 */
443int
444_fetch_close(conn_t *conn)
445{
446	int ret;
447
448	ret = close(conn->sd);
449	free(conn);
450	return (ret);
451}
452
453
454/*** Directory-related utility functions *************************************/
455
456int
457_fetch_add_entry(struct url_ent **p, int *size, int *len,
458    const char *name, struct url_stat *us)
459{
460	struct url_ent *tmp;
461
462	if (*p == NULL) {
463		*size = 0;
464		*len = 0;
465	}
466
467	if (*len >= *size - 1) {
468		tmp = realloc(*p, (*size * 2 + 1) * sizeof **p);
469		if (tmp == NULL) {
470			errno = ENOMEM;
471			_fetch_syserr();
472			return (-1);
473		}
474		*size = (*size * 2 + 1);
475		*p = tmp;
476	}
477
478	tmp = *p + *len;
479	snprintf(tmp->name, PATH_MAX, "%s", name);
480	bcopy(us, &tmp->stat, sizeof *us);
481
482	(*len)++;
483	(++tmp)->name[0] = 0;
484
485	return (0);
486}
487