xref: /freebsd/lib/libfetch/common.c (revision 9207b4cff7b8d483f4dd3c62266c2b58819eb7f9)
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 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <sys/time.h>
35 #include <sys/uio.h>
36 #include <netinet/in.h>
37 
38 #include <errno.h>
39 #include <netdb.h>
40 #include <stdarg.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <unistd.h>
45 
46 #include "fetch.h"
47 #include "common.h"
48 
49 
50 /*** Local data **************************************************************/
51 
52 /*
53  * Error messages for resolver errors
54  */
55 static struct fetcherr _netdb_errlist[] = {
56     { EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
57     { EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
58     { EAI_FAIL,		FETCH_RESOLV,	"Non-recoverable resolver failure" },
59     { EAI_NONAME,	FETCH_RESOLV,	"No address record" },
60     { -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
61 };
62 
63 /* End-of-Line */
64 static const char ENDL[2] = "\r\n";
65 
66 
67 /*** Error-reporting functions ***********************************************/
68 
69 /*
70  * Map error code to string
71  */
72 static struct fetcherr *
73 _fetch_finderr(struct fetcherr *p, int e)
74 {
75     while (p->num != -1 && p->num != e)
76 	p++;
77     return p;
78 }
79 
80 /*
81  * Set error code
82  */
83 void
84 _fetch_seterr(struct fetcherr *p, int e)
85 {
86     p = _fetch_finderr(p, e);
87     fetchLastErrCode = p->cat;
88     snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
89 }
90 
91 /*
92  * Set error code according to errno
93  */
94 void
95 _fetch_syserr(void)
96 {
97     int e;
98     e = errno;
99 
100     switch (errno) {
101     case 0:
102 	fetchLastErrCode = FETCH_OK;
103 	break;
104     case EPERM:
105     case EACCES:
106     case EROFS:
107     case EAUTH:
108     case ENEEDAUTH:
109 	fetchLastErrCode = FETCH_AUTH;
110 	break;
111     case ENOENT:
112     case EISDIR: /* XXX */
113 	fetchLastErrCode = FETCH_UNAVAIL;
114 	break;
115     case ENOMEM:
116 	fetchLastErrCode = FETCH_MEMORY;
117 	break;
118     case EBUSY:
119     case EAGAIN:
120 	fetchLastErrCode = FETCH_TEMP;
121 	break;
122     case EEXIST:
123 	fetchLastErrCode = FETCH_EXISTS;
124 	break;
125     case ENOSPC:
126 	fetchLastErrCode = FETCH_FULL;
127 	break;
128     case EADDRINUSE:
129     case EADDRNOTAVAIL:
130     case ENETDOWN:
131     case ENETUNREACH:
132     case ENETRESET:
133     case EHOSTUNREACH:
134 	fetchLastErrCode = FETCH_NETWORK;
135 	break;
136     case ECONNABORTED:
137     case ECONNRESET:
138 	fetchLastErrCode = FETCH_ABORT;
139 	break;
140     case ETIMEDOUT:
141 	fetchLastErrCode = FETCH_TIMEOUT;
142 	break;
143     case ECONNREFUSED:
144     case EHOSTDOWN:
145 	fetchLastErrCode = FETCH_DOWN;
146 	break;
147     default:
148 	fetchLastErrCode = FETCH_UNKNOWN;
149     }
150     snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(e));
151 }
152 
153 
154 /*
155  * Emit status message
156  */
157 void
158 _fetch_info(const char *fmt, ...)
159 {
160     va_list ap;
161 
162     va_start(ap, fmt);
163     vfprintf(stderr, fmt, ap);
164     va_end(ap);
165     fputc('\n', stderr);
166 }
167 
168 
169 /*** Network-related utility functions ***************************************/
170 
171 /*
172  * Return the default port for a scheme
173  */
174 int
175 _fetch_default_port(const char *scheme)
176 {
177     struct servent *se;
178 
179     if ((se = getservbyname(scheme, "tcp")) != NULL)
180 	return ntohs(se->s_port);
181     if (strcasecmp(scheme, SCHEME_FTP) == 0)
182 	return FTP_DEFAULT_PORT;
183     if (strcasecmp(scheme, SCHEME_HTTP) == 0)
184 	return HTTP_DEFAULT_PORT;
185     return 0;
186 }
187 
188 /*
189  * Return the default proxy port for a scheme
190  */
191 int
192 _fetch_default_proxy_port(const char *scheme)
193 {
194     if (strcasecmp(scheme, SCHEME_FTP) == 0)
195 	return FTP_DEFAULT_PROXY_PORT;
196     if (strcasecmp(scheme, SCHEME_HTTP) == 0)
197 	return HTTP_DEFAULT_PROXY_PORT;
198     return 0;
199 }
200 
201 /*
202  * Establish a TCP connection to the specified port on the specified host.
203  */
204 int
205 _fetch_connect(const char *host, int port, int af, int verbose)
206 {
207     char pbuf[10];
208     struct addrinfo hints, *res, *res0;
209     int sd, err;
210 
211     DEBUG(fprintf(stderr, "\033[1m---> %s:%d\033[m\n", host, port));
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     for (sd = -1, res = res0; res; res = res->ai_next) {
232 	if ((sd = socket(res->ai_family, res->ai_socktype,
233 			 res->ai_protocol)) == -1)
234 	    continue;
235 	if (connect(sd, res->ai_addr, res->ai_addrlen) != -1)
236 	    break;
237 	close(sd);
238 	sd = -1;
239     }
240     freeaddrinfo(res0);
241     if (sd == -1) {
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 
255 int
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 && fetchRestartCalls)
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 && fetchRestartCalls)
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     DEBUG(fprintf(stderr, "\033[1m<<< %.*s\033[m", (int)*len, *buf));
328     return 0;
329 }
330 
331 
332 /*
333  * Write a line of text to a socket w/ timeout
334  * XXX currently does not enforce timeout
335  */
336 int
337 _fetch_putln(int fd, const char *str, size_t len)
338 {
339     struct iovec iov[2];
340     ssize_t wlen;
341 
342     /* XXX should enforce timeout */
343     iov[0].iov_base = (char *)str;
344     iov[0].iov_len = len;
345     iov[1].iov_base = (char *)ENDL;
346     iov[1].iov_len = sizeof ENDL;
347     len += sizeof ENDL;
348     wlen = writev(fd, iov, 2);
349     if (wlen < 0 || (size_t)wlen != len)
350 	return -1;
351     DEBUG(fprintf(stderr, "\033[1m>>> %s\n\033[m", str));
352     return 0;
353 }
354 
355 
356 /*** Directory-related utility functions *************************************/
357 
358 int
359 _fetch_add_entry(struct url_ent **p, int *size, int *len,
360 		 const char *name, struct url_stat *us)
361 {
362     struct url_ent *tmp;
363 
364     if (*p == NULL) {
365 #define INITIAL_SIZE 8
366 	if ((*p = malloc(INITIAL_SIZE * sizeof **p)) == NULL) {
367 	    errno = ENOMEM;
368 	    _fetch_syserr();
369 	    return -1;
370 	}
371 	*size = INITIAL_SIZE;
372 	*len = 0;
373 #undef INITIAL_SIZE
374     }
375 
376     if (*len >= *size - 1) {
377 	tmp = realloc(*p, *size * 2 * sizeof **p);
378 	if (tmp == NULL) {
379 	    errno = ENOMEM;
380 	    _fetch_syserr();
381 	    return -1;
382 	}
383 	*size *= 2;
384 	*p = tmp;
385     }
386 
387     tmp = *p + *len;
388     snprintf(tmp->name, PATH_MAX, "%s", name);
389     bcopy(us, &tmp->stat, sizeof *us);
390 
391     (*len)++;
392     (++tmp)->name[0] = 0;
393 
394     return 0;
395 }
396