[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.122

1.122   ! jasper      1: /*     $OpenBSD$ */
        !             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.121     kmos       40: static int              auto_fetch(int, char **, int, char **);
                     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:
                    191:        return auto_fetch(argc, argv, save_argc, save_argv);
1.1       deraadt   192: }
                    193:
1.121     kmos      194: static int
                    195: auto_fetch(int argc, char **argv, 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);
                    306:        if (resume && tostdout)
                    307:                errx(1, "can't append to stdout");
1.1       deraadt   308:
1.121     kmos      309:        for (i = 0; i < argc; i++) {
                    310:                fd = -1;
                    311:                offset = sz = 0;
1.1       deraadt   312:
1.121     kmos      313:                if ((url = url_parse(argv[i])) == NULL)
                    314:                        exit(1);
1.1       deraadt   315:
1.121     kmos      316:                validate_output_fname(url, argv[i]);
                    317:                url_connect(url, get_proxy(url->scheme), connect_timeout);
                    318:                if (resume)
                    319:                        fd = fd_request(url->fname, O_WRONLY|O_APPEND, &offset);
1.1       deraadt   320:
1.121     kmos      321:                url = url_request(url, get_proxy(url->scheme), &offset, &sz);
                    322:                if (resume && offset == 0 && fd != -1)
                    323:                        if (ftruncate(fd, 0) != 0)
                    324:                                err(1, "ftruncate");
1.1       deraadt   325:
1.121     kmos      326:                if (fd == -1 && !tostdout &&
                    327:                    (fd = fd_request(url->fname,
                    328:                    O_CREAT|O_TRUNC|O_WRONLY, NULL)) == -1)
                    329:                        err(1, "Can't open file %s", url->fname);
1.1       deraadt   330:
1.121     kmos      331:                if (tostdout) {
                    332:                        dst_fp = stdout;
                    333:                        msgout = stderr;
                    334:                } else if ((dst_fp = fdopen(fd, "w")) == NULL)
                    335:                        err(1, "%s: fdopen", __func__);
1.1       deraadt   336:
1.121     kmos      337:                if (progressmeter) {
                    338:                        p = basename(url->path);
                    339:                        start_progress_meter(p, title, sz, &offset);
1.1       deraadt   340:                }
                    341:
1.121     kmos      342:                url_save(url, dst_fp, &offset);
                    343:                if (progressmeter)
                    344:                        stop_progress_meter();
1.1       deraadt   345:
1.121     kmos      346:                if (!tostdout)
                    347:                        fclose(dst_fp);
1.1       deraadt   348:
1.121     kmos      349:                url_close(url);
                    350:                url_free(url);
1.1       deraadt   351:        }
                    352:
1.121     kmos      353:        exit(0);
                    354: }
1.1       deraadt   355:
1.121     kmos      356: static struct url *
                    357: get_proxy(int scheme)
                    358: {
                    359:        static struct url       *ftp_proxy, *http_proxy;
1.1       deraadt   360:
1.121     kmos      361:        switch (scheme) {
                    362:        case S_HTTP:
                    363:        case S_HTTPS:
                    364:                if (http_proxy)
                    365:                        return http_proxy;
                    366:                else
                    367:                        return (http_proxy = proxy_parse("http_proxy"));
                    368:        case S_FTP:
                    369:                if (ftp_proxy)
                    370:                        return ftp_proxy;
                    371:                else
                    372:                        return (ftp_proxy = proxy_parse("ftp_proxy"));
1.1       deraadt   373:        default:
1.121     kmos      374:                return NULL;
1.1       deraadt   375:        }
1.121     kmos      376: }
1.1       deraadt   377:
1.121     kmos      378: static void
                    379: validate_output_fname(struct url *url, const char *name)
                    380: {
                    381:        url->fname = xstrdup(oarg ? oarg : basename(url->path));
                    382:        if (strcmp(url->fname, "/") == 0)
                    383:                errx(1, "No filename after host (use -o): %s", name);
1.1       deraadt   384:
1.121     kmos      385:        if (strcmp(url->fname, ".") == 0)
                    386:                errx(1, "No '/' after host (use -o): %s", name);
1.1       deraadt   387: }
                    388:
1.121     kmos      389: static struct url *
                    390: proxy_parse(const char *name)
1.1       deraadt   391: {
1.121     kmos      392:        struct url      *proxy;
                    393:        char            *str;
1.1       deraadt   394:
1.121     kmos      395:        if ((str = getenv(name)) == NULL)
                    396:                return NULL;
1.1       deraadt   397:
1.121     kmos      398:        if (strlen(str) == 0)
                    399:                return NULL;
1.17      millert   400:
1.121     kmos      401:        if ((proxy = url_parse(str)) == NULL)
                    402:                exit(1);
1.17      millert   403:
1.121     kmos      404:        if (proxy->scheme != S_HTTP)
                    405:                errx(1, "Malformed proxy URL: %s", str);
1.17      millert   406:
1.121     kmos      407:        return proxy;
1.17      millert   408: }
                    409:
1.121     kmos      410: static __dead void
1.58      deraadt   411: usage(void)
1.17      millert   412: {
1.121     kmos      413:        fprintf(stderr, "usage: %s [-46ACVM] [-D title] [-o output] "
                    414:            "[-S tls_options] [-U useragent] "
                    415:            "[-w seconds] url ...\n", getprogname());
                    416:
1.17      millert   417:        exit(1);
1.1       deraadt   418: }