common.c revision 55557
1168404Spjd/*-
2168404Spjd * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav
3168404Spjd * All rights reserved.
4168404Spjd *
5168404Spjd * Redistribution and use in source and binary forms, with or without
6168404Spjd * modification, are permitted provided that the following conditions
7168404Spjd * are met:
8168404Spjd * 1. Redistributions of source code must retain the above copyright
9168404Spjd *    notice, this list of conditions and the following disclaimer
10168404Spjd *    in this position and unchanged.
11168404Spjd * 2. Redistributions in binary form must reproduce the above copyright
12168404Spjd *    notice, this list of conditions and the following disclaimer in the
13168404Spjd *    documentation and/or other materials provided with the distribution.
14168404Spjd * 3. The name of the author may not be used to endorse or promote products
15168404Spjd *    derived from this software without specific prior written permission
16168404Spjd *
17168404Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18168404Spjd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19168404Spjd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20168404Spjd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21168404Spjd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22219089Spjd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23277826Sdelphij * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24286766Smav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25260835Sdelphij * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26286764Smav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27168404Spjd *
28168404Spjd * $FreeBSD: head/lib/libfetch/common.c 55557 2000-01-07 12:58:40Z des $
29168404Spjd */
30168404Spjd
31168404Spjd#include <sys/param.h>
32168404Spjd#include <sys/socket.h>
33168404Spjd#include <sys/time.h>
34168404Spjd#include <netinet/in.h>
35168404Spjd
36168404Spjd#include <com_err.h>
37168404Spjd#include <errno.h>
38168404Spjd#include <netdb.h>
39168404Spjd#include <stdlib.h>
40168404Spjd#include <stdio.h>
41168404Spjd#include <string.h>
42168404Spjd#include <unistd.h>
43168404Spjd
44168404Spjd#include "fetch.h"
45168404Spjd#include "common.h"
46168404Spjd
47168404Spjd
48168404Spjd/*** Local data **************************************************************/
49168404Spjd
50168404Spjd/*
51185029Spjd * Error messages for resolver errors
52185029Spjd */
53168404Spjdstatic struct fetcherr _netdb_errlist[] = {
54168404Spjd    { HOST_NOT_FOUND,	FETCH_RESOLV,	"Host not found" },
55168404Spjd    { TRY_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
56168404Spjd    { NO_RECOVERY,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
57185029Spjd    { NO_DATA,		FETCH_RESOLV,	"No address record" },
58168404Spjd    { -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
59168404Spjd};
60168404Spjd
61168404Spjdstatic int com_err_initialized;
62251631Sdelphij
63168404Spjd/*** Error-reporting functions ***********************************************/
64168404Spjd
65168404Spjd/*
66251631Sdelphij * Initialize the common error library
67168404Spjd */
68168404Spjdstatic void
69168404Spjd_fetch_init_com_err(void)
70168404Spjd{
71168404Spjd    initialize_ftch_error_table();
72168404Spjd    com_err_initialized = 1;
73168404Spjd}
74168404Spjd
75168404Spjd/*
76168404Spjd * Map error code to string
77168404Spjd */
78168404Spjdstatic int
79185029Spjd_fetch_finderr(struct fetcherr *p, int e)
80168404Spjd{
81251631Sdelphij    int i;
82168404Spjd    for (i = 0; p[i].num != -1; i++)
83168404Spjd	if (p[i].num == e)
84168404Spjd	    break;
85286774Smav    return i;
86286774Smav}
87286774Smav
88168404Spjd/*
89168404Spjd * Set error code
90168404Spjd */
91168404Spjdvoid
92168404Spjd_fetch_seterr(struct fetcherr *p, int e)
93168404Spjd{
94168404Spjd    int n;
95168404Spjd
96168404Spjd    if (!com_err_initialized)
97168404Spjd	_fetch_init_com_err();
98168404Spjd
99168404Spjd    n = _fetch_finderr(p, e);
100168404Spjd    fetchLastErrCode = p[n].cat;
101168404Spjd    com_err("libfetch", fetchLastErrCode, "(%03d %s)", e, p[n].string);
102168404Spjd}
103168404Spjd
104168404Spjd/*
105168404Spjd * Set error code according to errno
106168404Spjd */
107168404Spjdvoid
108268858Sdelphij_fetch_syserr(void)
109168404Spjd{
110168404Spjd    int e;
111168404Spjd    e = errno;
112168404Spjd
113185029Spjd    if (!com_err_initialized)
114286570Smav	_fetch_init_com_err();
115185029Spjd
116185029Spjd    switch (errno) {
117185029Spjd    case 0:
118185029Spjd	fetchLastErrCode = FETCH_OK;
119185029Spjd	break;
120185029Spjd    case EPERM:
121168404Spjd    case EACCES:
122168404Spjd    case EROFS:
123168404Spjd    case EAUTH:
124168404Spjd    case ENEEDAUTH:
125251478Sdelphij	fetchLastErrCode = FETCH_AUTH;
126168404Spjd	break;
127168404Spjd    case ENOENT:
128168404Spjd    case EISDIR: /* XXX */
129185029Spjd	fetchLastErrCode = FETCH_UNAVAIL;
130219089Spjd	break;
131258632Savg    case ENOMEM:
132286763Smav	fetchLastErrCode = FETCH_MEMORY;
133168404Spjd	break;
134168404Spjd    case EBUSY:
135297633Strasz    case EAGAIN:
136168404Spjd	fetchLastErrCode = FETCH_TEMP;
137168404Spjd	break;
138168404Spjd    case EEXIST:
139248572Ssmh	fetchLastErrCode = FETCH_EXISTS;
140219089Spjd	break;
141168404Spjd    case ENOSPC:
142168404Spjd	fetchLastErrCode = FETCH_FULL;
143272483Ssmh	break;
144191902Skmacy    case EADDRINUSE:
145240133Smm    case EADDRNOTAVAIL:
146240133Smm    case ENETDOWN:
147240133Smm    case ENETUNREACH:
148240133Smm    case ENETRESET:
149240133Smm    case EHOSTUNREACH:
150240133Smm	fetchLastErrCode = FETCH_NETWORK;
151240133Smm	break;
152240133Smm    case ECONNABORTED:
153286763Smav    case ECONNRESET:
154286763Smav	fetchLastErrCode = FETCH_ABORT;
155286763Smav	break;
156286763Smav    case ETIMEDOUT:
157168404Spjd	fetchLastErrCode = FETCH_TIMEOUT;
158286763Smav	break;
159286763Smav    case ECONNREFUSED:
160286763Smav    case EHOSTDOWN:
161286763Smav	fetchLastErrCode = FETCH_DOWN;
162301997Skib	break;
163301997Skib    default:
164301997Skib	fetchLastErrCode = FETCH_UNKNOWN;
165301997Skib    }
166286625Smav    com_err("libfetch", fetchLastErrCode, "(%03d %s)", e, strerror(e));
167168404Spjd}
168258632Savg
169286763Smav
170286763Smav/*
171286763Smav * Emit status message
172286763Smav */
173286763Smavint
174258632Savg_fetch_info(char *fmt, ...)
175286763Smav{
176258632Savg    va_list ap;
177286763Smav    char *s;
178286763Smav
179286763Smav    if (!com_err_initialized)
180286763Smav	_fetch_init_com_err();
181286763Smav
182286763Smav    va_start(ap, fmt);
183286763Smav    vasprintf(&s, fmt, ap);
184168404Spjd    va_end(ap);
185168404Spjd
186168404Spjd    if (s == NULL) {
187286763Smav	com_err("libfetch", FETCH_MEMORY, "");
188286763Smav	return -1;
189286763Smav    } else {
190208373Smm	com_err("libfetch", FETCH_VERBOSE, "%s", s);
191208373Smm	free(s);
192208373Smm	return 0;
193208373Smm    }
194286625Smav}
195208373Smm
196168404Spjd
197286625Smav/*** Network-related utility functions ***************************************/
198286625Smav
199286625Smav/*
200286625Smav * Establish a TCP connection to the specified port on the specified host.
201286625Smav */
202286625Smavint
203286625Smav_fetch_connect(char *host, int port, int verbose)
204286625Smav{
205286625Smav    struct sockaddr_in sin;
206286625Smav    struct hostent *he;
207286625Smav    int sd;
208286625Smav
209168404Spjd#ifndef NDEBUG
210168404Spjd    fprintf(stderr, "\033[1m---> %s:%d\033[m\n", host, port);
211168404Spjd#endif
212168404Spjd
213168404Spjd    if (verbose)
214258632Savg	_fetch_info("looking up %s", host);
215258632Savg
216258632Savg    /* look up host name */
217258632Savg    if ((he = gethostbyname(host)) == NULL) {
218258632Savg	_netdb_seterr(h_errno);
219208373Smm	return -1;
220287702Sdelphij    }
221168404Spjd
222168404Spjd    if (verbose)
223185029Spjd	_fetch_info("connecting to %s:%d", host, port);
224185029Spjd
225185029Spjd    /* set up socket address structure */
226185029Spjd    bzero(&sin, sizeof(sin));
227286762Smav    bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length);
228286762Smav    sin.sin_family = he->h_addrtype;
229286762Smav    sin.sin_port = htons(port);
230185029Spjd
231185029Spjd    /* try to connect */
232185029Spjd    if ((sd = socket(sin.sin_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
233275780Sdelphij	_fetch_syserr();
234208373Smm	return -1;
235208373Smm    }
236208373Smm    if (connect(sd, (struct sockaddr *)&sin, sizeof sin) == -1) {
237242845Sdelphij	_fetch_syserr();
238269230Sdelphij	close(sd);
239272483Ssmh	return -1;
240185029Spjd    }
241270759Ssmh
242275748Sdelphij    return sd;
243270759Ssmh}
244270759Ssmh
245270759Ssmh
246270759Ssmh/*
247270759Ssmh * Read a line of text from a socket w/ timeout
248270759Ssmh */
249272483Ssmh#define MIN_BUF_SIZE 1024
250270759Ssmh
251270759Ssmhint
252270759Ssmh_fetch_getln(int fd, char **buf, size_t *size, size_t *len)
253270759Ssmh{
254185029Spjd    struct timeval now, timeout, wait;
255275780Sdelphij    fd_set readfds;
256273026Sdelphij    int r;
257168473Spjd    char c;
258217367Smdf
259168473Spjd    if (*buf == NULL) {
260217367Smdf	if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) {
261168473Spjd	    errno = ENOMEM;
262269230Sdelphij	    return -1;
263269230Sdelphij	}
264269230Sdelphij	*size = MIN_BUF_SIZE;
265273026Sdelphij    }
266273026Sdelphij
267273026Sdelphij    **buf = '\0';
268273026Sdelphij    *len = 0;
269270759Ssmh
270270759Ssmh    if (fetchTimeout) {
271270759Ssmh	gettimeofday(&timeout, NULL);
272270759Ssmh	timeout.tv_sec += fetchTimeout;
273270759Ssmh	FD_ZERO(&readfds);
274270759Ssmh    }
275270759Ssmh
276270759Ssmh    do {
277168404Spjd	if (fetchTimeout) {
278270759Ssmh	    FD_SET(fd, &readfds);
279270759Ssmh	    gettimeofday(&now, NULL);
280270759Ssmh	    wait.tv_sec = timeout.tv_sec - now.tv_sec;
281270759Ssmh	    wait.tv_usec = timeout.tv_usec - now.tv_usec;
282270759Ssmh	    if (wait.tv_usec < 0) {
283270759Ssmh		wait.tv_usec += 1000000;
284270759Ssmh		wait.tv_sec--;
285270759Ssmh	    }
286270759Ssmh	    if (wait.tv_sec < 0) {
287270759Ssmh		errno = ETIMEDOUT;
288270759Ssmh		return -1;
289272483Ssmh	    }
290270759Ssmh	    r = select(fd+1, &readfds, NULL, NULL, &wait);
291272483Ssmh	    if (r == -1) {
292270759Ssmh		if (errno == EINTR)
293270759Ssmh		    continue;
294270759Ssmh		/* EBADF or EINVAL: shouldn't happen */
295270759Ssmh		return -1;
296270759Ssmh	    }
297270759Ssmh	    if (!FD_ISSET(fd, &readfds))
298275748Sdelphij		continue;
299275748Sdelphij	}
300275748Sdelphij	r = read(fd, &c, 1);
301275748Sdelphij	if (r == 0)
302275748Sdelphij	    break;
303275748Sdelphij	if (r == -1) {
304275748Sdelphij	    if (errno == EINTR)
305275748Sdelphij		continue;
306275748Sdelphij	    /* any other error is bad news */
307272483Ssmh	    return -1;
308270759Ssmh	}
309168404Spjd	(*buf)[*len] = c;
310185029Spjd	*len += 1;
311168404Spjd	if (*len == *size) {
312168404Spjd	    char *tmp;
313168404Spjd
314168404Spjd	    if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) {
315168404Spjd		errno = ENOMEM;
316185029Spjd		return -1;
317185029Spjd	    }
318185029Spjd	    *buf = tmp;
319185029Spjd	    *size = *size * 2 + 1;
320185029Spjd	}
321185029Spjd    } while (c != '\n');
322185029Spjd
323185029Spjd    return 0;
324168404Spjd}
325168404Spjd
326168404Spjd
327168404Spjd/*** Directory-related utility functions *************************************/
328168404Spjd
329168404Spjdint
330168404Spjd_fetch_add_entry(struct url_ent **p, int *size, int *len,
331185029Spjd		 char *name, struct url_stat *stat)
332185029Spjd{
333185029Spjd    struct url_ent *tmp;
334185029Spjd
335185029Spjd    if (*p == NULL) {
336185029Spjd#define INITIAL_SIZE 8
337185029Spjd	if ((*p = malloc(INITIAL_SIZE * sizeof **p)) == NULL) {
338185029Spjd	    errno = ENOMEM;
339168404Spjd	    _fetch_syserr();
340168404Spjd	    return -1;
341168404Spjd	}
342286763Smav	*size = INITIAL_SIZE;
343286763Smav	*len = 0;
344286763Smav#undef INITIAL_SIZE
345286763Smav    }
346286763Smav
347286763Smav    if (*len >= *size - 1) {
348286763Smav	tmp = realloc(*p, *size * 2 * sizeof **p);
349286763Smav	if (tmp == NULL) {
350286763Smav	    errno = ENOMEM;
351286763Smav	    _fetch_syserr();
352286763Smav	    return -1;
353286763Smav	}
354286766Smav	*size *= 2;
355168404Spjd	*p = tmp;
356168404Spjd    }
357185029Spjd
358168404Spjd    tmp = *p + *len;
359168404Spjd    snprintf(tmp->name, MAXPATHLEN, "%s", name);
360168404Spjd    bcopy(stat, &tmp->stat, sizeof *stat);
361168404Spjd
362168404Spjd    (*len)++;
363185029Spjd    (++tmp)->name[0] = 0;
364168404Spjd
365168404Spjd    return 0;
366168404Spjd}
367168404Spjd