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