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