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