Coverage Report

Created: 2026-07-30 07:03

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