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