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