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