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