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

Annotation of src/usr.bin/ftp/ftp.c, Revision 1.101

1.34      itojun      1: /*
1.101   ! kmos        2:  * Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org>
1.1       deraadt     3:  *
1.101   ! 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.1       deraadt     7:  *
1.101   ! 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.1       deraadt    15:  */
                     16:
                     17: #include <sys/socket.h>
                     18:
1.101   ! kmos       19: #include <arpa/inet.h>
1.1       deraadt    20: #include <netinet/in.h>
                     21:
                     22: #include <err.h>
                     23: #include <errno.h>
1.101   ! kmos       24: #include <libgen.h>
        !            25: #include <limits.h>
1.1       deraadt    26: #include <netdb.h>
1.54      millert    27: #include <stdarg.h>
1.1       deraadt    28: #include <stdio.h>
                     29: #include <stdlib.h>
                     30: #include <string.h>
                     31: #include <unistd.h>
                     32:
1.101   ! kmos       33: #include "ftp.h"
        !            34: #include "xmalloc.h"
        !            35:
        !            36: static FILE    *ctrl_fp;
        !            37: static int      data_fd;
        !            38:
        !            39: void
        !            40: ftp_connect(struct url *url, struct url *proxy, int timeout)
        !            41: {
        !            42:        char            *buf = NULL;
        !            43:        size_t           n = 0;
        !            44:        int              sock;
1.1       deraadt    45:
1.101   ! kmos       46:        if (proxy) {
        !            47:                http_connect(url, proxy, timeout);
        !            48:                return;
        !            49:        }
1.34      itojun     50:
1.101   ! kmos       51:        if ((sock = tcp_connect(url->host, url->port, timeout)) == -1)
        !            52:                exit(1);
1.34      itojun     53:
1.101   ! kmos       54:        if ((ctrl_fp = fdopen(sock, "r+")) == NULL)
        !            55:                err(1, "%s: fdopen", __func__);
1.1       deraadt    56:
1.101   ! kmos       57:        /* greeting */
        !            58:        if (ftp_getline(&buf, &n, 0, ctrl_fp) != P_OK) {
        !            59:                warnx("Can't connect to host `%s'", url->host);
        !            60:                ftp_command(ctrl_fp, "QUIT");
        !            61:                exit(1);
        !            62:        }
1.1       deraadt    63:
1.101   ! kmos       64:        free(buf);
        !            65:        log_info("Connected to %s\n", url->host);
        !            66:        if (ftp_auth(ctrl_fp, NULL, NULL) != P_OK) {
        !            67:                warnx("Can't login to host `%s'", url->host);
        !            68:                ftp_command(ctrl_fp, "QUIT");
        !            69:                exit(1);
        !            70:        }
        !            71: }
1.97      millert    72:
1.101   ! kmos       73: struct url *
        !            74: ftp_get(struct url *url, struct url *proxy, off_t *offset, off_t *sz)
