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