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