1.1       deraadt    75: {
1.101   ! kmos       76:        char    *buf = NULL, *dir, *file;
        !            77:
        !            78:        if (proxy) {
        !            79:                url = http_get(url, proxy, offset, sz);
        !            80:                /* this url should now be treated as HTTP */
        !            81:                url->scheme = S_HTTP;
        !            82:                return url;
        !            83:        }
        !            84:
        !            85:        log_info("Using binary mode to transfer files.\n");
        !            86:        if (ftp_command(ctrl_fp, "TYPE I") != P_OK)
        !            87:                errx(1, "Failed to set mode to binary");
        !            88:
        !            89:        dir = dirname(url->path);
        !            90:        if (ftp_command(ctrl_fp, "CWD %s", dir) != P_OK)
        !            91:                errx(1, "CWD command failed");
        !            92:
        !            93:        log_info("Retrieving %s\n", url->path);
        !            94:        file = basename(url->path);
        !            95:        if (strcmp(url->fname, "-"))
        !            96:                log_info("local: %s remote: %s\n", url->fname, file);
        !            97:        else
        !            98:                log_info("remote: %s\n", file);
        !            99:
        !           100:        if (ftp_size(ctrl_fp, file, sz, &buf) != P_OK) {
        !           101:                fprintf(stderr, "%s", buf);
        !           102:                ftp_command(ctrl_fp, "QUIT");
        !           103:                exit(1);
1.1       deraadt   104:        }
1.101   ! kmos      105:        free(buf);
        !           106:
        !           107:        if (activemode)
        !           108:                data_fd = ftp_eprt(ctrl_fp);
        !           109:        else if ((data_fd = ftp_epsv(ctrl_fp)) == -1)
        !           110:                data_fd = ftp_eprt(ctrl_fp);
1.34      itojun    111:
1.101   ! kmos      112:        if (data_fd == -1)
        !           113:                errx(1, "Failed to establish data connection");
1.82      haesbaer  114:
1.101   ! kmos      115:        if (*offset && ftp_command(ctrl_fp, "REST %lld", *offset) != P_INTER)
        !           116:                errx(1, "REST command failed");
1.34      itojun    117:
1.101   ! kmos      118:        if (ftp_command(ctrl_fp, "RETR %s", file) != P_PRE) {
        !           119:                ftp_command(ctrl_fp, "QUIT");
        !           120:                exit(1);
1.34      itojun    121:        }
1.101   ! kmos      122:
        !           123:        return url;
1.1       deraadt   124: }
                    125:
                    126: void
1.101   ! kmos      127: ftp_save(struct url *url, FILE *dst_fp, off_t *offset)
1.1       deraadt   128: {
1.101   ! kmos      129:        struct sockaddr_storage  ss;
        !           130:        FILE                    *data_fp;
        !           131:        socklen_t                len;
        !           132:        int                      s;
1.1       deraadt   133:
1.101   ! kmos      134:        if (activemode) {
        !           135:                len = sizeof(ss);
        !           136:                if ((s = accept(data_fd, (struct sockaddr *)&ss, &len)) == -1)
        !           137:                        err(1, "%s: accept", __func__);
1.1       deraadt   138:
1.101   ! kmos      139:                close(data_fd);
        !           140:                data_fd = s;
1.1       deraadt   141:        }
                    142:
1.101   ! kmos      143:        if ((data_fp = fdopen(data_fd, "r")) == NULL)
        !           144:                err(1, "%s: fdopen data_fd", __func__);
1.67      espie     145:
1.101   ! kmos      146:        copy_file(dst_fp, data_fp, offset);
        !           147:        fclose(data_fp);
1.67      espie     148: }
                    149:
1.101   ! kmos      150: void
        !           151: ftp_quit(struct url *url)
1.67      espie     152: {
1.101   ! kmos      153:        char    *buf = NULL;
        !           154:        size_t   n = 0;
        !           155:
        !           156:        if (ftp_getline(&buf, &n, 0, ctrl_fp) != P_OK)
        !           157:                errx(1, "error retrieving file %s", url->fname);
        !           158:
        !           159:        free(buf);
        !           160:        ftp_command(ctrl_fp, "QUIT");
        !           161:        fclose(ctrl_fp);
1.67      espie     162: }
                    163:
1.101   ! kmos      164: int
        !           165: ftp_getline(char **lineptr, size_t *n, int suppress_output, FILE *fp)
1.67      espie     166: {
1.101   ! kmos      167:        ssize_t          len;
        !           168:        char            *bufp, code[4];
        !           169:        const char      *errstr;
        !           170:        int              lookup[] = { P_PRE, P_OK, P_INTER, N_TRANS, N_PERM };
        !           171:
        !           172:
        !           173:        if ((len = getline(lineptr, n, fp)) == -1)
        !           174:                err(1, "%s: getline", __func__);
        !           175:
        !           176:        bufp = *lineptr;
        !           177:        if (!suppress_output)
        !           178:                log_info("%s", bufp);
        !           179:
        !           180:        if (len < 4)
        !           181:                errx(1, "%s: line too short", __func__);
1.74      martynas  182:
1.101   ! kmos      183:        (void)strlcpy(code, bufp, sizeof code);
        !           184:        if (bufp[3] == ' ')
        !           185:                goto done;
        !           186:
        !           187:        /* multi-line reply */
        !           188:        while (!(strncmp(code, bufp, 3) == 0 && bufp[3] == ' ')) {
        !           189:                if ((len = getline(lineptr, n, fp)) == -1)
        !           190:                        err(1, "%s: getline", __func__);
        !           191:
        !           192:                bufp = *lineptr;
        !           193:                if (!suppress_output)
        !           194:                        log_info("%s", bufp);
        !           195:
        !           196:                if (len < 4)
        !           197:                        continue;
1.74      martynas  198:        }
1.67      espie     199:
1.101   ! kmos      200:  done:
        !           201:        (void)strtonum(code, 100, 553, &errstr);
        !           202:        if (errstr)
        !           203:                errx(1, "%s: Response code is %s: %s", __func__, errstr, code);
1.67      espie     204:
1.101   ! kmos      205:        return lookup[code[0] - '1'];
1.67      espie     206: }
                    207:
