Annotation of src/usr.bin/ftp/main.c, Revision 1.126
1.126 ! florian 1: /* $OpenBSD: main.c,v 1.125 2019/05/14 02:32:08 sunil Exp $ */
1.122 jasper 2:
1.1 deraadt 3: /*
1.121 kmos 4: * Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org>
1.118 krw 5: *
1.121 kmos 6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
1.118 krw 9: *
1.121 kmos 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.44 itojun 17: */
18:
1.121 kmos 19: #include <sys/cdefs.h>
1.1 deraadt 20: #include <sys/types.h>
1.121 kmos 21: #include <sys/queue.h>
22: #include <sys/stat.h>
1.1 deraadt 23: #include <sys/socket.h>
1.121 kmos 24: #include <sys/wait.h>
1.1 deraadt 25:
26: #include <err.h>
1.121 kmos 27: #include <errno.h>
1.120 jsing 28: #include <fcntl.h>
1.121 kmos 29: #include <imsg.h>
30: #include <libgen.h>
31: #include <signal.h>
1.1 deraadt 32: #include <stdio.h>
1.18 millert 33: #include <stdlib.h>
1.1 deraadt 34: #include <string.h>
35: #include <unistd.h>
36:
1.121 kmos 37: #include "ftp.h"
38: #include "xmalloc.h"
1.91 jsing 39:
1.124 espie 40: static int auto_fetch(int, char **);
1.121 kmos 41: static void child(int, int, char **);
42: static int parent(int, pid_t);
43: static struct url *proxy_parse(const char *);
44: static struct url *get_proxy(int);
45: static void re_exec(int, int, char **);
46: static void validate_output_fname(struct url *, const char *);
47: static __dead void usage(void);
48:
49: struct imsgbuf child_ibuf;
50: const char *useragent = "OpenBSD ftp";
51: int activemode, family = AF_UNSPEC, io_debug;
52: int progressmeter, verbose = 1;
53: volatile sig_atomic_t interrupted = 0;
54: FILE *msgout = stdout;
55:
56: static const char *title;
57: static char *tls_options, *oarg;
58: static int connect_timeout, resume;
1.49 deraadt 59:
1.1 deraadt 60: int
1.121 kmos 61: main(int argc, char **argv)
1.1 deraadt 62: {
1.121 kmos 63: const char *e;
64: char **save_argv, *term;
65: int ch, csock, dumb_terminal, rexec, save_argc;
66:
67: if (isatty(fileno(stdin)) != 1)
68: verbose = 0;
69:
70: io_debug = getenv("IO_DEBUG") != NULL;
71: term = getenv("TERM");
72: dumb_terminal = (term == NULL || *term == '\0' ||
73: !strcmp(term, "dumb") || !strcmp(term, "emacs") ||
74: !strcmp(term, "su"));
75: if (isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) && !dumb_terminal)
76: progressmeter = 1;
77:
78: csock = rexec = 0;
79: save_argc = argc;
80: save_argv = argv;
1.86 jca 81: while ((ch = getopt(argc, argv,
1.121 kmos 82: "46AaCc:dD:Eegik:Mmno:pP:r:S:s:tU:vVw:xz:")) != -1) {
1.1 deraadt 83: switch (ch) {
1.49 deraadt 84: case '4':
1.121 kmos 85: family = AF_INET;
1.49 deraadt 86: break;
87: case '6':
1.121 kmos 88: family = AF_INET6;
1.49 deraadt 89: break;
1.37 millert 90: case 'A':
1.121 kmos 91: activemode = 1;
1.37 millert 92: break;
1.67 martynas 93: case 'C':
94: resume = 1;
1.63 pyr 95: break;
1.87 deraadt 96: case 'D':
1.121 kmos 97: title = optarg;
1.87 deraadt 98: break;
1.121 kmos 99: case 'o':
100: oarg = optarg;
101: if (!strlen(oarg))
102: oarg = NULL;
1.1 deraadt 103: break;
1.121 kmos 104: case 'M':
105: progressmeter = 0;
1.59 fgsch 106: break;
1.121 kmos 107: case 'm':
108: progressmeter = 1;
1.21 kstailey 109: break;
1.121 kmos 110: case 'S':
111: tls_options = optarg;
1.1 deraadt 112: break;
1.121 kmos 113: case 'U':
114: useragent = optarg;
1.41 millert 115: break;
1.121 kmos 116: case 'V':
117: verbose = 0;
1.65 espie 118: break;
1.126 ! florian 119: case 'v':
! 120: verbose = 1;
! 121: break;
1.121 kmos 122: case 'w':
123: connect_timeout = strtonum(optarg, 0, 200, &e);
124: if (e)
125: errx(1, "-w: %s", e);
126: break;
127: /* options for internal use only */
128: case 'x':
129: rexec = 1;
130: break;
131: case 'z':
132: csock = strtonum(optarg, 3, getdtablesize() - 1, &e);
133: if (e)
134: errx(1, "-z: %s", e);
1.100 tedu 135: break;
1.121 kmos 136: /* Ignoring all remaining options */
137: case 'a':
138: case 'c':
139: case 'd':
140: case 'E':
141: case 'e':
142: case 'g':
143: case 'i':
144: case 'k':
1.1 deraadt 145: case 'n':
1.121 kmos 146: case 'P':
1.3 deraadt 147: case 'p':
1.18 millert 148: case 'r':
1.82 haesbaer 149: case 's':
1.1 deraadt 150: case 't':
1.116 deraadt 151: break;
1.1 deraadt 152: default:
1.17 millert 153: usage();
1.1 deraadt 154: }
155: }
156: argc -= optind;
157: argv += optind;
1.63 pyr 158:
1.121 kmos 159: if (rexec)
160: child(csock, argc, argv);
1.9 michaels 161:
1.103 doug 162: #ifndef SMALL
1.121 kmos 163: struct url *url;
1.103 doug 164:
1.121 kmos 165: switch (argc) {
166: case 0:
167: cmd(NULL, NULL, NULL);
168: return 0;
169: case 1:
170: case 2:
171: switch (scheme_lookup(argv[0])) {
172: case -1:
173: cmd(argv[0], argv[1], NULL);
174: return 0;
175: case S_FTP:
176: if ((url = url_parse(argv[0])) == NULL)
177: exit(1);
178:
179: if (url->path &&
180: url->path[strlen(url->path) - 1] != '/')
181: break; /* auto fetch */
1.3 deraadt 182:
1.121 kmos 183: cmd(url->host, url->port, url->path);
184: return 0;
1.3 deraadt 185: }
1.121 kmos 186: break;
1.1 deraadt 187: }
1.121 kmos 188: #else
189: if (argc == 0)
190: usage();
191: #endif
192:
1.124 espie 193: return auto_fetch(save_argc, save_argv);
1.1 deraadt 194: }
195:
1.121 kmos 196: static int
1.124 espie 197: auto_fetch(int sargc, char **sargv)
1.1 deraadt 198: {
1.121 kmos 199: pid_t pid;
200: int sp[2];
201:
202: if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) != 0)
203: err(1, "socketpair");
1.1 deraadt 204:
1.121 kmos 205: switch (pid = fork()) {
206: case -1:
207: err(1, "fork");
208: case 0:
209: close(sp[0]);
210: re_exec(sp[1], sargc, sargv);
211: }
1.90 deraadt 212:
1.121 kmos 213: close(sp[1]);
214: return parent(sp[0], pid);
1.1 deraadt 215: }
216:
1.121 kmos 217: static void
218: re_exec(int sock, int argc, char **argv)
1.1 deraadt 219: {
1.121 kmos 220: char **nargv, *sock_str;
221: int i, j, nargc;
1.1 deraadt 222:
1.121 kmos 223: nargc = argc + 4;
224: nargv = xcalloc(nargc, sizeof(*nargv));
225: xasprintf(&sock_str, "%d", sock);
226: i = 0;
227: nargv[i++] = argv[0];
228: nargv[i++] = "-z";
229: nargv[i++] = sock_str;
230: nargv[i++] = "-x";
231: for (j = 1; j < argc; j++)
232: nargv[i++] = argv[j];
1.1 deraadt 233:
1.121 kmos 234: execvp(nargv[0], nargv);
235: err(1, "execvp");
1.1 deraadt 236: }
237:
1.121 kmos 238: static int
239: parent(int sock, pid_t child_pid)
1.1 deraadt 240: {
1.121 kmos 241: struct imsgbuf ibuf;
242: struct imsg imsg;
243: struct stat sb;
244: off_t offset;
245: int fd, save_errno, sig, status;
246:
247: setproctitle("%s", "parent");
248: if (pledge("stdio cpath rpath wpath sendfd", NULL) == -1)
249: err(1, "pledge");
1.1 deraadt 250:
1.121 kmos 251: imsg_init(&ibuf, sock);
1.1 deraadt 252: for (;;) {
1.121 kmos 253: if (read_message(&ibuf, &imsg) == 0)
254: break;
255:
256: if (imsg.hdr.type != IMSG_OPEN)
257: errx(1, "%s: IMSG_OPEN expected", __func__);
258:
259: offset = 0;
260: fd = open(imsg.data, imsg.hdr.peerid, 0666);
261: save_errno = errno;
262: if (fd != -1 && fstat(fd, &sb) == 0) {
263: if (sb.st_mode & S_IFDIR) {
264: close(fd);
265: fd = -1;
266: save_errno = EISDIR;
267: } else
268: offset = sb.st_size;
1.17 millert 269: }
270:
1.121 kmos 271: send_message(&ibuf, IMSG_OPEN, save_errno,
272: &offset, sizeof offset, fd);
273: imsg_free(&imsg);
1.1 deraadt 274: }
1.121 kmos 275:
276: close(sock);
277: if (waitpid(child_pid, &status, 0) == -1 && errno != ECHILD)
278: err(1, "wait");
279:
280: sig = WTERMSIG(status);
281: if (WIFSIGNALED(status) && sig != SIGPIPE)
282: errx(1, "child terminated: signal %d", sig);
283:
284: return WEXITSTATUS(status);
1.1 deraadt 285: }
286:
1.121 kmos 287: static void
288: child(int sock, int argc, char **argv)
1.1 deraadt 289: {
1.121 kmos 290: struct url *url;
291: FILE *dst_fp;
292: char *p;
293: off_t offset, sz;
294: int fd, i, tostdout;
1.1 deraadt 295:
1.121 kmos 296: setproctitle("%s", "child");
297: #ifndef NOSSL
298: https_init(tls_options);
299: #endif
300: if (pledge("stdio inet dns recvfd tty", NULL) == -1)
301: err(1, "pledge");
302: if (!progressmeter && pledge("stdio inet dns recvfd", NULL) == -1)
303: err(1, "pledge");
1.1 deraadt 304:
1.121 kmos 305: imsg_init(&child_ibuf, sock);
306: tostdout = oarg && (strcmp(oarg, "-") == 0);
1.123 jca 307: if (tostdout)
308: msgout = stderr;
1.121 kmos 309: if (resume && tostdout)
310: errx(1, "can't append to stdout");
1.1 deraadt 311:
1.121 kmos 312: for (i = 0; i < argc; i++) {
313: fd = -1;
314: offset = sz = 0;
1.1 deraadt 315:
1.121 kmos 316: if ((url = url_parse(argv[i])) == NULL)
317: exit(1);
1.1 deraadt 318:
1.121 kmos 319: validate_output_fname(url, argv[i]);
320: url_connect(url, get_proxy(url->scheme), connect_timeout);
321: if (resume)
322: fd = fd_request(url->fname, O_WRONLY|O_APPEND, &offset);
1.1 deraadt 323:
1.121 kmos 324: url = url_request(url, get_proxy(url->scheme), &offset, &sz);
325: if (resume && offset == 0 && fd != -1)
326: if (ftruncate(fd, 0) != 0)
327: err(1, "ftruncate");
1.1 deraadt 328:
1.121 kmos 329: if (fd == -1 && !tostdout &&
330: (fd = fd_request(url->fname,
331: O_CREAT|O_TRUNC|O_WRONLY, NULL)) == -1)
332: err(1, "Can't open file %s", url->fname);
1.1 deraadt 333:
1.121 kmos 334: if (tostdout) {
335: dst_fp = stdout;
336: } else if ((dst_fp = fdopen(fd, "w")) == NULL)
337: err(1, "%s: fdopen", __func__);
1.1 deraadt 338:
1.121 kmos 339: if (progressmeter) {
340: p = basename(url->path);
341: start_progress_meter(p, title, sz, &offset);
1.1 deraadt 342: }
343:
1.121 kmos 344: url_save(url, dst_fp, &offset);
345: if (progressmeter)
346: stop_progress_meter();
1.1 deraadt 347:
1.121 kmos 348: if (!tostdout)
349: fclose(dst_fp);
1.1 deraadt 350:
1.121 kmos 351: url_close(url);
352: url_free(url);
1.1 deraadt 353: }
354:
1.121 kmos 355: exit(0);
356: }
1.1 deraadt 357:
1.121 kmos 358: static struct url *
359: get_proxy(int scheme)
360: {
361: static struct url *ftp_proxy, *http_proxy;
1.1 deraadt 362:
1.121 kmos 363: switch (scheme) {
364: case S_HTTP:
365: case S_HTTPS:
366: if (http_proxy)
367: return http_proxy;
368: else
369: return (http_proxy = proxy_parse("http_proxy"));
370: case S_FTP:
371: if (ftp_proxy)
372: return ftp_proxy;
373: else
374: return (ftp_proxy = proxy_parse("ftp_proxy"));
1.1 deraadt 375: default:
1.121 kmos 376: return NULL;
1.1 deraadt 377: }
1.121 kmos 378: }
1.1 deraadt 379:
1.121 kmos 380: static void
381: validate_output_fname(struct url *url, const char *name)
382: {
383: url->fname = xstrdup(oarg ? oarg : basename(url->path));
384: if (strcmp(url->fname, "/") == 0)
385: errx(1, "No filename after host (use -o): %s", name);
1.1 deraadt 386:
1.121 kmos 387: if (strcmp(url->fname, ".") == 0)
388: errx(1, "No '/' after host (use -o): %s", name);
1.1 deraadt 389: }
390:
1.121 kmos 391: static struct url *
392: proxy_parse(const char *name)
1.1 deraadt 393: {
1.121 kmos 394: struct url *proxy;
395: char *str;
1.1 deraadt 396:
1.121 kmos 397: if ((str = getenv(name)) == NULL)
398: return NULL;
1.1 deraadt 399:
1.121 kmos 400: if (strlen(str) == 0)
401: return NULL;
1.17 millert 402:
1.121 kmos 403: if ((proxy = url_parse(str)) == NULL)
404: exit(1);
1.17 millert 405:
1.121 kmos 406: if (proxy->scheme != S_HTTP)
407: errx(1, "Malformed proxy URL: %s", str);
1.17 millert 408:
1.121 kmos 409: return proxy;
1.17 millert 410: }
411:
1.121 kmos 412: static __dead void
1.58 deraadt 413: usage(void)
1.17 millert 414: {
1.121 kmos 415: fprintf(stderr, "usage: %s [-46ACVM] [-D title] [-o output] "
416: "[-S tls_options] [-U useragent] "
417: "[-w seconds] url ...\n", getprogname());
418:
1.17 millert 419: exit(1);
1.1 deraadt 420: }