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