Bug Summary

File:src/usr.bin/ftp/fetch.c
Warning:line 589, column 8
Access to field 'ai_family' results in a dereference of a null pointer (loaded from variable 'ares')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name fetch.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/ftp/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/ftp/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/ftp/fetch.c
1/* $OpenBSD: fetch.c,v 1.208 2021/11/10 07:32:55 kn Exp $ */
2/* $NetBSD: fetch.c,v 1.14 1997/08/18 10:20:20 lukem Exp $ */
3
4/*-
5 * Copyright (c) 1997 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Jason Thorpe and Luke Mewburn.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * FTP User Program -- Command line file retrieval
35 */
36
37#include <sys/types.h>
38#include <sys/socket.h>
39#include <sys/stat.h>
40
41#include <netinet/in.h>
42
43#include <arpa/ftp.h>
44#include <arpa/inet.h>
45
46#include <ctype.h>
47#include <err.h>
48#include <libgen.h>
49#include <netdb.h>
50#include <fcntl.h>
51#include <signal.h>
52#include <vis.h>
53#include <stdio.h>
54#include <stdarg.h>
55#include <errno(*__errno()).h>
56#include <stdlib.h>
57#include <string.h>
58#include <unistd.h>
59#include <resolv.h>
60#include <utime.h>
61
62#ifndef NOSSL
63#include <tls.h>
64#else /* !NOSSL */
65struct tls;
66#endif /* !NOSSL */
67
68#include "ftp_var.h"
69#include "cmds.h"
70
71static int file_get(const char *, const char *);
72static int url_get(const char *, const char *, const char *, int);
73static int save_chunked(FILE *, struct tls *, int , char *, size_t);
74static void aborthttp(int);
75static char hextochar(const char *);
76static char *urldecode(const char *);
77static char *recode_credentials(const char *_userinfo);
78static void ftp_close(FILE **, struct tls **, int *);
79static const char *sockerror(struct tls *);
80#ifdef SMALL
81#define ftp_printf(fp, ...) fprintf(fp, __VA_ARGS__)
82#else
83static int ftp_printf(FILE *, const char *, ...);
84#endif /* SMALL */
85#ifndef NOSSL
86static int proxy_connect(int, char *, char *);
87static int stdio_tls_write_wrapper(void *, const char *, int);
88static int stdio_tls_read_wrapper(void *, char *, int);
89#endif /* !NOSSL */
90
91#define FTP_URL"ftp://" "ftp://" /* ftp URL prefix */
92#define HTTP_URL"http://" "http://" /* http URL prefix */
93#define HTTPS_URL"https://" "https://" /* https URL prefix */
94#define FILE_URL"file:" "file:" /* file URL prefix */
95#define FTP_PROXY"ftp_proxy" "ftp_proxy" /* env var with ftp proxy location */
96#define HTTP_PROXY"http_proxy" "http_proxy" /* env var with http proxy location */
97
98#define EMPTYSTRING(x)((x) == ((void*)0) || (*(x) == '\0')) ((x) == NULL((void*)0) || (*(x) == '\0'))
99
100static const char at_encoding_warning[] =
101 "Extra `@' characters in usernames and passwords should be encoded as %%40";
102
103static jmp_buf httpabort;
104
105static int redirect_loop;
106static int retried;
107
108/*
109 * Determine whether the character needs encoding, per RFC2396.
110 */
111static int
112to_encode(const char *c0)
113{
114 /* 2.4.3. Excluded US-ASCII Characters */
115 const char *excluded_chars =
116 " " /* space */
117 "<>#\"" /* delims (modulo "%", see below) */
118 "{}|\\^[]`" /* unwise */
119 ;
120 const unsigned char *c = (const unsigned char *)c0;
121
122 /*
123 * No corresponding graphic US-ASCII.
124 * Control characters and octets not used in US-ASCII.
125 */
126 return (iscntrl(*c) || !isascii(*c) ||
127
128 /*
129 * '%' is also reserved, if is not followed by two
130 * hexadecimal digits.
131 */
132 strchr(excluded_chars, *c) != NULL((void*)0) ||
133 (*c == '%' && (!isxdigit(c[1]) || !isxdigit(c[2]))));
134}
135
136/*
137 * Encode given URL, per RFC2396.
138 * Allocate and return string to the caller.
139 */
140static char *
141url_encode(const char *path)
142{
143 size_t i, length, new_length;
144 char *epath, *epathp;
145
146 length = new_length = strlen(path);
147
148 /*
149 * First pass:
150 * Count characters to encode and determine length of the final URL.
151 */
152 for (i = 0; i < length; i++)
153 if (to_encode(path + i))
154 new_length += 2;
155
156 epath = epathp = malloc(new_length + 1); /* One more for '\0'. */
157 if (epath == NULL((void*)0))
158 err(1, "Can't allocate memory for URL encoding");
159
160 /*
161 * Second pass:
162 * Encode, and copy final URL.
163 */
164 for (i = 0; i < length; i++)
165 if (to_encode(path + i)) {
166 snprintf(epathp, 4, "%%" "%02x",
167 (unsigned char)path[i]);
168 epathp += 3;
169 } else
170 *(epathp++) = path[i];
171
172 *epathp = '\0';
173 return (epath);
174}
175
176/* ARGSUSED */
177static void
178tooslow(int signo)
179{
180 dprintf(STDERR_FILENO2, "%s: connect taking too long\n", __progname);
181 _exit(2);
182}
183
184/*
185 * Copy a local file (used by the OpenBSD installer).
186 * Returns -1 on failure, 0 on success
187 */
188static int
189file_get(const char *path, const char *outfile)
190{
191 struct stat st;
192 int fd, out = -1, rval = -1, save_errno;
193 volatile sig_t oldintr, oldinti;
194 const char *savefile;
195 char *buf = NULL((void*)0), *cp, *pathbuf = NULL((void*)0);
196 const size_t buflen = 128 * 1024;
197 off_t hashbytes;
198 ssize_t len, wlen;
199
200 direction = "received";
201
202 fd = open(path, O_RDONLY0x0000);
203 if (fd == -1) {
204 warn("Can't open file %s", path);
205 return -1;
206 }
207
208 if (fstat(fd, &st) == -1)
209 filesize = -1;
210 else
211 filesize = st.st_size;
212
213 if (outfile != NULL((void*)0))
214 savefile = outfile;
215 else {
216 if (path[strlen(path) - 1] == '/') /* Consider no file */
217 savefile = NULL((void*)0); /* after dir invalid. */
218 else {
219 pathbuf = strdup(path);
220 if (pathbuf == NULL((void*)0))
221 errx(1, "Can't allocate memory for filename");
222 savefile = basename(pathbuf);
223 }
224 }
225
226 if (EMPTYSTRING(savefile)((savefile) == ((void*)0) || (*(savefile) == '\0'))) {
227 warnx("No filename after directory (use -o): %s", path);
228 goto cleanup_copy;
229 }
230
231 /* Open the output file. */
232 if (!pipeout) {
233 out = open(savefile, O_CREAT0x0200 | O_WRONLY0x0001 | O_TRUNC0x0400, 0666);
234 if (out == -1) {
235 warn("Can't open %s", savefile);
236 goto cleanup_copy;
237 }
238 } else
239 out = fileno(stdout)(!__isthreaded ? (((&__sF[1]))->_file) : (fileno)((&
__sF[1])))
;
240
241 if ((buf = malloc(buflen)) == NULL((void*)0))
242 errx(1, "Can't allocate memory for transfer buffer");
243
244 /* Trap signals */
245 oldintr = NULL((void*)0);
246 oldinti = NULL((void*)0);
247 if (setjmp(httpabort)) {
248 if (oldintr)
249 (void)signal(SIGINT2, oldintr);
250 if (oldinti)
251 (void)signal(SIGINFO29, oldinti);
252 goto cleanup_copy;
253 }
254 oldintr = signal(SIGINT2, aborthttp);
255
256 bytes = 0;
257 hashbytes = mark;
258 progressmeter(-1, path);
259
260 /* Finally, suck down the file. */
261 oldinti = signal(SIGINFO29, psummary);
262 while ((len = read(fd, buf, buflen)) > 0) {
263 bytes += len;
264 for (cp = buf; len > 0; len -= wlen, cp += wlen) {
265 if ((wlen = write(out, cp, len)) == -1) {
266 warn("Writing %s", savefile);
267 signal(SIGINT2, oldintr);
268 signal(SIGINFO29, oldinti);
269 goto cleanup_copy;
270 }
271 }
272 if (hash && !progress) {
273 while (bytes >= hashbytes) {
274 (void)putc('#', ttyout)(!__isthreaded ? __sputc('#', ttyout) : (putc)('#', ttyout));
275 hashbytes += mark;
276 }
277 (void)fflush(ttyout);
278 }
279 }
280 save_errno = errno(*__errno());
281 signal(SIGINT2, oldintr);
282 signal(SIGINFO29, oldinti);
283 if (hash && !progress && bytes > 0) {
284 if (bytes < mark)
285 (void)putc('#', ttyout)(!__isthreaded ? __sputc('#', ttyout) : (putc)('#', ttyout));
286 (void)putc('\n', ttyout)(!__isthreaded ? __sputc('\n', ttyout) : (putc)('\n', ttyout)
)
;
287 (void)fflush(ttyout);
288 }
289 if (len == -1) {
290 warnc(save_errno, "Reading from file");
291 goto cleanup_copy;
292 }
293 progressmeter(1, NULL((void*)0));
294 if (verbose)
295 ptransfer(0);
296
297 rval = 0;
298
299cleanup_copy:
300 free(buf);
301 free(pathbuf);
302 if (out >= 0 && out != fileno(stdout)(!__isthreaded ? (((&__sF[1]))->_file) : (fileno)((&
__sF[1])))
)
303 close(out);
304 close(fd);
305
306 return rval;
307}
308
309/*
310 * Retrieve URL, via the proxy in $proxyvar if necessary.
311 * Returns -1 on failure, 0 on success
312 */
313static int
314url_get(const char *origline, const char *proxyenv, const char *outfile, int lastfile)
315{
316 char pbuf[NI_MAXSERV32], hbuf[NI_MAXHOST256], *cp, *portnum, *path, ststr[4];
317 char *hosttail, *cause = "unknown", *newline, *host, *port, *buf = NULL((void*)0);
318 char *epath, *redirurl, *loctail, *h, *p, gerror[200];
319 int error, isftpurl = 0, isredirect = 0, rval = -1;
320 int isunavail = 0, retryafter = -1;
321 struct addrinfo hints, *res0, *res;
322 const char *savefile;
323 char *pathbuf = NULL((void*)0);
324 char *proxyurl = NULL((void*)0);
325 char *credentials = NULL((void*)0), *proxy_credentials = NULL((void*)0);
326 int fd = -1, out = -1;
327 volatile sig_t oldintr, oldinti;
328 FILE *fin = NULL((void*)0);
329 off_t hashbytes;
330 const char *errstr;
331 ssize_t len, wlen;
332 size_t bufsize;
333 char *proxyhost = NULL((void*)0);
334#ifndef NOSSL
335 char *sslpath = NULL((void*)0), *sslhost = NULL((void*)0);
336 int ishttpsurl = 0;
337#endif /* !NOSSL */
338#ifndef SMALL
339 char *full_host = NULL((void*)0);
340 const char *scheme;
341 char *locbase;
342 struct addrinfo *ares = NULL((void*)0);
1
'ares' initialized to a null pointer value
343 char tmbuf[32];
344 time_t mtime = 0;
345 struct stat stbuf;
346 struct tm lmt = { 0 };
347 struct timespec ts[2];
348#endif /* !SMALL */
349 struct tls *tls = NULL((void*)0);
350 int status;
351 int save_errno;
352 const size_t buflen = 128 * 1024;
353 int chunked = 0;
354
355 direction = "received";
356
357 newline = strdup(origline);
358 if (newline == NULL((void*)0))
2
Assuming 'newline' is not equal to NULL
3
Taking false branch
359 errx(1, "Can't allocate memory to parse URL");
360 if (strncasecmp(newline, HTTP_URL"http://", sizeof(HTTP_URL"http://") - 1) == 0) {
4
Assuming the condition is false
5
Taking false branch
361 host = newline + sizeof(HTTP_URL"http://") - 1;
362#ifndef SMALL
363 scheme = HTTP_URL"http://";
364#endif /* !SMALL */
365 } else if (strncasecmp(newline, FTP_URL"ftp://", sizeof(FTP_URL"ftp://") - 1) == 0) {
6
Assuming the condition is false
7
Taking false branch
366 host = newline + sizeof(FTP_URL"ftp://") - 1;
367 isftpurl = 1;
368#ifndef SMALL
369 scheme = FTP_URL"ftp://";
370#endif /* !SMALL */
371 } else if (strncasecmp(newline, HTTPS_URL"https://", sizeof(HTTPS_URL"https://") - 1) == 0) {
8
Taking true branch
372#ifndef NOSSL
373 host = newline + sizeof(HTTPS_URL"https://") - 1;
374 ishttpsurl = 1;
375#else
376 errx(1, "%s: No HTTPS support", newline);
377#endif /* !NOSSL */
378#ifndef SMALL
379 scheme = HTTPS_URL"https://";
380#endif /* !SMALL */
381 } else
382 errx(1, "%s: URL not permitted", newline);
383
384 path = strchr(host, '/'); /* Find path */
385
386 /*
387 * Look for auth header in host.
388 * Basic auth from RFC 2617, valid characters for path are in
389 * RFC 3986 section 3.3.
390 */
391 if (!isftpurl
8.1
'isftpurl' is 0
) {
9
Taking true branch
392 p = strchr(host, '@');
393 if (p != NULL((void*)0) && (path == NULL((void*)0) || p < path)) {
10
Assuming 'p' is equal to NULL
394 *p++ = '\0';
395 credentials = recode_credentials(host);
396
397 /* Overwrite userinfo */
398 memmove(host, p, strlen(p) + 1);
399 path = strchr(host, '/');
400 }
401 }
402
403 if (EMPTYSTRING(path)((path) == ((void*)0) || (*(path) == '\0'))) {
11
Assuming 'path' is not equal to null
12
Assuming the condition is false
13
Taking false branch
404 if (outfile) { /* No slash, but */
405 path = strchr(host,'\0'); /* we have outfile. */
406 goto noslash;
407 }
408 if (isftpurl)
409 goto noftpautologin;
410 warnx("No `/' after host (use -o): %s", origline);
411 goto cleanup_url_get;
412 }
413 *path++ = '\0';
414 if (EMPTYSTRING(path)((path) == ((void*)0) || (*(path) == '\0')) && !outfile) {
14
Assuming the condition is false
415 if (isftpurl)
416 goto noftpautologin;
417 warnx("No filename after host (use -o): %s", origline);
418 goto cleanup_url_get;
419 }
420
421noslash:
422 if (outfile)
15
Assuming 'outfile' is null
16
Taking false branch
423 savefile = outfile;
424 else {
425 if (path[strlen(path) - 1] == '/') /* Consider no file */
17
Assuming the condition is false
18
Taking false branch
426 savefile = NULL((void*)0); /* after dir invalid. */
427 else {
428 pathbuf = strdup(path);
429 if (pathbuf == NULL((void*)0))
19
Assuming 'pathbuf' is not equal to NULL
20
Taking false branch
430 errx(1, "Can't allocate memory for filename");
431 savefile = basename(pathbuf);
432 }
433 }
434
435 if (EMPTYSTRING(savefile)((savefile) == ((void*)0) || (*(savefile) == '\0'))) {
21
Assuming 'savefile' is not equal to null
22
Assuming the condition is false
23
Taking false branch
436 if (isftpurl)
437 goto noftpautologin;
438 warnx("No filename after directory (use -o): %s", origline);
439 goto cleanup_url_get;
440 }
441
442#ifndef SMALL
443 if (resume && pipeout) {
24
Assuming 'resume' is 0
444 warnx("can't append to stdout");
445 goto cleanup_url_get;
446 }
447#endif /* !SMALL */
448
449 if (proxyenv != NULL((void*)0)) { /* use proxy */
25
Assuming 'proxyenv' is equal to NULL
26
Taking false branch
450#ifndef NOSSL
451 if (ishttpsurl) {
452 sslpath = strdup(path);
453 sslhost = strdup(host);
454 if (! sslpath || ! sslhost)
455 errx(1, "Can't allocate memory for https path/host.");
456 }
457#endif /* !NOSSL */
458 proxyhost = strdup(host);
459 if (proxyhost == NULL((void*)0))
460 errx(1, "Can't allocate memory for proxy host.");
461 proxyurl = strdup(proxyenv);
462 if (proxyurl == NULL((void*)0))
463 errx(1, "Can't allocate memory for proxy URL.");
464 if (strncasecmp(proxyurl, HTTP_URL"http://", sizeof(HTTP_URL"http://") - 1) == 0)
465 host = proxyurl + sizeof(HTTP_URL"http://") - 1;
466 else if (strncasecmp(proxyurl, FTP_URL"ftp://", sizeof(FTP_URL"ftp://") - 1) == 0)
467 host = proxyurl + sizeof(FTP_URL"ftp://") - 1;
468 else {
469 warnx("Malformed proxy URL: %s", proxyenv);
470 goto cleanup_url_get;
471 }
472 if (EMPTYSTRING(host)((host) == ((void*)0) || (*(host) == '\0'))) {
473 warnx("Malformed proxy URL: %s", proxyenv);
474 goto cleanup_url_get;
475 }
476 if (*--path == '\0')
477 *path = '/'; /* add / back to real path */
478 path = strchr(host, '/'); /* remove trailing / on host */
479 if (!EMPTYSTRING(path)((path) == ((void*)0) || (*(path) == '\0')))
480 *path++ = '\0'; /* i guess this ++ is useless */
481
482 path = strchr(host, '@'); /* look for credentials in proxy */
483 if (!EMPTYSTRING(path)((path) == ((void*)0) || (*(path) == '\0'))) {
484 *path = '\0';
485 if (strchr(host, ':') == NULL((void*)0)) {
486 warnx("Malformed proxy URL: %s", proxyenv);
487 goto cleanup_url_get;
488 }
489 proxy_credentials = recode_credentials(host);
490 *path = '@'; /* restore @ in proxyurl */
491
492 /*
493 * This removes the password from proxyurl,
494 * filling with stars
495 */
496 for (host = 1 + strchr(proxyurl + 5, ':'); *host != '@';
497 host++)
498 *host = '*';
499
500 host = path + 1;
501 }
502
503 path = newline;
504 }
505
506 if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL((void*)0) &&
27
Assuming the condition is false
507 (hosttail[1] == '\0' || hosttail[1] == ':')) {
508 host++;
509 *hosttail++ = '\0';
510#ifndef SMALL
511 if (asprintf(&full_host, "[%s]", host) == -1)
512 errx(1, "Cannot allocate memory for hostname");
513#endif /* !SMALL */
514 } else
515 hosttail = host;
516
517 portnum = strrchr(hosttail, ':'); /* find portnum */
518 if (portnum != NULL((void*)0))
28
Assuming 'portnum' is equal to NULL
29
Taking false branch
519 *portnum++ = '\0';
520#ifndef NOSSL
521 port = portnum
29.1
'portnum' is null
? portnum : (ishttpsurl
30.1
'ishttpsurl' is 1
? httpsport : httpport);
30
'?' condition is false
31
'?' condition is true
522#else /* !NOSSL */
523 port = portnum ? portnum : httpport;
524#endif /* !NOSSL */
525
526#ifndef SMALL
527 if (full_host
31.1
'full_host' is equal to NULL
== NULL((void*)0))
32
Taking true branch
528 if ((full_host = strdup(host)) == NULL((void*)0))
33
Assuming the condition is false
34
Taking false branch
529 errx(1, "Cannot allocate memory for hostname");
530 if (debug)
35
Assuming 'debug' is 0
36
Taking false branch
531 fprintf(ttyout, "host %s, port %s, path %s, "
532 "save as %s, auth %s.\n", host, port, path,
533 savefile, credentials ? credentials : "none");
534#endif /* !SMALL */
535
536 memset(&hints, 0, sizeof(hints));
537 hints.ai_family = family;
538 hints.ai_socktype = SOCK_STREAM1;
539 error = getaddrinfo(host, port, &hints, &res0);
540 /*
541 * If the services file is corrupt/missing, fall back
542 * on our hard-coded defines.
543 */
544 if (error == EAI_SERVICE-8 && port == httpport) {
37
Assuming the condition is false
545 snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT80);
546 error = getaddrinfo(host, pbuf, &hints, &res0);
547#ifndef NOSSL
548 } else if (error == EAI_SERVICE-8 && port == httpsport) {
549 snprintf(pbuf, sizeof(pbuf), "%d", HTTPS_PORT443);
550 error = getaddrinfo(host, pbuf, &hints, &res0);
551#endif /* !NOSSL */
552 }
553 if (error) {
38
Assuming 'error' is 0
39
Taking false branch
554 warnx("%s: %s", host, gai_strerror(error));
555 goto cleanup_url_get;
556 }
557
558#ifndef SMALL
559 if (srcaddr) {
40
Assuming 'srcaddr' is null
41
Taking false branch
560 hints.ai_flags |= AI_NUMERICHOST4;
561 error = getaddrinfo(srcaddr, NULL((void*)0), &hints, &ares);
562 if (error) {
563 warnx("%s: %s", srcaddr, gai_strerror(error));
564 goto cleanup_url_get;
565 }
566 }
567#endif /* !SMALL */
568
569 /* ensure consistent order of the output */
570 if (verbose)
42
Assuming 'verbose' is 0
43
Taking false branch
571 setvbuf(ttyout, NULL((void*)0), _IOLBF1, 0);
572
573 fd = -1;
574 for (res = res0; res; res = res->ai_next) {
44
Loop condition is true. Entering loop body
58
Loop condition is true. Entering loop body
575 if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
45
Assuming the condition is false
46
Taking false branch
59
Assuming the condition is false
60
Taking false branch
576 sizeof(hbuf), NULL((void*)0), 0, NI_NUMERICHOST1) != 0)
577 strlcpy(hbuf, "(unknown)", sizeof(hbuf));
578 if (verbose
46.1
'verbose' is 0
)
47
Taking false branch
61
Assuming 'verbose' is 0
62
Taking false branch
579 fprintf(ttyout, "Trying %s...\n", hbuf);
580
581 fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
582 if (fd == -1) {
48
Assuming the condition is false
49
Taking false branch
63
Assuming the condition is false
64
Taking false branch
583 cause = "socket";
584 continue;
585 }
586
587#ifndef SMALL
588 if (srcaddr
49.1
'srcaddr' is null
) {
50
Taking false branch
65
Assuming 'srcaddr' is non-null
66
Taking true branch
589 if (ares->ai_family != res->ai_family) {
67
Access to field 'ai_family' results in a dereference of a null pointer (loaded from variable 'ares')
590 close(fd);
591 fd = -1;
592 errno(*__errno()) = EINVAL22;
593 cause = "bind";
594 continue;
595 }
596 if (bind(fd, ares->ai_addr, ares->ai_addrlen) == -1) {
597 save_errno = errno(*__errno());
598 close(fd);
599 errno(*__errno()) = save_errno;
600 fd = -1;
601 cause = "bind";
602 continue;
603 }
604 }
605#endif /* !SMALL */
606
607 if (connect_timeout) {
51
Assuming 'connect_timeout' is not equal to 0
52
Taking true branch
608 (void)signal(SIGALRM14, tooslow);
609 alarmtimer(connect_timeout);
610 }
611
612 for (error = connect(fd, res->ai_addr, res->ai_addrlen);
55
Loop condition is false. Execution continues on line 615
613 error != 0 && errno(*__errno()) == EINTR4; error = connect_wait(fd))
53
Assuming 'error' is not equal to 0
54
Assuming the condition is false
614 continue;
615 if (error
55.1
'error' is not equal to 0
!= 0) {
56
Taking true branch
616 save_errno = errno(*__errno());
617 close(fd);
618 errno(*__errno()) = save_errno;
619 fd = -1;
620 cause = "connect";
621 continue;
57
Execution continues on line 574
622 }
623
624 /* get port in numeric */
625 if (getnameinfo(res->ai_addr, res->ai_addrlen, NULL((void*)0), 0,
626 pbuf, sizeof(pbuf), NI_NUMERICSERV2) == 0)
627 port = pbuf;
628 else
629 port = NULL((void*)0);
630
631#ifndef NOSSL
632 if (proxyenv && sslhost)
633 proxy_connect(fd, sslhost, proxy_credentials);
634#endif /* !NOSSL */
635 break;
636 }
637 freeaddrinfo(res0);
638#ifndef SMALL
639 if (srcaddr)
640 freeaddrinfo(ares);
641#endif /* !SMALL */
642 if (fd < 0) {
643 warn("%s", cause);
644 goto cleanup_url_get;
645 }
646
647#ifndef NOSSL
648 if (ishttpsurl) {
649 ssize_t ret;
650 if (proxyenv && sslpath) {
651 ishttpsurl = 0;
652 proxyurl = NULL((void*)0);
653 path = sslpath;
654 }
655 if (sslhost == NULL((void*)0)) {
656 sslhost = strdup(host);
657 if (sslhost == NULL((void*)0))
658 errx(1, "Can't allocate memory for https host.");
659 }
660 if ((tls = tls_client()) == NULL((void*)0)) {
661 fprintf(ttyout, "failed to create SSL client\n");
662 goto cleanup_url_get;
663 }
664 if (tls_configure(tls, tls_config) != 0) {
665 fprintf(ttyout, "TLS configuration failure: %s\n",
666 tls_error(tls));
667 goto cleanup_url_get;
668 }
669 if (tls_connect_socket(tls, fd, sslhost) != 0) {
670 fprintf(ttyout, "TLS connect failure: %s\n", tls_error(tls));
671 goto cleanup_url_get;
672 }
673 do {
674 ret = tls_handshake(tls);
675 } while (ret == TLS_WANT_POLLIN-2 || ret == TLS_WANT_POLLOUT-3);
676 if (ret != 0) {
677 fprintf(ttyout, "TLS handshake failure: %s\n", tls_error(tls));
678 goto cleanup_url_get;
679 }
680 fin = funopen(tls, stdio_tls_read_wrapper,
681 stdio_tls_write_wrapper, NULL((void*)0), NULL((void*)0));
682 } else {
683 fin = fdopen(fd, "r+");
684 fd = -1;
685 }
686#else /* !NOSSL */
687 fin = fdopen(fd, "r+");
688 fd = -1;
689#endif /* !NOSSL */
690
691#ifdef SMALL
692 if (lastfile) {
693 if (pipeout) {
694 if (pledge("stdio rpath inet dns tty", NULL((void*)0)) == -1)
695 err(1, "pledge");
696 } else {
697 if (pledge("stdio rpath wpath cpath inet dns tty", NULL((void*)0)) == -1)
698 err(1, "pledge");
699 }
700 }
701#endif
702
703 if (connect_timeout) {
704 signal(SIGALRM14, SIG_DFL(void (*)(int))0);
705 alarmtimer(0);
706 }
707
708 /*
709 * Construct and send the request. Proxy requests don't want leading /.
710 */
711#ifndef NOSSL
712 cookie_get(host, path, ishttpsurl, &buf);
713#endif /* !NOSSL */
714
715 epath = url_encode(path);
716 if (proxyurl) {
717 if (verbose) {
718 fprintf(ttyout, "Requesting %s (via %s)\n",
719 origline, proxyurl);
720 }
721 /*
722 * Host: directive must use the destination host address for
723 * the original URI (path).
724 */
725 ftp_printf(fin, "GET %s HTTP/1.1\r\n"
726 "Connection: close\r\n"
727 "Host: %s\r\n%s%s\r\n",
728 epath, proxyhost, buf ? buf : "", httpuseragent);
729 if (credentials)
730 ftp_printf(fin, "Authorization: Basic %s\r\n",
731 credentials);
732 if (proxy_credentials)
733 ftp_printf(fin, "Proxy-Authorization: Basic %s\r\n",
734 proxy_credentials);
735 ftp_printf(fin, "\r\n");
736 } else {
737 if (verbose)
738 fprintf(ttyout, "Requesting %s\n", origline);
739#ifndef SMALL
740 if (resume || timestamp) {
741 if (stat(savefile, &stbuf) == 0) {
742 if (resume)
743 restart_point = stbuf.st_size;
744 if (timestamp)
745 mtime = stbuf.st_mtimest_mtim.tv_sec;
746 } else {
747 restart_point = 0;
748 mtime = 0;
749 }
750 }
751#endif /* SMALL */
752 ftp_printf(fin,
753 "GET /%s HTTP/1.1\r\n"
754 "Connection: close\r\n"
755 "Host: ", epath);
756 if (proxyhost) {
757 ftp_printf(fin, "%s", proxyhost);
758 port = NULL((void*)0);
759 } else if (strchr(host, ':')) {
760 /*
761 * strip off scoped address portion, since it's
762 * local to node
763 */
764 h = strdup(host);
765 if (h == NULL((void*)0))
766 errx(1, "Can't allocate memory.");
767 if ((p = strchr(h, '%')) != NULL((void*)0))
768 *p = '\0';
769 ftp_printf(fin, "[%s]", h);
770 free(h);
771 } else
772 ftp_printf(fin, "%s", host);
773
774 /*
775 * Send port number only if it's specified and does not equal
776 * 80. Some broken HTTP servers get confused if you explicitly
777 * send them the port number.
778 */
779#ifndef NOSSL
780 if (port && strcmp(port, (ishttpsurl ? "443" : "80")) != 0)
781 ftp_printf(fin, ":%s", port);
782 if (restart_point)
783 ftp_printf(fin, "\r\nRange: bytes=%lld-",
784 (long long)restart_point);
785#else /* !NOSSL */
786 if (port && strcmp(port, "80") != 0)
787 ftp_printf(fin, ":%s", port);
788#endif /* !NOSSL */
789
790#ifndef SMALL
791 if (mtime && (http_time(mtime, tmbuf, sizeof(tmbuf)) != 0))
792 ftp_printf(fin, "\r\nIf-Modified-Since: %s", tmbuf);
793#endif /* SMALL */
794
795 ftp_printf(fin, "\r\n%s%s\r\n",
796 buf ? buf : "", httpuseragent);
797 if (credentials)
798 ftp_printf(fin, "Authorization: Basic %s\r\n",
799 credentials);
800 ftp_printf(fin, "\r\n");
801 }
802 free(epath);
803
804#ifndef NOSSL
805 free(buf);
806#endif /* !NOSSL */
807 buf = NULL((void*)0);
808 bufsize = 0;
809
810 if (fflush(fin) == EOF(-1)) {
811 warnx("Writing HTTP request: %s", sockerror(tls));
812 goto cleanup_url_get;
813 }
814 if ((len = getline(&buf, &bufsize, fin)) == -1) {
815 warnx("Receiving HTTP reply: %s", sockerror(tls));
816 goto cleanup_url_get;
817 }
818
819 while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
820 buf[--len] = '\0';
821#ifndef SMALL
822 if (debug)
823 fprintf(ttyout, "received '%s'\n", buf);
824#endif /* !SMALL */
825
826 cp = strchr(buf, ' ');
827 if (cp == NULL((void*)0))
828 goto improper;
829 else
830 cp++;
831
832 strlcpy(ststr, cp, sizeof(ststr));
833 status = strtonum(ststr, 200, 503, &errstr);
834 if (errstr) {
835 strnvis(gerror, cp, sizeof gerror, VIS_SAFE0x20);
836 warnx("Error retrieving %s: %s", origline, gerror);
837 goto cleanup_url_get;
838 }
839
840 switch (status) {
841 case 200: /* OK */
842#ifndef SMALL
843 /*
844 * When we request a partial file, and we receive an HTTP 200
845 * it is a good indication that the server doesn't support
846 * range requests, and is about to send us the entire file.
847 * If the restart_point == 0, then we are not actually
848 * requesting a partial file, and an HTTP 200 is appropriate.
849 */
850 if (resume && restart_point != 0) {
851 warnx("Server does not support resume.");
852 restart_point = resume = 0;
853 }
854 /* FALLTHROUGH */
855 case 206: /* Partial Content */
856#endif /* !SMALL */
857 break;
858 case 301: /* Moved Permanently */
859 case 302: /* Found */
860 case 303: /* See Other */
861 case 307: /* Temporary Redirect */
862 case 308: /* Permanent Redirect (RFC 7538) */
863 isredirect++;
864 if (redirect_loop++ > 10) {
865 warnx("Too many redirections requested");
866 goto cleanup_url_get;
867 }
868 break;
869#ifndef SMALL
870 case 304: /* Not Modified */
871 warnx("File is not modified on the server");
872 goto cleanup_url_get;
873 case 416: /* Requested Range Not Satisfiable */
874 warnx("File is already fully retrieved.");
875 goto cleanup_url_get;
876#endif /* !SMALL */
877 case 503:
878 isunavail = 1;
879 break;
880 default:
881 strnvis(gerror, cp, sizeof gerror, VIS_SAFE0x20);
882 warnx("Error retrieving %s: %s", origline, gerror);
883 goto cleanup_url_get;
884 }
885
886 /*
887 * Read the rest of the header.
888 */
889 filesize = -1;
890
891 for (;;) {
892 if ((len = getline(&buf, &bufsize, fin)) == -1) {
893 warnx("Receiving HTTP reply: %s", sockerror(tls));
894 goto cleanup_url_get;
895 }
896
897 while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
898 buf[--len] = '\0';
899 if (len == 0)
900 break;
901#ifndef SMALL
902 if (debug)
903 fprintf(ttyout, "received '%s'\n", buf);
904#endif /* !SMALL */
905
906 /* Look for some headers */
907 cp = buf;
908#define CONTENTLEN"Content-Length: " "Content-Length: "
909 if (strncasecmp(cp, CONTENTLEN"Content-Length: ", sizeof(CONTENTLEN"Content-Length: ") - 1) == 0) {
910 size_t s;
911 cp += sizeof(CONTENTLEN"Content-Length: ") - 1;
912 if ((s = strcspn(cp, " \t")))
913 *(cp+s) = 0;
914 filesize = strtonum(cp, 0, LLONG_MAX9223372036854775807LL, &errstr);
915 if (errstr != NULL((void*)0))
916 goto improper;
917#ifndef SMALL
918 if (restart_point)
919 filesize += restart_point;
920#endif /* !SMALL */
921#define LOCATION"Location: " "Location: "
922 } else if (isredirect &&
923 strncasecmp(cp, LOCATION"Location: ", sizeof(LOCATION"Location: ") - 1) == 0) {
924 cp += sizeof(LOCATION"Location: ") - 1;
925 /*
926 * If there is a colon before the first slash, this URI
927 * is not relative. RFC 3986 4.2
928 */
929 if (cp[strcspn(cp, ":/")] != ':') {
930#ifdef SMALL
931 errx(1, "Relative redirect not supported");
932#else /* SMALL */
933 /* XXX doesn't handle protocol-relative URIs */
934 if (*cp == '/') {
935 locbase = NULL((void*)0);
936 cp++;
937 } else {
938 locbase = strdup(path);
939 if (locbase == NULL((void*)0))
940 errx(1, "Can't allocate memory"
941 " for location base");
942 loctail = strchr(locbase, '#');
943 if (loctail != NULL((void*)0))
944 *loctail = '\0';
945 loctail = strchr(locbase, '?');
946 if (loctail != NULL((void*)0))
947 *loctail = '\0';
948 loctail = strrchr(locbase, '/');
949 if (loctail == NULL((void*)0)) {
950 free(locbase);
951 locbase = NULL((void*)0);
952 } else
953 loctail[1] = '\0';
954 }
955 /* Contruct URL from relative redirect */
956 if (asprintf(&redirurl, "%s%s%s%s/%s%s",
957 scheme, full_host,
958 portnum ? ":" : "",
959 portnum ? portnum : "",
960 locbase ? locbase : "",
961 cp) == -1)
962 errx(1, "Cannot build "
963 "redirect URL");
964 free(locbase);
965#endif /* SMALL */
966 } else if ((redirurl = strdup(cp)) == NULL((void*)0))
967 errx(1, "Cannot allocate memory for URL");
968 loctail = strchr(redirurl, '#');
969 if (loctail != NULL((void*)0))
970 *loctail = '\0';
971 if (verbose)
972 fprintf(ttyout, "Redirected to %s\n", redirurl);
973 ftp_close(&fin, &tls, &fd);
974 rval = url_get(redirurl, proxyenv, savefile, lastfile);
975 free(redirurl);
976 goto cleanup_url_get;
977#define RETRYAFTER"Retry-After: " "Retry-After: "
978 } else if (isunavail &&
979 strncasecmp(cp, RETRYAFTER"Retry-After: ", sizeof(RETRYAFTER"Retry-After: ") - 1) == 0) {
980 size_t s;
981 cp += sizeof(RETRYAFTER"Retry-After: ") - 1;
982 if ((s = strcspn(cp, " \t")))
983 cp[s] = '\0';
984 retryafter = strtonum(cp, 0, 0, &errstr);
985 if (errstr != NULL((void*)0))
986 retryafter = -1;
987#define TRANSFER_ENCODING"Transfer-Encoding: " "Transfer-Encoding: "
988 } else if (strncasecmp(cp, TRANSFER_ENCODING"Transfer-Encoding: ",
989 sizeof(TRANSFER_ENCODING"Transfer-Encoding: ") - 1) == 0) {
990 cp += sizeof(TRANSFER_ENCODING"Transfer-Encoding: ") - 1;
991 cp[strcspn(cp, " \t")] = '\0';
992 if (strcasecmp(cp, "chunked") == 0)
993 chunked = 1;
994#ifndef SMALL
995#define LAST_MODIFIED"Last-Modified: " "Last-Modified: "
996 } else if (strncasecmp(cp, LAST_MODIFIED"Last-Modified: ",
997 sizeof(LAST_MODIFIED"Last-Modified: ") - 1) == 0) {
998 cp += sizeof(LAST_MODIFIED"Last-Modified: ") - 1;
999 cp[strcspn(cp, "\t")] = '\0';
1000 if (strptime(cp, "%a, %d %h %Y %T %Z", &lmt) == NULL((void*)0))
1001 server_timestamps = 0;
1002#endif /* !SMALL */
1003 }
1004 }
1005 free(buf);
1006 buf = NULL((void*)0);
1007
1008 /* Content-Length should be ignored for Transfer-Encoding: chunked */
1009 if (chunked)
1010 filesize = -1;
1011
1012 if (isunavail) {
1013 if (retried || retryafter != 0)
1014 warnx("Error retrieving %s: 503 Service Unavailable",
1015 origline);
1016 else {
1017 if (verbose)
1018 fprintf(ttyout, "Retrying %s\n", origline);
1019 retried = 1;
1020 ftp_close(&fin, &tls, &fd);
1021 rval = url_get(origline, proxyenv, savefile, lastfile);
1022 }
1023 goto cleanup_url_get;
1024 }
1025
1026 /* Open the output file. */
1027 if (!pipeout) {
1028#ifndef SMALL
1029 if (resume)
1030 out = open(savefile, O_CREAT0x0200 | O_WRONLY0x0001 | O_APPEND0x0008,
1031 0666);
1032 else
1033#endif /* !SMALL */
1034 out = open(savefile, O_CREAT0x0200 | O_WRONLY0x0001 | O_TRUNC0x0400,
1035 0666);
1036 if (out == -1) {
1037 warn("Can't open %s", savefile);
1038 goto cleanup_url_get;
1039 }
1040 } else {
1041 out = fileno(stdout)(!__isthreaded ? (((&__sF[1]))->_file) : (fileno)((&
__sF[1])))
;
1042#ifdef SMALL
1043 if (lastfile) {
1044 if (pledge("stdio tty", NULL((void*)0)) == -1)
1045 err(1, "pledge");
1046 }
1047#endif
1048 }
1049
1050 if ((buf = malloc(buflen)) == NULL((void*)0))
1051 errx(1, "Can't allocate memory for transfer buffer");
1052
1053 /* Trap signals */
1054 oldintr = NULL((void*)0);
1055 oldinti = NULL((void*)0);
1056 if (setjmp(httpabort)) {
1057 if (oldintr)
1058 (void)signal(SIGINT2, oldintr);
1059 if (oldinti)
1060 (void)signal(SIGINFO29, oldinti);
1061 goto cleanup_url_get;
1062 }
1063 oldintr = signal(SIGINT2, aborthttp);
1064
1065 bytes = 0;
1066 hashbytes = mark;
1067 progressmeter(-1, path);
1068
1069 /* Finally, suck down the file. */
1070 oldinti = signal(SIGINFO29, psummary);
1071 if (chunked) {
1072 error = save_chunked(fin, tls, out, buf, buflen);
1073 signal(SIGINT2, oldintr);
1074 signal(SIGINFO29, oldinti);
1075 if (error == -1)
1076 goto cleanup_url_get;
1077 } else {
1078 while ((len = fread(buf, 1, buflen, fin)) > 0) {
1079 bytes += len;
1080 for (cp = buf; len > 0; len -= wlen, cp += wlen) {
1081 if ((wlen = write(out, cp, len)) == -1) {
1082 warn("Writing %s", savefile);
1083 signal(SIGINT2, oldintr);
1084 signal(SIGINFO29, oldinti);
1085 goto cleanup_url_get;
1086 }
1087 }
1088 if (hash && !progress) {
1089 while (bytes >= hashbytes) {
1090 (void)putc('#', ttyout)(!__isthreaded ? __sputc('#', ttyout) : (putc)('#', ttyout));
1091 hashbytes += mark;
1092 }
1093 (void)fflush(ttyout);
1094 }
1095 }
1096 save_errno = errno(*__errno());
1097 signal(SIGINT2, oldintr);
1098 signal(SIGINFO29, oldinti);
1099 if (hash && !progress && bytes > 0) {
1100 if (bytes < mark)
1101 (void)putc('#', ttyout)(!__isthreaded ? __sputc('#', ttyout) : (putc)('#', ttyout));
1102 (void)putc('\n', ttyout)(!__isthreaded ? __sputc('\n', ttyout) : (putc)('\n', ttyout)
)
;
1103 (void)fflush(ttyout);
1104 }
1105 if (len == 0 && ferror(fin)(!__isthreaded ? (((fin)->_flags & 0x0040) != 0) : (ferror
)(fin))
) {
1106 errno(*__errno()) = save_errno;
1107 warnx("Reading from socket: %s", sockerror(tls));
1108 goto cleanup_url_get;
1109 }
1110 }
1111 progressmeter(1, NULL((void*)0));
1112 if (
1113#ifndef SMALL
1114 !resume &&
1115#endif /* !SMALL */
1116 filesize != -1 && len == 0 && bytes != filesize) {
1117 if (verbose)
1118 fputs("Read short file.\n", ttyout);
1119 goto cleanup_url_get;
1120 }
1121
1122 if (verbose)
1123 ptransfer(0);
1124
1125 rval = 0;
1126 goto cleanup_url_get;
1127
1128noftpautologin:
1129 warnx(
1130 "Auto-login using ftp URLs isn't supported when using $ftp_proxy");
1131 goto cleanup_url_get;
1132
1133improper:
1134 warnx("Improper response from %s", host);
1135
1136cleanup_url_get:
1137#ifndef SMALL
1138 free(full_host);
1139#endif /* !SMALL */
1140#ifndef NOSSL
1141 free(sslhost);
1142#endif /* !NOSSL */
1143 ftp_close(&fin, &tls, &fd);
1144 if (out >= 0 && out != fileno(stdout)(!__isthreaded ? (((&__sF[1]))->_file) : (fileno)((&
__sF[1])))
) {
1145#ifndef SMALL
1146 if (server_timestamps && lmt.tm_zone != 0 &&
1147 fstat(out, &stbuf) == 0 && S_ISREG(stbuf.st_mode)((stbuf.st_mode & 0170000) == 0100000) != 0) {
1148 ts[0].tv_nsec = UTIME_NOW-2L;
1149 ts[1].tv_nsec = 0;
1150 setenv("TZ", lmt.tm_zone, 1);
1151 if (((ts[1].tv_sec = mktime(&lmt)) != -1) &&
1152 (futimens(out, ts) == -1))
1153 warnx("Unable to set file modification time");
1154 }
1155#endif /* !SMALL */
1156 close(out);
1157 }
1158 free(buf);
1159 free(pathbuf);
1160 free(proxyhost);
1161 free(proxyurl);
1162 free(newline);
1163 free(credentials);
1164 free(proxy_credentials);
1165 return (rval);
1166}
1167
1168static int
1169save_chunked(FILE *fin, struct tls *tls, int out, char *buf, size_t buflen)
1170{
1171
1172 char *header = NULL((void*)0), *end, *cp;
1173 unsigned long chunksize;
1174 size_t hsize = 0, rlen, wlen;
1175 ssize_t written;
1176 char cr, lf;
1177
1178 for (;;) {
1179 if (getline(&header, &hsize, fin) == -1)
1180 break;
1181 /* strip CRLF and any optional chunk extension */
1182 header[strcspn(header, ";\r\n")] = '\0';
1183 errno(*__errno()) = 0;
1184 chunksize = strtoul(header, &end, 16);
1185 if (errno(*__errno()) || header[0] == '\0' || *end != '\0' ||
1186 chunksize > INT_MAX2147483647) {
1187 warnx("Invalid chunk size '%s'", header);
1188 free(header);
1189 return -1;
1190 }
1191
1192 if (chunksize == 0) {
1193 /* We're done. Ignore optional trailer. */
1194 free(header);
1195 return 0;
1196 }
1197
1198 for (written = 0; chunksize != 0; chunksize -= rlen) {
1199 rlen = (chunksize < buflen) ? chunksize : buflen;
1200 rlen = fread(buf, 1, rlen, fin);
1201 if (rlen == 0)
1202 break;
1203 bytes += rlen;
1204 for (cp = buf, wlen = rlen; wlen > 0;
1205 wlen -= written, cp += written) {
1206 if ((written = write(out, cp, wlen)) == -1) {
1207 warn("Writing output file");
1208 free(header);
1209 return -1;
1210 }
1211 }
1212 }
1213
1214 if (rlen == 0 ||
1215 fread(&cr, 1, 1, fin) != 1 ||
1216 fread(&lf, 1, 1, fin) != 1)
1217 break;
1218
1219 if (cr != '\r' || lf != '\n') {
1220 warnx("Invalid chunked encoding");
1221 free(header);
1222 return -1;
1223 }
1224 }
1225 free(header);
1226
1227 if (ferror(fin)(!__isthreaded ? (((fin)->_flags & 0x0040) != 0) : (ferror
)(fin))
)
1228 warnx("Error while reading from socket: %s", sockerror(tls));
1229 else
1230 warnx("Invalid chunked encoding: short read");
1231
1232 return -1;
1233}
1234
1235/*
1236 * Abort a http retrieval
1237 */
1238/* ARGSUSED */
1239static void
1240aborthttp(int signo)
1241{
1242 const char errmsg[] = "\nfetch aborted.\n";
1243
1244 alarmtimer(0);
1245 write(fileno(ttyout)(!__isthreaded ? ((ttyout)->_file) : (fileno)(ttyout)), errmsg, sizeof(errmsg) - 1);
1246 longjmp(httpabort, 1);
1247}
1248
1249/*
1250 * Retrieve multiple files from the command line, transferring
1251 * files of the form "host:path", "ftp://host/path" using the
1252 * ftp protocol, and files of the form "http://host/path" using
1253 * the http protocol.
1254 * If path has a trailing "/", then return (-1);
1255 * the path will be cd-ed into and the connection remains open,
1256 * and the function will return -1 (to indicate the connection
1257 * is alive).
1258 * If an error occurs the return value will be the offset+1 in
1259 * argv[] of the file that caused a problem (i.e, argv[x]
1260 * returns x+1)
1261 * Otherwise, 0 is returned if all files retrieved successfully.
1262 */
1263int
1264auto_fetch(int argc, char *argv[], char *outfile)
1265{
1266 char *xargv[5];
1267 char *cp, *url, *host, *dir, *file, *portnum;
1268 char *username, *pass, *pathstart;
1269 char *ftpproxy, *httpproxy;
1270 int rval, xargc, lastfile;
1271 volatile int argpos;
1272 int dirhasglob, filehasglob, oautologin;
1273 char rempath[PATH_MAX1024];
1274
1275 argpos = 0;
1276
1277 if (setjmp(toplevel)) {
1278 if (connected)
1279 disconnect(0, NULL((void*)0));
1280 return (argpos + 1);
1281 }
1282 (void)signal(SIGINT2, (sig_t)intr);
1283 (void)signal(SIGPIPE13, (sig_t)lostpeer);
1284
1285 if ((ftpproxy = getenv(FTP_PROXY"ftp_proxy")) != NULL((void*)0) && *ftpproxy == '\0')
1286 ftpproxy = NULL((void*)0);
1287 if ((httpproxy = getenv(HTTP_PROXY"http_proxy")) != NULL((void*)0) && *httpproxy == '\0')
1288 httpproxy = NULL((void*)0);
1289
1290 /*
1291 * Loop through as long as there's files to fetch.
1292 */
1293 username = pass = NULL((void*)0);
1294 for (rval = 0; (rval == 0) && (argpos < argc); free(url), argpos++) {
1295 if (strchr(argv[argpos], ':') == NULL((void*)0))
1296 break;
1297
1298 free(username);
1299 free(pass);
1300 host = dir = file = portnum = username = pass = NULL((void*)0);
1301
1302 lastfile = (argv[argpos+1] == NULL((void*)0));
1303
1304 /*
1305 * We muck with the string, so we make a copy.
1306 */
1307 url = strdup(argv[argpos]);
1308 if (url == NULL((void*)0))
1309 errx(1, "Can't allocate memory for auto-fetch.");
1310
1311 if (strncasecmp(url, FILE_URL"file:", sizeof(FILE_URL"file:") - 1) == 0) {
1312 if (file_get(url + sizeof(FILE_URL"file:") - 1, outfile) == -1)
1313 rval = argpos + 1;
1314 continue;
1315 }
1316
1317 /*
1318 * Try HTTP URL-style arguments next.
1319 */
1320 if (strncasecmp(url, HTTP_URL"http://", sizeof(HTTP_URL"http://") - 1) == 0 ||
1321 strncasecmp(url, HTTPS_URL"https://", sizeof(HTTPS_URL"https://") -1) == 0) {
1322 redirect_loop = 0;
1323 retried = 0;
1324 if (url_get(url, httpproxy, outfile, lastfile) == -1)
1325 rval = argpos + 1;
1326 continue;
1327 }
1328
1329 /*
1330 * Try FTP URL-style arguments next. If ftpproxy is
1331 * set, use url_get() instead of standard ftp.
1332 * Finally, try host:file.
1333 */
1334 host = url;
1335 if (strncasecmp(url, FTP_URL"ftp://", sizeof(FTP_URL"ftp://") - 1) == 0) {
1336 char *passend, *passagain, *userend;
1337
1338 if (ftpproxy) {
1339 if (url_get(url, ftpproxy, outfile, lastfile) == -1)
1340 rval = argpos + 1;
1341 continue;
1342 }
1343 host += sizeof(FTP_URL"ftp://") - 1;
1344 dir = strchr(host, '/');
1345
1346 /* Look for [user:pass@]host[:port] */
1347
1348 /* check if we have "user:pass@" */
1349 userend = strchr(host, ':');
1350 passend = strchr(host, '@');
1351 if (passend && userend && userend < passend &&
1352 (!dir || passend < dir)) {
1353 username = host;
1354 pass = userend + 1;
1355 host = passend + 1;
1356 *userend = *passend = '\0';
1357 passagain = strchr(host, '@');
1358 if (strchr(pass, '@') != NULL((void*)0) ||
1359 (passagain != NULL((void*)0) && passagain < dir)) {
1360 warnx(at_encoding_warning);
1361 username = pass = NULL((void*)0);
1362 goto bad_ftp_url;
1363 }
1364
1365 if (EMPTYSTRING(username)((username) == ((void*)0) || (*(username) == '\0'))) {
1366bad_ftp_url:
1367 warnx("Invalid URL: %s", argv[argpos]);
1368 rval = argpos + 1;
1369 username = pass = NULL((void*)0);
1370 continue;
1371 }
1372 username = urldecode(username);
1373 pass = urldecode(pass);
1374 }
1375
1376 /* check [host]:port, or [host] */
1377 if (host[0] == '[') {
1378 cp = strchr(host, ']');
1379 if (cp && (!dir || cp < dir)) {
1380 if (cp + 1 == dir || cp[1] == ':') {
1381 host++;
1382 *cp++ = '\0';
1383 } else
1384 cp = NULL((void*)0);
1385 } else
1386 cp = host;
1387 } else
1388 cp = host;
1389
1390 /* split off host[:port] if there is */
1391 if (cp) {
1392 portnum = strchr(cp, ':');
1393 pathstart = strchr(cp, '/');
1394 /* : in path is not a port # indicator */
1395 if (portnum && pathstart &&
1396 pathstart < portnum)
1397 portnum = NULL((void*)0);
1398
1399 if (!portnum)
1400 ;
1401 else {
1402 if (!dir)
1403 ;
1404 else if (portnum + 1 < dir) {
1405 *portnum++ = '\0';
1406 /*
1407 * XXX should check if portnum
1408 * is decimal number
1409 */
1410 } else {
1411 /* empty portnum */
1412 goto bad_ftp_url;
1413 }
1414 }
1415 } else
1416 portnum = NULL((void*)0);
1417 } else { /* classic style `host:file' */
1418 dir = strchr(host, ':');
1419 }
1420 if (EMPTYSTRING(host)((host) == ((void*)0) || (*(host) == '\0'))) {
1421 rval = argpos + 1;
1422 continue;
1423 }
1424
1425 /*
1426 * If dir is NULL, the file wasn't specified
1427 * (URL looked something like ftp://host)
1428 */
1429 if (dir != NULL((void*)0))
1430 *dir++ = '\0';
1431
1432 /*
1433 * Extract the file and (if present) directory name.
1434 */
1435 if (!EMPTYSTRING(dir)((dir) == ((void*)0) || (*(dir) == '\0'))) {
1436 cp = strrchr(dir, '/');
1437 if (cp != NULL((void*)0)) {
1438 *cp++ = '\0';
1439 file = cp;
1440 } else {
1441 file = dir;
1442 dir = NULL((void*)0);
1443 }
1444 }
1445#ifndef SMALL
1446 if (debug)
1447 fprintf(ttyout,
1448 "user %s:%s host %s port %s dir %s file %s\n",
1449 username, pass ? "XXXX" : NULL((void*)0), host, portnum,
1450 dir, file);
1451#endif /* !SMALL */
1452
1453 /*
1454 * Set up the connection.
1455 */
1456 if (connected)
1457 disconnect(0, NULL((void*)0));
1458 xargv[0] = __progname;
1459 xargv[1] = host;
1460 xargv[2] = NULL((void*)0);
1461 xargc = 2;
1462 if (!EMPTYSTRING(portnum)((portnum) == ((void*)0) || (*(portnum) == '\0'))) {
1463 xargv[2] = portnum;
1464 xargv[3] = NULL((void*)0);
1465 xargc = 3;
1466 }
1467 oautologin = autologin;
1468 if (username == NULL((void*)0))
1469 anonftp = 1;
1470 else {
1471 anonftp = 0;
1472 autologin = 0;
1473 }
1474 setpeer(xargc, xargv);
1475 autologin = oautologin;
1476 if (connected == 0 ||
1477 (connected == 1 && autologin && (username == NULL((void*)0) ||
1478 !ftp_login(host, username, pass)))) {
1479 warnx("Can't connect or login to host `%s'", host);
1480 rval = argpos + 1;
1481 continue;
1482 }
1483
1484 /* Always use binary transfers. */
1485 setbinary(0, NULL((void*)0));
1486
1487 dirhasglob = filehasglob = 0;
1488 if (doglob) {
1489 if (!EMPTYSTRING(dir)((dir) == ((void*)0) || (*(dir) == '\0')) &&
1490 strpbrk(dir, "*?[]{}") != NULL((void*)0))
1491 dirhasglob = 1;
1492 if (!EMPTYSTRING(file)((file) == ((void*)0) || (*(file) == '\0')) &&
1493 strpbrk(file, "*?[]{}") != NULL((void*)0))
1494 filehasglob = 1;
1495 }
1496
1497 /* Change directories, if necessary. */
1498 if (!EMPTYSTRING(dir)((dir) == ((void*)0) || (*(dir) == '\0')) && !dirhasglob) {
1499 xargv[0] = "cd";
1500 xargv[1] = dir;
1501 xargv[2] = NULL((void*)0);
1502 cd(2, xargv);
1503 if (!dirchange) {
1504 rval = argpos + 1;
1505 continue;
1506 }
1507 }
1508
1509 if (EMPTYSTRING(file)((file) == ((void*)0) || (*(file) == '\0'))) {
1510#ifndef SMALL
1511 rval = -1;
1512#else /* !SMALL */
1513 recvrequest("NLST", "-", NULL((void*)0), "w", 0, 0);
1514 rval = 0;
1515#endif /* !SMALL */
1516 continue;
1517 }
1518
1519 if (verbose)
1520 fprintf(ttyout, "Retrieving %s/%s\n", dir ? dir : "", file);
1521
1522 if (dirhasglob) {
1523 snprintf(rempath, sizeof(rempath), "%s/%s", dir, file);
1524 file = rempath;
1525 }
1526
1527 /* Fetch the file(s). */
1528 xargc = 2;
1529 xargv[0] = "get";
1530 xargv[1] = file;
1531 xargv[2] = NULL((void*)0);
1532 if (dirhasglob || filehasglob) {
1533 int ointeractive;
1534
1535 ointeractive = interactive;
1536 interactive = 0;
1537 xargv[0] = "mget";
1538#ifndef SMALL
1539 if (resume) {
1540 xargc = 3;
1541 xargv[1] = "-c";
1542 xargv[2] = file;
1543 xargv[3] = NULL((void*)0);
1544 }
1545#endif /* !SMALL */
1546 mget(xargc, xargv);
1547 interactive = ointeractive;
1548 } else {
1549 if (outfile != NULL((void*)0)) {
1550 xargv[2] = outfile;
1551 xargv[3] = NULL((void*)0);
1552 xargc++;
1553 }
1554#ifndef SMALL
1555 if (resume)
1556 reget(xargc, xargv);
1557 else
1558#endif /* !SMALL */
1559 get(xargc, xargv);
1560 }
1561
1562 if ((code / 100) != COMPLETE2)
1563 rval = argpos + 1;
1564 }
1565 if (connected && rval != -1)
1566 disconnect(0, NULL((void*)0));
1567 return (rval);
1568}
1569
1570char *
1571urldecode(const char *str)
1572{
1573 char *ret, c;
1574 int i, reallen;
1575
1576 if (str == NULL((void*)0))
1577 return NULL((void*)0);
1578 if ((ret = malloc(strlen(str) + 1)) == NULL((void*)0))
1579 err(1, "Can't allocate memory for URL decoding");
1580 for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) {
1581 c = str[i];
1582 if (c == '+') {
1583 *ret = ' ';
1584 continue;
1585 }
1586
1587 /* Cannot use strtol here because next char
1588 * after %xx may be a digit.
1589 */
1590 if (c == '%' && isxdigit((unsigned char)str[i + 1]) &&
1591 isxdigit((unsigned char)str[i + 2])) {
1592 *ret = hextochar(&str[i + 1]);
1593 i += 2;
1594 continue;
1595 }
1596 *ret = c;
1597 }
1598 *ret = '\0';
1599
1600 return ret - reallen;
1601}
1602
1603static char *
1604recode_credentials(const char *userinfo)
1605{
1606 char *ui, *creds;
1607 size_t ulen, credsize;
1608
1609 /* url-decode the user and pass */
1610 ui = urldecode(userinfo);
1611
1612 ulen = strlen(ui);
1613 credsize = (ulen + 2) / 3 * 4 + 1;
1614 creds = malloc(credsize);
1615 if (creds == NULL((void*)0))
1616 errx(1, "out of memory");
1617 if (b64_ntop__b64_ntop(ui, ulen, creds, credsize) == -1)
1618 errx(1, "error in base64 encoding");
1619 free(ui);
1620 return (creds);
1621}
1622
1623static char
1624hextochar(const char *str)
1625{
1626 unsigned char c, ret;
1627
1628 c = str[0];
1629 ret = c;
1630 if (isalpha(c))
1631 ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1632 else
1633 ret -= '0';
1634 ret *= 16;
1635
1636 c = str[1];
1637 ret += c;
1638 if (isalpha(c))
1639 ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1640 else
1641 ret -= '0';
1642 return ret;
1643}
1644
1645int
1646isurl(const char *p)
1647{
1648
1649 if (strncasecmp(p, FTP_URL"ftp://", sizeof(FTP_URL"ftp://") - 1) == 0 ||
1650 strncasecmp(p, HTTP_URL"http://", sizeof(HTTP_URL"http://") - 1) == 0 ||
1651#ifndef NOSSL
1652 strncasecmp(p, HTTPS_URL"https://", sizeof(HTTPS_URL"https://") - 1) == 0 ||
1653#endif /* !NOSSL */
1654 strncasecmp(p, FILE_URL"file:", sizeof(FILE_URL"file:") - 1) == 0 ||
1655 strstr(p, ":/"))
1656 return (1);
1657 return (0);
1658}
1659
1660#ifndef SMALL
1661static int
1662ftp_printf(FILE *fp, const char *fmt, ...)
1663{
1664 va_list ap;
1665 int ret;
1666
1667 va_start(ap, fmt)__builtin_va_start(ap, fmt);
1668 ret = vfprintf(fp, fmt, ap);
1669 va_end(ap)__builtin_va_end(ap);
1670
1671 if (debug) {
1672 va_start(ap, fmt)__builtin_va_start(ap, fmt);
1673 vfprintf(ttyout, fmt, ap);
1674 va_end(ap)__builtin_va_end(ap);
1675 }
1676
1677 return ret;
1678}
1679#endif /* !SMALL */
1680
1681static void
1682ftp_close(FILE **fin, struct tls **tls, int *fd)
1683{
1684#ifndef NOSSL
1685 int ret;
1686
1687 if (*tls != NULL((void*)0)) {
1688 if (tls_session_fd != -1)
1689 dprintf(STDERR_FILENO2, "tls session resumed: %s\n",
1690 tls_conn_session_resumed(*tls) ? "yes" : "no");
1691 do {
1692 ret = tls_close(*tls);
1693 } while (ret == TLS_WANT_POLLIN-2 || ret == TLS_WANT_POLLOUT-3);
1694 tls_free(*tls);
1695 *tls = NULL((void*)0);
1696 }
1697 if (*fd != -1) {
1698 close(*fd);
1699 *fd = -1;
1700 }
1701#endif
1702 if (*fin != NULL((void*)0)) {
1703 fclose(*fin);
1704 *fin = NULL((void*)0);
1705 }
1706}
1707
1708static const char *
1709sockerror(struct tls *tls)
1710{
1711 int save_errno = errno(*__errno());
1712#ifndef NOSSL
1713 if (tls != NULL((void*)0)) {
1714 const char *tlserr = tls_error(tls);
1715 if (tlserr != NULL((void*)0))
1716 return tlserr;
1717 }
1718#endif
1719 return strerror(save_errno);
1720}
1721
1722#ifndef NOSSL
1723static int
1724proxy_connect(int socket, char *host, char *cookie)
1725{
1726 int l;
1727 char buf[1024];
1728 char *connstr, *hosttail, *port;
1729
1730 if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL((void*)0) &&
1731 (hosttail[1] == '\0' || hosttail[1] == ':')) {
1732 host++;
1733 *hosttail++ = '\0';
1734 } else
1735 hosttail = host;
1736
1737 port = strrchr(hosttail, ':'); /* find portnum */
1738 if (port != NULL((void*)0))
1739 *port++ = '\0';
1740 if (!port)
1741 port = "443";
1742
1743 if (cookie) {
1744 l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n"
1745 "Proxy-Authorization: Basic %s\r\n%s\r\n\r\n",
1746 host, port, cookie, HTTP_USER_AGENT"User-Agent: OpenBSD ftp");
1747 } else {
1748 l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n%s\r\n\r\n",
1749 host, port, HTTP_USER_AGENT"User-Agent: OpenBSD ftp");
1750 }
1751
1752 if (l == -1)
1753 errx(1, "Could not allocate memory to assemble connect string!");
1754#ifndef SMALL
1755 if (debug)
1756 printf("%s", connstr);
1757#endif /* !SMALL */
1758 if (write(socket, connstr, l) != l)
1759 err(1, "Could not send connect string");
1760 read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */
1761 free(connstr);
1762 return(200);
1763}
1764
1765static int
1766stdio_tls_write_wrapper(void *arg, const char *buf, int len)
1767{
1768 struct tls *tls = arg;
1769 ssize_t ret;
1770
1771 do {
1772 ret = tls_write(tls, buf, len);
1773 } while (ret == TLS_WANT_POLLIN-2 || ret == TLS_WANT_POLLOUT-3);
1774
1775 return ret;
1776}
1777
1778static int
1779stdio_tls_read_wrapper(void *arg, char *buf, int len)
1780{
1781 struct tls *tls = arg;
1782 ssize_t ret;
1783
1784 do {
1785 ret = tls_read(tls, buf, len);
1786 } while (ret == TLS_WANT_POLLIN-2 || ret == TLS_WANT_POLLOUT-3);
1787
1788 return ret;
1789}
1790#endif /* !NOSSL */