common.c revision 62964
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 * $FreeBSD: head/lib/libfetch/common.c 62964 2000-07-11 18:07:09Z des $
29 */
30
31#include <sys/param.h>
32#include <sys/socket.h>
33#include <sys/time.h>
34#include <netinet/in.h>
35
36#include <errno.h>
37#include <netdb.h>
38#include <stdarg.h>
39#include <stdlib.h>
40#include <stdio.h>
41#include <string.h>
42#include <unistd.h>
43
44#include "fetch.h"
45#include "common.h"
46
47
48/*** Local data **************************************************************/
49
50/*
51 * Error messages for resolver errors
52 */
53static struct fetcherr _netdb_errlist[] = {
54    { EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
55    { EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
56    { EAI_FAIL,		FETCH_RESOLV,	"Non-recoverable resolver failure" },
57    { EAI_NONAME,	FETCH_RESOLV,	"No address record" },
58    { -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
59};
60
61
62/*** Error-reporting functions ***********************************************/
63
64/*
65 * Map error code to string
66 */
67static struct fetcherr *
68_fetch_finderr(struct fetcherr *p, int e)
69{
70    while (p->num != -1 && p->num != e)
71	p++;
72    return p;
73}
74
75/*
76 * Set error code
77 */
78void
79_fetch_seterr(struct fetcherr *p, int e)
80{
81    p = _fetch_finderr(p, e);
82    fetchLastErrCode = p->cat;
83    snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
84}
85
86/*
87 * Set error code according to errno
88 */
89void
90_fetch_syserr(void)
91{
92    int e;
93    e = errno;
94
95    switch (errno) {
96    case 0:
97	fetchLastErrCode = FETCH_OK;
98	break;
99    case EPERM:
100    case EACCES:
101    case EROFS:
102    case EAUTH:
103    case ENEEDAUTH:
104	fetchLastErrCode = FETCH_AUTH;
105	break;
106    case ENOENT:
107    case EISDIR: /* XXX */
108	fetchLastErrCode = FETCH_UNAVAIL;
109	break;
110    case ENOMEM:
111	fetchLastErrCode = FETCH_MEMORY;
112	break;
113    case EBUSY:
114    case EAGAIN:
115	fetchLastErrCode = FETCH_TEMP;
116	break;
117    case EEXIST:
118	fetchLastErrCode = FETCH_EXISTS;
119	break;
120    case ENOSPC:
121	fetchLastErrCode = FETCH_FULL;
122	break;
123    case EADDRINUSE:
124    case EADDRNOTAVAIL:
125    case ENETDOWN:
126    case ENETUNREACH:
127    case ENETRESET:
128    case EHOSTUNREACH:
129	fetchLastErrCode = FETCH_NETWORK;
130	break;
131    case ECONNABORTED:
132    case ECONNRESET:
133	fetchLastErrCode = FETCH_ABORT;
134	break;
135    case ETIMEDOUT:
136	fetchLastErrCode = FETCH_TIMEOUT;
137	break;
138    case ECONNREFUSED:
139    case EHOSTDOWN:
140	fetchLastErrCode = FETCH_DOWN;
141	break;
142    default:
143	fetchLastErrCode = FETCH_UNKNOWN;
144    }
145    snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(e));
146}
147
148
149/*
150 * Emit status message
151 */
152void
153_fetch_info(char *fmt, ...)
154{
155    va_list ap;
156
157    va_start(ap, fmt);
158    vfprintf(stderr, fmt, ap);
159    va_end(ap);
160    fputc('\n', stderr);
161}
162
163
164/*** Network-related utility functions ***************************************/
165
166/*
167 * Establish a TCP connection to the specified port on the specified host.
168 */
169int
170_fetch_connect(char *host, int port, int af, int verbose)
171{
172    char pbuf[10];
173    struct addrinfo hints, *res, *res0;
174    int sd, err;
175
176    DEBUG(fprintf(stderr, "\033[1m---> %s:%d\033[m\n", host, port));
177
178    if (verbose)
179	_fetch_info("looking up %s", host);
180
181    /* look up host name and set up socket address structure */
182    snprintf(pbuf, sizeof(pbuf), "%d", port);
183    memset(&hints, 0, sizeof(hints));
184    hints.ai_family = af;
185    hints.ai_socktype = SOCK_STREAM;
186    hints.ai_protocol = 0;
187    if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
188	_netdb_seterr(err);
189	return -1;
190    }
191
192    if (verbose)
193	_fetch_info("connecting to %s:%d", host, port);
194
195    /* try to connect */
196    sd = -1;
197    for (res = res0; res; res = res->ai_next) {
198	if ((sd = socket(res->ai_family, res->ai_socktype,
199			 res->ai_protocol)) < 0)
200	    continue;
201	if (connect(sd, res->ai_addr, res->ai_addrlen) >= 0)
202	    break;
203	close(sd);
204	sd = -1;
205    }
206    freeaddrinfo(res0);
207    if (sd < 0) {
208	_fetch_syserr();
209	return -1;
210    }
211
212    return sd;
213}
214
215
216/*
217 * Read a line of text from a socket w/ timeout
218 */
219#define MIN_BUF_SIZE 1024
220
221int
222_fetch_getln(int fd, char **buf, size_t *size, size_t *len)
223{
224    struct timeval now, timeout, wait;
225    fd_set readfds;
226    int r;
227    char c;
228
229    if (*buf == NULL) {
230	if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) {
231	    errno = ENOMEM;
232	    return -1;
233	}
234	*size = MIN_BUF_SIZE;
235    }
236
237    **buf = '\0';
238    *len = 0;
239
240    if (fetchTimeout) {
241	gettimeofday(&timeout, NULL);
242	timeout.tv_sec += fetchTimeout;
243	FD_ZERO(&readfds);
244    }
245
246    do {
247	if (fetchTimeout) {
248	    FD_SET(fd, &readfds);
249	    gettimeofday(&now, NULL);
250	    wait.tv_sec = timeout.tv_sec - now.tv_sec;
251	    wait.tv_usec = timeout.tv_usec - now.tv_usec;
252	    if (wait.tv_usec < 0) {
253		wait.tv_usec += 1000000;
254		wait.tv_sec--;
255	    }
256	    if (wait.tv_sec < 0) {
257		errno = ETIMEDOUT;
258		return -1;
259	    }
260	    r = select(fd+1, &readfds, NULL, NULL, &wait);
261	    if (r == -1) {
262		if (errno == EINTR)
263		    continue;
264		/* EBADF or EINVAL: shouldn't happen */
265		return -1;
266	    }
267	    if (!FD_ISSET(fd, &readfds))
268		continue;
269	}
270	r = read(fd, &c, 1);
271	if (r == 0)
272	    break;
273	if (r == -1) {
274	    if (errno == EINTR)
275		continue;
276	    /* any other error is bad news */
277	    return -1;
278	}
279	(*buf)[*len] = c;
280	*len += 1;
281	if (*len == *size) {
282	    char *tmp;
283
284	    if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) {
285		errno = ENOMEM;
286		return -1;
287	    }
288	    *buf = tmp;
289	    *size = *size * 2 + 1;
290	}
291    } while (c != '\n');
292
293    DEBUG(fprintf(stderr, "\033[1m<<< %.*s\033[m", (int)*len, *buf));
294    return 0;
295}
296
297
298/*** Directory-related utility functions *************************************/
299
300int
301_fetch_add_entry(struct url_ent **p, int *size, int *len,
302		 char *name, struct url_stat *stat)
303{
304    struct url_ent *tmp;
305
306    if (*p == NULL) {
307#define INITIAL_SIZE 8
308	if ((*p = malloc(INITIAL_SIZE * sizeof **p)) == NULL) {
309	    errno = ENOMEM;
310	    _fetch_syserr();
311	    return -1;
312	}
313	*size = INITIAL_SIZE;
314	*len = 0;
315#undef INITIAL_SIZE
316    }
317
318    if (*len >= *size - 1) {
319	tmp = realloc(*p, *size * 2 * sizeof **p);
320	if (tmp == NULL) {
321	    errno = ENOMEM;
322	    _fetch_syserr();
323	    return -1;
324	}
325	*size *= 2;
326	*p = tmp;
327    }
328
329    tmp = *p + *len;
330    snprintf(tmp->name, MAXPATHLEN, "%s", name);
331    bcopy(stat, &tmp->stat, sizeof *stat);
332
333    (*len)++;
334    (++tmp)->name[0] = 0;
335
336    return 0;
337}
338