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