xref: /freebsd/lib/libfetch/common.c (revision a79b71281cd63ad7a6cc43a6d5673a2510b51630)
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 <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  */
53 static 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  */
67 static 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  */
78 void
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  */
89 void
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  */
152 void
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  */
169 int
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 #ifndef NDEBUG
177     fprintf(stderr, "\033[1m---> %s:%d\033[m\n", host, port);
178 #endif
179 
180     if (verbose)
181 	_fetch_info("looking up %s", host);
182 
183     /* look up host name and set up socket address structure */
184     snprintf(pbuf, sizeof(pbuf), "%d", port);
185     memset(&hints, 0, sizeof(hints));
186     hints.ai_family = af;
187     hints.ai_socktype = SOCK_STREAM;
188     hints.ai_protocol = 0;
189     if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
190 	_netdb_seterr(err);
191 	return -1;
192     }
193 
194     if (verbose)
195 	_fetch_info("connecting to %s:%d", host, port);
196 
197     /* try to connect */
198     sd = -1;
199     for (res = res0; res; res = res->ai_next) {
200 	if ((sd = socket(res->ai_family, res->ai_socktype,
201 			 res->ai_protocol)) < 0)
202 	    continue;
203 	if (connect(sd, res->ai_addr, res->ai_addrlen) >= 0)
204 	    break;
205 	close(sd);
206 	sd = -1;
207     }
208     if (sd < 0) {
209 	_fetch_syserr();
210 	return -1;
211     }
212 
213     return sd;
214 }
215 
216 
217 /*
218  * Read a line of text from a socket w/ timeout
219  */
220 #define MIN_BUF_SIZE 1024
221 
222 int
223 _fetch_getln(int fd, char **buf, size_t *size, size_t *len)
224 {
225     struct timeval now, timeout, wait;
226     fd_set readfds;
227     int r;
228     char c;
229 
230     if (*buf == NULL) {
231 	if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) {
232 	    errno = ENOMEM;
233 	    return -1;
234 	}
235 	*size = MIN_BUF_SIZE;
236     }
237 
238     **buf = '\0';
239     *len = 0;
240 
241     if (fetchTimeout) {
242 	gettimeofday(&timeout, NULL);
243 	timeout.tv_sec += fetchTimeout;
244 	FD_ZERO(&readfds);
245     }
246 
247     do {
248 	if (fetchTimeout) {
249 	    FD_SET(fd, &readfds);
250 	    gettimeofday(&now, NULL);
251 	    wait.tv_sec = timeout.tv_sec - now.tv_sec;
252 	    wait.tv_usec = timeout.tv_usec - now.tv_usec;
253 	    if (wait.tv_usec < 0) {
254 		wait.tv_usec += 1000000;
255 		wait.tv_sec--;
256 	    }
257 	    if (wait.tv_sec < 0) {
258 		errno = ETIMEDOUT;
259 		return -1;
260 	    }
261 	    r = select(fd+1, &readfds, NULL, NULL, &wait);
262 	    if (r == -1) {
263 		if (errno == EINTR)
264 		    continue;
265 		/* EBADF or EINVAL: shouldn't happen */
266 		return -1;
267 	    }
268 	    if (!FD_ISSET(fd, &readfds))
269 		continue;
270 	}
271 	r = read(fd, &c, 1);
272 	if (r == 0)
273 	    break;
274 	if (r == -1) {
275 	    if (errno == EINTR)
276 		continue;
277 	    /* any other error is bad news */
278 	    return -1;
279 	}
280 	(*buf)[*len] = c;
281 	*len += 1;
282 	if (*len == *size) {
283 	    char *tmp;
284 
285 	    if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) {
286 		errno = ENOMEM;
287 		return -1;
288 	    }
289 	    *buf = tmp;
290 	    *size = *size * 2 + 1;
291 	}
292     } while (c != '\n');
293 
294     return 0;
295 }
296 
297 
298 /*** Directory-related utility functions *************************************/
299 
300 int
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