1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #ifndef CURL_DISABLE_FTP
28
29 #ifdef HAVE_NETINET_IN_H
30 #include <netinet/in.h>
31 #endif
32 #ifdef HAVE_ARPA_INET_H
33 #include <arpa/inet.h>
34 #endif
35 #ifdef HAVE_NETDB_H
36 #include <netdb.h>
37 #endif
38 #ifdef __VMS
39 #include <in.h>
40 #include <inet.h>
41 #endif
42
43 #include <curl/curl.h>
44 #include "urldata.h"
45 #include "sendf.h"
46 #include "if2ip.h"
47 #include "hostip.h"
48 #include "progress.h"
49 #include "transfer.h"
50 #include "escape.h"
51 #include "http.h" /* for HTTP proxy tunnel stuff */
52 #include "ftp.h"
53 #include "fileinfo.h"
54 #include "ftplistparser.h"
55 #include "curl_range.h"
56 #include "curl_krb5.h"
57 #include "strtoofft.h"
58 #include "strcase.h"
59 #include "vtls/vtls.h"
60 #include "cfilters.h"
61 #include "cf-socket.h"
62 #include "connect.h"
63 #include "strerror.h"
64 #include "inet_ntop.h"
65 #include "inet_pton.h"
66 #include "select.h"
67 #include "parsedate.h" /* for the week day and month names */
68 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
69 #include "multiif.h"
70 #include "url.h"
71 #include "speedcheck.h"
72 #include "warnless.h"
73 #include "http_proxy.h"
74 #include "socks.h"
75 #include "strdup.h"
76 /* The last 3 #include files should be in this order */
77 #include "curl_printf.h"
78 #include "curl_memory.h"
79 #include "memdebug.h"
80
81 #ifndef NI_MAXHOST
82 #define NI_MAXHOST 1025
83 #endif
84 #ifndef INET_ADDRSTRLEN
85 #define INET_ADDRSTRLEN 16
86 #endif
87
88 /* macro to check for a three-digit ftp status code at the start of the
89 given string */
90 #define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
91 ISDIGIT(line[2]))
92
93 /* macro to check for the last line in an FTP server response */
94 #define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
95
96 #ifdef CURL_DISABLE_VERBOSE_STRINGS
97 #define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
98 #define FTP_CSTATE(c) ""
99 #define FTP_DSTATE(d) ""
100 #else /* CURL_DISABLE_VERBOSE_STRINGS */
101 /* for tracing purposes */
102 static const char * const ftp_state_names[]={
103 "STOP",
104 "WAIT220",
105 "AUTH",
106 "USER",
107 "PASS",
108 "ACCT",
109 "PBSZ",
110 "PROT",
111 "CCC",
112 "PWD",
113 "SYST",
114 "NAMEFMT",
115 "QUOTE",
116 "RETR_PREQUOTE",
117 "STOR_PREQUOTE",
118 "POSTQUOTE",
119 "CWD",
120 "MKD",
121 "MDTM",
122 "TYPE",
123 "LIST_TYPE",
124 "RETR_TYPE",
125 "STOR_TYPE",
126 "SIZE",
127 "RETR_SIZE",
128 "STOR_SIZE",
129 "REST",
130 "RETR_REST",
131 "PORT",
132 "PRET",
133 "PASV",
134 "LIST",
135 "RETR",
136 "STOR",
137 "QUIT"
138 };
139 #define FTP_CSTATE(c) ((c)? ftp_state_names[(c)->proto.ftpc.state] : "???")
140 #define FTP_DSTATE(d) (((d) && (d)->conn)? \
141 ftp_state_names[(d)->conn->proto.ftpc.state] : "???")
142
143 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
144
145 /* This is the ONLY way to change FTP state! */
_ftp_state(struct Curl_easy * data,ftpstate newstate,int lineno)146 static void _ftp_state(struct Curl_easy *data,
147 ftpstate newstate
148 #ifdef DEBUGBUILD
149 , int lineno
150 #endif
151 )
152 {
153 struct connectdata *conn = data->conn;
154 struct ftp_conn *ftpc = &conn->proto.ftpc;
155
156 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
157 #ifdef DEBUGBUILD
158 (void)lineno;
159 #endif
160 #else /* CURL_DISABLE_VERBOSE_STRINGS */
161 if(ftpc->state != newstate)
162 #ifdef DEBUGBUILD
163 CURL_TRC_FTP(data, "[%s] -> [%s] (line %d)", FTP_DSTATE(data),
164 ftp_state_names[newstate], lineno);
165 #else
166 CURL_TRC_FTP(data, "[%s] -> [%s]", FTP_DSTATE(data),
167 ftp_state_names[newstate]);
168 #endif
169 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
170
171 ftpc->state = newstate;
172 }
173
174
175 /* Local API functions */
176 #ifndef DEBUGBUILD
177 #define ftp_state(x,y) _ftp_state(x,y)
178 #else /* !DEBUGBUILD */
179 #define ftp_state(x,y) _ftp_state(x,y,__LINE__)
180 #endif /* DEBUGBUILD */
181
182 static CURLcode ftp_sendquote(struct Curl_easy *data,
183 struct connectdata *conn,
184 struct curl_slist *quote);
185 static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn);
186 static CURLcode ftp_parse_url_path(struct Curl_easy *data);
187 static CURLcode ftp_regular_transfer(struct Curl_easy *data, bool *done);
188 #ifndef CURL_DISABLE_VERBOSE_STRINGS
189 static void ftp_pasv_verbose(struct Curl_easy *data,
190 struct Curl_addrinfo *ai,
191 char *newhost, /* ASCII version */
192 int port);
193 #endif
194 static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data);
195 static CURLcode ftp_state_mdtm(struct Curl_easy *data);
196 static CURLcode ftp_state_quote(struct Curl_easy *data,
197 bool init, ftpstate instate);
198 static CURLcode ftp_nb_type(struct Curl_easy *data,
199 struct connectdata *conn,
200 bool ascii, ftpstate newstate);
201 static int ftp_need_type(struct connectdata *conn,
202 bool ascii);
203 static CURLcode ftp_do(struct Curl_easy *data, bool *done);
204 static CURLcode ftp_done(struct Curl_easy *data,
205 CURLcode, bool premature);
206 static CURLcode ftp_connect(struct Curl_easy *data, bool *done);
207 static CURLcode ftp_disconnect(struct Curl_easy *data,
208 struct connectdata *conn, bool dead_connection);
209 static CURLcode ftp_do_more(struct Curl_easy *data, int *completed);
210 static CURLcode ftp_multi_statemach(struct Curl_easy *data, bool *done);
211 static int ftp_getsock(struct Curl_easy *data, struct connectdata *conn,
212 curl_socket_t *socks);
213 static int ftp_domore_getsock(struct Curl_easy *data,
214 struct connectdata *conn, curl_socket_t *socks);
215 static CURLcode ftp_doing(struct Curl_easy *data,
216 bool *dophase_done);
217 static CURLcode ftp_setup_connection(struct Curl_easy *data,
218 struct connectdata *conn);
219 static CURLcode init_wc_data(struct Curl_easy *data);
220 static CURLcode wc_statemach(struct Curl_easy *data);
221 static void wc_data_dtor(void *ptr);
222 static CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize);
223 static CURLcode ftp_readresp(struct Curl_easy *data,
224 int sockindex,
225 struct pingpong *pp,
226 int *ftpcode,
227 size_t *size);
228 static CURLcode ftp_dophase_done(struct Curl_easy *data,
229 bool connected);
230
231 /*
232 * FTP protocol handler.
233 */
234
235 const struct Curl_handler Curl_handler_ftp = {
236 "ftp", /* scheme */
237 ftp_setup_connection, /* setup_connection */
238 ftp_do, /* do_it */
239 ftp_done, /* done */
240 ftp_do_more, /* do_more */
241 ftp_connect, /* connect_it */
242 ftp_multi_statemach, /* connecting */
243 ftp_doing, /* doing */
244 ftp_getsock, /* proto_getsock */
245 ftp_getsock, /* doing_getsock */
246 ftp_domore_getsock, /* domore_getsock */
247 ZERO_NULL, /* perform_getsock */
248 ftp_disconnect, /* disconnect */
249 ZERO_NULL, /* write_resp */
250 ZERO_NULL, /* write_resp_hd */
251 ZERO_NULL, /* connection_check */
252 ZERO_NULL, /* attach connection */
253 ZERO_NULL, /* follow */
254 PORT_FTP, /* defport */
255 CURLPROTO_FTP, /* protocol */
256 CURLPROTO_FTP, /* family */
257 PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD |
258 PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP |
259 PROTOPT_WILDCARD /* flags */
260 };
261
262
263 #ifdef USE_SSL
264 /*
265 * FTPS protocol handler.
266 */
267
268 const struct Curl_handler Curl_handler_ftps = {
269 "ftps", /* scheme */
270 ftp_setup_connection, /* setup_connection */
271 ftp_do, /* do_it */
272 ftp_done, /* done */
273 ftp_do_more, /* do_more */
274 ftp_connect, /* connect_it */
275 ftp_multi_statemach, /* connecting */
276 ftp_doing, /* doing */
277 ftp_getsock, /* proto_getsock */
278 ftp_getsock, /* doing_getsock */
279 ftp_domore_getsock, /* domore_getsock */
280 ZERO_NULL, /* perform_getsock */
281 ftp_disconnect, /* disconnect */
282 ZERO_NULL, /* write_resp */
283 ZERO_NULL, /* write_resp_hd */
284 ZERO_NULL, /* connection_check */
285 ZERO_NULL, /* attach connection */
286 ZERO_NULL, /* follow */
287 PORT_FTPS, /* defport */
288 CURLPROTO_FTPS, /* protocol */
289 CURLPROTO_FTP, /* family */
290 PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
291 PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD /* flags */
292 };
293 #endif
294
close_secondarysocket(struct Curl_easy * data)295 static void close_secondarysocket(struct Curl_easy *data)
296 {
297 CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_DSTATE(data));
298 Curl_conn_close(data, SECONDARYSOCKET);
299 Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET);
300 }
301
302 /*
303 * NOTE: back in the old days, we added code in the FTP code that made NOBODY
304 * requests on files respond with headers passed to the client/stdout that
305 * looked like HTTP ones.
306 *
307 * This approach is not elegant, it causes confusion and is error-prone. It is
308 * subject for removal at the next (or at least a future) soname bump. Until
309 * then you can test the effects of the removal by undefining the following
310 * define named CURL_FTP_HTTPSTYLE_HEAD.
311 */
312 #define CURL_FTP_HTTPSTYLE_HEAD 1
313
freedirs(struct ftp_conn * ftpc)314 static void freedirs(struct ftp_conn *ftpc)
315 {
316 if(ftpc->dirs) {
317 int i;
318 for(i = 0; i < ftpc->dirdepth; i++) {
319 free(ftpc->dirs[i]);
320 ftpc->dirs[i] = NULL;
321 }
322 free(ftpc->dirs);
323 ftpc->dirs = NULL;
324 ftpc->dirdepth = 0;
325 }
326 Curl_safefree(ftpc->file);
327
328 /* no longer of any use */
329 Curl_safefree(ftpc->newhost);
330 }
331
332 #ifdef CURL_PREFER_LF_LINEENDS
333 /***********************************************************************
334 *
335 * Lineend Conversions
336 * On ASCII transfers, e.g. directory listings, we might get lines
337 * ending in '\r\n' and we prefer just '\n'.
338 * We might also get a lonely '\r' which we convert into a '\n'.
339 */
340 struct ftp_cw_lc_ctx {
341 struct Curl_cwriter super;
342 bool newline_pending;
343 };
344
ftp_cw_lc_write(struct Curl_easy * data,struct Curl_cwriter * writer,int type,const char * buf,size_t blen)345 static CURLcode ftp_cw_lc_write(struct Curl_easy *data,
346 struct Curl_cwriter *writer, int type,
347 const char *buf, size_t blen)
348 {
349 static const char nl = '\n';
350 struct ftp_cw_lc_ctx *ctx = writer->ctx;
351
352 if(!(type & CLIENTWRITE_BODY) ||
353 data->conn->proto.ftpc.transfertype != 'A')
354 return Curl_cwriter_write(data, writer->next, type, buf, blen);
355
356 /* ASCII mode BODY data, convert lineends */
357 while(blen) {
358 /* do not pass EOS when writing parts */
359 int chunk_type = (type & ~CLIENTWRITE_EOS);
360 const char *cp;
361 size_t chunk_len;
362 CURLcode result;
363
364 if(ctx->newline_pending) {
365 if(buf[0] != '\n') {
366 /* previous chunk ended in '\r' and we do not see a '\n' in this one,
367 * need to write a newline. */
368 result = Curl_cwriter_write(data, writer->next, chunk_type, &nl, 1);
369 if(result)
370 return result;
371 }
372 /* either we just wrote the newline or it is part of the next
373 * chunk of bytes we write. */
374 ctx->newline_pending = FALSE;
375 }
376
377 cp = memchr(buf, '\r', blen);
378 if(!cp)
379 break;
380
381 /* write the bytes before the '\r', excluding the '\r' */
382 chunk_len = cp - buf;
383 if(chunk_len) {
384 result = Curl_cwriter_write(data, writer->next, chunk_type,
385 buf, chunk_len);
386 if(result)
387 return result;
388 }
389 /* skip the '\r', we now have a newline pending */
390 buf = cp + 1;
391 blen = blen - chunk_len - 1;
392 ctx->newline_pending = TRUE;
393 }
394
395 /* Any remaining data does not contain a '\r' */
396 if(blen) {
397 DEBUGASSERT(!ctx->newline_pending);
398 return Curl_cwriter_write(data, writer->next, type, buf, blen);
399 }
400 else if(type & CLIENTWRITE_EOS) {
401 /* EndOfStream, if we have a trailing cr, now is the time to write it */
402 if(ctx->newline_pending) {
403 ctx->newline_pending = FALSE;
404 return Curl_cwriter_write(data, writer->next, type, &nl, 1);
405 }
406 /* Always pass on the EOS type indicator */
407 return Curl_cwriter_write(data, writer->next, type, buf, 0);
408 }
409 return CURLE_OK;
410 }
411
412 static const struct Curl_cwtype ftp_cw_lc = {
413 "ftp-lineconv",
414 NULL,
415 Curl_cwriter_def_init,
416 ftp_cw_lc_write,
417 Curl_cwriter_def_close,
418 sizeof(struct ftp_cw_lc_ctx)
419 };
420
421 #endif /* CURL_PREFER_LF_LINEENDS */
422 /***********************************************************************
423 *
424 * ftp_check_ctrl_on_data_wait()
425 *
426 */
ftp_check_ctrl_on_data_wait(struct Curl_easy * data)427 static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data)
428 {
429 struct connectdata *conn = data->conn;
430 curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
431 struct ftp_conn *ftpc = &conn->proto.ftpc;
432 struct pingpong *pp = &ftpc->pp;
433 ssize_t nread;
434 int ftpcode;
435 bool response = FALSE;
436
437 /* First check whether there is a cached response from server */
438 if(Curl_dyn_len(&pp->recvbuf) && (*Curl_dyn_ptr(&pp->recvbuf) > '3')) {
439 /* Data connection could not be established, let's return */
440 infof(data, "There is negative response in cache while serv connect");
441 (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
442 return CURLE_FTP_ACCEPT_FAILED;
443 }
444
445 if(pp->overflow)
446 /* there is pending control data still in the buffer to read */
447 response = TRUE;
448 else {
449 int socketstate = Curl_socket_check(ctrl_sock, CURL_SOCKET_BAD,
450 CURL_SOCKET_BAD, 0);
451 /* see if the connection request is already here */
452 switch(socketstate) {
453 case -1: /* error */
454 /* let's die here */
455 failf(data, "Error while waiting for server connect");
456 return CURLE_FTP_ACCEPT_FAILED;
457 default:
458 if(socketstate & CURL_CSELECT_IN)
459 response = TRUE;
460 break;
461 }
462 }
463
464 if(response) {
465 infof(data, "Ctrl conn has data while waiting for data conn");
466 if(pp->overflow > 3) {
467 char *r = Curl_dyn_ptr(&pp->recvbuf);
468
469 DEBUGASSERT((pp->overflow + pp->nfinal) <=
470 Curl_dyn_len(&pp->recvbuf));
471 /* move over the most recently handled response line */
472 r += pp->nfinal;
473
474 if(LASTLINE(r)) {
475 int status = curlx_sltosi(strtol(r, NULL, 10));
476 if(status == 226) {
477 /* funny timing situation where we get the final message on the
478 control connection before traffic on the data connection has been
479 noticed. Leave the 226 in there and use this as a trigger to read
480 the data socket. */
481 infof(data, "Got 226 before data activity");
482 return CURLE_OK;
483 }
484 }
485 }
486
487 (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
488
489 infof(data, "FTP code: %03d", ftpcode);
490
491 if(ftpcode/100 > 3)
492 return CURLE_FTP_ACCEPT_FAILED;
493
494 return CURLE_WEIRD_SERVER_REPLY;
495 }
496
497 return CURLE_OK;
498 }
499
500 /***********************************************************************
501 *
502 * InitiateTransfer()
503 *
504 * After connection from server is accepted this function is called to
505 * setup transfer parameters and initiate the data transfer.
506 *
507 */
InitiateTransfer(struct Curl_easy * data)508 static CURLcode InitiateTransfer(struct Curl_easy *data)
509 {
510 CURLcode result = CURLE_OK;
511 struct connectdata *conn = data->conn;
512 bool connected;
513
514 CURL_TRC_FTP(data, "InitiateTransfer()");
515 result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected);
516 if(result || !connected)
517 return result;
518
519 if(conn->proto.ftpc.state_saved == FTP_STOR) {
520 /* When we know we are uploading a specified file, we can get the file
521 size prior to the actual upload. */
522 Curl_pgrsSetUploadSize(data, data->state.infilesize);
523
524 /* set the SO_SNDBUF for the secondary socket for those who need it */
525 Curl_sndbuf_init(conn->sock[SECONDARYSOCKET]);
526
527 /* FTP upload, shutdown DATA, ignore shutdown errors, as we rely
528 * on the server response on the CONTROL connection. */
529 Curl_xfer_setup2(data, CURL_XFER_SEND, -1, TRUE, TRUE);
530 }
531 else {
532 /* FTP download, shutdown, do not ignore errors */
533 Curl_xfer_setup2(data, CURL_XFER_RECV,
534 conn->proto.ftpc.retr_size_saved, TRUE, FALSE);
535 }
536
537 conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
538 ftp_state(data, FTP_STOP);
539
540 return CURLE_OK;
541 }
542
ftp_endofresp(struct Curl_easy * data,struct connectdata * conn,char * line,size_t len,int * code)543 static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn,
544 char *line, size_t len, int *code)
545 {
546 (void)data;
547 (void)conn;
548
549 if((len > 3) && LASTLINE(line)) {
550 *code = curlx_sltosi(strtol(line, NULL, 10));
551 return TRUE;
552 }
553
554 return FALSE;
555 }
556
ftp_readresp(struct Curl_easy * data,int sockindex,struct pingpong * pp,int * ftpcode,size_t * size)557 static CURLcode ftp_readresp(struct Curl_easy *data,
558 int sockindex,
559 struct pingpong *pp,
560 int *ftpcode, /* return the ftp-code if done */
561 size_t *size) /* size of the response */
562 {
563 int code;
564 CURLcode result = Curl_pp_readresp(data, sockindex, pp, &code, size);
565
566 #ifdef HAVE_GSSAPI
567 {
568 struct connectdata *conn = data->conn;
569 char * const buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
570
571 /* handle the security-oriented responses 6xx ***/
572 switch(code) {
573 case 631:
574 code = Curl_sec_read_msg(data, conn, buf, PROT_SAFE);
575 break;
576 case 632:
577 code = Curl_sec_read_msg(data, conn, buf, PROT_PRIVATE);
578 break;
579 case 633:
580 code = Curl_sec_read_msg(data, conn, buf, PROT_CONFIDENTIAL);
581 break;
582 default:
583 /* normal ftp stuff we pass through! */
584 break;
585 }
586 }
587 #endif
588
589 /* store the latest code for later retrieval */
590 data->info.httpcode = code;
591
592 if(ftpcode)
593 *ftpcode = code;
594
595 if(421 == code) {
596 /* 421 means "Service not available, closing control connection." and FTP
597 * servers use it to signal that idle session timeout has been exceeded.
598 * If we ignored the response, it could end up hanging in some cases.
599 *
600 * This response code can come at any point so having it treated
601 * generically is a good idea.
602 */
603 infof(data, "We got a 421 - timeout");
604 ftp_state(data, FTP_STOP);
605 return CURLE_OPERATION_TIMEDOUT;
606 }
607
608 return result;
609 }
610
611 /* --- parse FTP server responses --- */
612
613 /*
614 * Curl_GetFTPResponse() is a BLOCKING function to read the full response
615 * from a server after a command.
616 *
617 */
618
Curl_GetFTPResponse(struct Curl_easy * data,ssize_t * nreadp,int * ftpcode)619 CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
620 ssize_t *nreadp, /* return number of bytes read */
621 int *ftpcode) /* return the ftp-code */
622 {
623 /*
624 * We cannot read just one byte per read() and then go back to select() as
625 * the OpenSSL read() does not grok that properly.
626 *
627 * Alas, read as much as possible, split up into lines, use the ending
628 * line in a response or continue reading. */
629
630 struct connectdata *conn = data->conn;
631 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
632 CURLcode result = CURLE_OK;
633 struct ftp_conn *ftpc = &conn->proto.ftpc;
634 struct pingpong *pp = &ftpc->pp;
635 size_t nread;
636 int cache_skip = 0;
637 int value_to_be_ignored = 0;
638
639 CURL_TRC_FTP(data, "getFTPResponse start");
640
641 if(ftpcode)
642 *ftpcode = 0; /* 0 for errors */
643 else
644 /* make the pointer point to something for the rest of this function */
645 ftpcode = &value_to_be_ignored;
646
647 *nreadp = 0;
648
649 while(!*ftpcode && !result) {
650 /* check and reset timeout value every lap */
651 timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE);
652 timediff_t interval_ms;
653
654 if(timeout <= 0) {
655 failf(data, "FTP response timeout");
656 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
657 }
658
659 interval_ms = 1000; /* use 1 second timeout intervals */
660 if(timeout < interval_ms)
661 interval_ms = timeout;
662
663 /*
664 * Since this function is blocking, we need to wait here for input on the
665 * connection and only then we call the response reading function. We do
666 * timeout at least every second to make the timeout check run.
667 *
668 * A caution here is that the ftp_readresp() function has a cache that may
669 * contain pieces of a response from the previous invoke and we need to
670 * make sure we do not just wait for input while there is unhandled data in
671 * that cache. But also, if the cache is there, we call ftp_readresp() and
672 * the cache was not good enough to continue we must not just busy-loop
673 * around this function.
674 *
675 */
676
677 if(Curl_dyn_len(&pp->recvbuf) && (cache_skip < 2)) {
678 /*
679 * There is a cache left since before. We then skipping the wait for
680 * socket action, unless this is the same cache like the previous round
681 * as then the cache was deemed not enough to act on and we then need to
682 * wait for more data anyway.
683 */
684 }
685 else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) {
686 curl_socket_t wsock = Curl_pp_needs_flush(data, pp) ?
687 sockfd : CURL_SOCKET_BAD;
688 int ev = Curl_socket_check(sockfd, CURL_SOCKET_BAD, wsock, interval_ms);
689 if(ev < 0) {
690 failf(data, "FTP response aborted due to select/poll error: %d",
691 SOCKERRNO);
692 return CURLE_RECV_ERROR;
693 }
694 else if(ev == 0) {
695 if(Curl_pgrsUpdate(data))
696 return CURLE_ABORTED_BY_CALLBACK;
697 continue; /* just continue in our loop for the timeout duration */
698 }
699 }
700
701 if(Curl_pp_needs_flush(data, pp)) {
702 result = Curl_pp_flushsend(data, pp);
703 if(result)
704 break;
705 }
706
707 result = ftp_readresp(data, FIRSTSOCKET, pp, ftpcode, &nread);
708 if(result)
709 break;
710
711 if(!nread && Curl_dyn_len(&pp->recvbuf))
712 /* bump cache skip counter as on repeated skips we must wait for more
713 data */
714 cache_skip++;
715 else
716 /* when we got data or there is no cache left, we reset the cache skip
717 counter */
718 cache_skip = 0;
719
720 *nreadp += nread;
721
722 } /* while there is buffer left and loop is requested */
723
724 pp->pending_resp = FALSE;
725 CURL_TRC_FTP(data, "getFTPResponse -> result=%d, nread=%zd, ftpcode=%d",
726 result, *nreadp, *ftpcode);
727
728 return result;
729 }
730
ftp_state_user(struct Curl_easy * data,struct connectdata * conn)731 static CURLcode ftp_state_user(struct Curl_easy *data,
732 struct connectdata *conn)
733 {
734 CURLcode result = Curl_pp_sendf(data,
735 &conn->proto.ftpc.pp, "USER %s",
736 conn->user ? conn->user : "");
737 if(!result) {
738 struct ftp_conn *ftpc = &conn->proto.ftpc;
739 ftpc->ftp_trying_alternative = FALSE;
740 ftp_state(data, FTP_USER);
741 }
742 return result;
743 }
744
ftp_state_pwd(struct Curl_easy * data,struct connectdata * conn)745 static CURLcode ftp_state_pwd(struct Curl_easy *data,
746 struct connectdata *conn)
747 {
748 CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD");
749 if(!result)
750 ftp_state(data, FTP_PWD);
751
752 return result;
753 }
754
755 /* For the FTP "protocol connect" and "doing" phases only */
ftp_getsock(struct Curl_easy * data,struct connectdata * conn,curl_socket_t * socks)756 static int ftp_getsock(struct Curl_easy *data,
757 struct connectdata *conn,
758 curl_socket_t *socks)
759 {
760 return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
761 }
762
763 /* For the FTP "DO_MORE" phase only */
ftp_domore_getsock(struct Curl_easy * data,struct connectdata * conn,curl_socket_t * socks)764 static int ftp_domore_getsock(struct Curl_easy *data,
765 struct connectdata *conn, curl_socket_t *socks)
766 {
767 struct ftp_conn *ftpc = &conn->proto.ftpc;
768 (void)data;
769
770 /* When in DO_MORE state, we could be either waiting for us to connect to a
771 * remote site, or we could wait for that site to connect to us. Or just
772 * handle ordinary commands.
773 */
774 CURL_TRC_FTP(data, "[%s] ftp_domore_getsock()", FTP_DSTATE(data));
775
776 if(FTP_STOP == ftpc->state) {
777 /* if stopped and still in this state, then we are also waiting for a
778 connect on the secondary connection */
779 DEBUGASSERT(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD ||
780 (conn->cfilter[SECONDARYSOCKET] &&
781 !Curl_conn_is_connected(conn, SECONDARYSOCKET)));
782 socks[0] = conn->sock[FIRSTSOCKET];
783 /* An unconnected SECONDARY will add its socket by itself
784 * via its adjust_pollset() */
785 return GETSOCK_READSOCK(0);
786 }
787 return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
788 }
789
790 /* This is called after the FTP_QUOTE state is passed.
791
792 ftp_state_cwd() sends the range of CWD commands to the server to change to
793 the correct directory. It may also need to send MKD commands to create
794 missing ones, if that option is enabled.
795 */
ftp_state_cwd(struct Curl_easy * data,struct connectdata * conn)796 static CURLcode ftp_state_cwd(struct Curl_easy *data,
797 struct connectdata *conn)
798 {
799 CURLcode result = CURLE_OK;
800 struct ftp_conn *ftpc = &conn->proto.ftpc;
801
802 if(ftpc->cwddone)
803 /* already done and fine */
804 result = ftp_state_mdtm(data);
805 else {
806 /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */
807 DEBUGASSERT((data->set.ftp_filemethod != FTPFILE_NOCWD) ||
808 !(ftpc->dirdepth && ftpc->dirs[0][0] == '/'));
809
810 ftpc->count2 = 0; /* count2 counts failed CWDs */
811
812 if(conn->bits.reuse && ftpc->entrypath &&
813 /* no need to go to entrypath when we have an absolute path */
814 !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) {
815 /* This is a reused connection. Since we change directory to where the
816 transfer is taking place, we must first get back to the original dir
817 where we ended up after login: */
818 ftpc->cwdcount = 0; /* we count this as the first path, then we add one
819 for all upcoming ones in the ftp->dirs[] array */
820 result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath);
821 if(!result)
822 ftp_state(data, FTP_CWD);
823 }
824 else {
825 if(ftpc->dirdepth) {
826 ftpc->cwdcount = 1;
827 /* issue the first CWD, the rest is sent when the CWD responses are
828 received... */
829 result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
830 ftpc->dirs[ftpc->cwdcount -1]);
831 if(!result)
832 ftp_state(data, FTP_CWD);
833 }
834 else {
835 /* No CWD necessary */
836 result = ftp_state_mdtm(data);
837 }
838 }
839 }
840 return result;
841 }
842
843 typedef enum {
844 EPRT,
845 PORT,
846 DONE
847 } ftpport;
848
ftp_state_use_port(struct Curl_easy * data,ftpport fcmd)849 static CURLcode ftp_state_use_port(struct Curl_easy *data,
850 ftpport fcmd) /* start with this */
851 {
852 CURLcode result = CURLE_FTP_PORT_FAILED;
853 struct connectdata *conn = data->conn;
854 struct ftp_conn *ftpc = &conn->proto.ftpc;
855 curl_socket_t portsock = CURL_SOCKET_BAD;
856 char myhost[MAX_IPADR_LEN + 1] = "";
857
858 struct Curl_sockaddr_storage ss;
859 struct Curl_addrinfo *res, *ai;
860 curl_socklen_t sslen;
861 char hbuf[NI_MAXHOST];
862 struct sockaddr *sa = (struct sockaddr *)&ss;
863 struct sockaddr_in * const sa4 = (void *)sa;
864 #ifdef USE_IPV6
865 struct sockaddr_in6 * const sa6 = (void *)sa;
866 #endif
867 static const char mode[][5] = { "EPRT", "PORT" };
868 enum resolve_t rc;
869 int error;
870 char *host = NULL;
871 char *string_ftpport = data->set.str[STRING_FTPPORT];
872 struct Curl_dns_entry *dns_entry = NULL;
873 unsigned short port_min = 0;
874 unsigned short port_max = 0;
875 unsigned short port;
876 bool possibly_non_local = TRUE;
877 char buffer[STRERROR_LEN];
878 char *addr = NULL;
879 size_t addrlen = 0;
880 char ipstr[50];
881
882 /* Step 1, figure out what is requested,
883 * accepted format :
884 * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
885 */
886
887 if(data->set.str[STRING_FTPPORT] &&
888 (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
889 char *ip_end = NULL;
890
891 #ifdef USE_IPV6
892 if(*string_ftpport == '[') {
893 /* [ipv6]:port(-range) */
894 char *ip_start = string_ftpport + 1;
895 ip_end = strchr(ip_start, ']');
896 if(ip_end) {
897 addrlen = ip_end - ip_start;
898 addr = ip_start;
899 }
900 }
901 else
902 #endif
903 if(*string_ftpport == ':') {
904 /* :port */
905 ip_end = string_ftpport;
906 }
907 else {
908 ip_end = strchr(string_ftpport, ':');
909 addr = string_ftpport;
910 if(ip_end) {
911 /* either ipv6 or (ipv4|domain|interface):port(-range) */
912 addrlen = ip_end - string_ftpport;
913 #ifdef USE_IPV6
914 if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
915 /* ipv6 */
916 port_min = port_max = 0;
917 ip_end = NULL; /* this got no port ! */
918 }
919 #endif
920 }
921 else
922 /* ipv4|interface */
923 addrlen = strlen(string_ftpport);
924 }
925
926 /* parse the port */
927 if(ip_end) {
928 char *port_sep = NULL;
929 char *port_start = strchr(ip_end, ':');
930 if(port_start) {
931 port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10));
932 port_sep = strchr(port_start, '-');
933 if(port_sep) {
934 port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
935 }
936 else
937 port_max = port_min;
938 }
939 }
940
941 /* correct errors like:
942 * :1234-1230
943 * :-4711, in this case port_min is (unsigned)-1,
944 * therefore port_min > port_max for all cases
945 * but port_max = (unsigned)-1
946 */
947 if(port_min > port_max)
948 port_min = port_max = 0;
949
950 if(addrlen) {
951 DEBUGASSERT(addr);
952 if(addrlen >= sizeof(ipstr))
953 goto out;
954 memcpy(ipstr, addr, addrlen);
955 ipstr[addrlen] = 0;
956
957 /* attempt to get the address of the given interface name */
958 switch(Curl_if2ip(conn->remote_addr->family,
959 #ifdef USE_IPV6
960 Curl_ipv6_scope(&conn->remote_addr->curl_sa_addr),
961 conn->scope_id,
962 #endif
963 ipstr, hbuf, sizeof(hbuf))) {
964 case IF2IP_NOT_FOUND:
965 /* not an interface, use the given string as hostname instead */
966 host = ipstr;
967 break;
968 case IF2IP_AF_NOT_SUPPORTED:
969 goto out;
970 case IF2IP_FOUND:
971 host = hbuf; /* use the hbuf for hostname */
972 break;
973 }
974 }
975 else
976 /* there was only a port(-range) given, default the host */
977 host = NULL;
978 } /* data->set.ftpport */
979
980 if(!host) {
981 const char *r;
982 /* not an interface and not a hostname, get default by extracting
983 the IP from the control connection */
984 sslen = sizeof(ss);
985 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
986 failf(data, "getsockname() failed: %s",
987 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
988 goto out;
989 }
990 switch(sa->sa_family) {
991 #ifdef USE_IPV6
992 case AF_INET6:
993 r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
994 break;
995 #endif
996 default:
997 r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
998 break;
999 }
1000 if(!r) {
1001 goto out;
1002 }
1003 host = hbuf; /* use this hostname */
1004 possibly_non_local = FALSE; /* we know it is local now */
1005 }
1006
1007 /* resolv ip/host to ip */
1008 rc = Curl_resolv(data, host, 0, FALSE, &dns_entry);
1009 if(rc == CURLRESOLV_PENDING)
1010 (void)Curl_resolver_wait_resolv(data, &dns_entry);
1011 if(dns_entry) {
1012 res = dns_entry->addr;
1013 }
1014 else
1015 res = NULL; /* failure! */
1016
1017 if(!res) {
1018 failf(data, "failed to resolve the address provided to PORT: %s", host);
1019 goto out;
1020 }
1021
1022 host = NULL;
1023
1024 /* step 2, create a socket for the requested address */
1025 error = 0;
1026 for(ai = res; ai; ai = ai->ai_next) {
1027 if(Curl_socket_open(data, ai, NULL, conn->transport, &portsock)) {
1028 error = SOCKERRNO;
1029 continue;
1030 }
1031 break;
1032 }
1033 if(!ai) {
1034 failf(data, "socket failure: %s",
1035 Curl_strerror(error, buffer, sizeof(buffer)));
1036 goto out;
1037 }
1038 CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket",
1039 FTP_DSTATE(data));
1040
1041 /* step 3, bind to a suitable local address */
1042
1043 memcpy(sa, ai->ai_addr, ai->ai_addrlen);
1044 sslen = ai->ai_addrlen;
1045
1046 for(port = port_min; port <= port_max;) {
1047 if(sa->sa_family == AF_INET)
1048 sa4->sin_port = htons(port);
1049 #ifdef USE_IPV6
1050 else
1051 sa6->sin6_port = htons(port);
1052 #endif
1053 /* Try binding the given address. */
1054 if(bind(portsock, sa, sslen) ) {
1055 /* It failed. */
1056 error = SOCKERRNO;
1057 if(possibly_non_local && (error == EADDRNOTAVAIL)) {
1058 /* The requested bind address is not local. Use the address used for
1059 * the control connection instead and restart the port loop
1060 */
1061 infof(data, "bind(port=%hu) on non-local address failed: %s", port,
1062 Curl_strerror(error, buffer, sizeof(buffer)));
1063
1064 sslen = sizeof(ss);
1065 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1066 failf(data, "getsockname() failed: %s",
1067 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1068 goto out;
1069 }
1070 port = port_min;
1071 possibly_non_local = FALSE; /* do not try this again */
1072 continue;
1073 }
1074 if(error != EADDRINUSE && error != EACCES) {
1075 failf(data, "bind(port=%hu) failed: %s", port,
1076 Curl_strerror(error, buffer, sizeof(buffer)));
1077 goto out;
1078 }
1079 }
1080 else
1081 break;
1082
1083 port++;
1084 }
1085
1086 /* maybe all ports were in use already */
1087 if(port > port_max) {
1088 failf(data, "bind() failed, we ran out of ports");
1089 goto out;
1090 }
1091
1092 /* get the name again after the bind() so that we can extract the
1093 port number it uses now */
1094 sslen = sizeof(ss);
1095 if(getsockname(portsock, sa, &sslen)) {
1096 failf(data, "getsockname() failed: %s",
1097 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1098 goto out;
1099 }
1100 CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), socket bound to port %d",
1101 FTP_DSTATE(data), port);
1102
1103 /* step 4, listen on the socket */
1104
1105 if(listen(portsock, 1)) {
1106 failf(data, "socket failure: %s",
1107 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1108 goto out;
1109 }
1110 CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), listening on %d",
1111 FTP_DSTATE(data), port);
1112
1113 /* step 5, send the proper FTP command */
1114
1115 /* get a plain printable version of the numerical address to work with
1116 below */
1117 Curl_printable_address(ai, myhost, sizeof(myhost));
1118
1119 #ifdef USE_IPV6
1120 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
1121 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
1122 request and enable EPRT again! */
1123 conn->bits.ftp_use_eprt = TRUE;
1124 #endif
1125
1126 for(; fcmd != DONE; fcmd++) {
1127
1128 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
1129 /* if disabled, goto next */
1130 continue;
1131
1132 if((PORT == fcmd) && sa->sa_family != AF_INET)
1133 /* PORT is IPv4 only */
1134 continue;
1135
1136 switch(sa->sa_family) {
1137 case AF_INET:
1138 port = ntohs(sa4->sin_port);
1139 break;
1140 #ifdef USE_IPV6
1141 case AF_INET6:
1142 port = ntohs(sa6->sin6_port);
1143 break;
1144 #endif
1145 default:
1146 continue; /* might as well skip this */
1147 }
1148
1149 if(EPRT == fcmd) {
1150 /*
1151 * Two fine examples from RFC2428;
1152 *
1153 * EPRT |1|132.235.1.2|6275|
1154 *
1155 * EPRT |2|1080::8:800:200C:417A|5282|
1156 */
1157
1158 result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
1159 sa->sa_family == AF_INET ? 1 : 2,
1160 myhost, port);
1161 if(result) {
1162 failf(data, "Failure sending EPRT command: %s",
1163 curl_easy_strerror(result));
1164 goto out;
1165 }
1166 break;
1167 }
1168 if(PORT == fcmd) {
1169 /* large enough for [IP address],[num],[num] */
1170 char target[sizeof(myhost) + 20];
1171 char *source = myhost;
1172 char *dest = target;
1173
1174 /* translate x.x.x.x to x,x,x,x */
1175 while(*source) {
1176 if(*source == '.')
1177 *dest = ',';
1178 else
1179 *dest = *source;
1180 dest++;
1181 source++;
1182 }
1183 *dest = 0;
1184 msnprintf(dest, 20, ",%d,%d", (int)(port >> 8), (int)(port & 0xff));
1185
1186 result = Curl_pp_sendf(data, &ftpc->pp, "%s %s", mode[fcmd], target);
1187 if(result) {
1188 failf(data, "Failure sending PORT command: %s",
1189 curl_easy_strerror(result));
1190 goto out;
1191 }
1192 break;
1193 }
1194 }
1195
1196 /* store which command was sent */
1197 ftpc->count1 = fcmd;
1198 ftp_state(data, FTP_PORT);
1199
1200 /* Replace any filter on SECONDARY with one listening on this socket */
1201 result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
1202 if(!result)
1203 portsock = CURL_SOCKET_BAD; /* now held in filter */
1204
1205 out:
1206 /* If we looked up a dns_entry, now is the time to safely release it */
1207 if(dns_entry)
1208 Curl_resolv_unlink(data, &dns_entry);
1209 if(result) {
1210 ftp_state(data, FTP_STOP);
1211 }
1212 else {
1213 /* successfully setup the list socket filter. Do we need more? */
1214 if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port &&
1215 !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) {
1216 result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET);
1217 }
1218 data->conn->bits.do_more = FALSE;
1219 Curl_pgrsTime(data, TIMER_STARTACCEPT);
1220 Curl_expire(data, data->set.accepttimeout ?
1221 data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT,
1222 EXPIRE_FTP_ACCEPT);
1223 }
1224 if(portsock != CURL_SOCKET_BAD)
1225 Curl_socket_close(data, conn, portsock);
1226 return result;
1227 }
1228
ftp_state_use_pasv(struct Curl_easy * data,struct connectdata * conn)1229 static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
1230 struct connectdata *conn)
1231 {
1232 struct ftp_conn *ftpc = &conn->proto.ftpc;
1233 CURLcode result = CURLE_OK;
1234 /*
1235 Here's the executive summary on what to do:
1236
1237 PASV is RFC959, expect:
1238 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1239
1240 LPSV is RFC1639, expect:
1241 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1242
1243 EPSV is RFC2428, expect:
1244 229 Entering Extended Passive Mode (|||port|)
1245
1246 */
1247
1248 static const char mode[][5] = { "EPSV", "PASV" };
1249 int modeoff;
1250
1251 #ifdef PF_INET6
1252 if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1253 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1254 request and enable EPSV again! */
1255 conn->bits.ftp_use_epsv = TRUE;
1256 #endif
1257
1258 modeoff = conn->bits.ftp_use_epsv ? 0 : 1;
1259
1260 result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]);
1261 if(!result) {
1262 ftpc->count1 = modeoff;
1263 ftp_state(data, FTP_PASV);
1264 infof(data, "Connect data stream passively");
1265 }
1266 return result;
1267 }
1268
1269 /*
1270 * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc.
1271 *
1272 * REST is the last command in the chain of commands when a "head"-like
1273 * request is made. Thus, if an actual transfer is to be made this is where we
1274 * take off for real.
1275 */
ftp_state_prepare_transfer(struct Curl_easy * data)1276 static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data)
1277 {
1278 CURLcode result = CURLE_OK;
1279 struct FTP *ftp = data->req.p.ftp;
1280 struct connectdata *conn = data->conn;
1281
1282 if(ftp->transfer != PPTRANSFER_BODY) {
1283 /* does not transfer any data */
1284
1285 /* still possibly do PRE QUOTE jobs */
1286 ftp_state(data, FTP_RETR_PREQUOTE);
1287 result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
1288 }
1289 else if(data->set.ftp_use_port) {
1290 /* We have chosen to use the PORT (or similar) command */
1291 result = ftp_state_use_port(data, EPRT);
1292 }
1293 else {
1294 /* We have chosen (this is default) to use the PASV (or similar) command */
1295 if(data->set.ftp_use_pret) {
1296 /* The user has requested that we send a PRET command
1297 to prepare the server for the upcoming PASV */
1298 struct ftp_conn *ftpc = &conn->proto.ftpc;
1299 if(!conn->proto.ftpc.file)
1300 result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s",
1301 data->set.str[STRING_CUSTOMREQUEST] ?
1302 data->set.str[STRING_CUSTOMREQUEST] :
1303 (data->state.list_only ? "NLST" : "LIST"));
1304 else if(data->state.upload)
1305 result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s",
1306 conn->proto.ftpc.file);
1307 else
1308 result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s",
1309 conn->proto.ftpc.file);
1310 if(!result)
1311 ftp_state(data, FTP_PRET);
1312 }
1313 else
1314 result = ftp_state_use_pasv(data, conn);
1315 }
1316 return result;
1317 }
1318
ftp_state_rest(struct Curl_easy * data,struct connectdata * conn)1319 static CURLcode ftp_state_rest(struct Curl_easy *data,
1320 struct connectdata *conn)
1321 {
1322 CURLcode result = CURLE_OK;
1323 struct FTP *ftp = data->req.p.ftp;
1324 struct ftp_conn *ftpc = &conn->proto.ftpc;
1325
1326 if((ftp->transfer != PPTRANSFER_BODY) && ftpc->file) {
1327 /* if a "head"-like request is being made (on a file) */
1328
1329 /* Determine if server can respond to REST command and therefore
1330 whether it supports range */
1331 result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0);
1332 if(!result)
1333 ftp_state(data, FTP_REST);
1334 }
1335 else
1336 result = ftp_state_prepare_transfer(data);
1337
1338 return result;
1339 }
1340
ftp_state_size(struct Curl_easy * data,struct connectdata * conn)1341 static CURLcode ftp_state_size(struct Curl_easy *data,
1342 struct connectdata *conn)
1343 {
1344 CURLcode result = CURLE_OK;
1345 struct FTP *ftp = data->req.p.ftp;
1346 struct ftp_conn *ftpc = &conn->proto.ftpc;
1347
1348 if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) {
1349 /* if a "head"-like request is being made (on a file) */
1350
1351 /* we know ftpc->file is a valid pointer to a filename */
1352 result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
1353 if(!result)
1354 ftp_state(data, FTP_SIZE);
1355 }
1356 else
1357 result = ftp_state_rest(data, conn);
1358
1359 return result;
1360 }
1361
ftp_state_list(struct Curl_easy * data)1362 static CURLcode ftp_state_list(struct Curl_easy *data)
1363 {
1364 CURLcode result = CURLE_OK;
1365 struct FTP *ftp = data->req.p.ftp;
1366 struct connectdata *conn = data->conn;
1367
1368 /* If this output is to be machine-parsed, the NLST command might be better
1369 to use, since the LIST command output is not specified or standard in any
1370 way. It has turned out that the NLST list output is not the same on all
1371 servers either... */
1372
1373 /*
1374 if FTPFILE_NOCWD was specified, we should add the path
1375 as argument for the LIST / NLST / or custom command.
1376 Whether the server will support this, is uncertain.
1377
1378 The other ftp_filemethods will CWD into dir/dir/ first and
1379 then just do LIST (in that case: nothing to do here)
1380 */
1381 char *lstArg = NULL;
1382 char *cmd;
1383
1384 if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) {
1385 /* url-decode before evaluation: e.g. paths starting/ending with %2f */
1386 const char *slashPos = NULL;
1387 char *rawPath = NULL;
1388 result = Curl_urldecode(ftp->path, 0, &rawPath, NULL, REJECT_CTRL);
1389 if(result)
1390 return result;
1391
1392 slashPos = strrchr(rawPath, '/');
1393 if(slashPos) {
1394 /* chop off the file part if format is dir/file otherwise remove
1395 the trailing slash for dir/dir/ except for absolute path / */
1396 size_t n = slashPos - rawPath;
1397 if(n == 0)
1398 ++n;
1399
1400 lstArg = rawPath;
1401 lstArg[n] = '\0';
1402 }
1403 else
1404 free(rawPath);
1405 }
1406
1407 cmd = aprintf("%s%s%s",
1408 data->set.str[STRING_CUSTOMREQUEST] ?
1409 data->set.str[STRING_CUSTOMREQUEST] :
1410 (data->state.list_only ? "NLST" : "LIST"),
1411 lstArg ? " " : "",
1412 lstArg ? lstArg : "");
1413 free(lstArg);
1414
1415 if(!cmd)
1416 return CURLE_OUT_OF_MEMORY;
1417
1418 result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", cmd);
1419 free(cmd);
1420
1421 if(!result)
1422 ftp_state(data, FTP_LIST);
1423
1424 return result;
1425 }
1426
ftp_state_retr_prequote(struct Curl_easy * data)1427 static CURLcode ftp_state_retr_prequote(struct Curl_easy *data)
1428 {
1429 /* We have sent the TYPE, now we must send the list of prequote strings */
1430 return ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
1431 }
1432
ftp_state_stor_prequote(struct Curl_easy * data)1433 static CURLcode ftp_state_stor_prequote(struct Curl_easy *data)
1434 {
1435 /* We have sent the TYPE, now we must send the list of prequote strings */
1436 return ftp_state_quote(data, TRUE, FTP_STOR_PREQUOTE);
1437 }
1438
ftp_state_type(struct Curl_easy * data)1439 static CURLcode ftp_state_type(struct Curl_easy *data)
1440 {
1441 CURLcode result = CURLE_OK;
1442 struct FTP *ftp = data->req.p.ftp;
1443 struct connectdata *conn = data->conn;
1444 struct ftp_conn *ftpc = &conn->proto.ftpc;
1445
1446 /* If we have selected NOBODY and HEADER, it means that we only want file
1447 information. Which in FTP cannot be much more than the file size and
1448 date. */
1449 if(data->req.no_body && ftpc->file &&
1450 ftp_need_type(conn, data->state.prefer_ascii)) {
1451 /* The SIZE command is _not_ RFC 959 specified, and therefore many servers
1452 may not support it! It is however the only way we have to get a file's
1453 size! */
1454
1455 ftp->transfer = PPTRANSFER_INFO;
1456 /* this means no actual transfer will be made */
1457
1458 /* Some servers return different sizes for different modes, and thus we
1459 must set the proper type before we check the size */
1460 result = ftp_nb_type(data, conn, data->state.prefer_ascii, FTP_TYPE);
1461 if(result)
1462 return result;
1463 }
1464 else
1465 result = ftp_state_size(data, conn);
1466
1467 return result;
1468 }
1469
1470 /* This is called after the CWD commands have been done in the beginning of
1471 the DO phase */
ftp_state_mdtm(struct Curl_easy * data)1472 static CURLcode ftp_state_mdtm(struct Curl_easy *data)
1473 {
1474 CURLcode result = CURLE_OK;
1475 struct connectdata *conn = data->conn;
1476 struct ftp_conn *ftpc = &conn->proto.ftpc;
1477
1478 /* Requested time of file or time-depended transfer? */
1479 if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
1480
1481 /* we have requested to get the modified-time of the file, this is a white
1482 spot as the MDTM is not mentioned in RFC959 */
1483 result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file);
1484
1485 if(!result)
1486 ftp_state(data, FTP_MDTM);
1487 }
1488 else
1489 result = ftp_state_type(data);
1490
1491 return result;
1492 }
1493
1494
1495 /* This is called after the TYPE and possible quote commands have been sent */
ftp_state_ul_setup(struct Curl_easy * data,bool sizechecked)1496 static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
1497 bool sizechecked)
1498 {
1499 CURLcode result = CURLE_OK;
1500 struct connectdata *conn = data->conn;
1501 struct FTP *ftp = data->req.p.ftp;
1502 struct ftp_conn *ftpc = &conn->proto.ftpc;
1503 bool append = data->set.remote_append;
1504
1505 if((data->state.resume_from && !sizechecked) ||
1506 ((data->state.resume_from > 0) && sizechecked)) {
1507 /* we are about to continue the uploading of a file */
1508 /* 1. get already existing file's size. We use the SIZE command for this
1509 which may not exist in the server! The SIZE command is not in
1510 RFC959. */
1511
1512 /* 2. This used to set REST. But since we can do append, we
1513 do not another ftp command. We just skip the source file
1514 offset and then we APPEND the rest on the file instead */
1515
1516 /* 3. pass file-size number of bytes in the source file */
1517 /* 4. lower the infilesize counter */
1518 /* => transfer as usual */
1519 int seekerr = CURL_SEEKFUNC_OK;
1520
1521 if(data->state.resume_from < 0) {
1522 /* Got no given size to start from, figure it out */
1523 result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
1524 if(!result)
1525 ftp_state(data, FTP_STOR_SIZE);
1526 return result;
1527 }
1528
1529 /* enable append */
1530 append = TRUE;
1531
1532 /* Let's read off the proper amount of bytes from the input. */
1533 if(data->set.seek_func) {
1534 Curl_set_in_callback(data, TRUE);
1535 seekerr = data->set.seek_func(data->set.seek_client,
1536 data->state.resume_from, SEEK_SET);
1537 Curl_set_in_callback(data, FALSE);
1538 }
1539
1540 if(seekerr != CURL_SEEKFUNC_OK) {
1541 curl_off_t passed = 0;
1542 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1543 failf(data, "Could not seek stream");
1544 return CURLE_FTP_COULDNT_USE_REST;
1545 }
1546 /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
1547 do {
1548 char scratch[4*1024];
1549 size_t readthisamountnow =
1550 (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ?
1551 sizeof(scratch) :
1552 curlx_sotouz(data->state.resume_from - passed);
1553
1554 size_t actuallyread =
1555 data->state.fread_func(scratch, 1, readthisamountnow,
1556 data->state.in);
1557
1558 passed += actuallyread;
1559 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1560 /* this checks for greater-than only to make sure that the
1561 CURL_READFUNC_ABORT return code still aborts */
1562 failf(data, "Failed to read data");
1563 return CURLE_FTP_COULDNT_USE_REST;
1564 }
1565 } while(passed < data->state.resume_from);
1566 }
1567 /* now, decrease the size of the read */
1568 if(data->state.infilesize > 0) {
1569 data->state.infilesize -= data->state.resume_from;
1570
1571 if(data->state.infilesize <= 0) {
1572 infof(data, "File already completely uploaded");
1573
1574 /* no data to transfer */
1575 Curl_xfer_setup_nop(data);
1576
1577 /* Set ->transfer so that we will not get any error in
1578 * ftp_done() because we did not transfer anything! */
1579 ftp->transfer = PPTRANSFER_NONE;
1580
1581 ftp_state(data, FTP_STOP);
1582 return CURLE_OK;
1583 }
1584 }
1585 /* we have passed, proceed as normal */
1586 } /* resume_from */
1587
1588 result = Curl_pp_sendf(data, &ftpc->pp, append ? "APPE %s" : "STOR %s",
1589 ftpc->file);
1590 if(!result)
1591 ftp_state(data, FTP_STOR);
1592
1593 return result;
1594 }
1595
ftp_state_quote(struct Curl_easy * data,bool init,ftpstate instate)1596 static CURLcode ftp_state_quote(struct Curl_easy *data,
1597 bool init,
1598 ftpstate instate)
1599 {
1600 CURLcode result = CURLE_OK;
1601 struct FTP *ftp = data->req.p.ftp;
1602 struct connectdata *conn = data->conn;
1603 struct ftp_conn *ftpc = &conn->proto.ftpc;
1604 bool quote = FALSE;
1605 struct curl_slist *item;
1606
1607 switch(instate) {
1608 case FTP_QUOTE:
1609 default:
1610 item = data->set.quote;
1611 break;
1612 case FTP_RETR_PREQUOTE:
1613 case FTP_STOR_PREQUOTE:
1614 item = data->set.prequote;
1615 break;
1616 case FTP_POSTQUOTE:
1617 item = data->set.postquote;
1618 break;
1619 }
1620
1621 /*
1622 * This state uses:
1623 * 'count1' to iterate over the commands to send
1624 * 'count2' to store whether to allow commands to fail
1625 */
1626
1627 if(init)
1628 ftpc->count1 = 0;
1629 else
1630 ftpc->count1++;
1631
1632 if(item) {
1633 int i = 0;
1634
1635 /* Skip count1 items in the linked list */
1636 while((i < ftpc->count1) && item) {
1637 item = item->next;
1638 i++;
1639 }
1640 if(item) {
1641 char *cmd = item->data;
1642 if(cmd[0] == '*') {
1643 cmd++;
1644 ftpc->count2 = 1; /* the sent command is allowed to fail */
1645 }
1646 else
1647 ftpc->count2 = 0; /* failure means cancel operation */
1648
1649 result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
1650 if(result)
1651 return result;
1652 ftp_state(data, instate);
1653 quote = TRUE;
1654 }
1655 }
1656
1657 if(!quote) {
1658 /* No more quote to send, continue to ... */
1659 switch(instate) {
1660 case FTP_QUOTE:
1661 default:
1662 result = ftp_state_cwd(data, conn);
1663 break;
1664 case FTP_RETR_PREQUOTE:
1665 if(ftp->transfer != PPTRANSFER_BODY)
1666 ftp_state(data, FTP_STOP);
1667 else {
1668 if(ftpc->known_filesize != -1) {
1669 Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
1670 result = ftp_state_retr(data, ftpc->known_filesize);
1671 }
1672 else {
1673 if(data->set.ignorecl || data->state.prefer_ascii) {
1674 /* 'ignorecl' is used to support download of growing files. It
1675 prevents the state machine from requesting the file size from
1676 the server. With an unknown file size the download continues
1677 until the server terminates it, otherwise the client stops if
1678 the received byte count exceeds the reported file size. Set
1679 option CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this
1680 behavior.
1681
1682 In addition: asking for the size for 'TYPE A' transfers is not
1683 constructive since servers do not report the converted size. So
1684 skip it.
1685 */
1686 result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
1687 if(!result)
1688 ftp_state(data, FTP_RETR);
1689 }
1690 else {
1691 result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
1692 if(!result)
1693 ftp_state(data, FTP_RETR_SIZE);
1694 }
1695 }
1696 }
1697 break;
1698 case FTP_STOR_PREQUOTE:
1699 result = ftp_state_ul_setup(data, FALSE);
1700 break;
1701 case FTP_POSTQUOTE:
1702 break;
1703 }
1704 }
1705
1706 return result;
1707 }
1708
1709 /* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
1710 problems */
ftp_epsv_disable(struct Curl_easy * data,struct connectdata * conn)1711 static CURLcode ftp_epsv_disable(struct Curl_easy *data,
1712 struct connectdata *conn)
1713 {
1714 CURLcode result = CURLE_OK;
1715
1716 if(conn->bits.ipv6
1717 #ifndef CURL_DISABLE_PROXY
1718 && !(conn->bits.tunnel_proxy || conn->bits.socksproxy)
1719 #endif
1720 ) {
1721 /* We cannot disable EPSV when doing IPv6, so this is instead a fail */
1722 failf(data, "Failed EPSV attempt, exiting");
1723 return CURLE_WEIRD_SERVER_REPLY;
1724 }
1725
1726 infof(data, "Failed EPSV attempt. Disabling EPSV");
1727 /* disable it for next transfer */
1728 conn->bits.ftp_use_epsv = FALSE;
1729 Curl_conn_close(data, SECONDARYSOCKET);
1730 Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET);
1731 data->state.errorbuf = FALSE; /* allow error message to get
1732 rewritten */
1733 result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV");
1734 if(!result) {
1735 conn->proto.ftpc.count1++;
1736 /* remain in/go to the FTP_PASV state */
1737 ftp_state(data, FTP_PASV);
1738 }
1739 return result;
1740 }
1741
1742
control_address(struct connectdata * conn)1743 static char *control_address(struct connectdata *conn)
1744 {
1745 /* Returns the control connection IP address.
1746 If a proxy tunnel is used, returns the original hostname instead, because
1747 the effective control connection address is the proxy address,
1748 not the ftp host. */
1749 #ifndef CURL_DISABLE_PROXY
1750 if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
1751 return conn->host.name;
1752 #endif
1753 return conn->primary.remote_ip;
1754 }
1755
match_pasv_6nums(const char * p,unsigned int * array)1756 static bool match_pasv_6nums(const char *p,
1757 unsigned int *array) /* 6 numbers */
1758 {
1759 int i;
1760 for(i = 0; i < 6; i++) {
1761 unsigned long num;
1762 char *endp;
1763 if(i) {
1764 if(*p != ',')
1765 return FALSE;
1766 p++;
1767 }
1768 if(!ISDIGIT(*p))
1769 return FALSE;
1770 num = strtoul(p, &endp, 10);
1771 if(num > 255)
1772 return FALSE;
1773 array[i] = (unsigned int)num;
1774 p = endp;
1775 }
1776 return TRUE;
1777 }
1778
ftp_state_pasv_resp(struct Curl_easy * data,int ftpcode)1779 static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
1780 int ftpcode)
1781 {
1782 struct connectdata *conn = data->conn;
1783 struct ftp_conn *ftpc = &conn->proto.ftpc;
1784 CURLcode result;
1785 struct Curl_dns_entry *addr = NULL;
1786 enum resolve_t rc;
1787 unsigned short connectport; /* the local port connect() should use! */
1788 struct pingpong *pp = &ftpc->pp;
1789 char *str =
1790 Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */
1791
1792 /* if we come here again, make sure the former name is cleared */
1793 Curl_safefree(ftpc->newhost);
1794
1795 if((ftpc->count1 == 0) &&
1796 (ftpcode == 229)) {
1797 /* positive EPSV response */
1798 char *ptr = strchr(str, '(');
1799 if(ptr) {
1800 char sep;
1801 ptr++;
1802 /* |||12345| */
1803 sep = ptr[0];
1804 /* the ISDIGIT() check here is because strtoul() accepts leading minus
1805 etc */
1806 if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) {
1807 char *endp;
1808 unsigned long num = strtoul(&ptr[3], &endp, 10);
1809 if(*endp != sep)
1810 ptr = NULL;
1811 else if(num > 0xffff) {
1812 failf(data, "Illegal port number in EPSV reply");
1813 return CURLE_FTP_WEIRD_PASV_REPLY;
1814 }
1815 if(ptr) {
1816 ftpc->newport = (unsigned short)(num & 0xffff);
1817 ftpc->newhost = strdup(control_address(conn));
1818 if(!ftpc->newhost)
1819 return CURLE_OUT_OF_MEMORY;
1820 }
1821 }
1822 else
1823 ptr = NULL;
1824 }
1825 if(!ptr) {
1826 failf(data, "Weirdly formatted EPSV reply");
1827 return CURLE_FTP_WEIRD_PASV_REPLY;
1828 }
1829 }
1830 else if((ftpc->count1 == 1) &&
1831 (ftpcode == 227)) {
1832 /* positive PASV response */
1833 unsigned int ip[6];
1834
1835 /*
1836 * Scan for a sequence of six comma-separated numbers and use them as
1837 * IP+port indicators.
1838 *
1839 * Found reply-strings include:
1840 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1841 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1842 * "227 Entering passive mode. 127,0,0,1,4,51"
1843 */
1844 while(*str) {
1845 if(match_pasv_6nums(str, ip))
1846 break;
1847 str++;
1848 }
1849
1850 if(!*str) {
1851 failf(data, "Couldn't interpret the 227-response");
1852 return CURLE_FTP_WEIRD_227_FORMAT;
1853 }
1854
1855 /* we got OK from server */
1856 if(data->set.ftp_skip_ip) {
1857 /* told to ignore the remotely given IP but instead use the host we used
1858 for the control connection */
1859 infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead",
1860 ip[0], ip[1], ip[2], ip[3],
1861 conn->host.name);
1862 ftpc->newhost = strdup(control_address(conn));
1863 }
1864 else
1865 ftpc->newhost = aprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
1866
1867 if(!ftpc->newhost)
1868 return CURLE_OUT_OF_MEMORY;
1869
1870 ftpc->newport = (unsigned short)(((ip[4] << 8) + ip[5]) & 0xffff);
1871 }
1872 else if(ftpc->count1 == 0) {
1873 /* EPSV failed, move on to PASV */
1874 return ftp_epsv_disable(data, conn);
1875 }
1876 else {
1877 failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
1878 return CURLE_FTP_WEIRD_PASV_REPLY;
1879 }
1880
1881 #ifndef CURL_DISABLE_PROXY
1882 if(conn->bits.proxy) {
1883 /*
1884 * This connection uses a proxy and we need to connect to the proxy again
1885 * here. We do not want to rely on a former host lookup that might've
1886 * expired now, instead we remake the lookup here and now!
1887 */
1888 const char * const host_name = conn->bits.socksproxy ?
1889 conn->socks_proxy.host.name : conn->http_proxy.host.name;
1890 rc = Curl_resolv(data, host_name, conn->primary.remote_port, FALSE, &addr);
1891 if(rc == CURLRESOLV_PENDING)
1892 /* BLOCKING, ignores the return code but 'addr' will be NULL in
1893 case of failure */
1894 (void)Curl_resolver_wait_resolv(data, &addr);
1895
1896 /* we connect to the proxy's port */
1897 connectport = (unsigned short)conn->primary.remote_port;
1898
1899 if(!addr) {
1900 failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport);
1901 return CURLE_COULDNT_RESOLVE_PROXY;
1902 }
1903 }
1904 else
1905 #endif
1906 {
1907 /* normal, direct, ftp connection */
1908 DEBUGASSERT(ftpc->newhost);
1909
1910 /* postponed address resolution in case of tcp fastopen */
1911 if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
1912 Curl_safefree(ftpc->newhost);
1913 ftpc->newhost = strdup(control_address(conn));
1914 if(!ftpc->newhost)
1915 return CURLE_OUT_OF_MEMORY;
1916 }
1917
1918 rc = Curl_resolv(data, ftpc->newhost, ftpc->newport, FALSE, &addr);
1919 if(rc == CURLRESOLV_PENDING)
1920 /* BLOCKING */
1921 (void)Curl_resolver_wait_resolv(data, &addr);
1922
1923 connectport = ftpc->newport; /* we connect to the remote port */
1924
1925 if(!addr) {
1926 failf(data, "cannot resolve new host %s:%hu",
1927 ftpc->newhost, connectport);
1928 return CURLE_FTP_CANT_GET_HOST;
1929 }
1930 }
1931
1932 result = Curl_conn_setup(data, conn, SECONDARYSOCKET, addr,
1933 conn->bits.ftp_use_data_ssl ?
1934 CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
1935
1936 if(result) {
1937 Curl_resolv_unlink(data, &addr); /* we are done using this address */
1938 if(ftpc->count1 == 0 && ftpcode == 229)
1939 return ftp_epsv_disable(data, conn);
1940
1941 return result;
1942 }
1943
1944
1945 /*
1946 * When this is used from the multi interface, this might've returned with
1947 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1948 * connect to connect.
1949 */
1950
1951 if(data->set.verbose)
1952 /* this just dumps information about this second connection */
1953 ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport);
1954
1955 Curl_resolv_unlink(data, &addr); /* we are done using this address */
1956
1957 Curl_safefree(conn->secondaryhostname);
1958 conn->secondary_port = ftpc->newport;
1959 conn->secondaryhostname = strdup(ftpc->newhost);
1960 if(!conn->secondaryhostname)
1961 return CURLE_OUT_OF_MEMORY;
1962
1963 conn->bits.do_more = TRUE;
1964 ftp_state(data, FTP_STOP); /* this phase is completed */
1965
1966 return result;
1967 }
1968
ftp_state_port_resp(struct Curl_easy * data,int ftpcode)1969 static CURLcode ftp_state_port_resp(struct Curl_easy *data,
1970 int ftpcode)
1971 {
1972 struct connectdata *conn = data->conn;
1973 struct ftp_conn *ftpc = &conn->proto.ftpc;
1974 ftpport fcmd = (ftpport)ftpc->count1;
1975 CURLcode result = CURLE_OK;
1976
1977 /* The FTP spec tells a positive response should have code 200.
1978 Be more permissive here to tolerate deviant servers. */
1979 if(ftpcode / 100 != 2) {
1980 /* the command failed */
1981
1982 if(EPRT == fcmd) {
1983 infof(data, "disabling EPRT usage");
1984 conn->bits.ftp_use_eprt = FALSE;
1985 }
1986 fcmd++;
1987
1988 if(fcmd == DONE) {
1989 failf(data, "Failed to do PORT");
1990 result = CURLE_FTP_PORT_FAILED;
1991 }
1992 else
1993 /* try next */
1994 result = ftp_state_use_port(data, fcmd);
1995 }
1996 else {
1997 infof(data, "Connect data stream actively");
1998 ftp_state(data, FTP_STOP); /* end of DO phase */
1999 result = ftp_dophase_done(data, FALSE);
2000 }
2001
2002 return result;
2003 }
2004
twodigit(const char * p)2005 static int twodigit(const char *p)
2006 {
2007 return (p[0]-'0') * 10 + (p[1]-'0');
2008 }
2009
ftp_213_date(const char * p,int * year,int * month,int * day,int * hour,int * minute,int * second)2010 static bool ftp_213_date(const char *p, int *year, int *month, int *day,
2011 int *hour, int *minute, int *second)
2012 {
2013 size_t len = strlen(p);
2014 if(len < 14)
2015 return FALSE;
2016 *year = twodigit(&p[0]) * 100 + twodigit(&p[2]);
2017 *month = twodigit(&p[4]);
2018 *day = twodigit(&p[6]);
2019 *hour = twodigit(&p[8]);
2020 *minute = twodigit(&p[10]);
2021 *second = twodigit(&p[12]);
2022
2023 if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) ||
2024 (*second > 60))
2025 return FALSE;
2026 return TRUE;
2027 }
2028
client_write_header(struct Curl_easy * data,char * buf,size_t blen)2029 static CURLcode client_write_header(struct Curl_easy *data,
2030 char *buf, size_t blen)
2031 {
2032 /* Some replies from an FTP server are written to the client
2033 * as CLIENTWRITE_HEADER, formatted as if they came from a
2034 * HTTP conversation.
2035 * In all protocols, CLIENTWRITE_HEADER data is only passed to
2036 * the body write callback when data->set.include_header is set
2037 * via CURLOPT_HEADER.
2038 * For historic reasons, FTP never played this game and expects
2039 * all its HEADERs to do that always. Set that flag during the
2040 * call to Curl_client_write() so it does the right thing.
2041 *
2042 * Notice that we cannot enable this flag for FTP in general,
2043 * as an FTP transfer might involve an HTTP proxy connection and
2044 * headers from CONNECT should not automatically be part of the
2045 * output. */
2046 CURLcode result;
2047 bool save = data->set.include_header;
2048 data->set.include_header = TRUE;
2049 result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, blen);
2050 data->set.include_header = save;
2051 return result;
2052 }
2053
ftp_state_mdtm_resp(struct Curl_easy * data,int ftpcode)2054 static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
2055 int ftpcode)
2056 {
2057 CURLcode result = CURLE_OK;
2058 struct FTP *ftp = data->req.p.ftp;
2059 struct connectdata *conn = data->conn;
2060 struct ftp_conn *ftpc = &conn->proto.ftpc;
2061
2062 switch(ftpcode) {
2063 case 213:
2064 {
2065 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
2066 last .sss part is optional and means fractions of a second */
2067 int year, month, day, hour, minute, second;
2068 struct pingpong *pp = &ftpc->pp;
2069 char *resp = Curl_dyn_ptr(&pp->recvbuf) + 4;
2070 if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) {
2071 /* we have a time, reformat it */
2072 char timebuf[24];
2073 msnprintf(timebuf, sizeof(timebuf),
2074 "%04d%02d%02d %02d:%02d:%02d GMT",
2075 year, month, day, hour, minute, second);
2076 /* now, convert this into a time() value: */
2077 data->info.filetime = Curl_getdate_capped(timebuf);
2078 }
2079
2080 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2081 /* If we asked for a time of the file and we actually got one as well,
2082 we "emulate" an HTTP-style header in our output. */
2083
2084 #if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__))
2085 #pragma GCC diagnostic push
2086 /* 'time_t' is unsigned in MSDOS and AmigaOS. Silence:
2087 warning: comparison of unsigned expression in '>= 0' is always true */
2088 #pragma GCC diagnostic ignored "-Wtype-limits"
2089 #endif
2090 if(data->req.no_body &&
2091 ftpc->file &&
2092 data->set.get_filetime &&
2093 (data->info.filetime >= 0) ) {
2094 #if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__))
2095 #pragma GCC diagnostic pop
2096 #endif
2097 char headerbuf[128];
2098 int headerbuflen;
2099 time_t filetime = data->info.filetime;
2100 struct tm buffer;
2101 const struct tm *tm = &buffer;
2102
2103 result = Curl_gmtime(filetime, &buffer);
2104 if(result)
2105 return result;
2106
2107 /* format: "Tue, 15 Nov 1994 12:45:26" */
2108 headerbuflen =
2109 msnprintf(headerbuf, sizeof(headerbuf),
2110 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
2111 Curl_wkday[tm->tm_wday ? tm->tm_wday-1 : 6],
2112 tm->tm_mday,
2113 Curl_month[tm->tm_mon],
2114 tm->tm_year + 1900,
2115 tm->tm_hour,
2116 tm->tm_min,
2117 tm->tm_sec);
2118 result = client_write_header(data, headerbuf, headerbuflen);
2119 if(result)
2120 return result;
2121 } /* end of a ridiculous amount of conditionals */
2122 #endif
2123 }
2124 break;
2125 default:
2126 infof(data, "unsupported MDTM reply format");
2127 break;
2128 case 550: /* 550 is used for several different problems, e.g.
2129 "No such file or directory" or "Permission denied".
2130 It does not mean that the file does not exist at all. */
2131 infof(data, "MDTM failed: file does not exist or permission problem,"
2132 " continuing");
2133 break;
2134 }
2135
2136 if(data->set.timecondition) {
2137 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2138 switch(data->set.timecondition) {
2139 case CURL_TIMECOND_IFMODSINCE:
2140 default:
2141 if(data->info.filetime <= data->set.timevalue) {
2142 infof(data, "The requested document is not new enough");
2143 ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
2144 data->info.timecond = TRUE;
2145 ftp_state(data, FTP_STOP);
2146 return CURLE_OK;
2147 }
2148 break;
2149 case CURL_TIMECOND_IFUNMODSINCE:
2150 if(data->info.filetime > data->set.timevalue) {
2151 infof(data, "The requested document is not old enough");
2152 ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
2153 data->info.timecond = TRUE;
2154 ftp_state(data, FTP_STOP);
2155 return CURLE_OK;
2156 }
2157 break;
2158 } /* switch */
2159 }
2160 else {
2161 infof(data, "Skipping time comparison");
2162 }
2163 }
2164
2165 if(!result)
2166 result = ftp_state_type(data);
2167
2168 return result;
2169 }
2170
ftp_state_type_resp(struct Curl_easy * data,int ftpcode,ftpstate instate)2171 static CURLcode ftp_state_type_resp(struct Curl_easy *data,
2172 int ftpcode,
2173 ftpstate instate)
2174 {
2175 CURLcode result = CURLE_OK;
2176 struct connectdata *conn = data->conn;
2177
2178 if(ftpcode/100 != 2) {
2179 /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
2180 successful 'TYPE I'. While that is not as RFC959 says, it is still a
2181 positive response code and we allow that. */
2182 failf(data, "Couldn't set desired mode");
2183 return CURLE_FTP_COULDNT_SET_TYPE;
2184 }
2185 if(ftpcode != 200)
2186 infof(data, "Got a %03d response code instead of the assumed 200",
2187 ftpcode);
2188
2189 if(instate == FTP_TYPE)
2190 result = ftp_state_size(data, conn);
2191 else if(instate == FTP_LIST_TYPE)
2192 result = ftp_state_list(data);
2193 else if(instate == FTP_RETR_TYPE)
2194 result = ftp_state_retr_prequote(data);
2195 else if(instate == FTP_STOR_TYPE)
2196 result = ftp_state_stor_prequote(data);
2197
2198 return result;
2199 }
2200
ftp_state_retr(struct Curl_easy * data,curl_off_t filesize)2201 static CURLcode ftp_state_retr(struct Curl_easy *data,
2202 curl_off_t filesize)
2203 {
2204 CURLcode result = CURLE_OK;
2205 struct FTP *ftp = data->req.p.ftp;
2206 struct connectdata *conn = data->conn;
2207 struct ftp_conn *ftpc = &conn->proto.ftpc;
2208
2209 CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_DSTATE(data));
2210 if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
2211 failf(data, "Maximum file size exceeded");
2212 return CURLE_FILESIZE_EXCEEDED;
2213 }
2214 ftp->downloadsize = filesize;
2215
2216 if(data->state.resume_from) {
2217 /* We always (attempt to) get the size of downloads, so it is done before
2218 this even when not doing resumes. */
2219 if(filesize == -1) {
2220 infof(data, "ftp server does not support SIZE");
2221 /* We could not get the size and therefore we cannot know if there really
2222 is a part of the file left to get, although the server will just
2223 close the connection when we start the connection so it will not cause
2224 us any harm, just not make us exit as nicely. */
2225 }
2226 else {
2227 /* We got a file size report, so we check that there actually is a
2228 part of the file left to get, or else we go home. */
2229 if(data->state.resume_from < 0) {
2230 /* We are supposed to download the last abs(from) bytes */
2231 if(filesize < -data->state.resume_from) {
2232 failf(data, "Offset (%" FMT_OFF_T
2233 ") was beyond file size (%" FMT_OFF_T ")",
2234 data->state.resume_from, filesize);
2235 return CURLE_BAD_DOWNLOAD_RESUME;
2236 }
2237 /* convert to size to download */
2238 ftp->downloadsize = -data->state.resume_from;
2239 /* download from where? */
2240 data->state.resume_from = filesize - ftp->downloadsize;
2241 }
2242 else {
2243 if(filesize < data->state.resume_from) {
2244 failf(data, "Offset (%" FMT_OFF_T
2245 ") was beyond file size (%" FMT_OFF_T ")",
2246 data->state.resume_from, filesize);
2247 return CURLE_BAD_DOWNLOAD_RESUME;
2248 }
2249 /* Now store the number of bytes we are expected to download */
2250 ftp->downloadsize = filesize-data->state.resume_from;
2251 }
2252 }
2253
2254 if(ftp->downloadsize == 0) {
2255 /* no data to transfer */
2256 Curl_xfer_setup_nop(data);
2257 infof(data, "File already completely downloaded");
2258
2259 /* Set ->transfer so that we will not get any error in ftp_done()
2260 * because we did not transfer the any file */
2261 ftp->transfer = PPTRANSFER_NONE;
2262 ftp_state(data, FTP_STOP);
2263 return CURLE_OK;
2264 }
2265
2266 /* Set resume file transfer offset */
2267 infof(data, "Instructs server to resume from offset %" FMT_OFF_T,
2268 data->state.resume_from);
2269
2270 result = Curl_pp_sendf(data, &ftpc->pp, "REST %" FMT_OFF_T,
2271 data->state.resume_from);
2272 if(!result)
2273 ftp_state(data, FTP_RETR_REST);
2274 }
2275 else {
2276 /* no resume */
2277 result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
2278 if(!result)
2279 ftp_state(data, FTP_RETR);
2280 }
2281
2282 return result;
2283 }
2284
ftp_state_size_resp(struct Curl_easy * data,int ftpcode,ftpstate instate)2285 static CURLcode ftp_state_size_resp(struct Curl_easy *data,
2286 int ftpcode,
2287 ftpstate instate)
2288 {
2289 CURLcode result = CURLE_OK;
2290 curl_off_t filesize = -1;
2291 char *buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
2292 size_t len = data->conn->proto.ftpc.pp.nfinal;
2293
2294 /* get the size from the ascii string: */
2295 if(ftpcode == 213) {
2296 /* To allow servers to prepend "rubbish" in the response string, we scan
2297 for all the digits at the end of the response and parse only those as a
2298 number. */
2299 char *start = &buf[4];
2300 char *fdigit = memchr(start, '\r', len);
2301 if(fdigit) {
2302 fdigit--;
2303 if(*fdigit == '\n')
2304 fdigit--;
2305 while(ISDIGIT(fdigit[-1]) && (fdigit > start))
2306 fdigit--;
2307 }
2308 else
2309 fdigit = start;
2310 /* ignores parsing errors, which will make the size remain unknown */
2311 (void)curlx_strtoofft(fdigit, NULL, 10, &filesize);
2312
2313 }
2314 else if(ftpcode == 550) { /* "No such file or directory" */
2315 /* allow a SIZE failure for (resumed) uploads, when probing what command
2316 to use */
2317 if(instate != FTP_STOR_SIZE) {
2318 failf(data, "The file does not exist");
2319 return CURLE_REMOTE_FILE_NOT_FOUND;
2320 }
2321 }
2322
2323 if(instate == FTP_SIZE) {
2324 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2325 if(-1 != filesize) {
2326 char clbuf[128];
2327 int clbuflen = msnprintf(clbuf, sizeof(clbuf),
2328 "Content-Length: %" FMT_OFF_T "\r\n", filesize);
2329 result = client_write_header(data, clbuf, clbuflen);
2330 if(result)
2331 return result;
2332 }
2333 #endif
2334 Curl_pgrsSetDownloadSize(data, filesize);
2335 result = ftp_state_rest(data, data->conn);
2336 }
2337 else if(instate == FTP_RETR_SIZE) {
2338 Curl_pgrsSetDownloadSize(data, filesize);
2339 result = ftp_state_retr(data, filesize);
2340 }
2341 else if(instate == FTP_STOR_SIZE) {
2342 data->state.resume_from = filesize;
2343 result = ftp_state_ul_setup(data, TRUE);
2344 }
2345
2346 return result;
2347 }
2348
ftp_state_rest_resp(struct Curl_easy * data,struct connectdata * conn,int ftpcode,ftpstate instate)2349 static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
2350 struct connectdata *conn,
2351 int ftpcode,
2352 ftpstate instate)
2353 {
2354 CURLcode result = CURLE_OK;
2355 struct ftp_conn *ftpc = &conn->proto.ftpc;
2356
2357 switch(instate) {
2358 case FTP_REST:
2359 default:
2360 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2361 if(ftpcode == 350) {
2362 char buffer[24]= { "Accept-ranges: bytes\r\n" };
2363 result = client_write_header(data, buffer, strlen(buffer));
2364 if(result)
2365 return result;
2366 }
2367 #endif
2368 result = ftp_state_prepare_transfer(data);
2369 break;
2370
2371 case FTP_RETR_REST:
2372 if(ftpcode != 350) {
2373 failf(data, "Couldn't use REST");
2374 result = CURLE_FTP_COULDNT_USE_REST;
2375 }
2376 else {
2377 result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
2378 if(!result)
2379 ftp_state(data, FTP_RETR);
2380 }
2381 break;
2382 }
2383
2384 return result;
2385 }
2386
ftp_state_stor_resp(struct Curl_easy * data,int ftpcode,ftpstate instate)2387 static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
2388 int ftpcode, ftpstate instate)
2389 {
2390 CURLcode result = CURLE_OK;
2391 struct connectdata *conn = data->conn;
2392
2393 if(ftpcode >= 400) {
2394 failf(data, "Failed FTP upload: %0d", ftpcode);
2395 ftp_state(data, FTP_STOP);
2396 /* oops, we never close the sockets! */
2397 return CURLE_UPLOAD_FAILED;
2398 }
2399
2400 conn->proto.ftpc.state_saved = instate;
2401
2402 /* PORT means we are now awaiting the server to connect to us. */
2403 if(data->set.ftp_use_port) {
2404 struct ftp_conn *ftpc = &conn->proto.ftpc;
2405 bool connected;
2406
2407 ftp_state(data, FTP_STOP); /* no longer in STOR state */
2408
2409 result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
2410 if(result)
2411 return result;
2412
2413 if(!connected) {
2414 infof(data, "Data conn was not available immediately");
2415 ftpc->wait_data_conn = TRUE;
2416 return ftp_check_ctrl_on_data_wait(data);
2417 }
2418 ftpc->wait_data_conn = FALSE;
2419 }
2420 return InitiateTransfer(data);
2421 }
2422
2423 /* for LIST and RETR responses */
ftp_state_get_resp(struct Curl_easy * data,int ftpcode,ftpstate instate)2424 static CURLcode ftp_state_get_resp(struct Curl_easy *data,
2425 int ftpcode,
2426 ftpstate instate)
2427 {
2428 CURLcode result = CURLE_OK;
2429 struct FTP *ftp = data->req.p.ftp;
2430 struct connectdata *conn = data->conn;
2431
2432 if((ftpcode == 150) || (ftpcode == 125)) {
2433
2434 /*
2435 A;
2436 150 Opening BINARY mode data connection for /etc/passwd (2241
2437 bytes). (ok, the file is being transferred)
2438
2439 B:
2440 150 Opening ASCII mode data connection for /bin/ls
2441
2442 C:
2443 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2444
2445 D:
2446 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
2447
2448 E:
2449 125 Data connection already open; Transfer starting. */
2450
2451 curl_off_t size = -1; /* default unknown size */
2452
2453
2454 /*
2455 * It appears that there are FTP-servers that return size 0 for files when
2456 * SIZE is used on the file while being in BINARY mode. To work around
2457 * that (stupid) behavior, we attempt to parse the RETR response even if
2458 * the SIZE returned size zero.
2459 *
2460 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2461 */
2462
2463 if((instate != FTP_LIST) &&
2464 !data->state.prefer_ascii &&
2465 !data->set.ignorecl &&
2466 (ftp->downloadsize < 1)) {
2467 /*
2468 * It seems directory listings either do not show the size or often uses
2469 * size 0 anyway. ASCII transfers may cause that the transferred amount
2470 * of data is not the same as this line tells, why using this number in
2471 * those cases only confuses us.
2472 *
2473 * Example D above makes this parsing a little tricky */
2474 char *bytes;
2475 char *buf = Curl_dyn_ptr(&conn->proto.ftpc.pp.recvbuf);
2476 bytes = strstr(buf, " bytes");
2477 if(bytes) {
2478 long in = (long)(--bytes-buf);
2479 /* this is a hint there is size information in there! ;-) */
2480 while(--in) {
2481 /* scan for the left parenthesis and break there */
2482 if('(' == *bytes)
2483 break;
2484 /* skip only digits */
2485 if(!ISDIGIT(*bytes)) {
2486 bytes = NULL;
2487 break;
2488 }
2489 /* one more estep backwards */
2490 bytes--;
2491 }
2492 /* if we have nothing but digits: */
2493 if(bytes) {
2494 ++bytes;
2495 /* get the number! */
2496 (void)curlx_strtoofft(bytes, NULL, 10, &size);
2497 }
2498 }
2499 }
2500 else if(ftp->downloadsize > -1)
2501 size = ftp->downloadsize;
2502
2503 if(size > data->req.maxdownload && data->req.maxdownload > 0)
2504 size = data->req.size = data->req.maxdownload;
2505 else if((instate != FTP_LIST) && (data->state.prefer_ascii))
2506 size = -1; /* kludge for servers that understate ASCII mode file size */
2507
2508 infof(data, "Maxdownload = %" FMT_OFF_T, data->req.maxdownload);
2509
2510 if(instate != FTP_LIST)
2511 infof(data, "Getting file with size: %" FMT_OFF_T, size);
2512
2513 /* FTP download: */
2514 conn->proto.ftpc.state_saved = instate;
2515 conn->proto.ftpc.retr_size_saved = size;
2516
2517 if(data->set.ftp_use_port) {
2518 struct ftp_conn *ftpc = &conn->proto.ftpc;
2519 bool connected;
2520
2521 result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
2522 if(result)
2523 return result;
2524
2525 if(!connected) {
2526 infof(data, "Data conn was not available immediately");
2527 ftp_state(data, FTP_STOP);
2528 ftpc->wait_data_conn = TRUE;
2529 return ftp_check_ctrl_on_data_wait(data);
2530 }
2531 ftpc->wait_data_conn = FALSE;
2532 }
2533 return InitiateTransfer(data);
2534 }
2535 else {
2536 if((instate == FTP_LIST) && (ftpcode == 450)) {
2537 /* simply no matching files in the dir listing */
2538 ftp->transfer = PPTRANSFER_NONE; /* do not download anything */
2539 ftp_state(data, FTP_STOP); /* this phase is over */
2540 }
2541 else {
2542 failf(data, "RETR response: %03d", ftpcode);
2543 return instate == FTP_RETR && ftpcode == 550 ?
2544 CURLE_REMOTE_FILE_NOT_FOUND :
2545 CURLE_FTP_COULDNT_RETR_FILE;
2546 }
2547 }
2548
2549 return result;
2550 }
2551
2552 /* after USER, PASS and ACCT */
ftp_state_loggedin(struct Curl_easy * data)2553 static CURLcode ftp_state_loggedin(struct Curl_easy *data)
2554 {
2555 CURLcode result = CURLE_OK;
2556 struct connectdata *conn = data->conn;
2557
2558 if(conn->bits.ftp_use_control_ssl) {
2559 /* PBSZ = PROTECTION BUFFER SIZE.
2560
2561 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2562
2563 Specifically, the PROT command MUST be preceded by a PBSZ
2564 command and a PBSZ command MUST be preceded by a successful
2565 security data exchange (the TLS negotiation in this case)
2566
2567 ... (and on page 8):
2568
2569 Thus the PBSZ command must still be issued, but must have a
2570 parameter of '0' to indicate that no buffering is taking place
2571 and the data connection should not be encapsulated.
2572 */
2573 result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0);
2574 if(!result)
2575 ftp_state(data, FTP_PBSZ);
2576 }
2577 else {
2578 result = ftp_state_pwd(data, conn);
2579 }
2580 return result;
2581 }
2582
2583 /* for USER and PASS responses */
ftp_state_user_resp(struct Curl_easy * data,int ftpcode)2584 static CURLcode ftp_state_user_resp(struct Curl_easy *data,
2585 int ftpcode)
2586 {
2587 CURLcode result = CURLE_OK;
2588 struct connectdata *conn = data->conn;
2589 struct ftp_conn *ftpc = &conn->proto.ftpc;
2590
2591 /* some need password anyway, and others just return 2xx ignored */
2592 if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2593 /* 331 Password required for ...
2594 (the server requires to send the user's password too) */
2595 result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
2596 conn->passwd ? conn->passwd : "");
2597 if(!result)
2598 ftp_state(data, FTP_PASS);
2599 }
2600 else if(ftpcode/100 == 2) {
2601 /* 230 User ... logged in.
2602 (the user logged in with or without password) */
2603 result = ftp_state_loggedin(data);
2604 }
2605 else if(ftpcode == 332) {
2606 if(data->set.str[STRING_FTP_ACCOUNT]) {
2607 result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s",
2608 data->set.str[STRING_FTP_ACCOUNT]);
2609 if(!result)
2610 ftp_state(data, FTP_ACCT);
2611 }
2612 else {
2613 failf(data, "ACCT requested but none available");
2614 result = CURLE_LOGIN_DENIED;
2615 }
2616 }
2617 else {
2618 /* All other response codes, like:
2619
2620 530 User ... access denied
2621 (the server denies to log the specified user) */
2622
2623 if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
2624 !ftpc->ftp_trying_alternative) {
2625 /* Ok, USER failed. Let's try the supplied command. */
2626 result =
2627 Curl_pp_sendf(data, &ftpc->pp, "%s",
2628 data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
2629 if(!result) {
2630 ftpc->ftp_trying_alternative = TRUE;
2631 ftp_state(data, FTP_USER);
2632 }
2633 }
2634 else {
2635 failf(data, "Access denied: %03d", ftpcode);
2636 result = CURLE_LOGIN_DENIED;
2637 }
2638 }
2639 return result;
2640 }
2641
2642 /* for ACCT response */
ftp_state_acct_resp(struct Curl_easy * data,int ftpcode)2643 static CURLcode ftp_state_acct_resp(struct Curl_easy *data,
2644 int ftpcode)
2645 {
2646 CURLcode result = CURLE_OK;
2647 if(ftpcode != 230) {
2648 failf(data, "ACCT rejected by server: %03d", ftpcode);
2649 result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2650 }
2651 else
2652 result = ftp_state_loggedin(data);
2653
2654 return result;
2655 }
2656
2657
ftp_statemachine(struct Curl_easy * data,struct connectdata * conn)2658 static CURLcode ftp_statemachine(struct Curl_easy *data,
2659 struct connectdata *conn)
2660 {
2661 CURLcode result;
2662 int ftpcode;
2663 struct ftp_conn *ftpc = &conn->proto.ftpc;
2664 struct pingpong *pp = &ftpc->pp;
2665 static const char * const ftpauth[] = { "SSL", "TLS" };
2666 size_t nread = 0;
2667
2668 if(pp->sendleft)
2669 return Curl_pp_flushsend(data, pp);
2670
2671 result = ftp_readresp(data, FIRSTSOCKET, pp, &ftpcode, &nread);
2672 if(result)
2673 return result;
2674
2675 if(ftpcode) {
2676 /* we have now received a full FTP server response */
2677 switch(ftpc->state) {
2678 case FTP_WAIT220:
2679 if(ftpcode == 230) {
2680 /* 230 User logged in - already! Take as 220 if TLS required. */
2681 if(data->set.use_ssl <= CURLUSESSL_TRY ||
2682 conn->bits.ftp_use_control_ssl)
2683 return ftp_state_user_resp(data, ftpcode);
2684 }
2685 else if(ftpcode != 220) {
2686 failf(data, "Got a %03d ftp-server response when 220 was expected",
2687 ftpcode);
2688 return CURLE_WEIRD_SERVER_REPLY;
2689 }
2690
2691 /* We have received a 220 response fine, now we proceed. */
2692 #ifdef HAVE_GSSAPI
2693 if(data->set.krb) {
2694 /* If not anonymous login, try a secure login. Note that this
2695 procedure is still BLOCKING. */
2696
2697 Curl_sec_request_prot(conn, "private");
2698 /* We set private first as default, in case the line below fails to
2699 set a valid level */
2700 Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
2701
2702 if(Curl_sec_login(data, conn)) {
2703 failf(data, "secure login failed");
2704 return CURLE_WEIRD_SERVER_REPLY;
2705 }
2706 infof(data, "Authentication successful");
2707 }
2708 #endif
2709
2710 if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) {
2711 /* We do not have a SSL/TLS control connection yet, but FTPS is
2712 requested. Try a FTPS connection now */
2713
2714 ftpc->count3 = 0;
2715 switch(data->set.ftpsslauth) {
2716 case CURLFTPAUTH_DEFAULT:
2717 case CURLFTPAUTH_SSL:
2718 ftpc->count2 = 1; /* add one to get next */
2719 ftpc->count1 = 0;
2720 break;
2721 case CURLFTPAUTH_TLS:
2722 ftpc->count2 = -1; /* subtract one to get next */
2723 ftpc->count1 = 1;
2724 break;
2725 default:
2726 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
2727 (int)data->set.ftpsslauth);
2728 return CURLE_UNKNOWN_OPTION; /* we do not know what to do */
2729 }
2730 result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
2731 ftpauth[ftpc->count1]);
2732 if(!result)
2733 ftp_state(data, FTP_AUTH);
2734 }
2735 else
2736 result = ftp_state_user(data, conn);
2737 break;
2738
2739 case FTP_AUTH:
2740 /* we have gotten the response to a previous AUTH command */
2741
2742 if(pp->overflow)
2743 return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
2744
2745 /* RFC2228 (page 5) says:
2746 *
2747 * If the server is willing to accept the named security mechanism,
2748 * and does not require any security data, it must respond with
2749 * reply code 234/334.
2750 */
2751
2752 if((ftpcode == 234) || (ftpcode == 334)) {
2753 /* this was BLOCKING, keep it so for now */
2754 bool done;
2755 if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
2756 result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
2757 if(result) {
2758 /* we failed and bail out */
2759 return CURLE_USE_SSL_FAILED;
2760 }
2761 }
2762 result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done);
2763 if(!result) {
2764 conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
2765 conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */
2766 result = ftp_state_user(data, conn);
2767 }
2768 }
2769 else if(ftpc->count3 < 1) {
2770 ftpc->count3++;
2771 ftpc->count1 += ftpc->count2; /* get next attempt */
2772 result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
2773 ftpauth[ftpc->count1]);
2774 /* remain in this same state */
2775 }
2776 else {
2777 if(data->set.use_ssl > CURLUSESSL_TRY)
2778 /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
2779 result = CURLE_USE_SSL_FAILED;
2780 else
2781 /* ignore the failure and continue */
2782 result = ftp_state_user(data, conn);
2783 }
2784 break;
2785
2786 case FTP_USER:
2787 case FTP_PASS:
2788 result = ftp_state_user_resp(data, ftpcode);
2789 break;
2790
2791 case FTP_ACCT:
2792 result = ftp_state_acct_resp(data, ftpcode);
2793 break;
2794
2795 case FTP_PBSZ:
2796 result =
2797 Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
2798 data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
2799 if(!result)
2800 ftp_state(data, FTP_PROT);
2801 break;
2802
2803 case FTP_PROT:
2804 if(ftpcode/100 == 2)
2805 /* We have enabled SSL for the data connection! */
2806 conn->bits.ftp_use_data_ssl =
2807 (data->set.use_ssl != CURLUSESSL_CONTROL);
2808 /* FTP servers typically responds with 500 if they decide to reject
2809 our 'P' request */
2810 else if(data->set.use_ssl > CURLUSESSL_CONTROL)
2811 /* we failed and bails out */
2812 return CURLE_USE_SSL_FAILED;
2813
2814 if(data->set.ftp_ccc) {
2815 /* CCC - Clear Command Channel
2816 */
2817 result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
2818 if(!result)
2819 ftp_state(data, FTP_CCC);
2820 }
2821 else
2822 result = ftp_state_pwd(data, conn);
2823 break;
2824
2825 case FTP_CCC:
2826 if(ftpcode < 500) {
2827 /* First shut down the SSL layer (note: this call will block) */
2828 /* This has only been tested on the proftpd server, and the mod_tls
2829 * code sends a close notify alert without waiting for a close notify
2830 * alert in response. Thus we wait for a close notify alert from the
2831 * server, but we do not send one. Let's hope other servers do
2832 * the same... */
2833 result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET,
2834 (data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE));
2835
2836 if(result)
2837 failf(data, "Failed to clear the command channel (CCC)");
2838 }
2839 if(!result)
2840 /* Then continue as normal */
2841 result = ftp_state_pwd(data, conn);
2842 break;
2843
2844 case FTP_PWD:
2845 if(ftpcode == 257) {
2846 char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
2847 letter */
2848 bool entry_extracted = FALSE;
2849 struct dynbuf out;
2850 Curl_dyn_init(&out, 1000);
2851
2852 /* Reply format is like
2853 257<space>[rubbish]"<directory-name>"<space><commentary> and the
2854 RFC959 says
2855
2856 The directory name can contain any character; embedded
2857 double-quotes should be escaped by double-quotes (the
2858 "quote-doubling" convention).
2859 */
2860
2861 /* scan for the first double-quote for non-standard responses */
2862 while(*ptr != '\n' && *ptr != '\0' && *ptr != '"')
2863 ptr++;
2864
2865 if('\"' == *ptr) {
2866 /* it started good */
2867 for(ptr++; *ptr; ptr++) {
2868 if('\"' == *ptr) {
2869 if('\"' == ptr[1]) {
2870 /* "quote-doubling" */
2871 result = Curl_dyn_addn(&out, &ptr[1], 1);
2872 ptr++;
2873 }
2874 else {
2875 /* end of path */
2876 if(Curl_dyn_len(&out))
2877 entry_extracted = TRUE;
2878 break; /* get out of this loop */
2879 }
2880 }
2881 else
2882 result = Curl_dyn_addn(&out, ptr, 1);
2883 if(result)
2884 return result;
2885 }
2886 }
2887 if(entry_extracted) {
2888 /* If the path name does not look like an absolute path (i.e.: it
2889 does not start with a '/'), we probably need some server-dependent
2890 adjustments. For example, this is the case when connecting to
2891 an OS400 FTP server: this server supports two name syntaxes,
2892 the default one being incompatible with standard paths. In
2893 addition, this server switches automatically to the regular path
2894 syntax when one is encountered in a command: this results in
2895 having an entrypath in the wrong syntax when later used in CWD.
2896 The method used here is to check the server OS: we do it only
2897 if the path name looks strange to minimize overhead on other
2898 systems. */
2899 char *dir = Curl_dyn_ptr(&out);
2900
2901 if(!ftpc->server_os && dir[0] != '/') {
2902 result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
2903 if(result) {
2904 free(dir);
2905 return result;
2906 }
2907 Curl_safefree(ftpc->entrypath);
2908 ftpc->entrypath = dir; /* remember this */
2909 infof(data, "Entry path is '%s'", ftpc->entrypath);
2910 /* also save it where getinfo can access it: */
2911 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2912 ftp_state(data, FTP_SYST);
2913 break;
2914 }
2915
2916 Curl_safefree(ftpc->entrypath);
2917 ftpc->entrypath = dir; /* remember this */
2918 infof(data, "Entry path is '%s'", ftpc->entrypath);
2919 /* also save it where getinfo can access it: */
2920 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2921 }
2922 else {
2923 /* could not get the path */
2924 Curl_dyn_free(&out);
2925 infof(data, "Failed to figure out path");
2926 }
2927 }
2928 ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
2929 CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data));
2930 break;
2931
2932 case FTP_SYST:
2933 if(ftpcode == 215) {
2934 char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
2935 letter */
2936 char *os;
2937 char *start;
2938
2939 /* Reply format is like
2940 215<space><OS-name><space><commentary>
2941 */
2942 while(*ptr == ' ')
2943 ptr++;
2944 for(start = ptr; *ptr && *ptr != ' '; ptr++)
2945 ;
2946 os = Curl_memdup0(start, ptr - start);
2947 if(!os)
2948 return CURLE_OUT_OF_MEMORY;
2949
2950 /* Check for special servers here. */
2951 if(strcasecompare(os, "OS/400")) {
2952 /* Force OS400 name format 1. */
2953 result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
2954 if(result) {
2955 free(os);
2956 return result;
2957 }
2958 /* remember target server OS */
2959 Curl_safefree(ftpc->server_os);
2960 ftpc->server_os = os;
2961 ftp_state(data, FTP_NAMEFMT);
2962 break;
2963 }
2964 /* Nothing special for the target server. */
2965 /* remember target server OS */
2966 Curl_safefree(ftpc->server_os);
2967 ftpc->server_os = os;
2968 }
2969 else {
2970 /* Cannot identify server OS. Continue anyway and cross fingers. */
2971 }
2972
2973 ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
2974 CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data));
2975 break;
2976
2977 case FTP_NAMEFMT:
2978 if(ftpcode == 250) {
2979 /* Name format change successful: reload initial path. */
2980 ftp_state_pwd(data, conn);
2981 break;
2982 }
2983
2984 ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
2985 CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data));
2986 break;
2987
2988 case FTP_QUOTE:
2989 case FTP_POSTQUOTE:
2990 case FTP_RETR_PREQUOTE:
2991 case FTP_STOR_PREQUOTE:
2992 if((ftpcode >= 400) && !ftpc->count2) {
2993 /* failure response code, and not allowed to fail */
2994 failf(data, "QUOT command failed with %03d", ftpcode);
2995 result = CURLE_QUOTE_ERROR;
2996 }
2997 else
2998 result = ftp_state_quote(data, FALSE, ftpc->state);
2999 break;
3000
3001 case FTP_CWD:
3002 if(ftpcode/100 != 2) {
3003 /* failure to CWD there */
3004 if(data->set.ftp_create_missing_dirs &&
3005 ftpc->cwdcount && !ftpc->count2) {
3006 /* try making it */
3007 ftpc->count2++; /* counter to prevent CWD-MKD loops */
3008
3009 /* count3 is set to allow MKD to fail once per dir. In the case when
3010 CWD fails and then MKD fails (due to another session raced it to
3011 create the dir) this then allows for a second try to CWD to it. */
3012 ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0;
3013
3014 result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s",
3015 ftpc->dirs[ftpc->cwdcount - 1]);
3016 if(!result)
3017 ftp_state(data, FTP_MKD);
3018 }
3019 else {
3020 /* return failure */
3021 failf(data, "Server denied you to change to the given directory");
3022 ftpc->cwdfail = TRUE; /* do not remember this path as we failed
3023 to enter it */
3024 result = CURLE_REMOTE_ACCESS_DENIED;
3025 }
3026 }
3027 else {
3028 /* success */
3029 ftpc->count2 = 0;
3030 if(++ftpc->cwdcount <= ftpc->dirdepth)
3031 /* send next CWD */
3032 result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
3033 ftpc->dirs[ftpc->cwdcount - 1]);
3034 else
3035 result = ftp_state_mdtm(data);
3036 }
3037 break;
3038
3039 case FTP_MKD:
3040 if((ftpcode/100 != 2) && !ftpc->count3--) {
3041 /* failure to MKD the dir */
3042 failf(data, "Failed to MKD dir: %03d", ftpcode);
3043 result = CURLE_REMOTE_ACCESS_DENIED;
3044 }
3045 else {
3046 ftp_state(data, FTP_CWD);
3047 /* send CWD */
3048 result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
3049 ftpc->dirs[ftpc->cwdcount - 1]);
3050 }
3051 break;
3052
3053 case FTP_MDTM:
3054 result = ftp_state_mdtm_resp(data, ftpcode);
3055 break;
3056
3057 case FTP_TYPE:
3058 case FTP_LIST_TYPE:
3059 case FTP_RETR_TYPE:
3060 case FTP_STOR_TYPE:
3061 result = ftp_state_type_resp(data, ftpcode, ftpc->state);
3062 break;
3063
3064 case FTP_SIZE:
3065 case FTP_RETR_SIZE:
3066 case FTP_STOR_SIZE:
3067 result = ftp_state_size_resp(data, ftpcode, ftpc->state);
3068 break;
3069
3070 case FTP_REST:
3071 case FTP_RETR_REST:
3072 result = ftp_state_rest_resp(data, conn, ftpcode, ftpc->state);
3073 break;
3074
3075 case FTP_PRET:
3076 if(ftpcode != 200) {
3077 /* there only is this one standard OK return code. */
3078 failf(data, "PRET command not accepted: %03d", ftpcode);
3079 return CURLE_FTP_PRET_FAILED;
3080 }
3081 result = ftp_state_use_pasv(data, conn);
3082 break;
3083
3084 case FTP_PASV:
3085 result = ftp_state_pasv_resp(data, ftpcode);
3086 break;
3087
3088 case FTP_PORT:
3089 result = ftp_state_port_resp(data, ftpcode);
3090 break;
3091
3092 case FTP_LIST:
3093 case FTP_RETR:
3094 result = ftp_state_get_resp(data, ftpcode, ftpc->state);
3095 break;
3096
3097 case FTP_STOR:
3098 result = ftp_state_stor_resp(data, ftpcode, ftpc->state);
3099 break;
3100
3101 case FTP_QUIT:
3102 default:
3103 /* internal error */
3104 ftp_state(data, FTP_STOP);
3105 break;
3106 }
3107 } /* if(ftpcode) */
3108
3109 return result;
3110 }
3111
3112
3113 /* called repeatedly until done from multi.c */
ftp_multi_statemach(struct Curl_easy * data,bool * done)3114 static CURLcode ftp_multi_statemach(struct Curl_easy *data,
3115 bool *done)
3116 {
3117 struct connectdata *conn = data->conn;
3118 struct ftp_conn *ftpc = &conn->proto.ftpc;
3119 CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE);
3120
3121 /* Check for the state outside of the Curl_socket_check() return code checks
3122 since at times we are in fact already in this state when this function
3123 gets called. */
3124 *done = (ftpc->state == FTP_STOP);
3125
3126 return result;
3127 }
3128
ftp_block_statemach(struct Curl_easy * data,struct connectdata * conn)3129 static CURLcode ftp_block_statemach(struct Curl_easy *data,
3130 struct connectdata *conn)
3131 {
3132 struct ftp_conn *ftpc = &conn->proto.ftpc;
3133 struct pingpong *pp = &ftpc->pp;
3134 CURLcode result = CURLE_OK;
3135
3136 while(ftpc->state != FTP_STOP) {
3137 result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
3138 if(result)
3139 break;
3140 }
3141
3142 return result;
3143 }
3144
3145 /*
3146 * ftp_connect() should do everything that is to be considered a part of
3147 * the connection phase.
3148 *
3149 * The variable 'done' points to will be TRUE if the protocol-layer connect
3150 * phase is done when this function returns, or FALSE if not.
3151 *
3152 */
ftp_connect(struct Curl_easy * data,bool * done)3153 static CURLcode ftp_connect(struct Curl_easy *data,
3154 bool *done) /* see description above */
3155 {
3156 CURLcode result;
3157 struct connectdata *conn = data->conn;
3158 struct ftp_conn *ftpc = &conn->proto.ftpc;
3159 struct pingpong *pp = &ftpc->pp;
3160
3161 *done = FALSE; /* default to not done yet */
3162
3163 /* We always support persistent connections on ftp */
3164 connkeep(conn, "FTP default");
3165
3166 PINGPONG_SETUP(pp, ftp_statemachine, ftp_endofresp);
3167
3168 if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
3169 /* BLOCKING */
3170 result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
3171 if(result)
3172 return result;
3173 conn->bits.ftp_use_control_ssl = TRUE;
3174 }
3175
3176 Curl_pp_init(pp); /* once per transfer */
3177
3178 /* When we connect, we start in the state where we await the 220
3179 response */
3180 ftp_state(data, FTP_WAIT220);
3181
3182 result = ftp_multi_statemach(data, done);
3183
3184 return result;
3185 }
3186
3187 /***********************************************************************
3188 *
3189 * ftp_done()
3190 *
3191 * The DONE function. This does what needs to be done after a single DO has
3192 * performed.
3193 *
3194 * Input argument is already checked for validity.
3195 */
ftp_done(struct Curl_easy * data,CURLcode status,bool premature)3196 static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
3197 bool premature)
3198 {
3199 struct connectdata *conn = data->conn;
3200 struct FTP *ftp = data->req.p.ftp;
3201 struct ftp_conn *ftpc = &conn->proto.ftpc;
3202 struct pingpong *pp = &ftpc->pp;
3203 ssize_t nread;
3204 int ftpcode;
3205 CURLcode result = CURLE_OK;
3206 char *rawPath = NULL;
3207 size_t pathLen = 0;
3208
3209 if(!ftp)
3210 return CURLE_OK;
3211
3212 switch(status) {
3213 case CURLE_BAD_DOWNLOAD_RESUME:
3214 case CURLE_FTP_WEIRD_PASV_REPLY:
3215 case CURLE_FTP_PORT_FAILED:
3216 case CURLE_FTP_ACCEPT_FAILED:
3217 case CURLE_FTP_ACCEPT_TIMEOUT:
3218 case CURLE_FTP_COULDNT_SET_TYPE:
3219 case CURLE_FTP_COULDNT_RETR_FILE:
3220 case CURLE_PARTIAL_FILE:
3221 case CURLE_UPLOAD_FAILED:
3222 case CURLE_REMOTE_ACCESS_DENIED:
3223 case CURLE_FILESIZE_EXCEEDED:
3224 case CURLE_REMOTE_FILE_NOT_FOUND:
3225 case CURLE_WRITE_ERROR:
3226 /* the connection stays alive fine even though this happened */
3227 case CURLE_OK: /* does not affect the control connection's status */
3228 if(!premature)
3229 break;
3230
3231 /* until we cope better with prematurely ended requests, let them
3232 * fallback as if in complete failure */
3233 FALLTHROUGH();
3234 default: /* by default, an error means the control connection is
3235 wedged and should not be used anymore */
3236 ftpc->ctl_valid = FALSE;
3237 ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3238 current path, as this connection is going */
3239 connclose(conn, "FTP ended with bad error code");
3240 result = status; /* use the already set error code */
3241 break;
3242 }
3243
3244 if(data->state.wildcardmatch) {
3245 if(data->set.chunk_end && ftpc->file) {
3246 Curl_set_in_callback(data, TRUE);
3247 data->set.chunk_end(data->set.wildcardptr);
3248 Curl_set_in_callback(data, FALSE);
3249 }
3250 ftpc->known_filesize = -1;
3251 }
3252
3253 if(!result)
3254 /* get the url-decoded "raw" path */
3255 result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen,
3256 REJECT_CTRL);
3257 if(result) {
3258 /* We can limp along anyway (and should try to since we may already be in
3259 * the error path) */
3260 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3261 connclose(conn, "FTP: out of memory!"); /* mark for connection closure */
3262 free(ftpc->prevpath);
3263 ftpc->prevpath = NULL; /* no path remembering */
3264 }
3265 else { /* remember working directory for connection reuse */
3266 if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
3267 free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */
3268 else {
3269 free(ftpc->prevpath);
3270
3271 if(!ftpc->cwdfail) {
3272 if(data->set.ftp_filemethod == FTPFILE_NOCWD)
3273 pathLen = 0; /* relative path => working directory is FTP home */
3274 else
3275 /* file is url-decoded */
3276 pathLen -= ftpc->file ? strlen(ftpc->file) : 0;
3277
3278 rawPath[pathLen] = '\0';
3279 ftpc->prevpath = rawPath;
3280 }
3281 else {
3282 free(rawPath);
3283 ftpc->prevpath = NULL; /* no path */
3284 }
3285 }
3286
3287 if(ftpc->prevpath)
3288 infof(data, "Remembering we are in dir \"%s\"", ftpc->prevpath);
3289 }
3290
3291 /* free the dir tree and file parts */
3292 freedirs(ftpc);
3293
3294 /* shut down the socket to inform the server we are done */
3295
3296 #ifdef _WIN32_WCE
3297 shutdown(conn->sock[SECONDARYSOCKET], 2); /* SD_BOTH */
3298 #endif
3299
3300 if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
3301 if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
3302 /* partial download completed */
3303 result = Curl_pp_sendf(data, pp, "%s", "ABOR");
3304 if(result) {
3305 failf(data, "Failure sending ABOR command: %s",
3306 curl_easy_strerror(result));
3307 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3308 connclose(conn, "ABOR command failed"); /* connection closure */
3309 }
3310 }
3311
3312 close_secondarysocket(data);
3313 }
3314
3315 if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid &&
3316 pp->pending_resp && !premature) {
3317 /*
3318 * Let's see what the server says about the transfer we just performed,
3319 * but lower the timeout as sometimes this connection has died while the
3320 * data has been transferred. This happens when doing through NATs etc that
3321 * abandon old silent connections.
3322 */
3323 timediff_t old_time = pp->response_time;
3324
3325 pp->response_time = 60*1000; /* give it only a minute for now */
3326 pp->response = Curl_now(); /* timeout relative now */
3327
3328 result = Curl_GetFTPResponse(data, &nread, &ftpcode);
3329
3330 pp->response_time = old_time; /* set this back to previous value */
3331
3332 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
3333 failf(data, "control connection looks dead");
3334 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3335 connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */
3336 }
3337
3338 if(result) {
3339 Curl_safefree(ftp->pathalloc);
3340 return result;
3341 }
3342
3343 if(ftpc->dont_check && data->req.maxdownload > 0) {
3344 /* we have just sent ABOR and there is no reliable way to check if it was
3345 * successful or not; we have to close the connection now */
3346 infof(data, "partial download completed, closing connection");
3347 connclose(conn, "Partial download with no ability to check");
3348 return result;
3349 }
3350
3351 if(!ftpc->dont_check) {
3352 /* 226 Transfer complete, 250 Requested file action okay, completed. */
3353 switch(ftpcode) {
3354 case 226:
3355 case 250:
3356 break;
3357 case 552:
3358 failf(data, "Exceeded storage allocation");
3359 result = CURLE_REMOTE_DISK_FULL;
3360 break;
3361 default:
3362 failf(data, "server did not report OK, got %d", ftpcode);
3363 result = CURLE_PARTIAL_FILE;
3364 break;
3365 }
3366 }
3367 }
3368
3369 if(result || premature)
3370 /* the response code from the transfer showed an error already so no
3371 use checking further */
3372 ;
3373 else if(data->state.upload) {
3374 if((-1 != data->state.infilesize) &&
3375 (data->state.infilesize != data->req.writebytecount) &&
3376 !data->set.crlf &&
3377 (ftp->transfer == PPTRANSFER_BODY)) {
3378 failf(data, "Uploaded unaligned file size (%" FMT_OFF_T
3379 " out of %" FMT_OFF_T " bytes)",
3380 data->req.writebytecount, data->state.infilesize);
3381 result = CURLE_PARTIAL_FILE;
3382 }
3383 }
3384 else {
3385 if((-1 != data->req.size) &&
3386 (data->req.size != data->req.bytecount) &&
3387 (data->req.maxdownload != data->req.bytecount)) {
3388 failf(data, "Received only partial file: %" FMT_OFF_T " bytes",
3389 data->req.bytecount);
3390 result = CURLE_PARTIAL_FILE;
3391 }
3392 else if(!ftpc->dont_check &&
3393 !data->req.bytecount &&
3394 (data->req.size > 0)) {
3395 failf(data, "No data was received");
3396 result = CURLE_FTP_COULDNT_RETR_FILE;
3397 }
3398 }
3399
3400 /* clear these for next connection */
3401 ftp->transfer = PPTRANSFER_BODY;
3402 ftpc->dont_check = FALSE;
3403
3404 /* Send any post-transfer QUOTE strings? */
3405 if(!status && !result && !premature && data->set.postquote)
3406 result = ftp_sendquote(data, conn, data->set.postquote);
3407 CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_DSTATE(data), result);
3408 Curl_safefree(ftp->pathalloc);
3409 return result;
3410 }
3411
3412 /***********************************************************************
3413 *
3414 * ftp_sendquote()
3415 *
3416 * Where a 'quote' means a list of custom commands to send to the server.
3417 * The quote list is passed as an argument.
3418 *
3419 * BLOCKING
3420 */
3421
3422 static
ftp_sendquote(struct Curl_easy * data,struct connectdata * conn,struct curl_slist * quote)3423 CURLcode ftp_sendquote(struct Curl_easy *data,
3424 struct connectdata *conn, struct curl_slist *quote)
3425 {
3426 struct curl_slist *item;
3427 struct ftp_conn *ftpc = &conn->proto.ftpc;
3428 struct pingpong *pp = &ftpc->pp;
3429
3430 item = quote;
3431 while(item) {
3432 if(item->data) {
3433 ssize_t nread;
3434 char *cmd = item->data;
3435 bool acceptfail = FALSE;
3436 CURLcode result;
3437 int ftpcode = 0;
3438
3439 /* if a command starts with an asterisk, which a legal FTP command never
3440 can, the command will be allowed to fail without it causing any
3441 aborts or cancels etc. It will cause libcurl to act as if the command
3442 is successful, whatever the server responds. */
3443
3444 if(cmd[0] == '*') {
3445 cmd++;
3446 acceptfail = TRUE;
3447 }
3448
3449 result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
3450 if(!result) {
3451 pp->response = Curl_now(); /* timeout relative now */
3452 result = Curl_GetFTPResponse(data, &nread, &ftpcode);
3453 }
3454 if(result)
3455 return result;
3456
3457 if(!acceptfail && (ftpcode >= 400)) {
3458 failf(data, "QUOT string not accepted: %s", cmd);
3459 return CURLE_QUOTE_ERROR;
3460 }
3461 }
3462
3463 item = item->next;
3464 }
3465
3466 return CURLE_OK;
3467 }
3468
3469 /***********************************************************************
3470 *
3471 * ftp_need_type()
3472 *
3473 * Returns TRUE if we in the current situation should send TYPE
3474 */
ftp_need_type(struct connectdata * conn,bool ascii_wanted)3475 static int ftp_need_type(struct connectdata *conn,
3476 bool ascii_wanted)
3477 {
3478 return conn->proto.ftpc.transfertype != (ascii_wanted ? 'A' : 'I');
3479 }
3480
3481 /***********************************************************************
3482 *
3483 * ftp_nb_type()
3484 *
3485 * Set TYPE. We only deal with ASCII or BINARY so this function
3486 * sets one of them.
3487 * If the transfer type is not sent, simulate on OK response in newstate
3488 */
ftp_nb_type(struct Curl_easy * data,struct connectdata * conn,bool ascii,ftpstate newstate)3489 static CURLcode ftp_nb_type(struct Curl_easy *data,
3490 struct connectdata *conn,
3491 bool ascii, ftpstate newstate)
3492 {
3493 struct ftp_conn *ftpc = &conn->proto.ftpc;
3494 CURLcode result;
3495 char want = (char)(ascii ? 'A' : 'I');
3496
3497 if(ftpc->transfertype == want) {
3498 ftp_state(data, newstate);
3499 return ftp_state_type_resp(data, 200, newstate);
3500 }
3501
3502 result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want);
3503 if(!result) {
3504 ftp_state(data, newstate);
3505
3506 /* keep track of our current transfer type */
3507 ftpc->transfertype = want;
3508 }
3509 return result;
3510 }
3511
3512 /***************************************************************************
3513 *
3514 * ftp_pasv_verbose()
3515 *
3516 * This function only outputs some informationals about this second connection
3517 * when we have issued a PASV command before and thus we have connected to a
3518 * possibly new IP address.
3519 *
3520 */
3521 #ifndef CURL_DISABLE_VERBOSE_STRINGS
3522 static void
ftp_pasv_verbose(struct Curl_easy * data,struct Curl_addrinfo * ai,char * newhost,int port)3523 ftp_pasv_verbose(struct Curl_easy *data,
3524 struct Curl_addrinfo *ai,
3525 char *newhost, /* ASCII version */
3526 int port)
3527 {
3528 char buf[256];
3529 Curl_printable_address(ai, buf, sizeof(buf));
3530 infof(data, "Connecting to %s (%s) port %d", newhost, buf, port);
3531 }
3532 #endif
3533
3534 /*
3535 * ftp_do_more()
3536 *
3537 * This function shall be called when the second FTP (data) connection is
3538 * connected.
3539 *
3540 * 'complete' can return 0 for incomplete, 1 for done and -1 for go back
3541 * (which basically is only for when PASV is being sent to retry a failed
3542 * EPSV).
3543 */
3544
ftp_do_more(struct Curl_easy * data,int * completep)3545 static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
3546 {
3547 struct connectdata *conn = data->conn;
3548 struct ftp_conn *ftpc = &conn->proto.ftpc;
3549 CURLcode result = CURLE_OK;
3550 bool connected = FALSE;
3551 bool complete = FALSE;
3552
3553 /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP
3554 * proxy then the state will not be valid until after that connection is
3555 * complete */
3556 struct FTP *ftp = NULL;
3557
3558 /* if the second connection has been set up, try to connect it fully
3559 * to the remote host. This may not complete at this time, for several
3560 * reasons:
3561 * - we do EPTR and the server will not connect to our listen socket
3562 * until we send more FTP commands
3563 * - an SSL filter is in place and the server will not start the TLS
3564 * handshake until we send more FTP commands
3565 */
3566 if(conn->cfilter[SECONDARYSOCKET]) {
3567 bool is_eptr = Curl_conn_is_tcp_listen(data, SECONDARYSOCKET);
3568 result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
3569 if(result || (!connected && !is_eptr &&
3570 !Curl_conn_is_ip_connected(data, SECONDARYSOCKET))) {
3571 if(result && !is_eptr && (ftpc->count1 == 0)) {
3572 *completep = -1; /* go back to DOING please */
3573 /* this is a EPSV connect failing, try PASV instead */
3574 return ftp_epsv_disable(data, conn);
3575 }
3576 *completep = (int)complete;
3577 return result;
3578 }
3579 }
3580
3581 /* Curl_proxy_connect might have moved the protocol state */
3582 ftp = data->req.p.ftp;
3583
3584 if(ftpc->state) {
3585 /* already in a state so skip the initial commands.
3586 They are only done to kickstart the do_more state */
3587 result = ftp_multi_statemach(data, &complete);
3588
3589 *completep = (int)complete;
3590
3591 /* if we got an error or if we do not wait for a data connection return
3592 immediately */
3593 if(result || !ftpc->wait_data_conn)
3594 return result;
3595
3596 /* if we reach the end of the FTP state machine here, *complete will be
3597 TRUE but so is ftpc->wait_data_conn, which says we need to wait for the
3598 data connection and therefore we are not actually complete */
3599 *completep = 0;
3600 }
3601
3602 if(ftp->transfer <= PPTRANSFER_INFO) {
3603 /* a transfer is about to take place, or if not a filename was given so we
3604 will do a SIZE on it later and then we need the right TYPE first */
3605
3606 if(ftpc->wait_data_conn) {
3607 bool serv_conned;
3608
3609 result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &serv_conned);
3610 if(result)
3611 return result; /* Failed to accept data connection */
3612
3613 if(serv_conned) {
3614 /* It looks data connection is established */
3615 ftpc->wait_data_conn = FALSE;
3616 result = InitiateTransfer(data);
3617
3618 if(result)
3619 return result;
3620
3621 *completep = 1; /* this state is now complete when the server has
3622 connected back to us */
3623 }
3624 else {
3625 result = ftp_check_ctrl_on_data_wait(data);
3626 if(result)
3627 return result;
3628 }
3629 }
3630 else if(data->state.upload) {
3631 result = ftp_nb_type(data, conn, data->state.prefer_ascii,
3632 FTP_STOR_TYPE);
3633 if(result)
3634 return result;
3635
3636 result = ftp_multi_statemach(data, &complete);
3637 *completep = (int)complete;
3638 }
3639 else {
3640 /* download */
3641 ftp->downloadsize = -1; /* unknown as of yet */
3642
3643 result = Curl_range(data);
3644
3645 if(result == CURLE_OK && data->req.maxdownload >= 0) {
3646 /* Do not check for successful transfer */
3647 ftpc->dont_check = TRUE;
3648 }
3649
3650 if(result)
3651 ;
3652 else if(data->state.list_only || !ftpc->file) {
3653 /* The specified path ends with a slash, and therefore we think this
3654 is a directory that is requested, use LIST. But before that we
3655 need to set ASCII transfer mode. */
3656
3657 /* But only if a body transfer was requested. */
3658 if(ftp->transfer == PPTRANSFER_BODY) {
3659 result = ftp_nb_type(data, conn, TRUE, FTP_LIST_TYPE);
3660 if(result)
3661 return result;
3662 }
3663 /* otherwise just fall through */
3664 }
3665 else {
3666 result = ftp_nb_type(data, conn, data->state.prefer_ascii,
3667 FTP_RETR_TYPE);
3668 if(result)
3669 return result;
3670 }
3671
3672 result = ftp_multi_statemach(data, &complete);
3673 *completep = (int)complete;
3674 }
3675 return result;
3676 }
3677
3678 /* no data to transfer */
3679 Curl_xfer_setup_nop(data);
3680
3681 if(!ftpc->wait_data_conn) {
3682 /* no waiting for the data connection so this is now complete */
3683 *completep = 1;
3684 CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_DSTATE(data),
3685 (int)result);
3686 }
3687
3688 return result;
3689 }
3690
3691
3692
3693 /***********************************************************************
3694 *
3695 * ftp_perform()
3696 *
3697 * This is the actual DO function for FTP. Get a file/directory according to
3698 * the options previously setup.
3699 */
3700
3701 static
ftp_perform(struct Curl_easy * data,bool * connected,bool * dophase_done)3702 CURLcode ftp_perform(struct Curl_easy *data,
3703 bool *connected, /* connect status after PASV / PORT */
3704 bool *dophase_done)
3705 {
3706 /* this is FTP and no proxy */
3707 CURLcode result = CURLE_OK;
3708
3709 CURL_TRC_FTP(data, "[%s] DO phase starts", FTP_DSTATE(data));
3710
3711 if(data->req.no_body) {
3712 /* requested no body means no transfer... */
3713 struct FTP *ftp = data->req.p.ftp;
3714 ftp->transfer = PPTRANSFER_INFO;
3715 }
3716
3717 *dophase_done = FALSE; /* not done yet */
3718
3719 /* start the first command in the DO phase */
3720 result = ftp_state_quote(data, TRUE, FTP_QUOTE);
3721 if(result)
3722 return result;
3723
3724 /* run the state-machine */
3725 result = ftp_multi_statemach(data, dophase_done);
3726
3727 *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET);
3728
3729 if(*connected)
3730 infof(data, "[FTP] [%s] perform, DATA connection established",
3731 FTP_DSTATE(data));
3732 else
3733 CURL_TRC_FTP(data, "[%s] perform, awaiting DATA connect",
3734 FTP_DSTATE(data));
3735
3736 if(*dophase_done)
3737 CURL_TRC_FTP(data, "[%s] DO phase is complete1", FTP_DSTATE(data));
3738
3739 return result;
3740 }
3741
wc_data_dtor(void * ptr)3742 static void wc_data_dtor(void *ptr)
3743 {
3744 struct ftp_wc *ftpwc = ptr;
3745 if(ftpwc && ftpwc->parser)
3746 Curl_ftp_parselist_data_free(&ftpwc->parser);
3747 free(ftpwc);
3748 }
3749
init_wc_data(struct Curl_easy * data)3750 static CURLcode init_wc_data(struct Curl_easy *data)
3751 {
3752 char *last_slash;
3753 struct FTP *ftp = data->req.p.ftp;
3754 char *path = ftp->path;
3755 struct WildcardData *wildcard = data->wildcard;
3756 CURLcode result = CURLE_OK;
3757 struct ftp_wc *ftpwc = NULL;
3758
3759 last_slash = strrchr(ftp->path, '/');
3760 if(last_slash) {
3761 last_slash++;
3762 if(last_slash[0] == '\0') {
3763 wildcard->state = CURLWC_CLEAN;
3764 return ftp_parse_url_path(data);
3765 }
3766 wildcard->pattern = strdup(last_slash);
3767 if(!wildcard->pattern)
3768 return CURLE_OUT_OF_MEMORY;
3769 last_slash[0] = '\0'; /* cut file from path */
3770 }
3771 else { /* there is only 'wildcard pattern' or nothing */
3772 if(path[0]) {
3773 wildcard->pattern = strdup(path);
3774 if(!wildcard->pattern)
3775 return CURLE_OUT_OF_MEMORY;
3776 path[0] = '\0';
3777 }
3778 else { /* only list */
3779 wildcard->state = CURLWC_CLEAN;
3780 return ftp_parse_url_path(data);
3781 }
3782 }
3783
3784 /* program continues only if URL is not ending with slash, allocate needed
3785 resources for wildcard transfer */
3786
3787 /* allocate ftp protocol specific wildcard data */
3788 ftpwc = calloc(1, sizeof(struct ftp_wc));
3789 if(!ftpwc) {
3790 result = CURLE_OUT_OF_MEMORY;
3791 goto fail;
3792 }
3793
3794 /* INITIALIZE parselist structure */
3795 ftpwc->parser = Curl_ftp_parselist_data_alloc();
3796 if(!ftpwc->parser) {
3797 result = CURLE_OUT_OF_MEMORY;
3798 goto fail;
3799 }
3800
3801 wildcard->ftpwc = ftpwc; /* put it to the WildcardData tmp pointer */
3802 wildcard->dtor = wc_data_dtor;
3803
3804 /* wildcard does not support NOCWD option (assert it?) */
3805 if(data->set.ftp_filemethod == FTPFILE_NOCWD)
3806 data->set.ftp_filemethod = FTPFILE_MULTICWD;
3807
3808 /* try to parse ftp URL */
3809 result = ftp_parse_url_path(data);
3810 if(result) {
3811 goto fail;
3812 }
3813
3814 wildcard->path = strdup(ftp->path);
3815 if(!wildcard->path) {
3816 result = CURLE_OUT_OF_MEMORY;
3817 goto fail;
3818 }
3819
3820 /* backup old write_function */
3821 ftpwc->backup.write_function = data->set.fwrite_func;
3822 /* parsing write function */
3823 data->set.fwrite_func = Curl_ftp_parselist;
3824 /* backup old file descriptor */
3825 ftpwc->backup.file_descriptor = data->set.out;
3826 /* let the writefunc callback know the transfer */
3827 data->set.out = data;
3828
3829 infof(data, "Wildcard - Parsing started");
3830 return CURLE_OK;
3831
3832 fail:
3833 if(ftpwc) {
3834 Curl_ftp_parselist_data_free(&ftpwc->parser);
3835 free(ftpwc);
3836 }
3837 Curl_safefree(wildcard->pattern);
3838 wildcard->dtor = ZERO_NULL;
3839 wildcard->ftpwc = NULL;
3840 return result;
3841 }
3842
wc_statemach(struct Curl_easy * data)3843 static CURLcode wc_statemach(struct Curl_easy *data)
3844 {
3845 struct WildcardData * const wildcard = data->wildcard;
3846 struct connectdata *conn = data->conn;
3847 CURLcode result = CURLE_OK;
3848
3849 for(;;) {
3850 switch(wildcard->state) {
3851 case CURLWC_INIT:
3852 result = init_wc_data(data);
3853 if(wildcard->state == CURLWC_CLEAN)
3854 /* only listing! */
3855 return result;
3856 wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
3857 return result;
3858
3859 case CURLWC_MATCHING: {
3860 /* In this state is LIST response successfully parsed, so lets restore
3861 previous WRITEFUNCTION callback and WRITEDATA pointer */
3862 struct ftp_wc *ftpwc = wildcard->ftpwc;
3863 data->set.fwrite_func = ftpwc->backup.write_function;
3864 data->set.out = ftpwc->backup.file_descriptor;
3865 ftpwc->backup.write_function = ZERO_NULL;
3866 ftpwc->backup.file_descriptor = NULL;
3867 wildcard->state = CURLWC_DOWNLOADING;
3868
3869 if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
3870 /* error found in LIST parsing */
3871 wildcard->state = CURLWC_CLEAN;
3872 continue;
3873 }
3874 if(Curl_llist_count(&wildcard->filelist) == 0) {
3875 /* no corresponding file */
3876 wildcard->state = CURLWC_CLEAN;
3877 return CURLE_REMOTE_FILE_NOT_FOUND;
3878 }
3879 continue;
3880 }
3881
3882 case CURLWC_DOWNLOADING: {
3883 /* filelist has at least one file, lets get first one */
3884 struct ftp_conn *ftpc = &conn->proto.ftpc;
3885 struct Curl_llist_node *head = Curl_llist_head(&wildcard->filelist);
3886 struct curl_fileinfo *finfo = Curl_node_elem(head);
3887 struct FTP *ftp = data->req.p.ftp;
3888
3889 char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
3890 if(!tmp_path)
3891 return CURLE_OUT_OF_MEMORY;
3892
3893 /* switch default ftp->path and tmp_path */
3894 free(ftp->pathalloc);
3895 ftp->pathalloc = ftp->path = tmp_path;
3896
3897 infof(data, "Wildcard - START of \"%s\"", finfo->filename);
3898 if(data->set.chunk_bgn) {
3899 long userresponse;
3900 Curl_set_in_callback(data, TRUE);
3901 userresponse = data->set.chunk_bgn(
3902 finfo, data->set.wildcardptr,
3903 (int)Curl_llist_count(&wildcard->filelist));
3904 Curl_set_in_callback(data, FALSE);
3905 switch(userresponse) {
3906 case CURL_CHUNK_BGN_FUNC_SKIP:
3907 infof(data, "Wildcard - \"%s\" skipped by user",
3908 finfo->filename);
3909 wildcard->state = CURLWC_SKIP;
3910 continue;
3911 case CURL_CHUNK_BGN_FUNC_FAIL:
3912 return CURLE_CHUNK_FAILED;
3913 }
3914 }
3915
3916 if(finfo->filetype != CURLFILETYPE_FILE) {
3917 wildcard->state = CURLWC_SKIP;
3918 continue;
3919 }
3920
3921 if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
3922 ftpc->known_filesize = finfo->size;
3923
3924 result = ftp_parse_url_path(data);
3925 if(result)
3926 return result;
3927
3928 /* we do not need the Curl_fileinfo of first file anymore */
3929 Curl_node_remove(Curl_llist_head(&wildcard->filelist));
3930
3931 if(Curl_llist_count(&wildcard->filelist) == 0) {
3932 /* remains only one file to down. */
3933 wildcard->state = CURLWC_CLEAN;
3934 /* after that will be ftp_do called once again and no transfer
3935 will be done because of CURLWC_CLEAN state */
3936 return CURLE_OK;
3937 }
3938 return result;
3939 }
3940
3941 case CURLWC_SKIP: {
3942 if(data->set.chunk_end) {
3943 Curl_set_in_callback(data, TRUE);
3944 data->set.chunk_end(data->set.wildcardptr);
3945 Curl_set_in_callback(data, FALSE);
3946 }
3947 Curl_node_remove(Curl_llist_head(&wildcard->filelist));
3948 wildcard->state = (Curl_llist_count(&wildcard->filelist) == 0) ?
3949 CURLWC_CLEAN : CURLWC_DOWNLOADING;
3950 continue;
3951 }
3952
3953 case CURLWC_CLEAN: {
3954 struct ftp_wc *ftpwc = wildcard->ftpwc;
3955 result = CURLE_OK;
3956 if(ftpwc)
3957 result = Curl_ftp_parselist_geterror(ftpwc->parser);
3958
3959 wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
3960 return result;
3961 }
3962
3963 case CURLWC_DONE:
3964 case CURLWC_ERROR:
3965 case CURLWC_CLEAR:
3966 if(wildcard->dtor) {
3967 wildcard->dtor(wildcard->ftpwc);
3968 wildcard->ftpwc = NULL;
3969 }
3970 return result;
3971 }
3972 }
3973 /* UNREACHABLE */
3974 }
3975
3976 /***********************************************************************
3977 *
3978 * ftp_do()
3979 *
3980 * This function is registered as 'curl_do' function. It decodes the path
3981 * parts etc as a wrapper to the actual DO function (ftp_perform).
3982 *
3983 * The input argument is already checked for validity.
3984 */
ftp_do(struct Curl_easy * data,bool * done)3985 static CURLcode ftp_do(struct Curl_easy *data, bool *done)
3986 {
3987 CURLcode result = CURLE_OK;
3988 struct connectdata *conn = data->conn;
3989 struct ftp_conn *ftpc = &conn->proto.ftpc;
3990
3991 *done = FALSE; /* default to false */
3992 ftpc->wait_data_conn = FALSE; /* default to no such wait */
3993
3994 #ifdef CURL_PREFER_LF_LINEENDS
3995 {
3996 /* FTP data may need conversion. */
3997 struct Curl_cwriter *ftp_lc_writer;
3998
3999 result = Curl_cwriter_create(&ftp_lc_writer, data, &ftp_cw_lc,
4000 CURL_CW_CONTENT_DECODE);
4001 if(result)
4002 return result;
4003
4004 result = Curl_cwriter_add(data, ftp_lc_writer);
4005 if(result) {
4006 Curl_cwriter_free(data, ftp_lc_writer);
4007 return result;
4008 }
4009 }
4010 #endif /* CURL_PREFER_LF_LINEENDS */
4011
4012 if(data->state.wildcardmatch) {
4013 result = wc_statemach(data);
4014 if(data->wildcard->state == CURLWC_SKIP ||
4015 data->wildcard->state == CURLWC_DONE) {
4016 /* do not call ftp_regular_transfer */
4017 return CURLE_OK;
4018 }
4019 if(result) /* error, loop or skipping the file */
4020 return result;
4021 }
4022 else { /* no wildcard FSM needed */
4023 result = ftp_parse_url_path(data);
4024 if(result)
4025 return result;
4026 }
4027
4028 result = ftp_regular_transfer(data, done);
4029
4030 return result;
4031 }
4032
4033 /***********************************************************************
4034 *
4035 * ftp_quit()
4036 *
4037 * This should be called before calling sclose() on an ftp control connection
4038 * (not data connections). We should then wait for the response from the
4039 * server before returning. The calling code should then try to close the
4040 * connection.
4041 *
4042 */
ftp_quit(struct Curl_easy * data,struct connectdata * conn)4043 static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn)
4044 {
4045 CURLcode result = CURLE_OK;
4046
4047 if(conn->proto.ftpc.ctl_valid) {
4048 result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT");
4049 if(result) {
4050 failf(data, "Failure sending QUIT command: %s",
4051 curl_easy_strerror(result));
4052 conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
4053 connclose(conn, "QUIT command failed"); /* mark for connection closure */
4054 ftp_state(data, FTP_STOP);
4055 return result;
4056 }
4057
4058 ftp_state(data, FTP_QUIT);
4059
4060 result = ftp_block_statemach(data, conn);
4061 }
4062
4063 return result;
4064 }
4065
4066 /***********************************************************************
4067 *
4068 * ftp_disconnect()
4069 *
4070 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
4071 * resources. BLOCKING.
4072 */
ftp_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)4073 static CURLcode ftp_disconnect(struct Curl_easy *data,
4074 struct connectdata *conn,
4075 bool dead_connection)
4076 {
4077 struct ftp_conn *ftpc = &conn->proto.ftpc;
4078 struct pingpong *pp = &ftpc->pp;
4079
4080 /* We cannot send quit unconditionally. If this connection is stale or
4081 bad in any way, sending quit and waiting around here will make the
4082 disconnect wait in vain and cause more problems than we need to.
4083
4084 ftp_quit() will check the state of ftp->ctl_valid. If it is ok it
4085 will try to send the QUIT command, otherwise it will just return.
4086 */
4087 if(dead_connection)
4088 ftpc->ctl_valid = FALSE;
4089
4090 /* The FTP session may or may not have been allocated/setup at this point! */
4091 (void)ftp_quit(data, conn); /* ignore errors on the QUIT */
4092
4093 if(ftpc->entrypath) {
4094 if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
4095 data->state.most_recent_ftp_entrypath = NULL;
4096 }
4097 Curl_safefree(ftpc->entrypath);
4098 }
4099
4100 freedirs(ftpc);
4101 Curl_safefree(ftpc->account);
4102 Curl_safefree(ftpc->alternative_to_user);
4103 Curl_safefree(ftpc->prevpath);
4104 Curl_safefree(ftpc->server_os);
4105 Curl_pp_disconnect(pp);
4106 Curl_sec_end(conn);
4107 return CURLE_OK;
4108 }
4109
4110 #ifdef _MSC_VER
4111 #pragma warning(push)
4112 #pragma warning(disable:4706) /* assignment within conditional expression */
4113 #endif
4114
4115 /***********************************************************************
4116 *
4117 * ftp_parse_url_path()
4118 *
4119 * Parse the URL path into separate path components.
4120 *
4121 */
4122 static
ftp_parse_url_path(struct Curl_easy * data)4123 CURLcode ftp_parse_url_path(struct Curl_easy *data)
4124 {
4125 /* the ftp struct is already inited in ftp_connect() */
4126 struct FTP *ftp = data->req.p.ftp;
4127 struct connectdata *conn = data->conn;
4128 struct ftp_conn *ftpc = &conn->proto.ftpc;
4129 const char *slashPos = NULL;
4130 const char *fileName = NULL;
4131 CURLcode result = CURLE_OK;
4132 char *rawPath = NULL; /* url-decoded "raw" path */
4133 size_t pathLen = 0;
4134
4135 ftpc->ctl_valid = FALSE;
4136 ftpc->cwdfail = FALSE;
4137
4138 /* url-decode ftp path before further evaluation */
4139 result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL);
4140 if(result) {
4141 failf(data, "path contains control characters");
4142 return result;
4143 }
4144
4145 switch(data->set.ftp_filemethod) {
4146 case FTPFILE_NOCWD: /* fastest, but less standard-compliant */
4147
4148 if((pathLen > 0) && (rawPath[pathLen - 1] != '/'))
4149 fileName = rawPath; /* this is a full file path */
4150 /*
4151 else: ftpc->file is not used anywhere other than for operations on
4152 a file. In other words, never for directory operations.
4153 So we can safely leave filename as NULL here and use it as a
4154 argument in dir/file decisions.
4155 */
4156 break;
4157
4158 case FTPFILE_SINGLECWD:
4159 slashPos = strrchr(rawPath, '/');
4160 if(slashPos) {
4161 /* get path before last slash, except for / */
4162 size_t dirlen = slashPos - rawPath;
4163 if(dirlen == 0)
4164 dirlen = 1;
4165
4166 ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
4167 if(!ftpc->dirs) {
4168 free(rawPath);
4169 return CURLE_OUT_OF_MEMORY;
4170 }
4171
4172 ftpc->dirs[0] = Curl_memdup0(rawPath, dirlen);
4173 if(!ftpc->dirs[0]) {
4174 free(rawPath);
4175 return CURLE_OUT_OF_MEMORY;
4176 }
4177
4178 ftpc->dirdepth = 1; /* we consider it to be a single dir */
4179 fileName = slashPos + 1; /* rest is filename */
4180 }
4181 else
4182 fileName = rawPath; /* filename only (or empty) */
4183 break;
4184
4185 default: /* allow pretty much anything */
4186 case FTPFILE_MULTICWD: {
4187 /* current position: begin of next path component */
4188 const char *curPos = rawPath;
4189
4190 /* number of entries allocated for the 'dirs' array */
4191 size_t dirAlloc = 0;
4192 const char *str = rawPath;
4193 for(; *str != 0; ++str)
4194 if(*str == '/')
4195 ++dirAlloc;
4196
4197 if(dirAlloc) {
4198 ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0]));
4199 if(!ftpc->dirs) {
4200 free(rawPath);
4201 return CURLE_OUT_OF_MEMORY;
4202 }
4203
4204 /* parse the URL path into separate path components */
4205 while((slashPos = strchr(curPos, '/'))) {
4206 size_t compLen = slashPos - curPos;
4207
4208 /* path starts with a slash: add that as a directory */
4209 if((compLen == 0) && (ftpc->dirdepth == 0))
4210 ++compLen;
4211
4212 /* we skip empty path components, like "x//y" since the FTP command
4213 CWD requires a parameter and a non-existent parameter a) does not
4214 work on many servers and b) has no effect on the others. */
4215 if(compLen > 0) {
4216 char *comp = Curl_memdup0(curPos, compLen);
4217 if(!comp) {
4218 free(rawPath);
4219 return CURLE_OUT_OF_MEMORY;
4220 }
4221 ftpc->dirs[ftpc->dirdepth++] = comp;
4222 }
4223 curPos = slashPos + 1;
4224 }
4225 }
4226 DEBUGASSERT((size_t)ftpc->dirdepth <= dirAlloc);
4227 fileName = curPos; /* the rest is the filename (or empty) */
4228 }
4229 break;
4230 } /* switch */
4231
4232 if(fileName && *fileName)
4233 ftpc->file = strdup(fileName);
4234 else
4235 ftpc->file = NULL; /* instead of point to a zero byte,
4236 we make it a NULL pointer */
4237
4238 if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) {
4239 /* We need a filename when uploading. Return error! */
4240 failf(data, "Uploading to a URL without a filename");
4241 free(rawPath);
4242 return CURLE_URL_MALFORMAT;
4243 }
4244
4245 ftpc->cwddone = FALSE; /* default to not done */
4246
4247 if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
4248 ftpc->cwddone = TRUE; /* skip CWD for absolute paths */
4249 else { /* newly created FTP connections are already in entry path */
4250 const char *oldPath = conn->bits.reuse ? ftpc->prevpath : "";
4251 if(oldPath) {
4252 size_t n = pathLen;
4253 if(data->set.ftp_filemethod == FTPFILE_NOCWD)
4254 n = 0; /* CWD to entry for relative paths */
4255 else
4256 n -= ftpc->file ? strlen(ftpc->file) : 0;
4257
4258 if((strlen(oldPath) == n) && rawPath && !strncmp(rawPath, oldPath, n)) {
4259 infof(data, "Request has same path as previous transfer");
4260 ftpc->cwddone = TRUE;
4261 }
4262 }
4263 }
4264
4265 free(rawPath);
4266 return CURLE_OK;
4267 }
4268
4269 #ifdef _MSC_VER
4270 #pragma warning(pop)
4271 #endif
4272
4273 /* call this when the DO phase has completed */
ftp_dophase_done(struct Curl_easy * data,bool connected)4274 static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
4275 {
4276 struct connectdata *conn = data->conn;
4277 struct FTP *ftp = data->req.p.ftp;
4278 struct ftp_conn *ftpc = &conn->proto.ftpc;
4279
4280 if(connected) {
4281 int completed;
4282 CURLcode result = ftp_do_more(data, &completed);
4283
4284 if(result) {
4285 close_secondarysocket(data);
4286 return result;
4287 }
4288 }
4289
4290 if(ftp->transfer != PPTRANSFER_BODY)
4291 /* no data to transfer */
4292 Curl_xfer_setup_nop(data);
4293 else if(!connected)
4294 /* since we did not connect now, we want do_more to get called */
4295 conn->bits.do_more = TRUE;
4296
4297 ftpc->ctl_valid = TRUE; /* seems good */
4298
4299 return CURLE_OK;
4300 }
4301
4302 /* called from multi.c while DOing */
ftp_doing(struct Curl_easy * data,bool * dophase_done)4303 static CURLcode ftp_doing(struct Curl_easy *data,
4304 bool *dophase_done)
4305 {
4306 CURLcode result = ftp_multi_statemach(data, dophase_done);
4307
4308 if(result)
4309 CURL_TRC_FTP(data, "[%s] DO phase failed", FTP_DSTATE(data));
4310 else if(*dophase_done) {
4311 result = ftp_dophase_done(data, FALSE /* not connected */);
4312
4313 CURL_TRC_FTP(data, "[%s] DO phase is complete2", FTP_DSTATE(data));
4314 }
4315 return result;
4316 }
4317
4318 /***********************************************************************
4319 *
4320 * ftp_regular_transfer()
4321 *
4322 * The input argument is already checked for validity.
4323 *
4324 * Performs all commands done before a regular transfer between a local and a
4325 * remote host.
4326 *
4327 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
4328 * ftp_done() function without finding any major problem.
4329 */
4330 static
ftp_regular_transfer(struct Curl_easy * data,bool * dophase_done)4331 CURLcode ftp_regular_transfer(struct Curl_easy *data,
4332 bool *dophase_done)
4333 {
4334 CURLcode result = CURLE_OK;
4335 bool connected = FALSE;
4336 struct connectdata *conn = data->conn;
4337 struct ftp_conn *ftpc = &conn->proto.ftpc;
4338 data->req.size = -1; /* make sure this is unknown at this point */
4339
4340 Curl_pgrsSetUploadCounter(data, 0);
4341 Curl_pgrsSetDownloadCounter(data, 0);
4342 Curl_pgrsSetUploadSize(data, -1);
4343 Curl_pgrsSetDownloadSize(data, -1);
4344
4345 ftpc->ctl_valid = TRUE; /* starts good */
4346
4347 result = ftp_perform(data,
4348 &connected, /* have we connected after PASV/PORT */
4349 dophase_done); /* all commands in the DO-phase done? */
4350
4351 if(!result) {
4352
4353 if(!*dophase_done)
4354 /* the DO phase has not completed yet */
4355 return CURLE_OK;
4356
4357 result = ftp_dophase_done(data, connected);
4358
4359 if(result)
4360 return result;
4361 }
4362 else
4363 freedirs(ftpc);
4364
4365 return result;
4366 }
4367
ftp_setup_connection(struct Curl_easy * data,struct connectdata * conn)4368 static CURLcode ftp_setup_connection(struct Curl_easy *data,
4369 struct connectdata *conn)
4370 {
4371 char *type;
4372 struct FTP *ftp;
4373 CURLcode result = CURLE_OK;
4374 struct ftp_conn *ftpc = &conn->proto.ftpc;
4375
4376 ftp = calloc(1, sizeof(struct FTP));
4377 if(!ftp)
4378 return CURLE_OUT_OF_MEMORY;
4379
4380 /* clone connection related data that is FTP specific */
4381 if(data->set.str[STRING_FTP_ACCOUNT]) {
4382 ftpc->account = strdup(data->set.str[STRING_FTP_ACCOUNT]);
4383 if(!ftpc->account) {
4384 free(ftp);
4385 return CURLE_OUT_OF_MEMORY;
4386 }
4387 }
4388 if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]) {
4389 ftpc->alternative_to_user =
4390 strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
4391 if(!ftpc->alternative_to_user) {
4392 Curl_safefree(ftpc->account);
4393 free(ftp);
4394 return CURLE_OUT_OF_MEMORY;
4395 }
4396 }
4397 data->req.p.ftp = ftp;
4398
4399 ftp->path = &data->state.up.path[1]; /* do not include the initial slash */
4400
4401 /* FTP URLs support an extension like ";type=<typecode>" that
4402 * we will try to get now! */
4403 type = strstr(ftp->path, ";type=");
4404
4405 if(!type)
4406 type = strstr(conn->host.rawalloc, ";type=");
4407
4408 if(type) {
4409 char command;
4410 *type = 0; /* it was in the middle of the hostname */
4411 command = Curl_raw_toupper(type[6]);
4412
4413 switch(command) {
4414 case 'A': /* ASCII mode */
4415 data->state.prefer_ascii = TRUE;
4416 break;
4417
4418 case 'D': /* directory mode */
4419 data->state.list_only = TRUE;
4420 break;
4421
4422 case 'I': /* binary mode */
4423 default:
4424 /* switch off ASCII */
4425 data->state.prefer_ascii = FALSE;
4426 break;
4427 }
4428 }
4429
4430 /* get some initial data into the ftp struct */
4431 ftp->transfer = PPTRANSFER_BODY;
4432 ftp->downloadsize = 0;
4433 ftpc->known_filesize = -1; /* unknown size for now */
4434 ftpc->use_ssl = data->set.use_ssl;
4435 ftpc->ccc = data->set.ftp_ccc;
4436
4437 CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(conn), result);
4438 return result;
4439 }
4440
4441 #endif /* CURL_DISABLE_FTP */
4442