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