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