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