[BACK]Return to main.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ftp

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: }