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