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