xref: /freebsd/lib/libfetch/common.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
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$
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  */
53 static struct fetcherr _netdb_errlist[] = {
54     { HOST_NOT_FOUND,	FETCH_RESOLV,	"Host not found" },
55     { TRY_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
56     { NO_RECOVERY,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
57     { NO_DATA,		FETCH_RESOLV,	"No address record" },
58     { -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
59 };
60 
61 static int com_err_initialized;
62 
63 /*** Error-reporting functions ***********************************************/
64 
65 /*
66  * Initialize the common error library
67  */
68 static 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  */
78 static 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  */
91 void
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  */
107 void
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  */
173 int
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  */
202 int
203 _fetch_connect(char *host, int port, int verbose)
204 {
205     struct sockaddr_in sin;
206     struct hostent *he;
207     int sd;
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 */
217     if ((he = gethostbyname(host)) == NULL) {
218 	_netdb_seterr(h_errno);
219 	return -1;
220     }
221 
222     if (verbose)
223 	_fetch_info("connecting to %s:%d", host, port);
224 
225     /* set up socket address structure */
226     bzero(&sin, sizeof(sin));
227     bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length);
228     sin.sin_family = he->h_addrtype;
229     sin.sin_port = htons(port);
230 
231     /* try to connect */
232     if ((sd = socket(sin.sin_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
233 	_fetch_syserr();
234 	return -1;
235     }
236     if (connect(sd, (struct sockaddr *)&sin, sizeof sin) == -1) {
237 	_fetch_syserr();
238 	close(sd);
239 	return -1;
240     }
241 
242     return sd;
243 }
244 
245 
246 /*
247  * Read a line of text from a socket w/ timeout
248  */
249 #define MIN_BUF_SIZE 1024
250 
251 int
252 _fetch_getln(int fd, char **buf, size_t *size, size_t *len)
253 {
254     struct timeval now, timeout, wait;
255     fd_set readfds;
256     int r;
257     char c;
258 
259     if (*buf == NULL) {
260 	if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) {
261 	    errno = ENOMEM;
262 	    return -1;
263 	}
264 	*size = MIN_BUF_SIZE;
265     }
266 
267     **buf = '\0';
268     *len = 0;
269 
270     if (fetchTimeout) {
271 	gettimeofday(&timeout, NULL);
272 	timeout.tv_sec += fetchTimeout;
273 	FD_ZERO(&readfds);
274     }
275 
276     do {
277 	if (fetchTimeout) {
278 	    FD_SET(fd, &readfds);
279 	    gettimeofday(&now, NULL);
280 	    wait.tv_sec = timeout.tv_sec - now.tv_sec;
281 	    wait.tv_usec = timeout.tv_usec - now.tv_usec;
282 	    if (wait.tv_usec < 0) {
283 		wait.tv_usec += 1000000;
284 		wait.tv_sec--;
285 	    }
286 	    if (wait.tv_sec < 0) {
287 		errno = ETIMEDOUT;
288 		return -1;
289 	    }
290 	    r = select(fd+1, &readfds, NULL, NULL, &wait);
291 	    if (r == -1) {
292 		if (errno == EINTR)
293 		    continue;
294 		/* EBADF or EINVAL: shouldn't happen */
295 		return -1;
296 	    }
297 	    if (!FD_ISSET(fd, &readfds))
298 		continue;
299 	}
300 	r = read(fd, &c, 1);
301 	if (r == 0)
302 	    break;
303 	if (r == -1) {
304 	    if (errno == EINTR)
305 		continue;
306 	    /* any other error is bad news */
307 	    return -1;
308 	}
309 	(*buf)[*len] = c;
310 	*len += 1;
311 	if (*len == *size) {
312 	    char *tmp;
313 
314 	    if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) {
315 		errno = ENOMEM;
316 		return -1;
317 	    }
318 	    *buf = tmp;
319 	    *size = *size * 2 + 1;
320 	}
321     } while (c != '\n');
322 
323     return 0;
324 }
325 
326 
327 /*** Directory-related utility functions *************************************/
328 
329 int
330 _fetch_add_entry(struct url_ent **p, int *size, int *len,
331 		 char *name, struct url_stat *stat)
332 {
333     struct url_ent *tmp;
334 
335     if (*p == NULL) {
336 #define INITIAL_SIZE 8
337 	if ((*p = malloc(INITIAL_SIZE * sizeof **p)) == NULL) {
338 	    errno = ENOMEM;
339 	    _fetch_syserr();
340 	    return -1;
341 	}
342 	*size = INITIAL_SIZE;
343 	*len = 0;
344 #undef INITIAL_SIZE
345     }
346 
347     if (*len >= *size - 1) {
348 	tmp = realloc(*p, *size * 2 * sizeof **p);
349 	if (tmp == NULL) {
350 	    errno = ENOMEM;
351 	    _fetch_syserr();
352 	    return -1;
353 	}
354 	*size *= 2;
355 	*p = tmp;
356     }
357 
358     tmp = *p + *len;
359     snprintf(tmp->name, MAXPATHLEN, "%s", name);
360     bcopy(stat, &tmp->stat, sizeof *stat);
361 
362     (*len)++;
363     (++tmp)->name[0] = 0;
364 
365     return 0;
366 }
367