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