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

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