1.101   ! kmos      208: int
        !           209: ftp_command(FILE *fp, const char *fmt, ...)
1.67      espie     210: {
1.101   ! kmos      211:        va_list  ap;
        !           212:        char    *buf = NULL, *cmd;
        !           213:        size_t   n = 0;
        !           214:        int      r;
        !           215:
        !           216:        va_start(ap, fmt);
        !           217:        r = vasprintf(&cmd, fmt, ap);
        !           218:        va_end(ap);
        !           219:        if (r < 0)
        !           220:                errx(1, "%s: vasprintf", __func__);
        !           221:
        !           222:        if (io_debug)
        !           223:                fprintf(stderr, ">>> %s\n", cmd);
        !           224:
        !           225:        if (fprintf(fp, "%s\r\n", cmd) < 0)
        !           226:                errx(1, "%s: fprintf", __func__);
        !           227:
        !           228:        (void)fflush(fp);
        !           229:        free(cmd);
        !           230:        r = ftp_getline(&buf, &n, 0, fp);
        !           231:        free(buf);
        !           232:        return r;
        !           233:
1.67      espie     234: }
                    235:
1.1       deraadt   236: int
1.101   ! kmos      237: ftp_auth(FILE *fp, const char *user, const char *pass)
1.1       deraadt   238: {
1.101   ! kmos      239:        char    *addr = NULL, hn[HOST_NAME_MAX+1], *un;
        !           240:        int      code;
        !           241:
        !           242:        code = ftp_command(fp, "USER %s", user ? user : "anonymous");
        !           243:        if (code != P_OK && code != P_INTER)
        !           244:                return code;
1.1       deraadt   245:
1.101   ! kmos      246:        if (pass == NULL) {
        !           247:                if (gethostname(hn, sizeof hn) == -1)
        !           248:                        err(1, "%s: gethostname", __func__);
1.1       deraadt   249:
1.101   ! kmos      250:                un = getlogin();
        !           251:                xasprintf(&addr, "%s@%s", un ? un : "anonymous", hn);
        !           252:        }
1.87      deraadt   253:
1.101   ! kmos      254:        code = ftp_command(fp, "PASS %s", pass ? pass : addr);
        !           255:        free(addr);
        !           256:        return code;
1.1       deraadt   257: }
                    258:
1.101   ! kmos      259: int
        !           260: ftp_size(FILE *fp, const char *fn, off_t *sizep, char **buf)
1.1       deraadt   261: {
1.101   ! kmos      262:        size_t   n = 0;
        !           263:        off_t    file_sz;
        !           264:        int      code;
        !           265:
        !           266:        if (io_debug)
        !           267:                fprintf(stderr, ">>> SIZE %s\n", fn);
1.1       deraadt   268:
1.101   ! kmos      269:        if (fprintf(fp, "SIZE %s\r\n", fn) < 0)
        !           270:                errx(1, "%s: fprintf", __func__);
1.1       deraadt   271:
1.101   ! kmos      272:        (void)fflush(fp);
        !           273:        if ((code = ftp_getline(buf, &n, 1, fp)) != P_OK)
        !           274:                return code;
1.1       deraadt   275:
1.101   ! kmos      276:        if (sscanf(*buf, "%*u %lld", &file_sz) != 1)
        !           277:                errx(1, "%s: sscanf size", __func__);
1.1       deraadt   278:
1.101   ! kmos      279:        if (sizep)
        !           280:                *sizep = file_sz;
1.1       deraadt   281:
1.101   ! kmos      282:        return code;
1.1       deraadt   283: }
                    284:
