Branch data Line data Source code
1 : : /*-
2 : : * SPDX-License-Identifier: BSD-3-Clause
3 : : *
4 : : * Copyright (c) 1998-2004 Dag-Erling Smørgrav
5 : : * All rights reserved.
6 : : *
7 : : * Redistribution and use in source and binary forms, with or without
8 : : * modification, are permitted provided that the following conditions
9 : : * are met:
10 : : * 1. Redistributions of source code must retain the above copyright
11 : : * notice, this list of conditions and the following disclaimer
12 : : * in this position and unchanged.
13 : : * 2. Redistributions in binary form must reproduce the above copyright
14 : : * notice, this list of conditions and the following disclaimer in the
15 : : * documentation and/or other materials provided with the distribution.
16 : : * 3. The name of the author may not be used to endorse or promote products
17 : : * derived from this software without specific prior written permission
18 : : *
19 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 : : * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #include <sys/cdefs.h>
32 : : #include "bsd_compat.h"
33 : : __FBSDID("$FreeBSD: head/lib/libfetch/fetch.c 357212 2020-01-28 18:37:18Z gordon $");
34 : :
35 : : #include <sys/param.h>
36 : :
37 : : #include <netinet/in.h>
38 : :
39 : : #include <errno.h>
40 : : #include <ctype.h>
41 : : #include <stdio.h>
42 : : #include <stdlib.h>
43 : : #include <string.h>
44 : :
45 : : #include "fetch.h"
46 : : #include "common.h"
47 : :
48 : : auth_t fetchAuthMethod;
49 : : int fetchLastErrCode;
50 : : char fetchLastErrString[MAXERRSTRING];
51 : : int fetchTimeout;
52 : : int fetchRestartCalls = 1;
53 : : int fetchDebug;
54 : :
55 : :
56 : : /*** Local data **************************************************************/
57 : :
58 : : /*
59 : : * Error messages for parser errors
60 : : */
61 : : #define URL_MALFORMED 1
62 : : #define URL_BAD_SCHEME 2
63 : : #define URL_BAD_PORT 3
64 : : static struct fetcherr url_errlist[] = {
65 : : { URL_MALFORMED, FETCH_URL, "Malformed URL" },
66 : : { URL_BAD_SCHEME, FETCH_URL, "Invalid URL scheme" },
67 : : { URL_BAD_PORT, FETCH_URL, "Invalid server port" },
68 : : { -1, FETCH_UNKNOWN, "Unknown parser error" }
69 : : };
70 : :
71 : :
72 : : /*** Public API **************************************************************/
73 : :
74 : : /*
75 : : * Select the appropriate protocol for the URL scheme, and return a
76 : : * read-only stream connected to the document referenced by the URL.
77 : : * Also fill out the struct url_stat.
78 : : */
79 : : FILE *
80 : 28 : fetchXGet(struct url *URL, struct url_stat *us, const char *flags)
81 : : {
82 : :
83 [ - + ]: 28 : if (us != NULL) {
84 : 28 : us->size = -1;
85 : 28 : us->atime = us->mtime = 0;
86 : 28 : }
87 [ + - ]: 28 : if (strcmp(URL->scheme, SCHEME_FILE) == 0)
88 : 0 : return (fetchXGetFile(URL, us, flags));
89 [ + - ]: 28 : else if (strcmp(URL->scheme, SCHEME_FTP) == 0)
90 : 0 : return (fetchXGetFTP(URL, us, flags));
91 [ - + ]: 28 : else if (strcmp(URL->scheme, SCHEME_HTTP) == 0)
92 : 28 : return (fetchXGetHTTP(URL, us, flags));
93 [ # # ]: 0 : else if (strcmp(URL->scheme, SCHEME_HTTPS) == 0)
94 : 0 : return (fetchXGetHTTP(URL, us, flags));
95 : 0 : url_seterr(URL_BAD_SCHEME);
96 : 0 : return (NULL);
97 : 28 : }
98 : :
99 : : /*
100 : : * Select the appropriate protocol for the URL scheme, and return a
101 : : * read-only stream connected to the document referenced by the URL.
102 : : */
103 : : FILE *
104 : 0 : fetchGet(struct url *URL, const char *flags)
105 : : {
106 : 0 : return (fetchXGet(URL, NULL, flags));
107 : : }
108 : :
109 : : /*
110 : : * Select the appropriate protocol for the URL scheme, and return a
111 : : * write-only stream connected to the document referenced by the URL.
112 : : */
113 : : FILE *
114 : 0 : fetchPut(struct url *URL, const char *flags)
115 : : {
116 : :
117 [ # # ]: 0 : if (strcmp(URL->scheme, SCHEME_FILE) == 0)
118 : 0 : return (fetchPutFile(URL, flags));
119 [ # # ]: 0 : else if (strcmp(URL->scheme, SCHEME_FTP) == 0)
120 : 0 : return (fetchPutFTP(URL, flags));
121 [ # # ]: 0 : else if (strcmp(URL->scheme, SCHEME_HTTP) == 0)
122 : 0 : return (fetchPutHTTP(URL, flags));
123 [ # # ]: 0 : else if (strcmp(URL->scheme, SCHEME_HTTPS) == 0)
124 : 0 : return (fetchPutHTTP(URL, flags));
125 : 0 : url_seterr(URL_BAD_SCHEME);
126 : 0 : return (NULL);
127 : 0 : }
128 : :
129 : : /*
130 : : * Select the appropriate protocol for the URL scheme, and return the
131 : : * size of the document referenced by the URL if it exists.
132 : : */
133 : : int
134 : 0 : fetchStat(struct url *URL, struct url_stat *us, const char *flags)
135 : : {
136 : :
137 [ # # ]: 0 : if (us != NULL) {
138 : 0 : us->size = -1;
139 : 0 : us->atime = us->mtime = 0;
140 : 0 : }
141 [ # # ]: 0 : if (strcmp(URL->scheme, SCHEME_FILE) == 0)
142 : 0 : return (fetchStatFile(URL, us, flags));
143 [ # # ]: 0 : else if (strcmp(URL->scheme, SCHEME_FTP) == 0)
144 : 0 : return (fetchStatFTP(URL, us, flags));
145 [ # # ]: 0 : else if (strcmp(URL->scheme, SCHEME_HTTP) == 0)
146 : 0 : return (fetchStatHTTP(URL, us, flags));
147 [ # # ]: 0 : else if (strcmp(URL->scheme, SCHEME_HTTPS) == 0)
148 : 0 : return (fetchStatHTTP(URL, us, flags));
149 : 0 : url_seterr(URL_BAD_SCHEME);
150 : 0 : return (-1);
151 : 0 : }
152 : :
153 : : /*
154 : : * Select the appropriate protocol for the URL scheme, and return a
155 : : * list of files in the directory pointed to by the URL.
156 : : */
157 : : struct url_ent *
158 : 0 : fetchList(struct url *URL, const char *flags)
159 : : {
160 : :
161 [ # # ]: 0 : if (strcmp(URL->scheme, SCHEME_FILE) == 0)
162 : 0 : return (fetchListFile(URL, flags));
163 [ # # ]: 0 : else if (strcmp(URL->scheme, SCHEME_FTP) == 0)
164 : 0 : return (fetchListFTP(URL, flags));
165 [ # # ]: 0 : else if (strcmp(URL->scheme, SCHEME_HTTP) == 0)
166 : 0 : return (fetchListHTTP(URL, flags));
167 [ # # ]: 0 : else if (strcmp(URL->scheme, SCHEME_HTTPS) == 0)
168 : 0 : return (fetchListHTTP(URL, flags));
169 : 0 : url_seterr(URL_BAD_SCHEME);
170 : 0 : return (NULL);
171 : 0 : }
172 : :
173 : : /*
174 : : * Attempt to parse the given URL; if successful, call fetchXGet().
175 : : */
176 : : FILE *
177 : 0 : fetchXGetURL(const char *URL, struct url_stat *us, const char *flags)
178 : : {
179 : : struct url *u;
180 : : FILE *f;
181 : :
182 [ # # ]: 0 : if ((u = fetchParseURL(URL)) == NULL)
183 : 0 : return (NULL);
184 : :
185 : 0 : f = fetchXGet(u, us, flags);
186 : :
187 : 0 : fetchFreeURL(u);
188 : 0 : return (f);
189 : 0 : }
190 : :
191 : : /*
192 : : * Attempt to parse the given URL; if successful, call fetchGet().
193 : : */
194 : : FILE *
195 : 0 : fetchGetURL(const char *URL, const char *flags)
196 : : {
197 : 0 : return (fetchXGetURL(URL, NULL, flags));
198 : : }
199 : :
200 : : /*
201 : : * Attempt to parse the given URL; if successful, call fetchPut().
202 : : */
203 : : FILE *
204 : 0 : fetchPutURL(const char *URL, const char *flags)
205 : : {
206 : : struct url *u;
207 : : FILE *f;
208 : :
209 [ # # ]: 0 : if ((u = fetchParseURL(URL)) == NULL)
210 : 0 : return (NULL);
211 : :
212 : 0 : f = fetchPut(u, flags);
213 : :
214 : 0 : fetchFreeURL(u);
215 : 0 : return (f);
216 : 0 : }
217 : :
218 : : /*
219 : : * Attempt to parse the given URL; if successful, call fetchStat().
220 : : */
221 : : int
222 : 0 : fetchStatURL(const char *URL, struct url_stat *us, const char *flags)
223 : : {
224 : : struct url *u;
225 : : int s;
226 : :
227 [ # # ]: 0 : if ((u = fetchParseURL(URL)) == NULL)
228 : 0 : return (-1);
229 : :
230 : 0 : s = fetchStat(u, us, flags);
231 : :
232 : 0 : fetchFreeURL(u);
233 : 0 : return (s);
234 : 0 : }
235 : :
236 : : /*
237 : : * Attempt to parse the given URL; if successful, call fetchList().
238 : : */
239 : : struct url_ent *
240 : 0 : fetchListURL(const char *URL, const char *flags)
241 : : {
242 : : struct url *u;
243 : : struct url_ent *ue;
244 : :
245 [ # # ]: 0 : if ((u = fetchParseURL(URL)) == NULL)
246 : 0 : return (NULL);
247 : :
248 : 0 : ue = fetchList(u, flags);
249 : :
250 : 0 : fetchFreeURL(u);
251 : 0 : return (ue);
252 : 0 : }
253 : :
254 : : /*
255 : : * Make a URL
256 : : */
257 : : struct url *
258 : 0 : fetchMakeURL(const char *scheme, const char *host, int port, const char *doc,
259 : : const char *user, const char *pwd)
260 : : {
261 : : struct url *u;
262 : :
263 [ # # # # : 0 : if (!scheme || (!host && !doc)) {
# # ]
264 : 0 : url_seterr(URL_MALFORMED);
265 : 0 : return (NULL);
266 : : }
267 : :
268 [ # # # # ]: 0 : if (port < 0 || port > 65535) {
269 : 0 : url_seterr(URL_BAD_PORT);
270 : 0 : return (NULL);
271 : : }
272 : :
273 : : /* allocate struct url */
274 [ # # ]: 0 : if ((u = calloc(1, sizeof(*u))) == NULL) {
275 : 0 : fetch_syserr();
276 : 0 : return (NULL);
277 : : }
278 : 0 : u->netrcfd = -1;
279 : :
280 [ # # # # ]: 0 : if ((u->doc = strdup(doc ? doc : "/")) == NULL) {
281 : 0 : fetch_syserr();
282 : 0 : free(u);
283 : 0 : return (NULL);
284 : : }
285 : :
286 : : #define seturl(x) snprintf(u->x, sizeof(u->x), "%s", x)
287 : 0 : seturl(scheme);
288 : 0 : seturl(host);
289 : 0 : seturl(user);
290 : 0 : seturl(pwd);
291 : : #undef seturl
292 : 0 : u->port = port;
293 : :
294 : 0 : return (u);
295 : 0 : }
296 : :
297 : : /*
298 : : * Return value of the given hex digit.
299 : : */
300 : : static int
301 : 0 : fetch_hexval(char ch)
302 : : {
303 : :
304 [ # # # # ]: 0 : if (ch >= '0' && ch <= '9')
305 : 0 : return (ch - '0');
306 [ # # # # ]: 0 : else if (ch >= 'a' && ch <= 'f')
307 : 0 : return (ch - 'a' + 10);
308 [ # # # # ]: 0 : else if (ch >= 'A' && ch <= 'F')
309 : 0 : return (ch - 'A' + 10);
310 : 0 : return (-1);
311 : 0 : }
312 : :
313 : : /*
314 : : * Decode percent-encoded URL component from src into dst, stopping at end
315 : : * of string, or at @ or : separators. Returns a pointer to the unhandled
316 : : * part of the input string (null terminator, @, or :). No terminator is
317 : : * written to dst (it is the caller's responsibility).
318 : : */
319 : : static const char *
320 : 0 : fetch_pctdecode(char *dst, const char *src, size_t dlen)
321 : : {
322 : : int d1, d2;
323 : : char c;
324 : : const char *s;
325 : :
326 [ # # # # : 0 : for (s = src; *s != '\0' && *s != '@' && *s != ':'; s++) {
# # ]
327 [ # # # # : 0 : if (s[0] == '%' && (d1 = fetch_hexval(s[1])) >= 0 &&
# # ]
328 [ # # # # ]: 0 : (d2 = fetch_hexval(s[2])) >= 0 && (d1 > 0 || d2 > 0)) {
329 : 0 : c = d1 << 4 | d2;
330 : 0 : s += 2;
331 : 0 : } else {
332 : 0 : c = *s;
333 : : }
334 [ # # ]: 0 : if (dlen-- > 0)
335 : 0 : *dst++ = c;
336 : : else
337 : 0 : return (NULL);
338 : 0 : }
339 : 0 : return (s);
340 : 0 : }
341 : :
342 : : /*
343 : : * Split an URL into components. URL syntax is:
344 : : * [method:/][/[user[:pwd]@]host[:port]/][document]
345 : : * This almost, but not quite, RFC1738 URL syntax.
346 : : */
347 : : struct url *
348 : 1156 : fetchParseURL(const char *URL)
349 : : {
350 : : char *doc;
351 : : const char *p, *q;
352 : : struct url *u;
353 : : int i, n;
354 : :
355 : : /* allocate struct url */
356 [ + - ]: 1156 : if ((u = calloc(1, sizeof(*u))) == NULL) {
357 : 0 : fetch_syserr();
358 : 0 : return (NULL);
359 : : }
360 : 1156 : u->netrcfd = -1;
361 : :
362 : : /* scheme name */
363 [ + + ]: 1156 : if ((p = strstr(URL, ":/"))) {
364 [ - + ]: 1044 : if (p - URL > URL_SCHEMELEN)
365 : 0 : goto ouch;
366 [ + + ]: 5220 : for (i = 0; URL + i < p; i++)
367 : 4176 : u->scheme[i] = tolower((unsigned char)URL[i]);
368 : 1044 : URL = ++p;
369 : : /*
370 : : * Only one slash: no host, leave slash as part of document
371 : : * Two slashes: host follows, strip slashes
372 : : */
373 [ - + ]: 1044 : if (URL[1] == '/')
374 : 1044 : URL = (p += 2);
375 : 1044 : } else {
376 : 112 : p = URL;
377 : : }
378 [ + + + + : 1156 : if (!*URL || *URL == '/' || *URL == '.' ||
+ - + + ]
379 [ - + ]: 72 : (u->scheme[0] == '\0' &&
380 [ # # ]: 0 : strchr(URL, '/') == NULL && strchr(URL, ':') == NULL))
381 : 1196 : goto nohost;
382 : :
383 : 184 : p = strpbrk(URL, "/@");
384 [ + + + - ]: 184 : if (p && *p == '@') {
385 : : /* username */
386 : 0 : q = fetch_pctdecode(u->user, URL, URL_USERLEN);
387 [ # # ]: 0 : if (q == NULL)
388 : 0 : goto ouch;
389 : :
390 : : /* password */
391 [ # # ]: 0 : if (*q == ':') {
392 : 0 : q = fetch_pctdecode(u->pwd, q + 1, URL_PWDLEN);
393 [ # # ]: 0 : if (q == NULL)
394 : 0 : goto ouch;
395 : 0 : }
396 : 0 : p++;
397 : 0 : } else {
398 : 184 : p = URL;
399 : : }
400 : :
401 : : /* hostname */
402 [ - + ]: 184 : if (*p == '[') {
403 : 0 : q = p + 1 + strspn(p + 1, ":0123456789ABCDEFabcdef");
404 [ # # ]: 0 : if (*q++ != ']')
405 : 0 : goto ouch;
406 : 0 : } else {
407 : : /* valid characters in a DNS name */
408 : 184 : q = p + strspn(p, "-." "0123456789"
409 : : "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "_"
410 : : "abcdefghijklmnopqrstuvwxyz");
411 : : }
412 [ + + + + : 184 : if ((*q != '\0' && *q != '/' && *q != ':') || q - p > MAXHOSTNAMELEN)
+ + ]
413 : 112 : goto ouch;
414 [ + + ]: 656 : for (i = 0; p + i < q; i++)
415 : 584 : u->host[i] = tolower((unsigned char)p[i]);
416 : 72 : u->host[i] = '\0';
417 : 72 : p = q;
418 : :
419 : : /* port */
420 [ + + ]: 128 : if (*p == ':') {
421 [ + + + + ]: 336 : for (n = 0, q = ++p; *q && (*q != '/'); q++) {
422 [ + - + - : 280 : if (*q >= '0' && *q <= '9' && n < INT_MAX / 10) {
+ - ]
423 : 280 : n = n * 10 + (*q - '0');
424 : 280 : } else {
425 : : /* invalid port */
426 : 0 : url_seterr(URL_BAD_PORT);
427 : 0 : goto ouch;
428 : : }
429 : 280 : }
430 : : /* pkg extension allow for ssh compat */
431 : : /*if (n < 1 || n > IPPORT_MAX) */
432 : : #ifndef IPPORT_MAX
433 : : #define IPPORT_MAX 65535
434 : : #endif
435 [ + - + - ]: 56 : if (n < 0 || n > IPPORT_MAX)
436 : 0 : goto ouch;
437 : 56 : u->port = n;
438 : 56 : p = q;
439 : 56 : }
440 : :
441 : : nohost:
442 : : /* document */
443 [ + + ]: 1044 : if (!*p)
444 : 24 : p = "/";
445 : :
446 [ + + - + ]: 1044 : if (strcmp(u->scheme, SCHEME_HTTP) == 0 ||
447 : 988 : strcmp(u->scheme, SCHEME_HTTPS) == 0) {
448 : 56 : const char hexnums[] = "0123456789abcdef";
449 : :
450 : : /* percent-escape whitespace. */
451 [ + - ]: 56 : if ((doc = malloc(strlen(p) * 3 + 1)) == NULL) {
452 : 0 : fetch_syserr();
453 : 0 : goto ouch;
454 : : }
455 : 56 : u->doc = doc;
456 [ + + ]: 460 : while (*p != '\0') {
457 [ - + ]: 404 : if (!isspace((unsigned char)*p)) {
458 : 404 : *doc++ = *p++;
459 : 404 : } else {
460 : 0 : *doc++ = '%';
461 : 0 : *doc++ = hexnums[((unsigned int)*p) >> 4];
462 : 0 : *doc++ = hexnums[((unsigned int)*p) & 0xf];
463 : 0 : p++;
464 : : }
465 : : }
466 : 56 : *doc = '\0';
467 [ + - ]: 1044 : } else if ((u->doc = strdup(p)) == NULL) {
468 : 0 : fetch_syserr();
469 : 0 : goto ouch;
470 : : }
471 : :
472 [ - + ]: 1044 : DEBUGF("scheme: \"%s\"\n"
473 : : "user: \"%s\"\n"
474 : : "password: \"%s\"\n"
475 : : "host: \"%s\"\n"
476 : : "port: \"%d\"\n"
477 : : "document: \"%s\"\n",
478 : : u->scheme, u->user, u->pwd,
479 : : u->host, u->port, u->doc);
480 : :
481 : 1044 : return (u);
482 : :
483 : : ouch:
484 : 0 : free(u);
485 : 0 : return (NULL);
486 : 1044 : }
487 : :
488 : : /*
489 : : * Free a URL
490 : : */
491 : : void
492 : 1044 : fetchFreeURL(struct url *u)
493 : : {
494 : 1044 : free(u->doc);
495 : 1044 : free(u);
496 : 1044 : }
|