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