1.101   ! kmos      285: int
        !           286: ftp_eprt(FILE *fp)
1.1       deraadt   287: {
1.101   ! kmos      288:        struct sockaddr_storage  ss;
        !           289:        char                     addr[NI_MAXHOST], port[NI_MAXSERV], *eprt;
        !           290:        socklen_t                len;
        !           291:        int                      e, on, ret, sock;
        !           292:
        !           293:        len = sizeof(ss);
        !           294:        memset(&ss, 0, len);
        !           295:        if (getsockname(fileno(fp), (struct sockaddr *)&ss, &len) == -1) {
        !           296:                warn("%s: getsockname", __func__);
        !           297:                return -1;
1.1       deraadt   298:        }
1.101   ! kmos      299:
        !           300:        /* pick a free port */
        !           301:        switch (ss.ss_family) {
        !           302:        case AF_INET:
        !           303:                ((struct sockaddr_in *)&ss)->sin_port = 0;
1.1       deraadt   304:                break;
1.101   ! kmos      305:        case AF_INET6:
        !           306:                ((struct sockaddr_in6 *)&ss)->sin6_port = 0;
1.1       deraadt   307:                break;
1.101   ! kmos      308:        default:
        !           309:                errx(1, "%s: Invalid socket family", __func__);
1.1       deraadt   310:        }
1.101   ! kmos      311:
        !           312:        if ((sock = socket(ss.ss_family, SOCK_STREAM, 0)) == -1) {
        !           313:                warn("%s: socket", __func__);
        !           314:                return -1;
1.1       deraadt   315:        }
                    316:
1.101   ! kmos      317:        switch (ss.ss_family) {
1.49      jakob     318:        case AF_INET:
                    319:                on = IP_PORTRANGE_HIGH;
1.101   ! kmos      320:                if (setsockopt(sock, IPPROTO_IP, IP_PORTRANGE,
1.49      jakob     321:                    (char *)&on, sizeof(on)) < 0)
                    322:                        warn("setsockopt IP_PORTRANGE (ignored)");
                    323:                break;
                    324:        case AF_INET6:
                    325:                on = IPV6_PORTRANGE_HIGH;
1.101   ! kmos      326:                if (setsockopt(sock, IPPROTO_IPV6, IPV6_PORTRANGE,
1.49      jakob     327:                    (char *)&on, sizeof(on)) < 0)
                    328:                        warn("setsockopt IPV6_PORTRANGE (ignored)");
                    329:                break;
                    330:        }
1.1       deraadt   331:
1.101   ! kmos      332:        if (bind(sock, (struct sockaddr *)&ss, len) == -1) {
        !           333:                close(sock);
        !           334:                warn("%s: bind", __func__);
        !           335:                return -1;
1.34      itojun    336:        }
1.1       deraadt   337:
1.101   ! kmos      338:        if (listen(sock, 1) == -1) {
        !           339:                close(sock);
        !           340:                warn("%s: listen", __func__);
        !           341:                return -1;
        !           342:        }
1.10      millert   343:
1.101   ! kmos      344:        /* Find out the ephermal port chosen */
        !           345:        len = sizeof(ss);
        !           346:        memset(&ss, 0, len);
        !           347:        if (getsockname(sock, (struct sockaddr *)&ss, &len) == -1) {
        !           348:                close(sock);
        !           349:                warn("%s: getsockname", __func__);
        !           350:                return -1;
        !           351:        }
1.1       deraadt   352:
1.101   ! kmos      353:        if ((e = getnameinfo((struct sockaddr *)&ss, len,
        !           354:            addr, sizeof(addr), port, sizeof(port),
        !           355:            NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
        !           356:                close(sock);
        !           357:                warn("%s: getnameinfo: %s", __func__, gai_strerror(e));
        !           358:                return -1;
        !           359:        }
1.1       deraadt   360:
1.101   ! kmos      361:        xasprintf(&eprt, "EPRT |%d|%s|%s|",
        !           362:            ss.ss_family == AF_INET ? 1 : 2, addr, port);
1.1       deraadt   363:
1.101   ! kmos      364:        ret = ftp_command(fp, "%s", eprt);
        !           365:        free(eprt);
        !           366:        if (ret != P_OK) {
        !           367:                close(sock);
        !           368:                return -1;
1.1       deraadt   369:        }
1.101   ! kmos      370:
        !           371:        return sock;
1.1       deraadt   372: }
                    373:
1.101   ! kmos      374: int
        !           375: ftp_epsv(FILE *fp)
1.1       deraadt   376: {
1.101   ! kmos      377:        struct sockaddr_storage  ss;
        !           378:        char                    *buf = NULL, delim[4], *s, *e;
        !           379:        size_t                   n = 0;
        !           380:        socklen_t                len;
        !           381:        int                      error, port, sock;
1.1       deraadt   382:
1.101   ! kmos      383:        if (io_debug)
        !           384:                fprintf(stderr, ">>> EPSV\n");
        !           385:
        !           386:        if (fprintf(fp, "EPSV\r\n") < 0)
        !           387:                errx(1, "%s: fprintf", __func__);
1.1       deraadt   388:
1.101   ! kmos      389:        (void)fflush(fp);
        !           390:        if (ftp_getline(&buf, &n, 1, fp) != P_OK) {
        !           391:                free(buf);
        !           392:                return -1;
1.1       deraadt   393:        }
1.101   ! kmos      394:
        !           395:        if ((s = strchr(buf, '(')) == NULL || (e = strchr(s, ')')) == NULL) {
        !           396:                warnx("Malformed EPSV reply");
        !           397:                free(buf);
        !           398:                return -1;
1.1       deraadt   399:        }
1.101   ! kmos      400:
        !           401:        s++;
        !           402:        *e = '\0';
        !           403:        if (sscanf(s, "%c%c%c%d%c", &delim[0], &delim[1], &delim[2],
        !           404:            &port, &delim[3]) != 5) {
        !           405:                warnx("EPSV parse error");
        !           406:                free(buf);
        !           407:                return -1;
1.1       deraadt   408:        }
1.101   ! kmos      409:        free(buf);
        !           410:
        !           411:        if (delim[0] != delim[1] || delim[0] != delim[2]
        !           412:            || delim[0] != delim[3]) {
        !           413:                warnx("EPSV parse error");
        !           414:                return -1;
1.1       deraadt   415:        }
1.101   ! kmos      416:
        !           417:        len = sizeof(ss);
        !           418:        memset(&ss, 0, len);
        !           419:        if (getpeername(fileno(fp), (struct sockaddr *)&ss, &len) == -1) {
        !           420:                warn("%s: getpeername", __func__);
        !           421:                return -1;
1.1       deraadt   422:        }
                    423:
1.101   ! kmos      424:        switch (ss.ss_family) {
        !           425:        case AF_INET:
        !           426:                ((struct sockaddr_in *)&ss)->sin_port = htons(port);
        !           427:                break;
        !           428:        case AF_INET6:
        !           429:                ((struct sockaddr_in6 *)&ss)->sin6_port = htons(port);
        !           430:                break;
        !           431:        default:
        !           432:                errx(1, "%s: Invalid socket family", __func__);
1.1       deraadt   433:        }
                    434:
1.101   ! kmos      435:        if ((sock = socket(ss.ss_family, SOCK_STREAM, 0)) == -1) {
        !           436:                warn("%s: socket", __func__);
        !           437:                return -1;
1.1       deraadt   438:        }
                    439:
1.101   ! kmos      440:        for (error = connect(sock, (struct sockaddr *)&ss, len);
        !           441:             error != 0 && errno == EINTR; error = connect_wait(sock))
        !           442:                continue;
1.94      krw       443:
1.101   ! kmos      444:        if (error != 0) {
        !           445:                warn("%s: connect", __func__);
        !           446:                return -1;
1.13      millert   447:        }
1.1       deraadt   448:
1.101   ! kmos      449:        return sock;
1.1       deraadt   450: }