Annotation of src/usr.bin/ftp/main.c, Revision 1.124
1.124 ! espie 1: /* $OpenBSD: main.c,v 1.123 2019/05/12 21:30:48 jca 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.121 kmos 119: case 'w':
120: connect_timeout = strtonum(optarg, 0, 200, &e);
121: if (e)
122: errx(1, "-w: %s", e);
123: break;
124: /* options for internal use only */
125: case 'x':
126: rexec = 1;
127: break;
128: case 'z':
129: csock = strtonum(optarg, 3, getdtablesize() - 1, &e);
130: if (e)
131: errx(1, "-z: %s", e);
1.100 tedu 132: break;
1.121 kmos 133: /* Ignoring all remaining options */
134: case 'a':
135: case 'c':
136: case 'd':
137: case 'E':
138: case 'e':
139: case 'g':
140: case 'i':
141: case 'k':
1.1 deraadt 142: case 'n':
1.121 kmos 143: case 'P':
1.3 deraadt 144: case 'p':
1.18 millert 145: case 'r':
1.82 haesbaer 146: case 's':
1.1 deraadt 147: case 't':
148: case 'v':
1.116 deraadt 149: break;
1.1 deraadt 150: default:
1.17 millert 151: usage();
1.1 deraadt 152: }
153: }
154: argc -= optind;
155: argv += optind;
1.63 pyr 156:
1.121 kmos 157: if (rexec)
158: child(csock, argc, argv);
1.9 michaels 159:
1.103 doug 160: #ifndef SMALL
1.121 kmos 161: struct url *url;
1.103 doug 162:
1.121 kmos 163: switch (argc) {
164: case 0:
165: cmd(NULL, NULL, NULL);
166: return 0;
167: case 1:
168: case 2:
169: switch (scheme_lookup(argv[0])) {
170: case -1:
171: cmd(argv[0], argv[1], NULL);
172: return 0;
173: case S_FTP:
174: if ((url = url_parse(argv[0])) == NULL)
175: exit(1);
176:
177: if (url->path &&
178: url->path[strlen(url->path) - 1] != '/')
179: break; /* auto fetch */
1.3 deraadt 180:
1.121 kmos 181: cmd(url->host, url->port, url->path);
182: return 0;
1.3 deraadt 183: }
1.121 kmos 184: break;
1.1 deraadt 185: }
1.121 kmos 186: #else
187: if (argc == 0)
188: usage();
189: #endif
190:
1.124 ! espie 191: return auto_fetch(save_argc, save_argv);
1.1 deraadt 192: }
193:
1.121 kmos 194: static int
1.124 ! espie 195: auto_fetch(int sargc, char **sargv)
1.1 deraadt 196: {
1.121 kmos 197: pid_t pid;
198: int sp[2];
199:
200: if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) != 0)
201: err(1, "socketpair");
1.1 deraadt 202:
1.121 kmos 203: switch (pid = fork()) {
204: case -1:
205: err(1, "fork");
206: case 0:
207: close(sp[0]);
208: re_exec(sp[1], sargc, sargv);
209: }
1.90 deraadt 210:
1.121 kmos 211: close(sp[1]);
212: return parent(sp[0], pid);
1.1 deraadt 213: }
214:
1.121 kmos 215: static void
216: re_exec(int sock, int argc, char **argv)
1.1 deraadt 217: {
1.121 kmos 218: char **nargv, *sock_str;
219: int i, j, nargc;
1.1 deraadt 220:
1.121 kmos 221: nargc = argc + 4;
222: nargv = xcalloc(nargc, sizeof(*nargv));
223: xasprintf(&sock_str, "%d", sock);
224: i = 0;
225: nargv[i++] = argv[0];
226: nargv[i++] = "-z";
227: nargv[i++] = sock_str;
228: nargv[i++] = "-x";
229: for (j = 1; j < argc; j++)
230: nargv[i++] = argv[j];
1.1 deraadt 231:
1.121 kmos 232: execvp(nargv[0], nargv);
233: err(1, "execvp");
1.1 deraadt 234: }
235:
1.121 kmos 236: static int
237: parent(int sock, pid_t child_pid)
1.1 deraadt 238: {
1.121 kmos 239: struct imsgbuf ibuf;
240: struct imsg imsg;
241: struct stat sb;
242: off_t offset;
243: int fd, save_errno, sig, status;
244:
245: setproctitle("%s", "parent");
246: if (pledge("stdio cpath rpath wpath sendfd", NULL) == -1)
247: err(1, "pledge");
1.1 deraadt 248:
1.121 kmos 249: imsg_init(&ibuf, sock);
1.1 deraadt 250: for (;;) {
1.121 kmos 251: if (read_message(&ibuf, &imsg) == 0)
252: break;
253:
254: if (imsg.hdr.type != IMSG_OPEN)
255: errx(1, "%s: IMSG_OPEN expected", __func__);
256:
257: offset = 0;
258: fd = open(imsg.data, imsg.hdr.peerid, 0666);
259: save_errno = errno;
260: if (fd != -1 && fstat(fd, &sb) == 0) {
261: if (sb.st_mode & S_IFDIR) {
262: close(fd);
263: fd = -1;
264: save_errno = EISDIR;
265: } else
266: offset = sb.st_size;
1.17 millert 267: }
268:
1.121 kmos 269: send_message(&ibuf, IMSG_OPEN, save_errno,
270: &offset, sizeof offset, fd);
271: imsg_free(&imsg);
1.1 deraadt 272: }
1.121 kmos 273:
274: close(sock);
275: done:
276: if (waitpid(child_pid, &status, 0) == -1 && errno != ECHILD)
277: err(1, "wait");
278:
279: sig = WTERMSIG(status);
280: if (WIFSIGNALED(status) && sig != SIGPIPE)
281: errx(1, "child terminated: signal %d", sig);
282:
283: return WEXITSTATUS(status);
1.1 deraadt 284: }
285:
1.121 kmos 286: static void
287: child(int sock, int argc, char **argv)
1.1 deraadt 288: {
1.121 kmos 289: struct url *url;
290: FILE *dst_fp;
291: char *p;
292: off_t offset, sz;
293: int fd, i, tostdout;
1.1 deraadt 294:
1.121 kmos 295: setproctitle("%s", "child");
296: #ifndef NOSSL
297: https_init(tls_options);
298: #endif
299: if (pledge("stdio inet dns recvfd tty", NULL) == -1)
300: err(1, "pledge");
301: if (!progressmeter && pledge("stdio inet dns recvfd", NULL) == -1)
302: err(1, "pledge");
1.1 deraadt 303:
1.121 kmos 304: imsg_init(&child_ibuf, sock);
305: tostdout = oarg && (strcmp(oarg, "-") == 0);
1.123 jca 306: if (tostdout)
307: msgout = stderr;
1.121 kmos 308: if (resume && tostdout)
309: errx(1, "can't append to stdout");
1.1 deraadt 310:
1.121 kmos 311: for (i = 0; i < argc; i++) {
312: fd = -1;
313: offset = sz = 0;
1.1 deraadt 314:
1.121 kmos 315: if ((url = url_parse(argv[i])) == NULL)
316: exit(1);
1.1 deraadt 317:
1.121 kmos 318: validate_output_fname(url, argv[i]);
319: url_connect(url, get_proxy(url->scheme), connect_timeout);
320: if (resume)
321: fd = fd_request(url->fname, O_WRONLY|O_APPEND, &offset);
1.1 deraadt 322:
1.121 kmos 323: url = url_request(url, get_proxy(url->scheme), &offset, &sz);
324: if (resume && offset == 0 && fd != -1)
325: if (ftruncate(fd, 0) != 0)
326: err(1, "ftruncate");
1.1 deraadt 327:
1.121 kmos 328: if (fd == -1 && !tostdout &&
329: (fd = fd_request(url->fname,
330: O_CREAT|O_TRUNC|O_WRONLY, NULL)) == -1)
331: err(1, "Can't open file %s", url->fname);
1.1 deraadt 332:
1.121 kmos 333: if (tostdout) {
334: dst_fp = stdout;
335: } else if ((dst_fp = fdopen(fd, "w")) == NULL)
336: err(1, "%s: fdopen", __func__);
1.1 deraadt 337:
1.121 kmos 338: if (progressmeter) {
339: p = basename(url->path);
340: start_progress_meter(p, title, sz, &offset);
1.1 deraadt 341: }
342:
1.121 kmos 343: url_save(url, dst_fp, &offset);
344: if (progressmeter)
345: stop_progress_meter();
1.1 deraadt 346:
1.121 kmos 347: if (!tostdout)
348: fclose(dst_fp);
1.1 deraadt 349:
1.121 kmos 350: url_close(url);
351: url_free(url);
1.1 deraadt 352: }
353:
1.121 kmos 354: exit(0);
355: }
1.1 deraadt 356:
1.121 kmos 357: static struct url *
358: get_proxy(int scheme)
359: {
360: static struct url *ftp_proxy, *http_proxy;
1.1 deraadt 361:
1.121 kmos 362: switch (scheme) {
363: case S_HTTP:
364: case S_HTTPS:
365: if (http_proxy)
366: return http_proxy;
367: else
368: return (http_proxy = proxy_parse("http_proxy"));
369: case S_FTP:
370: if (ftp_proxy)
371: return ftp_proxy;
372: else
373: return (ftp_proxy = proxy_parse("ftp_proxy"));
1.1 deraadt 374: default:
1.121 kmos 375: return NULL;
1.1 deraadt 376: }
1.121 kmos 377: }
1.1 deraadt 378:
1.121 kmos 379: static void
380: validate_output_fname(struct url *url, const char *name)
381: {
382: url->fname = xstrdup(oarg ? oarg : basename(url->path));
383: if (strcmp(url->fname, "/") == 0)
384: errx(1, "No filename after host (use -o): %s", name);
1.1 deraadt 385:
1.121 kmos 386: if (strcmp(url->fname, ".") == 0)
387: errx(1, "No '/' after host (use -o): %s", name);
1.1 deraadt 388: }
389:
1.121 kmos 390: static struct url *
391: proxy_parse(const char *name)
1.1 deraadt 392: {
1.121 kmos 393: struct url *proxy;
394: char *str;
1.1 deraadt 395:
1.121 kmos 396: if ((str = getenv(name)) == NULL)
397: return NULL;
1.1 deraadt 398:
1.121 kmos 399: if (strlen(str) == 0)
400: return NULL;
1.17 millert 401:
1.121 kmos 402: if ((proxy = url_parse(str)) == NULL)
403: exit(1);
1.17 millert 404:
1.121 kmos 405: if (proxy->scheme != S_HTTP)
406: errx(1, "Malformed proxy URL: %s", str);
1.17 millert 407:
1.121 kmos 408: return proxy;
1.17 millert 409: }
410:
1.121 kmos 411: static __dead void
1.58 deraadt 412: usage(void)
1.17 millert 413: {
1.121 kmos 414: fprintf(stderr, "usage: %s [-46ACVM] [-D title] [-o output] "
415: "[-S tls_options] [-U useragent] "
416: "[-w seconds] url ...\n", getprogname());
417:
1.17 millert 418: exit(1);
1.1 deraadt 419: }