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