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