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